# Pastebin d1w5VsPA diff --git a/system/user.py b/system/user.py index f1bd8da..9044092 100755 --- a/system/user.py +++ b/system/user.py @@ -21,7 +21,7 @@ DOCUMENTATION = ''' --- module: user -author: "Stephen Fromm (@sfromm)" +author: Stephen Fromm version_added: "0.2" short_description: Manage user accounts requirements: [ useradd, userdel, usermod ] @@ -74,18 +74,13 @@ options: required: false description: - Optionally set the user's home directory. - skeleton: - required: false - description: - - Optionally set a home skeleton directory. Requires createhome option! - version_added: "2.0" password: required: false description: - Optionally set the user's password to this crypted value. See the user example in the github examples directory for what this looks - like in a playbook. See U(http://docs.ansible.com/ansible/faq.html#how-do-i-generate-crypted-passwords-for-the-user-module) - for details on various ways to generate these password values. + like in a playbook. The `FAQ `_ + contains details on various ways to generate these password values. Note on Darwin system, this value has to be cleartext. Beware of security issues. state: @@ -213,6 +208,7 @@ EXAMPLES = ''' import os import pwd import grp +import syslog import platform import socket import time @@ -257,13 +253,13 @@ class User(object): self.group = module.params['group'] self.groups = module.params['groups'] self.comment = module.params['comment'] + self.home = module.params['home'] self.shell = module.params['shell'] self.password = module.params['password'] self.force = module.params['force'] self.remove = module.params['remove'] self.createhome = module.params['createhome'] self.move_home = module.params['move_home'] - self.skeleton = module.params['skeleton'] self.system = module.params['system'] self.login_class = module.params['login_class'] self.append = module.params['append'] @@ -273,12 +269,8 @@ class User(object): self.ssh_comment = module.params['ssh_key_comment'] self.ssh_passphrase = module.params['ssh_key_passphrase'] self.update_password = module.params['update_password'] - self.home = None self.expires = None - if module.params['home'] is not None: - self.home = os.path.expanduser(module.params['home']) - if module.params['expires']: try: self.expires = time.gmtime(module.params['expires']) @@ -290,13 +282,16 @@ class User(object): else: self.ssh_file = os.path.join('.ssh', 'id_%s' % self.ssh_type) + # select whether we dump additional debug info through syslog + self.syslogging = False - def execute_command(self, cmd, use_unsafe_shell=False, data=None, obey_checkmode=True): - if self.module.check_mode and obey_checkmode: - self.module.debug('In check mode, would have run: "%s"' % cmd) - return (0, '','') - else: - return self.module.run_command(cmd, use_unsafe_shell=use_unsafe_shell, data=data) + + def execute_command(self, cmd, use_unsafe_shell=False, data=None): + if self.syslogging: + syslog.openlog('ansible-%s' % os.path.basename(__file__)) + syslog.syslog(syslog.LOG_NOTICE, 'Command %s' % '|'.join(cmd)) + + return self.module.run_command(cmd, use_unsafe_shell=use_unsafe_shell, data=data) def remove_user_userdel(self): cmd = [self.module.get_bin_path('userdel', True)] @@ -365,10 +360,6 @@ class User(object): if self.createhome: cmd.append('-m') - - if self.skeleton is not None: - cmd.append('-k') - cmd.append(self.skeleton) else: cmd.append('-M') @@ -388,8 +379,9 @@ class User(object): if not os.access(usermod_path, os.X_OK): return False - cmd = [usermod_path, '--help'] - (rc, data1, data2) = self.execute_command(cmd, obey_checkmode=False) + cmd = [usermod_path] + cmd.append('--help') + rc, data1, data2 = self.execute_command(cmd) helpout = data1 + data2 # check if --append exists @@ -479,6 +471,8 @@ class User(object): # skip if no changes to be made if len(cmd) == 1: return (None, '', '') + elif self.module.check_mode: + return (0, '', '') cmd.append(self.name) return self.execute_command(cmd) @@ -571,13 +565,11 @@ class User(object): def ssh_key_gen(self): info = self.user_info() - if not os.path.exists(info[5]) and not self.module.check_mode: + if not os.path.exists(info[5]): return (1, '', 'User %s home directory does not exist' % self.name) ssh_key_file = self.get_ssh_key_path() ssh_dir = os.path.dirname(ssh_key_file) if not os.path.exists(ssh_dir): - if self.module.check_mode: - return (0, '', '') try: os.mkdir(ssh_dir, 0700) os.chown(ssh_dir, info[2], info[3]) @@ -601,7 +593,7 @@ class User(object): cmd.append('') (rc, out, err) = self.execute_command(cmd) - if rc == 0 and not self.module.check_mode: + if rc == 0: # If the keys were successfully created, we should be able # to tweak ownership. os.chown(ssh_key_file, info[2], info[3]) @@ -617,7 +609,7 @@ class User(object): cmd.append('-f') cmd.append(ssh_key_file) - return self.execute_command(cmd, obey_checkmode=False) + return self.execute_command(cmd) def get_ssh_public_key(self): ssh_public_key_file = '%s.pub' % self.get_ssh_key_path() @@ -643,14 +635,10 @@ class User(object): def create_homedir(self, path): if not os.path.exists(path): - if self.skeleton is not None: - skeleton = self.skeleton - else: - skeleton = '/etc/skel' - - if os.path.exists(skeleton): + # use /etc/skel if possible + if os.path.exists('/etc/skel'): try: - shutil.copytree(skeleton, path, symlinks=True) + shutil.copytree('/etc/skel', path, symlinks=True) except OSError, e: self.module.exit_json(failed=True, msg="%s" % e) else: @@ -738,10 +726,6 @@ class FreeBsdUser(User): if self.createhome: cmd.append('-m') - if self.skeleton is not None: - cmd.append('-k') - cmd.append(self.skeleton) - if self.shell is not None: cmd.append('-s') cmd.append(self.shell) @@ -929,17 +913,13 @@ class OpenBSDUser(User): cmd.append('-L') cmd.append(self.login_class) - if self.password is not None and self.password != '*': + if self.password is not None: cmd.append('-p') cmd.append(self.password) if self.createhome: cmd.append('-m') - if self.skeleton is not None: - cmd.append('-k') - cmd.append(self.skeleton) - cmd.append(self.name) return self.execute_command(cmd) @@ -1014,7 +994,7 @@ class OpenBSDUser(User): # find current login class user_login_class = None userinfo_cmd = [self.module.get_bin_path('userinfo', True), self.name] - (rc, out, err) = self.execute_command(userinfo_cmd, obey_checkmode=False) + (rc, out, err) = self.execute_command(userinfo_cmd) for line in out.splitlines(): tokens = line.split() @@ -1027,14 +1007,15 @@ class OpenBSDUser(User): cmd.append('-L') cmd.append(self.login_class) - if self.update_password == 'always' and self.password is not None \ - and self.password != '*' and info[1] != self.password: + if self.update_password == 'always' and self.password is not None and info[1] != self.password: cmd.append('-p') cmd.append(self.password) # skip if no changes to be made if len(cmd) == 1: return (None, '', '') + elif self.module.check_mode: + return (0, '', '') cmd.append(self.name) return self.execute_command(cmd) @@ -1106,10 +1087,6 @@ class NetBSDUser(User): if self.createhome: cmd.append('-m') - if self.skeleton is not None: - cmd.append('-k') - cmd.append(self.skeleton) - cmd.append(self.name) return self.execute_command(cmd) @@ -1192,6 +1169,8 @@ class NetBSDUser(User): # skip if no changes to be made if len(cmd) == 1: return (None, '', '') + elif self.module.check_mode: + return (0, '', '') cmd.append(self.name) return self.execute_command(cmd) @@ -1260,18 +1239,16 @@ class SunOS(User): if self.createhome: cmd.append('-m') - if self.skeleton is not None: - cmd.append('-k') - cmd.append(self.skeleton) - cmd.append(self.name) - (rc, out, err) = self.execute_command(cmd) - if rc is not None and rc != 0: - self.module.fail_json(name=self.name, msg=err, rc=rc) + if self.module.check_mode: + return (0, '', '') + else: + (rc, out, err) = self.execute_command(cmd) + if rc is not None and rc != 0: + self.module.fail_json(name=self.name, msg=err, rc=rc) - if not self.module.check_mode: - # we have to set the password by editing the /etc/shadow file + # we have to set the password by editing the /etc/shadow file if self.password is not None: try: lines = [] @@ -1288,7 +1265,7 @@ class SunOS(User): except Exception, err: self.module.fail_json(msg="failed to update users password: %s" % str(err)) - return (rc, out, err) + return (rc, out, err) def modify_user_usermod(self): cmd = [self.module.get_bin_path('usermod', True)] @@ -1346,19 +1323,20 @@ class SunOS(User): cmd.append('-s') cmd.append(self.shell) - # modify the user if cmd will do anything - if cmd_len != len(cmd): - cmd.append(self.name) - (rc, out, err) = self.execute_command(cmd) - if rc is not None and rc != 0: - self.module.fail_json(name=self.name, msg=err, rc=rc) + if self.module.check_mode: + return (0, '', '') else: - (rc, out, err) = (None, '', '') + # modify the user if cmd will do anything + if cmd_len != len(cmd): + cmd.append(self.name) + (rc, out, err) = self.execute_command(cmd) + if rc is not None and rc != 0: + self.module.fail_json(name=self.name, msg=err, rc=rc) + else: + (rc, out, err) = (None, '', '') - # we have to set the password by editing the /etc/shadow file - if self.update_password == 'always' and self.password is not None and info[1] != self.password: - (rc, out, err) = (0, '', '') - if not self.module.check_mode: + # we have to set the password by editing the /etc/shadow file + if self.update_password == 'always' and self.password is not None and info[1] != self.password: try: lines = [] for line in open(self.SHADOWFILE, 'rb').readlines(): @@ -1367,7 +1345,7 @@ class SunOS(User): lines.append(line) continue fields[1] = self.password - fields[2] = str(int(time.time() / 86400)) + fields[2] = str(int(time.time() / 86400)) line = ':'.join(fields) lines.append('%s\n' % line) open(self.SHADOWFILE, 'w+').writelines(lines) @@ -1375,7 +1353,7 @@ class SunOS(User): except Exception, err: self.module.fail_json(msg="failed to update users password: %s" % str(err)) - return (rc, out, err) + return (rc, out, err) # =========================================== class DarwinUser(User): @@ -1415,7 +1393,7 @@ class DarwinUser(User): def _list_user_groups(self): cmd = self._get_dscl() cmd += [ '-search', '/Groups', 'GroupMembership', self.name ] - (rc, out, err) = self.execute_command(cmd, obey_checkmode=False) + (rc, out, err) = self.execute_command(cmd) groups = [] for line in out.splitlines(): if line.startswith(' ') or line.startswith(')'): @@ -1427,7 +1405,7 @@ class DarwinUser(User): '''Return user PROPERTY as given my dscl(1) read or None if not found.''' cmd = self._get_dscl() cmd += [ '-read', '/Users/%s' % self.name, property ] - (rc, out, err) = self.execute_command(cmd, obey_checkmode=False) + (rc, out, err) = self.execute_command(cmd) if rc != 0: return None # from dscl(1) @@ -1450,7 +1428,7 @@ class DarwinUser(User): '''Return the next available uid''' cmd = self._get_dscl() cmd += ['-list', '/Users', 'UniqueID'] - (rc, out, err) = self.execute_command(cmd, obey_checkmode=False) + (rc, out, err) = self.execute_command(cmd) if rc != 0: self.module.fail_json( msg="Unable to get the next available uid", @@ -1483,7 +1461,8 @@ class DarwinUser(User): cmd += [ '-create', '/Users/%s' % self.name, 'Password', '*'] (rc, out, err) = self.execute_command(cmd) if rc != 0: - self.module.fail_json(msg='Error when changing password', err=err, out=out, rc=rc) + self.module.fail_json(msg='Error when changing password', + err=err, out=out, rc=rc) return (rc, out, err) def _make_group_numerical(self): @@ -1504,11 +1483,13 @@ class DarwinUser(User): option = '-a' else: option = '-d' - cmd = [ 'dseditgroup', '-o', 'edit', option, self.name, '-t', 'user', group ] + cmd = [ 'dseditgroup', '-o', 'edit', option, self.name, + '-t', 'user', group ] (rc, out, err) = self.execute_command(cmd) if rc != 0: self.module.fail_json(msg='Cannot %s user "%s" to group "%s".' - % (action, self.name, group), err=err, out=out, rc=rc) + % (action, self.name, group), + err=err, out=out, rc=rc) return (rc, out, err) def _modify_group(self): @@ -1551,7 +1532,7 @@ class DarwinUser(User): # http://support.apple.com/kb/HT5017?viewlocale=en_US cmd = [ 'defaults', 'read', plist_file, 'HiddenUsersList' ] - (rc, out, err) = self.execute_command(cmd, obey_checkmode=False) + (rc, out, err) = self.execute_command(cmd) # returned value is # ( # "_userA", @@ -1572,23 +1553,28 @@ class DarwinUser(User): 'HiddenUsersList', '-array-add', self.name ] (rc, out, err) = self.execute_command(cmd) if rc != 0: - self.module.fail_json( msg='Cannot user "%s" to hidden user list.' % self.name, err=err, out=out, rc=rc) + self.module.fail_json( + msg='Cannot user "%s" to hidden user list.' + % self.name, err=err, out=out, rc=rc) return 0 else: if self.name in hidden_users: del(hidden_users[hidden_users.index(self.name)]) - cmd = [ 'defaults', 'write', plist_file, 'HiddenUsersList', '-array' ] + hidden_users + cmd = [ 'defaults', 'write', plist_file, + 'HiddenUsersList', '-array' ] + hidden_users (rc, out, err) = self.execute_command(cmd) if rc != 0: - self.module.fail_json( msg='Cannot remove user "%s" from hidden user list.' % self.name, err=err, out=out, rc=rc) + self.module.fail_json( + msg='Cannot remove user "%s" from hidden user list.' + % self.name, err=err, out=out, rc=rc) return 0 def user_exists(self): '''Check is SELF.NAME is a known user on the system.''' cmd = self._get_dscl() cmd += [ '-list', '/Users/%s' % self.name] - (rc, out, err) = self.execute_command(cmd, obey_checkmode=False) + (rc, out, err) = self.execute_command(cmd) return rc == 0 def remove_user(self): @@ -1600,7 +1586,9 @@ class DarwinUser(User): (rc, out, err) = self.execute_command(cmd) if rc != 0: - self.module.fail_json( msg='Cannot delete user "%s".' % self.name, err=err, out=out, rc=rc) + self.module.fail_json( + msg='Cannot delete user "%s".' + % self.name, err=err, out=out, rc=rc) if self.force: if os.path.exists(info[5]): @@ -1614,10 +1602,13 @@ class DarwinUser(User): cmd += [ '-create', '/Users/%s' % self.name] (rc, err, out) = self.execute_command(cmd) if rc != 0: - self.module.fail_json( msg='Cannot create user "%s".' % self.name, err=err, out=out, rc=rc) + self.module.fail_json( + msg='Cannot create user "%s".' + % self.name, err=err, out=out, rc=rc) + if self.group: + self._make_group_numerical() - self._make_group_numerical() if self.uid is None: self.uid = str(self._get_next_uid()) @@ -1625,19 +1616,20 @@ class DarwinUser(User): if self.createhome: if self.home is None: self.home = '/Users/%s' % self.name - if not self.module.check_mode: - if not os.path.exists(self.home): - os.makedirs(self.home) - self.chown_homedir(int(self.uid), int(self.group), self.home) + if not os.path.exists(self.home): + os.makedirs(self.home) + self.chown_homedir(int(self.uid), int(self.group), self.home) for field in self.fields: if self.__dict__.has_key(field[0]) and self.__dict__[field[0]]: cmd = self._get_dscl() - cmd += [ '-create', '/Users/%s' % self.name, field[1], self.__dict__[field[0]]] + cmd += [ '-create', '/Users/%s' % self.name, + field[1], self.__dict__[field[0]]] (rc, _err, _out) = self.execute_command(cmd) if rc != 0: - self.module.fail_json( msg='Cannot add property "%s" to user "%s".' + self.module.fail_json( + msg='Cannot add property "%s" to user "%s".' % (field[0], self.name), err=err, out=out, rc=rc) out += _out @@ -1653,10 +1645,9 @@ class DarwinUser(User): self._update_system_user() # here we don't care about change status since it is a creation, # thus changed is always true. - if self.groups: - (rc, _out, _err, changed) = self._modify_group() - out += _out - err += _err + (rc, _out, _err, changed) = self._modify_group() + out += _out + err += _err return (rc, err, out) def modify_user(self): @@ -1664,15 +1655,15 @@ class DarwinUser(User): out = '' err = '' - if self.group: - self._make_group_numerical() + self._make_group_numerical() for field in self.fields: if self.__dict__.has_key(field[0]) and self.__dict__[field[0]]: current = self._get_user_property(field[1]) if current is None or current != self.__dict__[field[0]]: cmd = self._get_dscl() - cmd += [ '-create', '/Users/%s' % self.name, field[1], self.__dict__[field[0]]] + cmd += [ '-create', '/Users/%s' % self.name, + field[1], self.__dict__[field[0]]] (rc, _err, _out) = self.execute_command(cmd) if rc != 0: self.module.fail_json( @@ -1687,13 +1678,12 @@ class DarwinUser(User): err += _err changed = rc - if self.groups: - (rc, _out, _err, _changed) = self._modify_group() - out += _out - err += _err + (rc, _out, _err, _changed) = self._modify_group() + out += _out + err += _err - if _changed is True: - changed = rc + if _changed is True: + changed = rc rc = self._update_system_user() if rc == 0: @@ -1758,10 +1748,6 @@ class AIX(User): if self.createhome: cmd.append('-m') - if self.skeleton is not None: - cmd.append('-k') - cmd.append(self.skeleton) - cmd.append(self.name) (rc, out, err) = self.execute_command(cmd) @@ -1833,6 +1819,8 @@ class AIX(User): # skip if no changes to be made if len(cmd) == 1: (rc, out, err) = (None, '', '') + elif self.module.check_mode: + return (True, '', '') else: cmd.append(self.name) (rc, out, err) = self.execute_command(cmd) @@ -1998,6 +1986,8 @@ class HPUX(User): # skip if no changes to be made if len(cmd) == 1: return (None, '', '') + elif self.module.check_mode: + return (0, '', '') cmd.append(self.name) return self.execute_command(cmd) @@ -2022,14 +2012,13 @@ def main(): comment=dict(default=None, type='str'), home=dict(default=None, type='str'), shell=dict(default=None, type='str'), - password=dict(default=None, type='str', no_log=True), + password=dict(default=None, type='str'), login_class=dict(default=None, type='str'), # following options are specific to userdel force=dict(default='no', type='bool'), remove=dict(default='no', type='bool'), # following options are specific to useradd createhome=dict(default='yes', type='bool'), - skeleton=dict(default=None, type='str'), system=dict(default='no', type='bool'), # following options are specific to usermod move_home=dict(default='no', type='bool'), @@ -2040,7 +2029,7 @@ def main(): ssh_key_type=dict(default=ssh_defaults['type'], type='str'), ssh_key_file=dict(default=None, type='str'), ssh_key_comment=dict(default=ssh_defaults['comment'], type='str'), - ssh_key_passphrase=dict(default=None, type='str', no_log=True), + ssh_key_passphrase=dict(default=None, type='str'), update_password=dict(default='always',choices=['always','on_create'],type='str'), expires=dict(default=None, type='float'), ), @@ -2049,9 +2038,11 @@ def main(): user = User(module) - module.debug('User instantiated - platform %s' % user.platform) - if user.distribution: - module.debug('User instantiated - distribution %s' % user.distribution) + if user.syslogging: + syslog.openlog('ansible-%s' % os.path.basename(__file__)) + syslog.syslog(syslog.LOG_NOTICE, 'User instantiated - platform %s' % user.platform) + if user.distribution: + syslog.syslog(syslog.LOG_NOTICE, 'User instantiated - distribution %s' % user.distribution) rc = None out = '' @@ -2073,11 +2064,8 @@ def main(): if module.check_mode: module.exit_json(changed=True) (rc, out, err) = user.create_user() - if module.check_mode: - result['system'] = user.name - else: - result['system'] = user.system - result['createhome'] = user.createhome + result['system'] = user.system + result['createhome'] = user.createhome else: # modify user (note: this function is check mode aware) (rc, out, err) = user.modify_user() @@ -2123,7 +2111,6 @@ def main(): # deal with ssh key if user.sshkeygen: - # generate ssh key (note: this function is check mode aware) (rc, out, err) = user.ssh_key_gen() if rc is not None and rc != 0: module.fail_json(name=user.name, msg=err, rc=rc) @@ -2141,5 +2128,4 @@ def main(): # import module snippets from ansible.module_utils.basic import * -if __name__ == '__main__': - main() +main()