ansible/library/user
Michael DeHaan 7e9e29011e Add encoding lines to python modules such that they can take unicode options if they are fed them, since the
AnsibleModule stuff no longer base64 encodes for simplicity and speed reasons.
2012-08-02 21:29:10 -04:00

311 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 pwd
import grp
try:
import spwd
HAVE_SPWD=True
except:
HAVE_SPWD=False
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
try:
info = get_pwd_info(user)
if HAVE_SPWD:
sinfo = spwd.getspnam(user)
except KeyError:
return False
if HAVE_SPWD:
info[1] = sinfo[1]
return info
# ===========================================
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()