mirror of
				https://github.com/ansible-collections/community.general.git
				synced 2024-09-14 20:13:21 +02:00 
			
		
		
		
	
		
			
				
	
	
		
			332 lines
		
	
	
	
		
			11 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable file
		
	
	
	
	
			
		
		
	
	
			332 lines
		
	
	
	
		
			11 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable file
		
	
	
	
	
#!/usr/bin/python
 | 
						|
# -*- coding: utf-8 -*-
 | 
						|
 | 
						|
# (c) 2012, Stephen Fromm <sfromm@gmail.com>
 | 
						|
#
 | 
						|
# This file is part of Ansible
 | 
						|
#
 | 
						|
# Ansible is free software: you can redistribute it and/or modify
 | 
						|
# it under the terms of the GNU General Public License as published by
 | 
						|
# the Free Software Foundation, either version 3 of the License, or
 | 
						|
# (at your option) any later version.
 | 
						|
#
 | 
						|
# Ansible is distributed in the hope that it will be useful,
 | 
						|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
						|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
						|
# GNU General Public License for more details.
 | 
						|
#
 | 
						|
# You should have received a copy of the GNU General Public License
 | 
						|
# along with Ansible.  If not, see <http://www.gnu.org/licenses/>.
 | 
						|
 | 
						|
import os
 | 
						|
import pwd
 | 
						|
import grp
 | 
						|
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 get_bin_path(module, arg):
 | 
						|
    if os.path.exists('/usr/sbin/%s' % arg):
 | 
						|
        return '/usr/sbin/%s' % arg
 | 
						|
    elif os.path.exists('/sbin/%s' % arg):
 | 
						|
        return '/sbin/%s' % arg
 | 
						|
    else:
 | 
						|
        module.fail_json(msg="Cannot find %s" % arg)
 | 
						|
 | 
						|
def user_del(module, user, **kwargs):
 | 
						|
    cmd = [get_bin_path(module, 'userdel')]
 | 
						|
    for key in kwargs:
 | 
						|
        if key == 'force' and kwargs[key] == 'yes':
 | 
						|
            cmd.append('-f')
 | 
						|
        elif key == 'remove' and kwargs[key] == 'yes':
 | 
						|
            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 = [get_bin_path(module, 'useradd')]
 | 
						|
    for key in kwargs:
 | 
						|
        if key == 'uid' and kwargs[key] 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('-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('-G')
 | 
						|
            cmd.append(kwargs[key])
 | 
						|
        elif key == 'comment' and kwargs[key] is not None:
 | 
						|
            cmd.append('-c')
 | 
						|
            cmd.append(kwargs[key])
 | 
						|
        elif key == 'home' and kwargs[key] is not None:
 | 
						|
            cmd.append('-d')
 | 
						|
            cmd.append(kwargs[key])
 | 
						|
        elif key == 'shell' and kwargs[key] is not None:
 | 
						|
            cmd.append('-s')
 | 
						|
            cmd.append(kwargs[key])
 | 
						|
        elif key == 'password' and kwargs[key] is not None:
 | 
						|
            cmd.append('-p')
 | 
						|
            cmd.append(kwargs[key])
 | 
						|
        elif key == 'createhome':
 | 
						|
            if kwargs[key] is not None:
 | 
						|
                if kwargs[key] == 'yes':
 | 
						|
                    cmd.append('-m')
 | 
						|
                else:
 | 
						|
                    cmd.append('-M')
 | 
						|
        elif key == 'system' and kwargs[key] == 'yes':
 | 
						|
            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 = [get_bin_path(module, 'usermod')]
 | 
						|
    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])
 | 
						|
            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(',')
 | 
						|
            for g in groups:
 | 
						|
                if not 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 kwargs['append'] == 'yes':
 | 
						|
                    for g in groups:
 | 
						|
                        if g in group_diff:
 | 
						|
                            cmd.append('-a')
 | 
						|
                            groups_need_mod = True
 | 
						|
                else:
 | 
						|
                    groups_need_mod = True
 | 
						|
 | 
						|
            if groups_need_mod:
 | 
						|
                cmd.append('-G')
 | 
						|
                cmd.append(','.join(groups))
 | 
						|
 | 
						|
        elif key == 'comment':
 | 
						|
            if kwargs[key] is not None and info[4] != kwargs[key]:
 | 
						|
                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:
 | 
						|
        if group.isdigit():
 | 
						|
            if grp.getgrgid(group):
 | 
						|
                return True
 | 
						|
        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] and info[3] != group[2]:
 | 
						|
            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:
 | 
						|
        try:
 | 
						|
            passwd = spwd.getspnam(user)[1]
 | 
						|
        except KeyError:
 | 
						|
            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 main():
 | 
						|
    module = AnsibleModule(
 | 
						|
        argument_spec = dict(
 | 
						|
            state=dict(default='present', choices=['present', 'absent']),
 | 
						|
            name=dict(required=True),
 | 
						|
            uid=dict(default=None),
 | 
						|
            group=dict(default=None),
 | 
						|
            groups=dict(default=None),
 | 
						|
            comment=dict(default=None),
 | 
						|
            home=dict(default=None),
 | 
						|
            shell=dict(default=None),
 | 
						|
            password=dict(default=None),
 | 
						|
            # following options are specific to userdel
 | 
						|
            force=dict(default='no', choices=['yes', 'no']),
 | 
						|
            remove=dict(default='no', choices=['yes', 'no']),
 | 
						|
            # following options are specific to useradd
 | 
						|
            createhome=dict(default='yes', choices=['yes', 'no']),
 | 
						|
            system=dict(default='no', choices=['yes', 'no']),
 | 
						|
            # following options are specific to usermod
 | 
						|
            append=dict(default='no', choices=['yes', 'no']),
 | 
						|
        )
 | 
						|
    )
 | 
						|
 | 
						|
    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']
 | 
						|
 | 
						|
    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)
 | 
						|
            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
 | 
						|
        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
 | 
						|
        if rc is not None and rc != 0:
 | 
						|
            module.fail_json(name=name, msg=err, rc=rc)
 | 
						|
        if password is not None:
 | 
						|
            result['password'] = 'NOT_LOGGING_PASSWORD'
 | 
						|
 | 
						|
    if rc is None:
 | 
						|
        result['changed'] = False
 | 
						|
    else:
 | 
						|
        result['changed'] = True
 | 
						|
    if out:
 | 
						|
        result['stdout'] = out
 | 
						|
    if err:
 | 
						|
        result['stderr'] = err
 | 
						|
    if user_exists(name):
 | 
						|
        info = user_info(name)
 | 
						|
        if info == False:
 | 
						|
            result['msg'] = "failed to look up user name: %s" % 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)
 | 
						|
        result['uid'] = info[2]
 | 
						|
        if len(groups) > 0:
 | 
						|
            result['groups'] = groups
 | 
						|
 | 
						|
    module.exit_json(**result)
 | 
						|
 | 
						|
# include magic from lib/ansible/module_common.py
 | 
						|
#<<INCLUDE_ANSIBLE_MODULE_COMMON>>
 | 
						|
main()
 |