From 346f8ffe0e6a95d561bb51288a340f5a2768860e Mon Sep 17 00:00:00 2001 From: Nigel Metheringham Date: Mon, 29 Oct 2012 22:00:58 +0000 Subject: [PATCH 1/3] Rework of user module to support portability Split module into a main calling function, and a generic (Linux useradd/usermod/userdel) User class. Added a __new__ function that selects most appropriate superclass Added a FreeBSD User class Tested against FreeBSD 9.0 --- user | 739 +++++++++++++++++++++++++++++++++++++---------------------- 1 file changed, 460 insertions(+), 279 deletions(-) diff --git a/user b/user index 773516a4ff8..21c0d0d0d9f 100755 --- a/user +++ b/user @@ -155,111 +155,174 @@ examples: import os import pwd import grp +import syslog +import platform try: import spwd HAVE_SPWD=True except: HAVE_SPWD=False -SHADOWFILE = '/etc/shadow' -if os.path.exists('/etc/master.passwd'): - SHADOWFILE = '/etc/master.passwd' # FreeBSD passwd -# Note: while the above has the correct location for where -# encrypted passwords are stored on FreeBSD, the code below doesn't -# invoke adduser in lieu of useradd, nor pw in lieu of usermod. -# That is, this won't work on FreeBSD. - -def user_del(module, user, **kwargs): - cmd = [module.get_bin_path('userdel', True)] - for key in kwargs: - if key == 'force' and module.boolean(kwargs[key]): + +class User(object): + + platform = 'Generic' + distribution = None + SHADOWFILE = '/etc/shadow' + + def __new__(cls, *arguments, **keyword): + subclass = None + this_platform = platform.system() + if this_platform == 'Linux': + try: + distribution = platform.linux_distribution()[0].capitalize + except: + distribution = platform.dist()[0].capitalize + else: + distribution = None + + # get the most specific superclass for this platform + if distribution is not None: + for sc in User.__subclasses__(): + if sc.distribution is not None and sc.distribution == distribution and sc.platform == this_platform: + subclass = sc + if subclass is None: + for sc in User.__subclasses__(): + if sc.platform == this_platform and sc.distribution is None: + subclass = sc + if subclass is None: + subclass = cls + + return super(cls, subclass).__new__(subclass, *arguments, **keyword) + + def __init__(self, module): + self.module = module + self.state = module.params['state'] + self.name = module.params['name'] + self.uid = module.params['uid'] + 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.boolean(module.params['force']) + self.remove = module.boolean(module.params['remove']) + self.createhome = module.boolean(module.params['createhome']) + self.system = module.boolean(module.params['system']) + self.append = module.boolean(module.params['append']) + self.sshkeygen = module.boolean(module.params['ssh_key']) + self.ssh_bits = module.params['ssh_key_bits'] + self.ssh_type = module.params['ssh_key_type'] + self.ssh_comment = module.params['ssh_key_comment'] + self.ssh_passphrase = module.params['ssh_key_passphrase'] + if module.params['ssh_key_file'] is not None: + self.ssh_file = module.params['ssh_key_file'] + else: + self.ssh_file = os.path.join('.ssh', 'id_%s' % self.ssh_type) + self.syslogging = True + + def execute_command(self,cmd): + if self.syslogging: + syslog.openlog('ansible-%s' % os.path.basename(__file__)) + syslog.syslog(syslog.LOG_NOTICE, 'Command %s' % '|'.join(cmd)) + + p = subprocess.Popen(cmd, shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + (out, err) = p.communicate() + rc = p.returncode + return (rc, out, err) + + def remove_user_userdel(self): + cmd = [self.module.get_bin_path('userdel', True)] + if self.force: cmd.append('-f') - elif key == 'remove' and module.boolean(kwargs[key]): + elif self.remove: cmd.append('-r') - cmd.append(user) - p = subprocess.Popen(cmd, shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - (out, err) = p.communicate() - rc = p.returncode - return (rc, out, err) - -def user_add(module, user, **kwargs): - cmd = [module.get_bin_path('useradd', True)] - for key in kwargs: - if key == 'uid' and kwargs[key] is not None: + cmd.append(self.name) + + return self.execute_command(cmd) + + def create_user_useradd(self, command_name='useradd'): + cmd = [self.module.get_bin_path(command_name, True)] + + if self.uid is not None: cmd.append('-u') - cmd.append(kwargs[key]) - elif key == 'group' and kwargs[key] is not None: - if not group_exists(kwargs[key]): - module.fail_json(msg="Group %s does not exist" % (kwargs[key])) + cmd.append(self.uid) + + if self.group is not None: + if not user.group_exists(self.group): + self.module.fail_json(msg="Group %s does not exist" % self.group) cmd.append('-g') - cmd.append(kwargs[key]) - elif key == 'groups' and kwargs[key] is not None: - for g in kwargs[key].split(','): - if not group_exists(g): - module.fail_json(msg="Group %s does not exist" % (g)) + cmd.append(self.group) + + if self.groups is not None: + for g in self.groups.split(','): + if not self.group_exists(g): + self.module.fail_json(msg="Group %s does not exist" % (g)) cmd.append('-G') - cmd.append(kwargs[key]) - elif key == 'comment' and kwargs[key] is not None: + cmd.append(self.groups) + + if self.comment is not None: cmd.append('-c') - cmd.append(kwargs[key]) - elif key == 'home' and kwargs[key] is not None: + cmd.append(self.comment) + + if self.home is not None: cmd.append('-d') - cmd.append(kwargs[key]) - elif key == 'shell' and kwargs[key] is not None: + cmd.append(self.home) + + if self.shell is not None: cmd.append('-s') - cmd.append(kwargs[key]) - elif key == 'password' and kwargs[key] is not None: + cmd.append(self.shell) + + if self.password is not None: cmd.append('-p') - cmd.append(kwargs[key]) - elif key == 'createhome': - if kwargs[key] is not None: - value = module.boolean(kwargs[key]) - if value: - cmd.append('-m') - else: - cmd.append('-M') - elif key == 'system' and module.boolean(kwargs[key]): + cmd.append(self.password) + + if self.createhome: + cmd.append('-m') + else: + cmd.append('-M') + + if self.system: cmd.append('-r') - cmd.append(user) - p = subprocess.Popen(cmd, shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - (out, err) = p.communicate() - rc = p.returncode - return (rc, out, err) - -""" -Without spwd, we would have to resort to reading /etc/shadow -to get the encrypted string. For now, punt on idempotent password changes. -""" -def user_mod(module, user, **kwargs): - cmd = [module.get_bin_path('usermod', True)] - info = user_info(user) - for key in kwargs: - if key == 'uid': - if kwargs[key] is not None and info[2] != int(kwargs[key]): - cmd.append('-u') - cmd.append(kwargs[key]) - elif key == 'group' and kwargs[key] is not None: - if not group_exists(kwargs[key]): - module.fail_json(msg="Group %s does not exist" % (kwargs[key])) - ginfo = group_info(kwargs[key]) + + cmd.append(self.name) + return self.execute_command(cmd) + + + def modify_user_usermod(self): + cmd = [self.module.get_bin_path('usermod', True)] + info = self.user_info() + + if self.uid is not None and info[2] != int(self.uid): + cmd.append('-u') + cmd.append(self.uid) + + if self.group is not None: + if not self.group_exists(self.group): + module.fail_json(msg="Group %s does not exist" % self.group) + ginfo = self.group_info(self.group) if info[3] != ginfo[2]: cmd.append('-g') - cmd.append(kwargs[key]) - elif key == 'groups' and kwargs[key] is not None: - current_groups = user_group_membership(user) - groups = kwargs[key].split(',') + cmd.append(self.group) + + if self.groups is not None: + current_groups = self.user_group_membership() + groups = self.groups.split(',') for g in groups: - if not group_exists(g): + if not self.group_exists(g): module.fail_json(msg="Group %s does not exist" % (g)) + group_diff = set(sorted(current_groups)).symmetric_difference(set(sorted(groups))) groups_need_mod = False if group_diff: - if kwargs['append'] is not None and module.boolean(kwargs['append']): + if self.append: for g in groups: if g in group_diff: cmd.append('-a') groups_need_mod = True + break else: groups_need_mod = True @@ -267,156 +330,298 @@ def user_mod(module, user, **kwargs): cmd.append('-G') cmd.append(','.join(groups)) - elif key == 'comment': - if kwargs[key] is not None and info[4] != kwargs[key]: + if self.comment is not None and info[4] != self.comment: cmd.append('-c') - cmd.append(kwargs[key]) - elif key == 'home': - if kwargs[key] is not None and info[5] != kwargs[key]: - cmd.append('-d') - cmd.append(kwargs[key]) - elif key == 'shell': - if kwargs[key] is not None and info[6] != kwargs[key]: - cmd.append('-s') - cmd.append(kwargs[key]) - elif key == 'password': - if kwargs[key] is not None and info[1] != kwargs[key]: - cmd.append('-p') - cmd.append(kwargs[key]) - # skip if no changes to be made - if len(cmd) == 1: - return (None, '', '') - cmd.append(user) - p = subprocess.Popen(cmd, shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - (out, err) = p.communicate() - rc = p.returncode - return (rc, out, err) - -def group_exists(group): - try: + cmd.append(self.comment) + + if self.home is not None and info[5] != self.home: + cmd.append('-d') + cmd.append(self.home) + + if self.shell is not None and info[6] != self.shell: + cmd.append('-s') + cmd.append(self.shell) + + if 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, '', '') + + cmd.append(self.name) + return self.execute_command(cmd) + + def group_exists(self,group): + try: + if group.isdigit(): + if grp.getgrgid(group): + return True + else: + if grp.getgrnam(group): + return True + except KeyError: + return False + + def group_info(self,group): + if not self.group_exists(group): + return False if group.isdigit(): - if grp.getgrgid(group): - return True + return list(grp.getgrgid(group)) else: - if grp.getgrnam(group): - return True - except KeyError: - return False - -def group_info(group): - if not group_exists(group): - return False - if group.isdigit(): - return list(grp.getgrgid(group)) - else: - return list(grp.getgrnam(group)) - -def user_group_membership(user): - groups = [] - info = get_pwd_info(user) - for group in grp.getgrall(): - if user in group[3]: - groups.append(group[0]) - return groups - -def user_exists(user): - try: - if pwd.getpwnam(user): - return True - except KeyError: - return False - -def get_pwd_info(user): - if not user_exists(user): - return False - return list(pwd.getpwnam(user)) - -def user_info(user): - if not user_exists(user): - return False - info = get_pwd_info(user) - if len(info[1]) == 1 or len(info[1]) == 0: - info[1] = user_password(user) - return info - -def user_password(user): - passwd = '' - if not user_exists(user): - return passwd - if HAVE_SPWD: + return list(grp.getgrnam(group)) + + def user_group_membership(self): + groups = [] + info = self.get_pwd_info() + for group in grp.getgrall(): + if self.name in group[3] and info[3] != group[2]: + groups.append(group[0]) + return groups + + def user_exists(self): try: - passwd = spwd.getspnam(user)[1] + if pwd.getpwnam(self.name): + return True except KeyError: + return False + + def get_pwd_info(self): + if not self.user_exists(): + return False + return list(pwd.getpwnam(self.name)) + + def user_info(self): + if not self.user_exists(): + return False + info = self.get_pwd_info() + if len(info[1]) == 1 or len(info[1]) == 0: + info[1] = self.user_password() + return info + + def user_password(self): + passwd = '' + if not self.user_exists(): return passwd - else: - # Read shadow file for user's encrypted password string - if os.path.exists(SHADOWFILE) and os.access(SHADOWFILE, os.R_OK): - for line in open(SHADOWFILE).readlines(): - if line.startswith('%s:' % user): - passwd = line.split(':')[1] - return passwd - -def get_ssh_key_path(user, ssh_file): - info = user_info(user) - if os.path.isabs(ssh_file): - ssh_key_file = ssh_file - else: - ssh_key_file = os.path.join(info[5], ssh_file) - return ssh_key_file - -def ssh_key_gen(module, user, ssh): - info = user_info(user) - if not os.path.exists(info[5]): - return (1, '', 'User %s home directory does not exist' % user) - ssh_key_file = get_ssh_key_path(user, ssh['file']) - ssh_dir = os.path.dirname(ssh_key_file) - if not os.path.exists(ssh_dir): - try: - os.mkdir(ssh_dir, 0700) - except OSError, e: - return (1, '', 'Failed to create %s: %s' % (ssh_dir, str(e))) - if os.path.exists(ssh_key_file): - return (None, 'Key already exists', '') - cmd = [module.get_bin_path('ssh-keygen', True)] - for key in ssh: - if key == 'type' and ssh[key] is not None: - cmd.append('-t') - cmd.append(ssh[key]) - elif key == 'bits' and ssh[key] is not None: - cmd.append('-b') - cmd.append(ssh[key]) - elif key == 'comment' and ssh[key] is not None: - cmd.append('-C') - cmd.append(ssh[key]) - elif key == 'file' and ssh[key] is not None: - cmd.append('-f') - cmd.append(ssh_key_file) - elif key == 'passphrase': - cmd.append('-N') - if ssh[key] is not None: - cmd.append(ssh['passphrase']) - else: - cmd.append('') - p = subprocess.Popen(cmd, shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - (out, err) = p.communicate() - rc = p.returncode - return (rc, out, err) - -def ssh_key_fingerprint(module, user, ssh): - ssh_key_file = get_ssh_key_path(user, ssh['file']) - if not os.path.exists(ssh_key_file): - return (1, 'SSH Key file %s does not exist' % ssh_key_file, '') - cmd = [module.get_bin_path('ssh-keygen', True)] - cmd.append('-l') - cmd.append('-f') - cmd.append(ssh_key_file) - p = subprocess.Popen(cmd, shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - (out, err) = p.communicate() - rc = p.returncode - return (rc, out, err) + if HAVE_SPWD: + try: + passwd = spwd.getspnam(self.name)[1] + except KeyError: + return passwd + else: + # Read shadow file for user's encrypted password string + if os.path.exists(self.SHADOWFILE) and os.access(self.SHADOWFILE, os.R_OK): + for line in open(self.SHADOWFILE).readlines(): + if line.startswith('%s:' % self.name): + passwd = line.split(':')[1] + return passwd + + def get_ssh_key_path(self): + info = self.user_info + if os.path.isabs(self.ssh_file): + ssh_key_file = self.ssh_file + else: + ssh_key_file = os.path.join(info[5], self.ssh_file) + return ssh_key_file + + def ssh_key_gen(self): + info = self.user_info + 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): + try: + os.mkdir(ssh_dir, 0700) + except OSError, e: + return (1, '', 'Failed to create %s: %s' % (ssh_dir, str(e))) + if os.path.exists(ssh_key_file): + return (None, 'Key already exists', '') + cmd = [self.module.get_bin_path('ssh-keygen', True)] + cmd.append('-t') + cmd.append(self.ssh_type) + cmd.append('-b') + cmd.append(self.ssh_bits) + cmd.append('-C') + cmd.append(self.ssh_comment) + cmd.append('-f') + cmd.append(ssh_key_file) + cmd.append('-N') + if self.ssh_passphrase is not None: + cmd.append(self.ssh_passphrase) + else: + cmd.append('') + return self.execute_command(cmd) + def ssh_key_fingerprint(self): + ssh_key_file = self.get_ssh_key_path + if not os.path.exists(ssh_key_file): + return (1, 'SSH Key file %s does not exist' % ssh_key_file, '') + cmd = [module.get_bin_path('ssh-keygen', True)] + cmd.append('-l') + cmd.append('-f') + cmd.append(ssh_key_file) + return self.execute_command(cmd) + + def create_user(self): + # by default we use the create_user_useradd method + return self.create_user_useradd() + + def remove_user(self): + # by default we use the remove_user_userdel method + return self.remove_user_userdel() + + def modify_user(self): + # by default we use the modify_user_usermod method + return self.modify_user_usermod() + + +# =========================================== + +class FreeBSD_User(User): + + platform = 'FreeBSD' + distribution = None + SHADOWFILE = '/etc/master.passwd' + + def remove_user(self): + cmd = [self.module.get_bin_path('pw', True), + 'userdel', + '-n', + self.name ] + if self.remove: + cmd.append('-r') + + return self.execute_command(cmd) + + def create_user(self): + cmd = [self.module.get_bin_path('pw', True), + 'useradd', + '-n', + self.name ] + + if self.uid is not None: + cmd.append('-u') + cmd.append(self.uid) + + if self.comment is not None: + cmd.append('-c') + cmd.append(self.comment) + + if self.home is not None: + cmd.append('-d') + cmd.append(self.home) + + if self.group is not None: + if not user.group_exists(self.group): + self.module.fail_json(msg="Group %s does not exist" % self.group) + cmd.append('-g') + cmd.append(self.group) + + if self.groups is not None: + for g in self.groups.split(','): + if not self.group_exists(g): + self.module.fail_json(msg="Group %s does not exist" % (g)) + cmd.append('-G') + cmd.append(self.groups) + + if self.createhome: + cmd.append('-m') + + if self.shell is not None: + cmd.append('-s') + cmd.append(self.shell) + + # system cannot be handled currently - should we error if its requested? + # create the user + (rc, out, err) = self.execute_command(cmd) + if rc is not None and rc != 0: + module.fail_json(name=self.name, msg=err, rc=rc) + + # we have to set the password in a second command + if self.password is not None: + cmd = [self.module.get_bin_path('chpass', True), + '-p', + self.password, + self.name ] + return self.execute_command(cmd) + + return (rc, out, err) + + def modify_user(self): + cmd = [self.module.get_bin_path('pw', True), + 'usermod', + '-n', + self.name ] + info = self.user_info() + + if self.uid is not None and info[2] != int(self.uid): + cmd.append('-u') + cmd.append(self.uid) + + if self.comment is not None and info[4] != self.comment: + cmd.append('-c') + cmd.append(self.comment) + + if self.home is not None and info[5] != self.home: + cmd.append('-d') + cmd.append(self.home) + + if self.group is not None: + if not user.group_exists(self.group): + self.module.fail_json(msg="Group %s does not exist" % self.group) + ginfo = self.group_info(self.group) + if info[3] != ginfo[2]: + cmd.append('-g') + cmd.append(self.group) + + if self.shell is not None and info[6] != self.shell: + cmd.append('-s') + cmd.append(self.shell) + + if self.groups is not None: + current_groups = self.user_group_membership() + groups = self.groups.split(',') + for g in groups: + if not self.group_exists(g): + module.fail_json(msg="Group %s does not exist" % (g)) + + group_diff = set(sorted(current_groups)).symmetric_difference(set(sorted(groups))) + groups_need_mod = False + + if group_diff: + if self.append: + for g in groups: + if g in group_diff: + groups_need_mod = True + break + else: + groups_need_mod = True + + if groups_need_mod: + new_groups = groups + if self.append: + new_groups.append(current_groups) + cmd.append(','.join(new_groups)) + + # modify the user + (rc, out, err) = self.execute_command(cmd) + if rc is not None and rc != 0: + module.fail_json(name=self.name, msg=err, rc=rc) + + # we have to set the password in a second command + if self.password is not None and info[1] != self.password: + cmd = [self.module.get_bin_path('chpass', True), + '-p', + self.password, + self.name ] + return self.execute_command(cmd) + + return (rc, out, err) + # =========================================== def main(): @@ -426,8 +631,6 @@ def main(): 'passphrase': None, 'comment': 'ansible-generated' } - ssh_defaults['file'] = os.path.join('.ssh', 'id_%s' % ssh_defaults['type']) - ssh = dict(ssh_defaults) module = AnsibleModule( argument_spec = dict( state=dict(default='present', choices=['present', 'absent']), @@ -451,69 +654,44 @@ def main(): ssh_key=dict(choices=['generate']), ssh_key_bits=dict(default=ssh_defaults['bits']), ssh_key_type=dict(default=ssh_defaults['type']), - ssh_key_file=dict(default=ssh_defaults['file']), + ssh_key_file=dict(default=None), ssh_key_comment=dict(default=ssh_defaults['comment']), ssh_key_passphrase=dict(default=None) ) ) - state = module.params['state'] - name = module.params['name'] - uid = module.params['uid'] - group = module.params['group'] - groups = module.params['groups'] - comment = module.params['comment'] - home = module.params['home'] - shell = module.params['shell'] - password = module.params['password'] - force = module.params['force'] - remove = module.params['remove'] - createhome = module.params['createhome'] - system = module.params['system'] - append = module.params['append'] - sshkeygen = module.params['ssh_key'] - - ssh['bits'] = module.params['ssh_key_bits'] - ssh['type'] = module.params['ssh_key_type'] - ssh['file'] = module.params['ssh_key_file'] - ssh['comment'] = module.params['ssh_key_comment'] - ssh['passphrase'] = module.params['ssh_key_passphrase'] - # If using default filename, make sure it is named appropriately - if ssh['file'] == ssh_defaults['file']: - ssh['file'] = os.path.join('.ssh', 'id_%s' % ssh_defaults['type']) + user = User(module) + 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 = '' err = '' result = {} - result['name'] = name - result['state'] = state - if state == 'absent': - if user_exists(name): - (rc, out, err) = user_del(module, name, force=force, remove=remove) + result['name'] = user.name + result['state'] = user.state + if user.state == 'absent': + if user.user_exists(): + (rc, out, err) = user.remove_user() if rc != 0: module.fail_json(name=name, msg=err, rc=rc) - result['force'] = force - result['remove'] = remove - elif state == 'present': - if not user_exists(name): - (rc, out, err) = user_add(module, - name, uid=uid, group=group, groups=groups, - comment=comment, home=home, shell=shell, - password=password, createhome=createhome, - system=system) - result['system'] = system - result['createhome'] = createhome + result['force'] = user.force + result['remove'] = user.remove + elif user.state == 'present': + if not user.user_exists(): + (rc, out, err) = user.create_user() + result['system'] = user.system + result['createhome'] = user.createhome else: - (rc, out, err) = user_mod(module, - name, uid=uid, group=group, groups=groups, - comment=comment, home=home, shell=shell, - password=password, append=append) - result['append'] = append + (rc, out, err) = user.modify_user() + result['append'] = user.append if rc is not None and rc != 0: - module.fail_json(name=name, msg=err, rc=rc) - if password is not None: + module.fail_json(name=user.name, msg=err, rc=rc) + if user.password is not None: result['password'] = 'NOT_LOGGING_PASSWORD' if rc is None: @@ -524,32 +702,35 @@ def main(): result['stdout'] = out if err: result['stderr'] = err - if user_exists(name): - info = user_info(name) + + if user.user_exists(): + info = user.user_info() if info == False: - result['msg'] = "failed to look up user name: %s" % name + result['msg'] = "failed to look up user name: %s" % user.name result['failed'] = True result['uid'] = info[2] result['group'] = info[3] result['comment'] = info[4] result['home'] = info[5] result['shell'] = info[6] - groups = user_group_membership(name) + groups = user.user_group_membership() result['uid'] = info[2] - if len(groups) > 0: - result['groups'] = groups - if sshkeygen: - (rc, out, err) = ssh_key_gen(module, name, ssh) + if user.groups is not None: + result['groups'] = user.groups + + # deal with ssh key + if user.sshkeygen: + (rc, out, err) = user.ssh_key_gen() if rc is not None and rc != 0: - module.fail_json(name=name, msg=err, rc=rc) + module.fail_json(name=user.name, msg=err, rc=rc) if rc == 0: result['changed'] = True - (rc, out, err) = ssh_key_fingerprint(module, name, ssh) + (rc, out, err) = user.ssh_key_fingerprint() if rc == 0: result['ssh_fingerprint'] = out.strip() else: result['ssh_fingerprint'] = err.strip() - result['ssh_key_file'] = get_ssh_key_path(name, ssh['file']) + result['ssh_key_file'] = user.get_ssh_key_path() module.exit_json(**result) From 0fc939d33f7b4df24f1dfd5d18dd84c1b55db6f6 Mon Sep 17 00:00:00 2001 From: Nigel Metheringham Date: Mon, 29 Oct 2012 22:04:06 +0000 Subject: [PATCH 2/3] Turn off some of the additional debug syslogging --- user | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/user b/user index 21c0d0d0d9f..88b20b89b48 100755 --- a/user +++ b/user @@ -220,7 +220,9 @@ class User(object): self.ssh_file = module.params['ssh_key_file'] else: self.ssh_file = os.path.join('.ssh', 'id_%s' % self.ssh_type) - self.syslogging = True + + # select whether we dump additional debug info through syslog + self.syslogging = False def execute_command(self,cmd): if self.syslogging: From 871d3afdf473407ed9bd3f965232125910d54ac1 Mon Sep 17 00:00:00 2001 From: Nigel Metheringham Date: Thu, 1 Nov 2012 19:16:54 +0000 Subject: [PATCH 3/3] Added some documentation to the classes --- user | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/user b/user index 88b20b89b48..0236e3a8e6d 100755 --- a/user +++ b/user @@ -165,6 +165,25 @@ except: class User(object): + """ + This is a generic User manipulation class - generic in this case + meaning it is aimed at Linuxish platforms using useradd/userdel/usermod + On object creation it will load the most appropriate subclass based + on the platform and distribution. + + A subclass may wish to override the following action methods:- + - create_user() + - remove_user() + - modify_user() + - ssh_key_gen() + - ssh_key_fingerprint() + + The main function also uses the following User methods to decide + what to do:- + - user_exists() + + All subclasses MUST define platform and distribution (which may be None). + """ platform = 'Generic' distribution = None @@ -484,6 +503,16 @@ class User(object): # =========================================== class FreeBSD_User(User): + """ + This is a FreeBSD User manipulation class - it uses the pw command + to manipulate the user database, followed by the chpass command + to change the password. + + This overrides the following methods from the generic class:- + - create_user() + - remove_user() + - modify_user() + """ platform = 'FreeBSD' distribution = None