26b1bed22c
This adds some logic when usings groups possibly in combination with append if just specifying groups and the current groups do not match the list set groups if specifying groups with append and any group thats not in the current groups set groups with -a
329 lines
10 KiB
Python
Executable file
329 lines
10 KiB
Python
Executable file
#!/usr/bin/python
|
|
|
|
# (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/>.
|
|
|
|
try:
|
|
import json
|
|
except ImportError:
|
|
import simplejson as json
|
|
import os
|
|
import pwd
|
|
import grp
|
|
import shlex
|
|
import spwd
|
|
import subprocess
|
|
import sys
|
|
|
|
USERADD = "/usr/sbin/useradd"
|
|
USERMOD = "/usr/sbin/usermod"
|
|
USERDEL = "/usr/sbin/userdel"
|
|
|
|
def exit_json(rc=0, **kwargs):
|
|
if 'name' in kwargs:
|
|
add_user_info(kwargs)
|
|
print json.dumps(kwargs)
|
|
sys.exit(rc)
|
|
|
|
def fail_json(**kwargs):
|
|
kwargs['failed'] = True
|
|
exit_json(rc=1, **kwargs)
|
|
|
|
def add_user_info(kwargs):
|
|
name = kwargs['name']
|
|
if user_exists(name):
|
|
kwargs['state'] = 'present'
|
|
info = user_info(name)
|
|
kwargs['uid'] = info[2]
|
|
kwargs['group'] = info[3]
|
|
kwargs['comment'] = info[4]
|
|
kwargs['home'] = info[5]
|
|
kwargs['shell'] = info[6]
|
|
kwargs['createhome'] = os.path.exists(info[5])
|
|
groups = user_group_membership(name)
|
|
if len(groups) > 0:
|
|
kwargs['groups'] = groups
|
|
else:
|
|
kwargs['state'] = 'absent'
|
|
return kwargs
|
|
|
|
def user_del(user, **kwargs):
|
|
cmd = [USERDEL]
|
|
for key in kwargs:
|
|
if key == 'force' and kwargs[key]:
|
|
cmd.append('-f')
|
|
elif key == 'remove' and kwargs[key]:
|
|
cmd.append('-r')
|
|
cmd.append(user)
|
|
rc = subprocess.call(cmd, shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
|
if rc == 0:
|
|
return True
|
|
else:
|
|
return False
|
|
|
|
def user_add(user, **kwargs):
|
|
cmd = [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]):
|
|
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):
|
|
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)
|
|
rc = subprocess.call(cmd, shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
|
if rc == 0:
|
|
return True
|
|
else:
|
|
return False
|
|
|
|
def user_mod(user, **kwargs):
|
|
cmd = [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]):
|
|
fail_json(msg="Group %s does not exist" % (kwargs[key]))
|
|
ginfo = group_info(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(',')
|
|
for g in groups:
|
|
if not group_exists(g):
|
|
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:
|
|
debug("groups differ, trigger usemod")
|
|
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])
|
|
elif key == 'append':
|
|
if kwargs[key] is not None and kwargs[key] == 'yes':
|
|
if 'groups' in kwargs and kwargs['groups'] is not None:
|
|
cmd.append('-a')
|
|
# skip if no changes to be made
|
|
if len(cmd) == 1:
|
|
return False
|
|
cmd.append(user)
|
|
rc = subprocess.call(cmd, shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
|
if rc == 0:
|
|
return True
|
|
else:
|
|
return False
|
|
|
|
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)
|
|
sinfo = spwd.getspnam(user)
|
|
except KeyError:
|
|
return False
|
|
info[1] = sinfo[1]
|
|
return info
|
|
|
|
# ===========================================
|
|
|
|
if not os.path.exists(USERADD):
|
|
if os.path.exists("/sbin/useradd"):
|
|
USERADD = "/sbin/useradd"
|
|
else:
|
|
fail_json(msg="Cannot find useradd")
|
|
if not os.path.exists(USERMOD):
|
|
if os.path.exists("/sbin/usermod"):
|
|
USERMOD = "/sbin/usermod"
|
|
else:
|
|
fail_json(msg="Cannot find usermod")
|
|
if not os.path.exists(USERDEL):
|
|
if os.path.exists("/sbin/userdel"):
|
|
USERDEL = "/sbin/userdel"
|
|
else:
|
|
fail_json(msg="Cannot find userdel")
|
|
|
|
argfile = sys.argv[1]
|
|
args = open(argfile, 'r').read()
|
|
items = shlex.split(args)
|
|
|
|
if not len(items):
|
|
fail_json(msg='the module requires arguments -a')
|
|
sys.exit(1)
|
|
|
|
params = {}
|
|
for x in items:
|
|
(k, v) = x.split("=")
|
|
params[k] = v
|
|
|
|
state = params.get('state','present')
|
|
name = params.get('name', None)
|
|
uid = params.get('uid', None)
|
|
group = params.get('group', None)
|
|
groups = params.get('groups', None)
|
|
comment = params.get('comment', None)
|
|
home = params.get('home', None)
|
|
shell = params.get('shell', None)
|
|
password = params.get('password', None)
|
|
|
|
# ===========================================
|
|
# following options are specific to userdel
|
|
force = params.get('force', False)
|
|
remove = params.get('remove', False)
|
|
|
|
# ===========================================
|
|
# following options are specific to useradd
|
|
createhome = params.get('createhome', 'yes')
|
|
system = params.get('system', 'no')
|
|
|
|
# ===========================================
|
|
# following options are specific to usermod
|
|
append = params.get('append', 'no')
|
|
|
|
if state not in [ 'present', 'absent' ]:
|
|
fail_json(msg='invalid state')
|
|
if createhome not in [ 'yes', 'no' ]:
|
|
fail_json(msg='invalid createhome')
|
|
if system not in ['yes', 'no']:
|
|
fail_json(msg='invalid system')
|
|
if append not in [ 'yes', 'no' ]:
|
|
fail_json(msg='invalid append')
|
|
if name is None:
|
|
fail_json(msg='name is required')
|
|
|
|
changed = False
|
|
rc = 0
|
|
if state == 'absent':
|
|
if user_exists(name):
|
|
changed = user_del(name, force=force, remove=remove)
|
|
exit_json(name=name, changed=changed, force=force, remove=remove)
|
|
elif state == 'present':
|
|
if not user_exists(name):
|
|
changed = user_add(name, uid=uid, group=group, groups=groups,
|
|
comment=comment, home=home, shell=shell,
|
|
password=password, createhome=createhome,
|
|
system=system)
|
|
else:
|
|
changed = user_mod(name, uid=uid, group=group, groups=groups,
|
|
comment=comment, home=home, shell=shell,
|
|
password=password, append=append)
|
|
|
|
if password is not None:
|
|
exit_json(name=name, changed=changed, password="XXXXXXXX")
|
|
else:
|
|
exit_json(name=name, changed=changed)
|
|
|
|
fail_json(name=name, msg='Unexpected position reached')
|
|
sys.exit(0)
|