%PDF- %PDF-
Direktori : /lib/python2.7/site-packages/salt/modules/ |
Current File : //lib/python2.7/site-packages/salt/modules/useradd.py |
# -*- coding: utf-8 -*- ''' Manage users with the useradd command .. important:: If you feel that Salt should be using this module to manage users on a minion, and it is using a different module (or gives an error similar to *'user.info' is not available*), see :ref:`here <module-provider-override>`. ''' from __future__ import absolute_import, print_function, unicode_literals try: import pwd HAS_PWD = True except ImportError: HAS_PWD = False import logging import copy # Import salt libs import salt.utils.data import salt.utils.files import salt.utils.decorators.path import salt.utils.stringutils import salt.utils.user from salt.exceptions import CommandExecutionError # Import 3rd-party libs from salt.ext import six log = logging.getLogger(__name__) # Define the module's virtual name __virtualname__ = 'user' def __virtual__(): ''' Set the user module if the kernel is Linux, OpenBSD, NetBSD or AIX ''' if HAS_PWD and __grains__['kernel'] in ('Linux', 'OpenBSD', 'NetBSD', 'AIX'): return __virtualname__ return (False, 'useradd execution module not loaded: either pwd python library not available or system not one of Linux, OpenBSD, NetBSD or AIX') def _quote_username(name): ''' Usernames can only contain ascii chars, so make sure we return a str type ''' if not isinstance(name, six.string_types): return str(name) # future lint: disable=blacklisted-function else: return salt.utils.stringutils.to_str(name) def _get_gecos(name): ''' Retrieve GECOS field info and return it in dictionary form ''' gecos_field = salt.utils.stringutils.to_unicode( pwd.getpwnam(_quote_username(name)).pw_gecos).split(',', 4) if not gecos_field: return {} else: # Assign empty strings for any unspecified trailing GECOS fields while len(gecos_field) < 5: gecos_field.append('') return {'fullname': salt.utils.data.decode(gecos_field[0]), 'roomnumber': salt.utils.data.decode(gecos_field[1]), 'workphone': salt.utils.data.decode(gecos_field[2]), 'homephone': salt.utils.data.decode(gecos_field[3]), 'other': salt.utils.data.decode(gecos_field[4])} def _build_gecos(gecos_dict): ''' Accepts a dictionary entry containing GECOS field names and their values, and returns a full GECOS comment string, to be used with usermod. ''' return '{0},{1},{2},{3},{4}'.format(gecos_dict.get('fullname', ''), gecos_dict.get('roomnumber', ''), gecos_dict.get('workphone', ''), gecos_dict.get('homephone', ''), gecos_dict.get('other', ''),).rstrip(',') def _update_gecos(name, key, value, root=None): ''' Common code to change a user's GECOS information ''' if value is None: value = '' elif not isinstance(value, six.string_types): value = six.text_type(value) else: value = salt.utils.stringutils.to_unicode(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] if root is not None and __grains__['kernel'] != 'AIX': cmd.extend(('-R', root)) __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, groups=None, home=None, shell=None, unique=True, system=False, fullname='', roomnumber='', workphone='', homephone='', other='', createhome=True, loginclass=None, root=None, nologinit=False): ''' Add a user to the minion CLI Example: .. code-block:: bash salt '*' user.add name <uid> <gid> <groups> <home> <shell> ''' cmd = ['useradd'] if shell: cmd.extend(['-s', shell]) if uid not in (None, ''): cmd.extend(['-u', uid]) if gid not in (None, ''): cmd.extend(['-g', gid]) elif groups is not None and name in groups: defs_file = '/etc/login.defs' if __grains__['kernel'] != 'OpenBSD': try: with salt.utils.files.fopen(defs_file) as fp_: for line in fp_: line = salt.utils.stringutils.to_unicode(line) if 'USERGROUPS_ENAB' not in line[:15]: continue if 'yes' in line: cmd.extend([ '-g', __salt__['file.group_to_gid'](name) ]) # We found what we wanted, let's break out of the loop break except OSError: log.debug( 'Error reading ' + defs_file, exc_info_on_loglevel=logging.DEBUG ) else: usermgmt_file = '/etc/usermgmt.conf' try: with salt.utils.files.fopen(usermgmt_file) as fp_: for line in fp_: line = salt.utils.stringutils.to_unicode(line) if 'group' not in line[:5]: continue cmd.extend([ '-g', line.split()[-1] ]) # 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 createhome: cmd.append('-m') elif (__grains__['kernel'] != 'NetBSD' and __grains__['kernel'] != 'OpenBSD'): cmd.append('-M') if nologinit: cmd.append('-l') if home is not None: cmd.extend(['-d', home]) if not unique and __grains__['kernel'] != 'AIX': cmd.append('-o') if (system and __grains__['kernel'] != 'NetBSD' and __grains__['kernel'] != 'OpenBSD'): cmd.append('-r') if __grains__['kernel'] == 'OpenBSD': if loginclass is not None: cmd.extend(['-L', loginclass]) cmd.append(name) if root is not None and __grains__['kernel'] != 'AIX': cmd.extend(('-R', root)) ret = __salt__['cmd.run_all'](cmd, python_shell=False) if ret['retcode'] != 0: return False # At this point, the user was successfully created, so return true # regardless of the outcome of the below functions. If there is a # problem wth changing any of the user's info below, it will be raised # in a future highstate call. If anyone has a better idea on how to do # this, feel free to change it, but I didn't think it was a good idea # to return False when the user was successfully created since A) the # user does exist, and B) running useradd again would result in a # nonzero exit status and be interpreted as a False result. if groups: chgroups(name, groups) if fullname: chfullname(name, fullname) if roomnumber: chroomnumber(name, roomnumber) if workphone: chworkphone(name, workphone) if homephone: chhomephone(name, homephone) if other: chother(name, other) return True def delete(name, remove=False, force=False, root=None): ''' Remove a user from the minion CLI Example: .. code-block:: bash salt '*' user.delete name remove=True force=True ''' cmd = ['userdel'] if remove: cmd.append('-r') if force and __grains__['kernel'] != 'OpenBSD' and __grains__['kernel'] != 'AIX': cmd.append('-f') cmd.append(name) if root is not None and __grains__['kernel'] != 'AIX': cmd.extend(('-R', root)) ret = __salt__['cmd.run_all'](cmd, python_shell=False) if ret['retcode'] == 0: # Command executed with no errors return True if ret['retcode'] == 12: # There's a known bug in Debian based distributions, at least, that # makes the command exit with 12, see: # https://bugs.launchpad.net/ubuntu/+source/shadow/+bug/1023509 if __grains__['os_family'] not in ('Debian',): return False if 'var/mail' in ret['stderr'] or 'var/spool/mail' in ret['stderr']: # We've hit the bug, let's log it and not fail log.debug( 'While the userdel exited with code 12, this is a known bug on ' 'debian based distributions. See http://goo.gl/HH3FzT' ) return True return False def getent(refresh=False): ''' Return the list of all info for all users CLI Example: .. code-block:: bash salt '*' user.getent ''' if 'user.getent' in __context__ and not refresh: return __context__['user.getent'] ret = [] for data in pwd.getpwall(): ret.append(_format_info(data)) __context__['user.getent'] = ret return ret def chuid(name, uid): ''' Change the uid for a named user CLI Example: .. code-block:: bash salt '*' user.chuid foo 4376 ''' pre_info = info(name) if uid == pre_info['uid']: return True cmd = ['usermod', '-u', uid, name] __salt__['cmd.run'](cmd, python_shell=False) return info(name).get('uid') == uid def chgid(name, gid, root=None): ''' Change the default group of the user CLI Example: .. code-block:: bash salt '*' user.chgid foo 4376 ''' pre_info = info(name) if gid == pre_info['gid']: return True cmd = ['usermod', '-g', gid, name] if root is not None and __grains__['kernel'] != 'AIX': cmd.extend(('-R', root)) __salt__['cmd.run'](cmd, python_shell=False) return info(name).get('gid') == gid def chshell(name, shell, root=None): ''' Change the default shell of the user CLI Example: .. code-block:: bash salt '*' user.chshell foo /bin/zsh ''' pre_info = info(name) if shell == pre_info['shell']: return True cmd = ['usermod', '-s', shell, name] if root is not None and __grains__['kernel'] != 'AIX': cmd.extend(('-R', root)) __salt__['cmd.run'](cmd, python_shell=False) return info(name).get('shell') == shell def chhome(name, home, persist=False, root=None): ''' Change the home directory of the user, pass True for persist to move files to the new home directory if the old home directory exist. CLI Example: .. code-block:: bash salt '*' user.chhome foo /home/users/foo True ''' pre_info = info(name) if home == pre_info['home']: return True cmd = ['usermod', '-d', home] if root is not None and __grains__['kernel'] != 'AIX': cmd.extend(('-R', root)) if persist and __grains__['kernel'] != 'OpenBSD': cmd.append('-m') cmd.append(name) __salt__['cmd.run'](cmd, python_shell=False) return info(name).get('home') == home def chgroups(name, groups, append=False, root=None): ''' Change the groups to which this user belongs 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 salt '*' user.chgroups foo wheel,root append=True ''' if isinstance(groups, six.string_types): groups = groups.split(',') ugrps = set(list_groups(name)) if ugrps == set(groups): return True cmd = ['usermod'] if __grains__['kernel'] != 'OpenBSD': if append and __grains__['kernel'] != 'AIX': cmd.append('-a') cmd.append('-G') else: if append: cmd.append('-G') else: cmd.append('-S') if append and __grains__['kernel'] == 'AIX': cmd.extend([','.join(ugrps) + ',' + ','.join(groups), name]) else: cmd.extend([','.join(groups), name]) if root is not None and __grains__['kernel'] != 'AIX': cmd.extend(('-R', root)) 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' and __grains__['kernel'] != 'AIX': if result['retcode'] != 0 and 'not found in' in result['stderr']: ret = True for group in groups: cmd = ['gpasswd', '-a', name, group] if __salt__['cmd.retcode'](cmd, python_shell=False) != 0: ret = False return ret return result['retcode'] == 0 def chfullname(name, fullname): ''' Change the user's Full Name CLI Example: .. code-block:: bash salt '*' user.chfullname foo "Foo Bar" ''' return _update_gecos(name, 'fullname', fullname) def chroomnumber(name, roomnumber): ''' Change the user's Room Number CLI Example: .. code-block:: bash salt '*' user.chroomnumber foo 123 ''' return _update_gecos(name, 'roomnumber', roomnumber) def chworkphone(name, workphone): ''' Change the user's Work Phone CLI Example: .. code-block:: bash salt '*' user.chworkphone foo 7735550123 ''' return _update_gecos(name, 'workphone', workphone) def chhomephone(name, homephone): ''' Change the user's Home Phone CLI Example: .. code-block:: bash salt '*' user.chhomephone foo 7735551234 ''' return _update_gecos(name, 'homephone', homephone) def chother(name, other): ''' Change the user's other GECOS attribute CLI Example: .. code-block:: bash salt '*' user.chother foobar ''' return _update_gecos(name, 'other', other) def chloginclass(name, loginclass, root=None): ''' Change the default login class of the user .. note:: This function only applies to OpenBSD systems. CLI Example: .. code-block:: bash salt '*' user.chloginclass foo staff ''' if __grains__['kernel'] != 'OpenBSD': return False if loginclass == get_loginclass(name): return True cmd = ['usermod', '-L', loginclass, name] if root is not None: cmd.extend(('-R', root)) __salt__['cmd.run'](cmd, python_shell=False) return get_loginclass(name) == loginclass def info(name): ''' Return user information CLI Example: .. code-block:: bash salt '*' user.info root ''' try: data = pwd.getpwnam(_quote_username(name)) except KeyError: return {} else: return _format_info(data) def get_loginclass(name): ''' Get the login class of the user .. note:: This function only applies to OpenBSD systems. CLI Example: .. code-block:: bash salt '*' user.get_loginclass foo ''' if __grains__['kernel'] != 'OpenBSD': return False userinfo = __salt__['cmd.run_stdout']( ['userinfo', name], python_shell=False) for line in userinfo.splitlines(): if line.startswith('class'): try: ret = line.split(None, 1)[1] break except (ValueError, IndexError): continue else: ret = '' return ret def _format_info(data): ''' Return user information in a pretty way ''' # Put GECOS info into a list gecos_field = salt.utils.stringutils.to_unicode(data.pw_gecos).split(',', 4) # Make sure our list has at least five elements while len(gecos_field) < 5: gecos_field.append('') return {'gid': data.pw_gid, 'groups': list_groups(data.pw_name), 'home': data.pw_dir, 'name': data.pw_name, 'passwd': data.pw_passwd, 'shell': data.pw_shell, 'uid': data.pw_uid, 'fullname': gecos_field[0], 'roomnumber': gecos_field[1], 'workphone': gecos_field[2], 'homephone': gecos_field[3], 'other': gecos_field[4]} @salt.utils.decorators.path.which('id') def primary_group(name): ''' Return the primary group of the named user .. versionadded:: 2016.3.0 CLI Example: .. code-block:: bash salt '*' user.primary_group saltadmin ''' return __salt__['cmd.run'](['id', '-g', '-n', name]) def list_groups(name): ''' Return a list of groups the named user belongs to CLI Example: .. code-block:: bash salt '*' user.list_groups foo ''' return salt.utils.user.get_group_list(name) def list_users(): ''' Return a list of all users CLI Example: .. code-block:: bash salt '*' user.list_users ''' return sorted([user.pw_name for user in pwd.getpwall()]) def rename(name, new_name, root=None): ''' Change the username for a named user CLI Example: .. code-block:: bash salt '*' user.rename name new_name ''' current_info = info(name) if not current_info: raise CommandExecutionError('User \'{0}\' does not exist'.format(name)) new_info = info(new_name) if new_info: raise CommandExecutionError( 'User \'{0}\' already exists'.format(new_name) ) cmd = ['usermod', '-l', new_name, name] if root is not None and __grains__['kernel'] != 'AIX': cmd.extend(('-R', root)) __salt__['cmd.run'](cmd, python_shell=False) return info(name).get('name') == new_name