useradd.py: Use contextmanager to prevent leaked filehandles

This also makes some optimizations by not assigning the output of
functions to variables when unnecessary, and changes this module to pass
arguments to the cmd.run functions as list instead of a string.

It also changes some weird behavior for the loginclass functions.
There's no reason to return a dictionary that will only have one
element, and there is no reason to return a string that is literally
just two quotes (i.e. '""') when there is no login class found. An empty
string is sufficient, and has the benefit of also evaluating as False as
a boolean.
This commit is contained in:
Erik Johnson 2015-09-10 17:24:56 -05:00
parent 3e902e86b1
commit f24b979c7c

View file

@ -17,7 +17,7 @@ import copy
# Import salt libs
import salt.utils
from salt.ext.six import string_types
from salt.ext import six
from salt.exceptions import CommandExecutionError
log = logging.getLogger(__name__)
@ -68,6 +68,25 @@ def _build_gecos(gecos_dict):
gecos_dict.get('homephone', ''))
def _update_gecos(name, key, value):
'''
Common code to change a user's GECOS information
'''
if not isinstance(value, six.string_types):
value = str(value)
pre_info = _get_gecos(name)
if not pre_info:
return False
if value == pre_info[key]:
return True
gecos_data = copy.deepcopy(pre_info)
gecos_data[key] = value
cmd = ['usermod', '-c', _build_gecos(gecos_data), name]
__salt__['cmd.run'](cmd, python_shell=False)
post_info = info(name)
return _get_gecos(name).get(key) == value
def add(name,
uid=None,
gid=None,
@ -99,47 +118,51 @@ def add(name,
if gid not in (None, ''):
cmd.extend(['-g', str(gid)])
elif groups is not None and name in groups:
defs_file = '/etc/login.defs'
if __grains__['kernel'] != 'OpenBSD':
try:
for line in salt.utils.fopen('/etc/login.defs'):
if 'USERGROUPS_ENAB' not in line[:15]:
continue
with salt.utils.fopen(defs_file) as fp_:
for line in fp_:
if 'USERGROUPS_ENAB' not in line[:15]:
continue
if 'yes' in line:
cmd.extend([
'-g', str(__salt__['file.group_to_gid'](name))
])
if 'yes' in line:
cmd.extend([
'-g', str(__salt__['file.group_to_gid'](name))
])
# We found what we wanted, let's break out of the loop
break
# We found what we wanted, let's break out of the loop
break
except OSError:
log.debug('Error reading /etc/login.defs', exc_info=True)
log.debug(
'Error reading ' + defs_file,
exc_info_on_loglevel=logging.DEBUG
)
else:
usermgmt_file = '/etc/usermgmt.conf'
try:
for line in salt.utils.fopen('/etc/usermgmt.conf'):
if 'group' not in line[:5]:
continue
with salt.utils.fopen(usermgmt_file) as fp_:
for line in fp_:
if 'group' not in line[:5]:
continue
for val in line.split(" "):
cmd.extend([
'-g', str(val[1])
])
for val in line.split(' '):
cmd.extend([
'-g', str(val[1])
])
# We found what we wanted, let's break out of the loop
break
# We found what we wanted, let's break out of the loop
break
except OSError:
# /etc/usermgmt.conf not present: defaults will be used
pass
if isinstance(createhome, bool):
if salt.utils.is_true(createhome):
if createhome:
cmd.append('-m')
elif (__grains__['kernel'] != 'NetBSD'
and __grains__['kernel'] != 'OpenBSD'):
cmd.append('-M')
else:
log.error('Value passes to ``createhome`` must be a boolean')
return False
if home is not None:
cmd.extend(['-d', home])
@ -220,7 +243,7 @@ def delete(name, remove=False, force=False):
if RETCODE_12_ERROR_REGEX.match(ret['stderr']) is not None:
# We've hit the bug, let's log it and not fail
log.debug(
'While the userdel exited with code 12, this is a know bug on '
'While the userdel exited with code 12, this is a known bug on '
'debian based distributions. See http://goo.gl/HH3FzT'
)
return True
@ -263,10 +286,7 @@ def chuid(name, uid):
return True
cmd = ['usermod', '-u', '{0}'.format(uid), name]
__salt__['cmd.run'](cmd, python_shell=False)
post_info = info(name)
if post_info['uid'] != pre_info['uid']:
return post_info['uid'] == uid
return False
return info(name).get('uid') == uid
def chgid(name, gid):
@ -284,10 +304,7 @@ def chgid(name, gid):
return True
cmd = ['usermod', '-g', '{0}'.format(gid), name]
__salt__['cmd.run'](cmd, python_shell=False)
post_info = info(name)
if post_info['gid'] != pre_info['gid']:
return post_info['gid'] == gid
return False
return info(name).get('gid') == gid
def chshell(name, shell):
@ -305,10 +322,7 @@ def chshell(name, shell):
return True
cmd = ['usermod', '-s', shell, name]
__salt__['cmd.run'](cmd, python_shell=False)
post_info = info(name)
if post_info['shell'] != pre_info['shell']:
return post_info['shell'] == shell
return False
return info(name).get('shell') == shell
def chhome(name, home, persist=False):
@ -325,58 +339,64 @@ def chhome(name, home, persist=False):
pre_info = info(name)
if home == pre_info['home']:
return True
cmd = 'usermod -d {0} '.format(home)
cmd = ['usermod', '-d', '{0}'.format(home)]
if persist and __grains__['kernel'] != 'OpenBSD':
cmd += ' -m '
cmd += name
cmd.append('-m')
cmd.append(name)
__salt__['cmd.run'](cmd, python_shell=False)
post_info = info(name)
if post_info['home'] != pre_info['home']:
return post_info['home'] == home
return False
return info(name).get('home') == home
def chgroups(name, groups, append=False):
'''
Change the groups this user belongs to, add append to append the specified
groups
Change the groups to which this user belongs
CLI Example:
name
User to modify
groups
Groups to set for the user
append : False
If ``True``, append the specified group(s). Otherwise, this function
will replace the user's groups with the specified group(s).
CLI Examples:
.. code-block:: bash
salt '*' user.chgroups foo wheel,root True
salt '*' user.chgroups foo wheel,root
salt '*' user.chgroups foo wheel,root append=True
'''
if isinstance(groups, string_types):
if isinstance(groups, six.string_types):
groups = groups.split(',')
ugrps = set(list_groups(name))
if ugrps == set(groups):
return True
cmd = 'usermod '
cmd = ['usermod']
if __grains__['kernel'] != 'OpenBSD':
if append:
cmd += '-a '
cmd.append('-a')
else:
if append:
cmd += '-G '
cmd.append('-G')
else:
cmd += '-S '
cmd.append('-S')
if __grains__['kernel'] != 'OpenBSD':
cmd += '-G '
cmd += '"{0}" {1}'.format(','.join(groups), name)
cmdret = __salt__['cmd.run_all'](cmd, python_shell=False)
ret = not cmdret['retcode']
cmd.append('-G')
cmd.extend([','.join(groups), name])
result = __salt__['cmd.run_all'](cmd, python_shell=False)
# try to fallback on gpasswd to add user to localgroups
# for old lib-pamldap support
if __grains__['kernel'] != 'OpenBSD':
if not ret and ('not found in' in cmdret['stderr']):
if result['retcode'] != 0 and 'not found in' in result['stderr']:
ret = True
for group in groups:
cmd = 'gpasswd -a {0} {1}'.format(name, group)
cmdret = __salt__['cmd.run_all'](cmd, python_shell=False)
if cmdret['retcode']:
cmd = ['gpasswd', '-a', '{0}'.format(name), '{1}'.format(group)]
if __salt__['cmd.retcode'](cmd, python_shell=False) != 0:
ret = False
return ret
return ret
return result['retcode'] == 0
def chfullname(name, fullname):
@ -389,20 +409,7 @@ def chfullname(name, fullname):
salt '*' user.chfullname foo "Foo Bar"
'''
fullname = str(fullname)
pre_info = _get_gecos(name)
if not pre_info:
return False
if fullname == pre_info['fullname']:
return True
gecos_field = copy.deepcopy(pre_info)
gecos_field['fullname'] = fullname
cmd = ['usermod', '-c', _build_gecos(gecos_field), name]
__salt__['cmd.run'](cmd, python_shell=False)
post_info = info(name)
if post_info['fullname'] != pre_info['fullname']:
return post_info['fullname'] == fullname
return False
return _update_gecos(name, 'fullname', fullname)
def chroomnumber(name, roomnumber):
@ -415,20 +422,7 @@ def chroomnumber(name, roomnumber):
salt '*' user.chroomnumber foo 123
'''
roomnumber = str(roomnumber)
pre_info = _get_gecos(name)
if not pre_info:
return False
if roomnumber == pre_info['roomnumber']:
return True
gecos_field = copy.deepcopy(pre_info)
gecos_field['roomnumber'] = roomnumber
cmd = ['usermod', '-c', _build_gecos(gecos_field), name]
__salt__['cmd.run'](cmd, python_shell=False)
post_info = info(name)
if post_info['roomnumber'] != pre_info['roomnumber']:
return post_info['roomnumber'] == roomnumber
return False
return _update_gecos(name, 'roomnumber', roomnumber)
def chworkphone(name, workphone):
@ -439,22 +433,9 @@ def chworkphone(name, workphone):
.. code-block:: bash
salt '*' user.chworkphone foo "7735550123"
salt '*' user.chworkphone foo 7735550123
'''
workphone = str(workphone)
pre_info = _get_gecos(name)
if not pre_info:
return False
if workphone == pre_info['workphone']:
return True
gecos_field = copy.deepcopy(pre_info)
gecos_field['workphone'] = workphone
cmd = ['usermod', '-c', _build_gecos(gecos_field), name]
__salt__['cmd.run'](cmd, python_shell=False)
post_info = info(name)
if post_info['workphone'] != pre_info['workphone']:
return post_info['workphone'] == workphone
return False
return _update_gecos(name, 'workphone', workphone)
def chhomephone(name, homephone):
@ -465,28 +446,18 @@ def chhomephone(name, homephone):
.. code-block:: bash
salt '*' user.chhomephone foo "7735551234"
salt '*' user.chhomephone foo 7735551234
'''
homephone = str(homephone)
pre_info = _get_gecos(name)
if not pre_info:
return False
if homephone == pre_info['homephone']:
return True
gecos_field = copy.deepcopy(pre_info)
gecos_field['homephone'] = homephone
cmd = ['usermod', '-c', _build_gecos(gecos_field), name]
__salt__['cmd.run'](cmd, python_shell=False)
post_info = info(name)
if post_info['homephone'] != pre_info['homephone']:
return post_info['homephone'] == homephone
return False
return _update_gecos(name, 'homephone', homephone)
def chloginclass(name, loginclass):
'''
Change the default login class of the user
.. note::
This function only applies to OpenBSD systems.
CLI Example:
.. code-block:: bash
@ -495,15 +466,11 @@ def chloginclass(name, loginclass):
'''
if __grains__['kernel'] != 'OpenBSD':
return False
pre_info = get_loginclass(name)
if loginclass == pre_info['loginclass']:
if loginclass == get_loginclass(name):
return True
cmd = 'usermod -L {0} {1}'.format(loginclass, name)
__salt__['cmd.run'](cmd)
post_info = get_loginclass(name)
if post_info['loginclass'] != pre_info['loginclass']:
return post_info['loginclass'] == loginclass
return False
cmd = ['usermod', '-L', '{0}'.format(loginclass), '{0}'.format(name)]
__salt__['cmd.run'](cmd, python_shell=False)
return get_loginclass(name) == loginclass
def info(name):
@ -528,6 +495,9 @@ def get_loginclass(name):
'''
Get the login class of the user
.. note::
This function only applies to OpenBSD systems.
CLI Example:
.. code-block:: bash
@ -536,15 +506,19 @@ def get_loginclass(name):
'''
if __grains__['kernel'] != 'OpenBSD':
return False
userinfo = __salt__['cmd.run_stdout']('userinfo {0}'.format(name),
output_loglevel='debug')
userinfo = __salt__['cmd.run_stdout'](
['userinfo', name],
python_shell=False)
for line in userinfo.splitlines():
if line.startswith("class"):
loginclass = line.split()
if len(loginclass) == 2:
return {'loginclass': loginclass[1]}
if line.startswith('class'):
try:
ret = line.split(None, 1)[1]
break
except ValueError:
continue
else:
return {'loginclass': '""'}
ret = ''
return ret
def _format_info(data):
@ -608,13 +582,12 @@ def rename(name, new_name):
'''
current_info = info(name)
if not current_info:
raise CommandExecutionError('User {0!r} does not exist'.format(name))
raise CommandExecutionError('User \'{0}\' does not exist'.format(name))
new_info = info(new_name)
if new_info:
raise CommandExecutionError('User {0!r} already exists'.format(new_name))
cmd = 'usermod -l {0} {1}'.format(new_name, name)
__salt__['cmd.run'](cmd)
post_info = info(new_name)
if post_info['name'] != current_info['name']:
return post_info['name'] == new_name
return False
raise CommandExecutionError(
'User \'{0}\' already exists'.format(new_name)
)
cmd = ['usermod', '-l', '{0}'.format(new_name), '{0}'.format(name)]
__salt__['cmd.run'](cmd, python_shell=False)
return info(name).get('name') == new_name