From aa3309ef5920db2c5ed8b26b8e142da1c84c49e0 Mon Sep 17 00:00:00 2001 From: Erik Johnson Date: Thu, 5 Oct 2017 13:37:27 -0500 Subject: [PATCH] Move several functions from salt.utils to salt.utils.user These functions are as follows: - salt.utils.get_user - salt.utils.get_uid - salt.utils.get_specific_user - salt.utils.chugid - salt.utils.chugid_and_umask - salt.utils.get_default_group - salt.utils.get_group_list - salt.utils.get_group_dict - salt.utils.get_gid_list - salt.utils.get_gid --- salt/auth/__init__.py | 7 +- salt/auth/pam.py | 4 +- salt/client/__init__.py | 5 +- salt/client/mixins.py | 5 +- salt/cloud/cli.py | 6 +- salt/config/__init__.py | 7 +- salt/crypt.py | 5 +- salt/daemons/masterapi.py | 8 +- salt/key.py | 9 +- salt/master.py | 7 +- salt/minion.py | 11 +- salt/modules/file.py | 3 +- salt/modules/mac_user.py | 3 +- salt/modules/npm.py | 13 +- salt/modules/pw_user.py | 5 +- salt/modules/rabbitmq.py | 68 +-- salt/modules/solaris_user.py | 5 +- salt/modules/useradd.py | 4 +- salt/modules/win_file.py | 4 +- salt/runner.py | 4 +- salt/states/user.py | 5 +- salt/utils/__init__.py | 521 ++++++++------------ salt/utils/gitfs.py | 6 +- salt/utils/parsers.py | 3 +- salt/utils/user.py | 341 +++++++++++++ salt/utils/verify.py | 10 +- tests/integration/modules/test_linux_acl.py | 7 +- tests/unit/modules/test_pw_user.py | 2 +- tests/unit/modules/test_useradd.py | 2 +- 29 files changed, 651 insertions(+), 429 deletions(-) create mode 100644 salt/utils/user.py diff --git a/salt/auth/__init__.py b/salt/auth/__init__.py index 2ab9ad9967f..81a979dd248 100644 --- a/salt/auth/__init__.py +++ b/salt/auth/__init__.py @@ -33,6 +33,7 @@ import salt.utils.args import salt.utils.files import salt.utils.minions import salt.utils.versions +import salt.utils.user import salt.payload log = logging.getLogger(__name__) @@ -333,7 +334,7 @@ class LoadAuth(object): log.warning(error_msg) return False else: - if auth_key != key[salt.utils.get_user()]: + if auth_key != key[salt.utils.user.get_user()]: log.warning(error_msg) return False return True @@ -695,7 +696,7 @@ class Resolver(object): # Use current user if empty if 'username' in ret and not ret['username']: - ret['username'] = salt.utils.get_user() + ret['username'] = salt.utils.user.get_user() return ret @@ -766,7 +767,7 @@ class AuthUser(object): Returns True if the user is the same user as the one running this process and False if not. ''' - return self.user == salt.utils.get_user() + return self.user == salt.utils.user.get_user() def sudo_name(self): ''' diff --git a/salt/auth/pam.py b/salt/auth/pam.py index 8387e910f88..4a65cd1adb8 100644 --- a/salt/auth/pam.py +++ b/salt/auth/pam.py @@ -42,7 +42,7 @@ from ctypes import c_void_p, c_uint, c_char_p, c_char, c_int from ctypes.util import find_library # Import Salt libs -import salt.utils # Can be removed once get_group_list is moved +import salt.utils.user from salt.ext.six.moves import range # pylint: disable=import-error,redefined-builtin # Import 3rd-party libs @@ -214,4 +214,4 @@ def groups(username, *args, **kwargs): Uses system groups ''' - return salt.utils.get_group_list(username) + return salt.utils.user.get_group_list(username) diff --git a/salt/client/__init__.py b/salt/client/__init__.py index da32b4181df..834ee7577f8 100644 --- a/salt/client/__init__.py +++ b/salt/client/__init__.py @@ -32,12 +32,13 @@ import salt.cache import salt.payload import salt.transport import salt.loader -import salt.utils +import salt.utils # Can be removed once ip_bracket is moved import salt.utils.args import salt.utils.event import salt.utils.files import salt.utils.minions import salt.utils.platform +import salt.utils.user import salt.utils.verify import salt.utils.versions import salt.utils.jid @@ -157,7 +158,7 @@ class LocalClient(object): ) self.opts = salt.config.client_config(c_path) self.serial = salt.payload.Serial(self.opts) - self.salt_user = salt.utils.get_specific_user() + self.salt_user = salt.utils.user.get_specific_user() self.skip_perm_errors = skip_perm_errors self.key = self.__read_master_key() self.auto_reconnect = auto_reconnect diff --git a/salt/client/mixins.py b/salt/client/mixins.py index f5fc1d22f71..c16ec72f53b 100644 --- a/salt/client/mixins.py +++ b/salt/client/mixins.py @@ -16,7 +16,7 @@ import copy as pycopy # Import Salt libs import salt.exceptions import salt.minion -import salt.utils # Can be removed once daemonize, get_specific_user, format_call are moved +import salt.utils # Can be removed once daemonize, format_call are moved import salt.utils.args import salt.utils.doc import salt.utils.error @@ -27,6 +27,7 @@ import salt.utils.lazy import salt.utils.platform import salt.utils.process import salt.utils.state +import salt.utils.user import salt.utils.versions import salt.transport import salt.log.setup @@ -96,7 +97,7 @@ class ClientFuncsDict(collections.MutableMapping): async_pub = self.client._gen_async_pub(pub_data.get(u'__pub_jid')) - user = salt.utils.get_specific_user() + user = salt.utils.user.get_specific_user() return self.client._proc_function( key, low, diff --git a/salt/cloud/cli.py b/salt/cloud/cli.py index 7c85edce412..cd9b35fbbf6 100644 --- a/salt/cloud/cli.py +++ b/salt/cloud/cli.py @@ -21,13 +21,13 @@ from salt.ext.six.moves import input # Import salt libs import salt.cloud -import salt.utils.cloud import salt.config import salt.defaults.exitcodes import salt.output import salt.syspaths as syspaths -import salt.utils +import salt.utils.cloud import salt.utils.parsers +import salt.utils.user from salt.exceptions import SaltCloudException, SaltCloudSystemExit from salt.utils.verify import check_user, verify_env, verify_files, verify_log @@ -48,7 +48,7 @@ class SaltCloud(salt.utils.parsers.SaltCloudParser): salt_master_user = self.config.get('user') if salt_master_user is None: - salt_master_user = salt.utils.get_user() + salt_master_user = salt.utils.user.get_user() if not check_user(salt_master_user): self.error( diff --git a/salt/config/__init__.py b/salt/config/__init__.py index f47b0ef373f..2caf21a4030 100644 --- a/salt/config/__init__.py +++ b/salt/config/__init__.py @@ -24,13 +24,14 @@ from salt.ext.six.moves.urllib.parse import urlparse # pylint: enable=import-error,no-name-in-module # Import salt libs -import salt.utils +import salt.utils # Can be removed once is_dictlist, ip_bracket are moved import salt.utils.dictupdate import salt.utils.files import salt.utils.network import salt.utils.path import salt.utils.platform import salt.utils.stringutils +import salt.utils.user import salt.utils.validate.path import salt.utils.xdg import salt.utils.yamlloader as yamlloader @@ -69,7 +70,7 @@ if salt.utils.platform.is_windows(): else: _DFLT_IPC_MODE = 'ipc' _MASTER_TRIES = 1 - _MASTER_USER = salt.utils.get_user() + _MASTER_USER = salt.utils.user.get_user() def _gather_buffer_space(): @@ -1145,7 +1146,7 @@ DEFAULT_MINION_OPTS = { 'always_verify_signature': False, 'master_sign_key_name': 'master_sign', 'syndic_finger': '', - 'user': salt.utils.get_user(), + 'user': salt.utils.user.get_user(), 'root_dir': salt.syspaths.ROOT_DIR, 'pki_dir': os.path.join(salt.syspaths.CONFIG_DIR, 'pki', 'minion'), 'id': '', diff --git a/salt/crypt.py b/salt/crypt.py index f5f945a7da2..3d002a38471 100644 --- a/salt/crypt.py +++ b/salt/crypt.py @@ -50,13 +50,14 @@ import salt.defaults.exitcodes import salt.payload import salt.transport.client import salt.transport.frame -import salt.utils +import salt.utils # Can be removed when pem_finger, reinit_crypto are moved import salt.utils.decorators import salt.utils.event import salt.utils.files import salt.utils.rsax931 import salt.utils.sdb import salt.utils.stringutils +import salt.utils.user import salt.utils.verify import salt.version from salt.exceptions import ( @@ -858,7 +859,7 @@ class AsyncAuth(object): self.opts[u'master'] ) m_pub_fn = os.path.join(self.opts[u'pki_dir'], self.mpub) - uid = salt.utils.get_uid(self.opts.get(u'user', None)) + uid = salt.utils.user.get_uid(self.opts.get(u'user', None)) with salt.utils.files.fpopen(m_pub_fn, u'wb+', uid=uid) as wfh: wfh.write(salt.utils.stringutils.to_bytes(payload[u'pub_key'])) return True diff --git a/salt/daemons/masterapi.py b/salt/daemons/masterapi.py index 07b2738b799..56fb0c33689 100644 --- a/salt/daemons/masterapi.py +++ b/salt/daemons/masterapi.py @@ -15,7 +15,7 @@ import stat # Import salt libs import salt.crypt -import salt.utils +import salt.utils # Can be removed once check_whitelist_blacklist, expr_match, get_values_of_matching_keys are moved import salt.cache import salt.client import salt.payload @@ -29,6 +29,7 @@ import salt.key import salt.fileserver import salt.utils.args import salt.utils.atomicfile +import salt.utils.dictupdate import salt.utils.event import salt.utils.files import salt.utils.gitfs @@ -38,6 +39,7 @@ import salt.utils.gzip_util import salt.utils.jid import salt.utils.minions import salt.utils.platform +import salt.utils.user import salt.utils.verify from salt.defaults import DEFAULT_TARGET_DELIM from salt.pillar import git_pillar @@ -227,7 +229,7 @@ def access_keys(opts): acl_users = set(publisher_acl.keys()) if opts.get('user'): acl_users.add(opts['user']) - acl_users.add(salt.utils.get_user()) + acl_users.add(salt.utils.user.get_user()) for user in acl_users: log.info('Preparing the %s key for local communication', user) key = mk_key(opts, user) @@ -286,7 +288,7 @@ class AutoKey(object): pwnam = pwd.getpwnam(user) uid = pwnam[2] gid = pwnam[3] - groups = salt.utils.get_gid_list(user, include_default=False) + groups = salt.utils.user.get_gid_list(user, include_default=False) except KeyError: log.error( 'Failed to determine groups for user {0}. The user is not ' diff --git a/salt/key.py b/salt/key.py index 40881e479a2..4d315a9e74e 100644 --- a/salt/key.py +++ b/salt/key.py @@ -26,8 +26,9 @@ import salt.utils import salt.utils.args import salt.utils.event import salt.utils.files +import salt.utils.kinds import salt.utils.sdb -import salt.utils.kinds as kinds +import salt.utils.user # pylint: disable=import-error,no-name-in-module,redefined-builtin from salt.ext import six @@ -145,7 +146,7 @@ class KeyCLI(object): low.update(res) low[u'eauth'] = self.opts[u'eauth'] else: - low[u'user'] = salt.utils.get_specific_user() + low[u'user'] = salt.utils.user.get_specific_user() low[u'key'] = salt.utils.get_master_key(low[u'user'], self.opts, skip_perm_errors) self.auth = low @@ -364,7 +365,7 @@ class Key(object): def __init__(self, opts, io_loop=None): self.opts = opts kind = self.opts.get(u'__role', u'') # application kind - if kind not in kinds.APPL_KINDS: + if kind not in salt.utils.kinds.APPL_KINDS: emsg = (u"Invalid application kind = '{0}'.".format(kind)) log.error(emsg + u'\n') raise ValueError(emsg) @@ -1000,7 +1001,7 @@ class RaetKey(Key): cache.flush(u'{0}/{1}'.format(self.ACC, minion)) kind = self.opts.get(u'__role', u'') # application kind - if kind not in kinds.APPL_KINDS: + if kind not in salt.utils.kinds.APPL_KINDS: emsg = (u"Invalid application kind = '{0}'.".format(kind)) log.error(emsg + u'\n') raise ValueError(emsg) diff --git a/salt/master.py b/salt/master.py index 19e11002c20..af868ea1344 100644 --- a/salt/master.py +++ b/salt/master.py @@ -77,6 +77,7 @@ import salt.utils.minions import salt.utils.platform import salt.utils.process import salt.utils.schedule +import salt.utils.user import salt.utils.verify import salt.utils.zeromq from salt.defaults import DEFAULT_TARGET_DELIM @@ -507,7 +508,7 @@ class Master(SMaster): Turn on the master server components ''' self._pre_flight() - log.info(u'salt-master is starting as user \'%s\'', salt.utils.get_user()) + log.info(u'salt-master is starting as user \'%s\'', salt.utils.user.get_user()) enable_sigusr1_handler() enable_sigusr2_handler() @@ -1703,7 +1704,7 @@ class ClearFuncs(object): if salt.auth.AuthUser(username).is_sudo(): username = self.opts.get(u'user', u'root') else: - username = salt.utils.get_user() + username = salt.utils.user.get_user() # Authorized. Do the job! try: @@ -1758,7 +1759,7 @@ class ClearFuncs(object): if salt.auth.AuthUser(username).is_sudo(): username = self.opts.get(u'user', u'root') else: - username = salt.utils.get_user() + username = salt.utils.user.get_user() # Authorized. Do the job! try: diff --git a/salt/minion.py b/salt/minion.py index 1760fb4c8e6..82f7a9db3a0 100644 --- a/salt/minion.py +++ b/salt/minion.py @@ -98,6 +98,7 @@ import salt.utils.minions import salt.utils.network import salt.utils.platform import salt.utils.schedule +import salt.utils.user import salt.utils.zeromq import salt.defaults.exitcodes import salt.cli.daemons @@ -1112,7 +1113,7 @@ class Minion(MinionBase): self.mod_opts = self._prep_mod_opts() self.matcher = Matcher(self.opts, self.functions) self.beacons = salt.beacons.Beacon(self.opts, self.functions) - uid = salt.utils.get_uid(user=self.opts.get(u'user', None)) + uid = salt.utils.user.get_uid(user=self.opts.get(u'user', None)) self.proc_dir = get_proc_dir(self.opts[u'cachedir'], uid=uid) self.schedule = salt.utils.schedule.Schedule( @@ -1445,7 +1446,7 @@ class Minion(MinionBase): if not hasattr(minion_instance, u'serial'): minion_instance.serial = salt.payload.Serial(opts) if not hasattr(minion_instance, u'proc_dir'): - uid = salt.utils.get_uid(user=opts.get(u'user', None)) + uid = salt.utils.user.get_uid(user=opts.get(u'user', None)) minion_instance.proc_dir = ( get_proc_dir(opts[u'cachedir'], uid=uid) ) @@ -2022,7 +2023,7 @@ class Minion(MinionBase): try: log.info( u'%s is starting as user \'%s\'', - self.__class__.__name__, salt.utils.get_user() + self.__class__.__name__, salt.utils.user.get_user() ) except Exception as err: # Only windows is allowed to fail here. See #3189. Log as debug in @@ -3321,7 +3322,7 @@ class ProxyMinion(Minion): self.mod_opts = self._prep_mod_opts() self.matcher = Matcher(self.opts, self.functions) self.beacons = salt.beacons.Beacon(self.opts, self.functions) - uid = salt.utils.get_uid(user=self.opts.get(u'user', None)) + uid = salt.utils.user.get_uid(user=self.opts.get(u'user', None)) self.proc_dir = get_proc_dir(self.opts[u'cachedir'], uid=uid) if self.connected and self.opts[u'pillar']: @@ -3467,7 +3468,7 @@ class ProxyMinion(Minion): if not hasattr(minion_instance, u'serial'): minion_instance.serial = salt.payload.Serial(opts) if not hasattr(minion_instance, u'proc_dir'): - uid = salt.utils.get_uid(user=opts.get(u'user', None)) + uid = salt.utils.user.get_uid(user=opts.get(u'user', None)) minion_instance.proc_dir = ( get_proc_dir(opts[u'cachedir'], uid=uid) ) diff --git a/salt/modules/file.py b/salt/modules/file.py index 20edba8460e..0ca2a16dc69 100644 --- a/salt/modules/file.py +++ b/salt/modules/file.py @@ -59,6 +59,7 @@ import salt.utils.platform import salt.utils.stringutils import salt.utils.templates import salt.utils.url +import salt.utils.user from salt.exceptions import CommandExecutionError, MinionError, SaltInvocationError, get_error_message as _get_error_message from salt.utils.files import HASHES, HASHES_REVMAP @@ -289,7 +290,7 @@ def user_to_uid(user): salt '*' file.user_to_uid root ''' if user is None: - user = salt.utils.get_user() + user = salt.utils.user.get_user() try: if isinstance(user, int): return user diff --git a/salt/modules/mac_user.py b/salt/modules/mac_user.py index 9fe8d443bbf..930ee36b16c 100644 --- a/salt/modules/mac_user.py +++ b/salt/modules/mac_user.py @@ -27,6 +27,7 @@ import salt.utils import salt.utils.args import salt.utils.decorators.path import salt.utils.stringutils +import salt.utils.user from salt.utils.locales import sdecode as _sdecode from salt.exceptions import CommandExecutionError, SaltInvocationError @@ -454,7 +455,7 @@ def list_groups(name): salt '*' user.list_groups foo ''' - groups = [group for group in salt.utils.get_group_list(name)] + groups = [group for group in salt.utils.user.get_group_list(name)] return groups diff --git a/salt/modules/npm.py b/salt/modules/npm.py index 4a0bbf9c91d..5e430f26365 100644 --- a/salt/modules/npm.py +++ b/salt/modules/npm.py @@ -15,6 +15,7 @@ import logging # Import salt libs import salt.utils import salt.utils.path +import salt.utils.user import salt.modules.cmdmod from salt.exceptions import CommandExecutionError from salt.utils.versions import LooseVersion as _LooseVersion @@ -156,7 +157,7 @@ def install(pkg=None, env = env or {} if runas: - uid = salt.utils.get_uid(runas) + uid = salt.utils.user.get_uid(runas) if uid: env.update({'SUDO_UID': b'{0}'.format(uid), 'SUDO_USER': b''}) @@ -235,7 +236,7 @@ def uninstall(pkg, dir=None, runas=None, env=None): env = env or {} if runas: - uid = salt.utils.get_uid(runas) + uid = salt.utils.user.get_uid(runas) if uid: env.update({'SUDO_UID': b'{0}'.format(uid), 'SUDO_USER': b''}) @@ -294,7 +295,7 @@ def list_(pkg=None, dir=None, runas=None, env=None, depth=None): env = env or {} if runas: - uid = salt.utils.get_uid(runas) + uid = salt.utils.user.get_uid(runas) if uid: env.update({'SUDO_UID': b'{0}'.format(uid), 'SUDO_USER': b''}) @@ -357,7 +358,7 @@ def cache_clean(path=None, runas=None, env=None, force=False): env = env or {} if runas: - uid = salt.utils.get_uid(runas) + uid = salt.utils.user.get_uid(runas) if uid: env.update({'SUDO_UID': b'{0}'.format(uid), 'SUDO_USER': b''}) @@ -404,7 +405,7 @@ def cache_list(path=None, runas=None, env=None): env = env or {} if runas: - uid = salt.utils.get_uid(runas) + uid = salt.utils.user.get_uid(runas) if uid: env.update({'SUDO_UID': b'{0}'.format(uid), 'SUDO_USER': b''}) @@ -444,7 +445,7 @@ def cache_path(runas=None, env=None): env = env or {} if runas: - uid = salt.utils.get_uid(runas) + uid = salt.utils.user.get_uid(runas) if uid: env.update({'SUDO_UID': b'{0}'.format(uid), 'SUDO_USER': b''}) diff --git a/salt/modules/pw_user.py b/salt/modules/pw_user.py index c63b97b7b1b..3d1ca978569 100644 --- a/salt/modules/pw_user.py +++ b/salt/modules/pw_user.py @@ -47,9 +47,10 @@ except ImportError: from salt.ext import six # Import salt libs -import salt.utils +import salt.utils # Can be removed once is_true is moved import salt.utils.args import salt.utils.locales +import salt.utils.user from salt.exceptions import CommandExecutionError log = logging.getLogger(__name__) @@ -489,7 +490,7 @@ def list_groups(name): salt '*' user.list_groups foo ''' - return salt.utils.get_group_list(name) + return salt.utils.user.get_group_list(name) def list_users(): diff --git a/salt/modules/rabbitmq.py b/salt/modules/rabbitmq.py index 8eb3351bea9..23fe2d3ea38 100644 --- a/salt/modules/rabbitmq.py +++ b/salt/modules/rabbitmq.py @@ -16,10 +16,10 @@ import random import string # Import salt libs -import salt.utils import salt.utils.itertools import salt.utils.path import salt.utils.platform +import salt.utils.user from salt.ext import six from salt.exceptions import SaltInvocationError from salt.ext.six.moves import range @@ -222,7 +222,7 @@ def list_users(runas=None): # Due to this, don't use a default value for # runas in Windows. if runas is None and not salt.utils.platform.is_windows(): - runas = salt.utils.get_user() + runas = salt.utils.user.get_user() res = __salt__['cmd.run_all']( [RABBITMQCTL, 'list_users', '-q'], runas=runas, @@ -245,7 +245,7 @@ def list_vhosts(runas=None): salt '*' rabbitmq.list_vhosts ''' if runas is None and not salt.utils.platform.is_windows(): - runas = salt.utils.get_user() + runas = salt.utils.user.get_user() res = __salt__['cmd.run_all']( [RABBITMQCTL, 'list_vhosts', '-q'], runas=runas, @@ -265,7 +265,7 @@ def user_exists(name, runas=None): salt '*' rabbitmq.user_exists rabbit_user ''' if runas is None and not salt.utils.platform.is_windows(): - runas = salt.utils.get_user() + runas = salt.utils.user.get_user() return name in list_users(runas=runas) @@ -280,7 +280,7 @@ def vhost_exists(name, runas=None): salt '*' rabbitmq.vhost_exists rabbit_host ''' if runas is None and not salt.utils.platform.is_windows(): - runas = salt.utils.get_user() + runas = salt.utils.user.get_user() return name in list_vhosts(runas=runas) @@ -303,7 +303,7 @@ def add_user(name, password=None, runas=None): string.ascii_uppercase + string.digits) for x in range(15)) if runas is None and not salt.utils.platform.is_windows(): - runas = salt.utils.get_user() + runas = salt.utils.user.get_user() if salt.utils.platform.is_windows(): # On Windows, if the password contains a special character @@ -351,7 +351,7 @@ def delete_user(name, runas=None): salt '*' rabbitmq.delete_user rabbit_user ''' if runas is None and not salt.utils.platform.is_windows(): - runas = salt.utils.get_user() + runas = salt.utils.user.get_user() res = __salt__['cmd.run_all']( [RABBITMQCTL, 'delete_user', name], python_shell=False, @@ -372,7 +372,7 @@ def change_password(name, password, runas=None): salt '*' rabbitmq.change_password rabbit_user password ''' if runas is None and not salt.utils.platform.is_windows(): - runas = salt.utils.get_user() + runas = salt.utils.user.get_user() if salt.utils.platform.is_windows(): # On Windows, if the password contains a special character # such as '|', normal execution will fail. For example: @@ -408,7 +408,7 @@ def clear_password(name, runas=None): salt '*' rabbitmq.clear_password rabbit_user ''' if runas is None and not salt.utils.platform.is_windows(): - runas = salt.utils.get_user() + runas = salt.utils.user.get_user() res = __salt__['cmd.run_all']( [RABBITMQCTL, 'clear_password', name], runas=runas, @@ -433,7 +433,7 @@ def check_password(name, password, runas=None): # try to get the rabbitmq-version - adapted from _get_rabbitmq_plugin if runas is None and not salt.utils.platform.is_windows(): - runas = salt.utils.get_user() + runas = salt.utils.user.get_user() try: res = __salt__['cmd.run']([RABBITMQCTL, 'status'], runas=runas, python_shell=False) @@ -508,7 +508,7 @@ def add_vhost(vhost, runas=None): salt '*' rabbitmq add_vhost '' ''' if runas is None and not salt.utils.platform.is_windows(): - runas = salt.utils.get_user() + runas = salt.utils.user.get_user() res = __salt__['cmd.run_all']( [RABBITMQCTL, 'add_vhost', vhost], runas=runas, @@ -529,7 +529,7 @@ def delete_vhost(vhost, runas=None): salt '*' rabbitmq.delete_vhost '' ''' if runas is None and not salt.utils.platform.is_windows(): - runas = salt.utils.get_user() + runas = salt.utils.user.get_user() res = __salt__['cmd.run_all']( [RABBITMQCTL, 'delete_vhost', vhost], runas=runas, @@ -549,7 +549,7 @@ def set_permissions(vhost, user, conf='.*', write='.*', read='.*', runas=None): salt '*' rabbitmq.set_permissions 'myvhost' 'myuser' ''' if runas is None and not salt.utils.platform.is_windows(): - runas = salt.utils.get_user() + runas = salt.utils.user.get_user() res = __salt__['cmd.run_all']( [RABBITMQCTL, 'set_permissions', '-p', vhost, user, conf, write, read], @@ -570,7 +570,7 @@ def list_permissions(vhost, runas=None): salt '*' rabbitmq.list_permissions '/myvhost' ''' if runas is None and not salt.utils.platform.is_windows(): - runas = salt.utils.get_user() + runas = salt.utils.user.get_user() res = __salt__['cmd.run_all']( [RABBITMQCTL, 'list_permissions', '-q', '-p', vhost], runas=runas, @@ -590,7 +590,7 @@ def list_user_permissions(name, runas=None): salt '*' rabbitmq.list_user_permissions 'user'. ''' if runas is None and not salt.utils.platform.is_windows(): - runas = salt.utils.get_user() + runas = salt.utils.user.get_user() res = __salt__['cmd.run_all']( [RABBITMQCTL, 'list_user_permissions', name, '-q'], runas=runas, @@ -609,7 +609,7 @@ def set_user_tags(name, tags, runas=None): salt '*' rabbitmq.set_user_tags 'myadmin' 'administrator' ''' if runas is None and not salt.utils.platform.is_windows(): - runas = salt.utils.get_user() + runas = salt.utils.user.get_user() if not isinstance(tags, (list, tuple)): tags = [tags] @@ -633,7 +633,7 @@ def status(runas=None): salt '*' rabbitmq.status ''' if runas is None and not salt.utils.platform.is_windows(): - runas = salt.utils.get_user() + runas = salt.utils.user.get_user() res = __salt__['cmd.run_all']( [RABBITMQCTL, 'status'], runas=runas, @@ -653,7 +653,7 @@ def cluster_status(runas=None): salt '*' rabbitmq.cluster_status ''' if runas is None and not salt.utils.platform.is_windows(): - runas = salt.utils.get_user() + runas = salt.utils.user.get_user() res = __salt__['cmd.run_all']( [RABBITMQCTL, 'cluster_status'], runas=runas, @@ -678,7 +678,7 @@ def join_cluster(host, user='rabbit', ram_node=None, runas=None): cmd.append('{0}@{1}'.format(user, host)) if runas is None and not salt.utils.platform.is_windows(): - runas = salt.utils.get_user() + runas = salt.utils.user.get_user() stop_app(runas) res = __salt__['cmd.run_all'](cmd, runas=runas, python_shell=False) start_app(runas) @@ -697,7 +697,7 @@ def stop_app(runas=None): salt '*' rabbitmq.stop_app ''' if runas is None and not salt.utils.platform.is_windows(): - runas = salt.utils.get_user() + runas = salt.utils.user.get_user() res = __salt__['cmd.run_all']( [RABBITMQCTL, 'stop_app'], runas=runas, @@ -717,7 +717,7 @@ def start_app(runas=None): salt '*' rabbitmq.start_app ''' if runas is None and not salt.utils.platform.is_windows(): - runas = salt.utils.get_user() + runas = salt.utils.user.get_user() res = __salt__['cmd.run_all']( [RABBITMQCTL, 'start_app'], runas=runas, @@ -737,7 +737,7 @@ def reset(runas=None): salt '*' rabbitmq.reset ''' if runas is None and not salt.utils.platform.is_windows(): - runas = salt.utils.get_user() + runas = salt.utils.user.get_user() res = __salt__['cmd.run_all']( [RABBITMQCTL, 'reset'], runas=runas, @@ -757,7 +757,7 @@ def force_reset(runas=None): salt '*' rabbitmq.force_reset ''' if runas is None and not salt.utils.platform.is_windows(): - runas = salt.utils.get_user() + runas = salt.utils.user.get_user() res = __salt__['cmd.run_all']( [RABBITMQCTL, 'force_reset'], runas=runas, @@ -777,7 +777,7 @@ def list_queues(runas=None, *args): salt '*' rabbitmq.list_queues messages consumers ''' if runas is None and not salt.utils.platform.is_windows(): - runas = salt.utils.get_user() + runas = salt.utils.user.get_user() cmd = [RABBITMQCTL, 'list_queues', '-q'] cmd.extend(args) res = __salt__['cmd.run_all'](cmd, runas=runas, python_shell=False) @@ -799,7 +799,7 @@ def list_queues_vhost(vhost, runas=None, *args): salt '*' rabbitmq.list_queues messages consumers ''' if runas is None and not salt.utils.platform.is_windows(): - runas = salt.utils.get_user() + runas = salt.utils.user.get_user() cmd = [RABBITMQCTL, 'list_queues', '-q', '-p', vhost] cmd.extend(args) res = __salt__['cmd.run_all'](cmd, runas=runas, python_shell=False) @@ -822,7 +822,7 @@ def list_policies(vhost="/", runas=None): ''' ret = {} if runas is None and not salt.utils.platform.is_windows(): - runas = salt.utils.get_user() + runas = salt.utils.user.get_user() res = __salt__['cmd.run_all']( [RABBITMQCTL, 'list_policies', '-q', '-p', vhost], runas=runas, @@ -864,7 +864,7 @@ def set_policy(vhost, name, pattern, definition, priority=None, apply_to=None, r salt '*' rabbitmq.set_policy / HA '.*' '{"ha-mode":"all"}' ''' if runas is None and not salt.utils.platform.is_windows(): - runas = salt.utils.get_user() + runas = salt.utils.user.get_user() if isinstance(definition, dict): definition = json.dumps(definition) if not isinstance(definition, six.string_types): @@ -895,7 +895,7 @@ def delete_policy(vhost, name, runas=None): salt '*' rabbitmq.delete_policy / HA' ''' if runas is None and not salt.utils.platform.is_windows(): - runas = salt.utils.get_user() + runas = salt.utils.user.get_user() res = __salt__['cmd.run_all']( [RABBITMQCTL, 'clear_policy', '-p', vhost, name], runas=runas, @@ -917,7 +917,7 @@ def policy_exists(vhost, name, runas=None): salt '*' rabbitmq.policy_exists / HA ''' if runas is None and not salt.utils.platform.is_windows(): - runas = salt.utils.get_user() + runas = salt.utils.user.get_user() policies = list_policies(runas=runas) return bool(vhost in policies and name in policies[vhost]) @@ -933,7 +933,7 @@ def list_available_plugins(runas=None): salt '*' rabbitmq.list_available_plugins ''' if runas is None and not salt.utils.platform.is_windows(): - runas = salt.utils.get_user() + runas = salt.utils.user.get_user() cmd = [_get_rabbitmq_plugin(), 'list', '-m'] ret = __salt__['cmd.run_all'](cmd, python_shell=False, runas=runas) _check_response(ret) @@ -951,7 +951,7 @@ def list_enabled_plugins(runas=None): salt '*' rabbitmq.list_enabled_plugins ''' if runas is None and not salt.utils.platform.is_windows(): - runas = salt.utils.get_user() + runas = salt.utils.user.get_user() cmd = [_get_rabbitmq_plugin(), 'list', '-m', '-e'] ret = __salt__['cmd.run_all'](cmd, python_shell=False, runas=runas) _check_response(ret) @@ -969,7 +969,7 @@ def plugin_is_enabled(name, runas=None): salt '*' rabbitmq.plugin_is_enabled rabbitmq_plugin_name ''' if runas is None and not salt.utils.platform.is_windows(): - runas = salt.utils.get_user() + runas = salt.utils.user.get_user() return name in list_enabled_plugins(runas) @@ -984,7 +984,7 @@ def enable_plugin(name, runas=None): salt '*' rabbitmq.enable_plugin foo ''' if runas is None and not salt.utils.platform.is_windows(): - runas = salt.utils.get_user() + runas = salt.utils.user.get_user() cmd = [_get_rabbitmq_plugin(), 'enable', name] ret = __salt__['cmd.run_all'](cmd, runas=runas, python_shell=False) return _format_response(ret, 'Enabled') @@ -1001,7 +1001,7 @@ def disable_plugin(name, runas=None): salt '*' rabbitmq.disable_plugin foo ''' if runas is None and not salt.utils.platform.is_windows(): - runas = salt.utils.get_user() + runas = salt.utils.user.get_user() cmd = [_get_rabbitmq_plugin(), 'disable', name] ret = __salt__['cmd.run_all'](cmd, runas=runas, python_shell=False) return _format_response(ret, 'Disabled') diff --git a/salt/modules/solaris_user.py b/salt/modules/solaris_user.py index e52c82c1377..276dcbe9160 100644 --- a/salt/modules/solaris_user.py +++ b/salt/modules/solaris_user.py @@ -22,7 +22,8 @@ import copy import logging # Import salt libs -import salt.utils +import salt.utils # Can be removed once is_true is moved +import salt.utils.user from salt.ext import six from salt.exceptions import CommandExecutionError @@ -431,7 +432,7 @@ def list_groups(name): salt '*' user.list_groups foo ''' - return salt.utils.get_group_list(name) + return salt.utils.user.get_group_list(name) def list_users(): diff --git a/salt/modules/useradd.py b/salt/modules/useradd.py index 24900f34801..4f74d48cc4d 100644 --- a/salt/modules/useradd.py +++ b/salt/modules/useradd.py @@ -19,10 +19,10 @@ import logging import copy # Import salt libs -import salt.utils # Can be removed when get_group_list is moved import salt.utils.files import salt.utils.decorators.path import salt.utils.locales +import salt.utils.user from salt.exceptions import CommandExecutionError # Import 3rd-party libs @@ -623,7 +623,7 @@ def list_groups(name): salt '*' user.list_groups foo ''' - return salt.utils.get_group_list(name) + return salt.utils.user.get_group_list(name) def list_users(): diff --git a/salt/modules/win_file.py b/salt/modules/win_file.py index 26fa0a3c1a9..ba047e5da06 100644 --- a/salt/modules/win_file.py +++ b/salt/modules/win_file.py @@ -42,9 +42,9 @@ from salt.exceptions import CommandExecutionError, SaltInvocationError # pylint: enable=W0611 # Import salt libs -import salt.utils import salt.utils.path import salt.utils.platform +import salt.utils.user from salt.modules.file import (check_hash, # pylint: disable=W0611 directory_exists, get_managed, check_managed, check_managed_changes, source_list, @@ -496,7 +496,7 @@ def user_to_uid(user): salt '*' file.user_to_uid myusername ''' if user is None: - user = salt.utils.get_user() + user = salt.utils.user.get_user() return salt.utils.win_dacl.get_sid_string(user) diff --git a/salt/runner.py b/salt/runner.py index fa71e520ee5..6a44cd5f2d2 100644 --- a/salt/runner.py +++ b/salt/runner.py @@ -12,10 +12,10 @@ import logging import salt.exceptions import salt.loader import salt.minion -import salt.utils # Can be removed when get_specific_user is moved import salt.utils.args import salt.utils.event import salt.utils.files +import salt.utils.user from salt.client import mixins from salt.output import display_output from salt.utils.lazy import verify_fun @@ -230,7 +230,7 @@ class Runner(RunnerClient): low.update(res) low[u'eauth'] = self.opts[u'eauth'] else: - user = salt.utils.get_specific_user() + user = salt.utils.user.get_specific_user() if low[u'fun'] == u'state.orchestrate': low[u'kwarg'][u'orchestration_jid'] = async_pub[u'jid'] diff --git a/salt/states/user.py b/salt/states/user.py index 027b587802b..1328960675c 100644 --- a/salt/states/user.py +++ b/salt/states/user.py @@ -29,8 +29,9 @@ import os import logging # Import Salt libs -import salt.utils +import salt.utils # Can be removed once date_format is moved import salt.utils.platform +import salt.utils.user from salt.utils.locales import sdecode, sdecode_if_string # Import 3rd-party libs @@ -799,7 +800,7 @@ def absent(name, purge=False, force=False): ret['result'] = None ret['comment'] = 'User {0} set for removal'.format(name) return ret - beforegroups = set(salt.utils.get_group_list(name)) + beforegroups = set(salt.utils.user.get_group_list(name)) ret['result'] = __salt__['user.delete'](name, purge, force) aftergroups = set([g for g in beforegroups if __salt__['group.info'](g)]) if ret['result']: diff --git a/salt/utils/__init__.py b/salt/utils/__init__.py index c83bccdf6fa..2284f5d19d8 100644 --- a/salt/utils/__init__.py +++ b/salt/utils/__init__.py @@ -31,7 +31,6 @@ import time import types import string import subprocess -import getpass # Import 3rd-party libs from salt.ext import six @@ -66,7 +65,6 @@ except ImportError: try: import parsedatetime - HAS_PARSEDATETIME = True except ImportError: HAS_PARSEDATETIME = False @@ -77,26 +75,6 @@ try: except ImportError: HAS_WIN32API = False -try: - import salt.utils.win_functions - HAS_WIN_FUNCTIONS = True -except ImportError: - HAS_WIN_FUNCTIONS = False - -try: - import grp - HAS_GRP = True -except ImportError: - # grp is not available on windows - HAS_GRP = False - -try: - import pwd - HAS_PWD = True -except ImportError: - # pwd is not available on windows - HAS_PWD = False - try: import setproctitle HAS_SETPROCTITLE = True @@ -166,141 +144,6 @@ def get_context(template, line, num_lines=5, marker=None): return u'---\n{0}\n---'.format(u'\n'.join(buf)) -def get_user(): - ''' - Get the current user - ''' - if HAS_PWD: - return pwd.getpwuid(os.geteuid()).pw_name - elif HAS_WIN_FUNCTIONS and salt.utils.win_functions.HAS_WIN32: - return salt.utils.win_functions.get_current_user() - else: - raise CommandExecutionError("Required external libraries not found. Need 'pwd' or 'win32api") - - -@jinja_filter('get_uid') -def get_uid(user=None): - """ - Get the uid for a given user name. If no user given, - the current euid will be returned. If the user - does not exist, None will be returned. On - systems which do not support pwd or os.geteuid - it will return None. - """ - if not HAS_PWD: - result = None - elif user is None: - try: - result = os.geteuid() - except AttributeError: - result = None - else: - try: - u_struct = pwd.getpwnam(user) - except KeyError: - result = None - else: - result = u_struct.pw_uid - return result - - -def get_gid(group=None): - """ - Get the gid for a given group name. If no group given, - the current egid will be returned. If the group - does not exist, None will be returned. On - systems which do not support grp or os.getegid - it will return None. - """ - if grp is None: - result = None - elif group is None: - try: - result = os.getegid() - except AttributeError: - result = None - else: - try: - g_struct = grp.getgrnam(group) - except KeyError: - result = None - else: - result = g_struct.gr_gid - return result - - -def _win_user_token_is_admin(user_token): - ''' - Using the win32 api, determine if the user with token 'user_token' has - administrator rights. - - See MSDN entry here: - http://msdn.microsoft.com/en-us/library/aa376389(VS.85).aspx - ''' - class SID_IDENTIFIER_AUTHORITY(ctypes.Structure): - _fields_ = [ - ("byte0", ctypes.c_byte), - ("byte1", ctypes.c_byte), - ("byte2", ctypes.c_byte), - ("byte3", ctypes.c_byte), - ("byte4", ctypes.c_byte), - ("byte5", ctypes.c_byte), - ] - nt_authority = SID_IDENTIFIER_AUTHORITY() - nt_authority.byte5 = 5 - - SECURITY_BUILTIN_DOMAIN_RID = 0x20 - DOMAIN_ALIAS_RID_ADMINS = 0x220 - administrators_group = ctypes.c_void_p() - if ctypes.windll.advapi32.AllocateAndInitializeSid( - ctypes.byref(nt_authority), - 2, - SECURITY_BUILTIN_DOMAIN_RID, - DOMAIN_ALIAS_RID_ADMINS, - 0, 0, 0, 0, 0, 0, - ctypes.byref(administrators_group)) == 0: - raise Exception("AllocateAndInitializeSid failed") - - try: - is_admin = ctypes.wintypes.BOOL() - if ctypes.windll.advapi32.CheckTokenMembership( - user_token, - administrators_group, - ctypes.byref(is_admin)) == 0: - raise Exception("CheckTokenMembership failed") - return is_admin.value != 0 - - finally: - ctypes.windll.advapi32.FreeSid(administrators_group) - - -def _win_current_user_is_admin(): - ''' - ctypes.windll.shell32.IsUserAnAdmin() is intentionally avoided due to this - function being deprecated. - ''' - return _win_user_token_is_admin(0) - - -def get_specific_user(): - ''' - Get a user name for publishing. If you find the user is "root" attempt to be - more specific - ''' - import salt.utils.platform - user = get_user() - if salt.utils.platform.is_windows(): - if _win_current_user_is_admin(): - return 'sudo_{0}'.format(user) - else: - env_vars = ('SUDO_USER',) - if user == 'root': - for evar in env_vars: - if evar in os.environ: - return 'sudo_{0}'.format(os.environ[evar]) - return user - - def get_master_key(key_user, opts, skip_perm_errors=False): # Late import to avoid circular import. import salt.utils.files @@ -1859,107 +1702,6 @@ def repack_dictlist(data, return ret -def get_default_group(user): - if HAS_GRP is False or HAS_PWD is False: - # We don't work on platforms that don't have grp and pwd - # Just return an empty list - return None - return grp.getgrgid(pwd.getpwnam(user).pw_gid).gr_name - - -def get_group_list(user=None, include_default=True): - ''' - Returns a list of all of the system group names of which the user - is a member. - ''' - if HAS_GRP is False or HAS_PWD is False: - # We don't work on platforms that don't have grp and pwd - # Just return an empty list - return [] - group_names = None - ugroups = set() - if not isinstance(user, six.string_types): - raise Exception - if hasattr(os, 'getgrouplist'): - # Try os.getgrouplist, available in python >= 3.3 - log.trace('Trying os.getgrouplist for \'{0}\''.format(user)) - try: - group_names = [ - grp.getgrgid(grpid).gr_name for grpid in - os.getgrouplist(user, pwd.getpwnam(user).pw_gid) - ] - except Exception: - pass - else: - # Try pysss.getgrouplist - log.trace('Trying pysss.getgrouplist for \'{0}\''.format(user)) - try: - import pysss # pylint: disable=import-error - group_names = list(pysss.getgrouplist(user)) - except Exception: - pass - if group_names is None: - # Fall back to generic code - # Include the user's default group to behave like - # os.getgrouplist() and pysss.getgrouplist() do - log.trace('Trying generic group list for \'{0}\''.format(user)) - group_names = [g.gr_name for g in grp.getgrall() if user in g.gr_mem] - try: - default_group = get_default_group(user) - if default_group not in group_names: - group_names.append(default_group) - except KeyError: - # If for some reason the user does not have a default group - pass - ugroups.update(group_names) - if include_default is False: - # Historically, saltstack code for getting group lists did not - # include the default group. Some things may only want - # supplemental groups, so include_default=False omits the users - # default group. - try: - default_group = grp.getgrgid(pwd.getpwnam(user).pw_gid).gr_name - ugroups.remove(default_group) - except KeyError: - # If for some reason the user does not have a default group - pass - log.trace('Group list for user \'{0}\': \'{1}\''.format(user, sorted(ugroups))) - return sorted(ugroups) - - -def get_group_dict(user=None, include_default=True): - ''' - Returns a dict of all of the system groups as keys, and group ids - as values, of which the user is a member. - E.g.: {'staff': 501, 'sudo': 27} - ''' - if HAS_GRP is False or HAS_PWD is False: - # We don't work on platforms that don't have grp and pwd - # Just return an empty dict - return {} - group_dict = {} - group_names = get_group_list(user, include_default=include_default) - for group in group_names: - group_dict.update({group: grp.getgrnam(group).gr_gid}) - return group_dict - - -def get_gid_list(user=None, include_default=True): - ''' - Returns a list of all of the system group IDs of which the user - is a member. - ''' - if HAS_GRP is False or HAS_PWD is False: - # We don't work on platforms that don't have grp and pwd - # Just return an empty list - return [] - gid_list = [ - gid for (group, gid) in - six.iteritems(salt.utils.get_group_dict(user, include_default=include_default)) - ] - return sorted(set(gid_list)) - - def total_seconds(td): ''' Takes a timedelta and returns the total number of seconds @@ -1990,77 +1732,6 @@ def appendproctitle(name): setproctitle.setproctitle(setproctitle.getproctitle() + ' ' + name) -def chugid(runas): - ''' - Change the current process to belong to - the imputed user (and the groups he belongs to) - ''' - uinfo = pwd.getpwnam(runas) - supgroups = [] - supgroups_seen = set() - - # The line below used to exclude the current user's primary gid. - # However, when root belongs to more than one group - # this causes root's primary group of '0' to be dropped from - # his grouplist. On FreeBSD, at least, this makes some - # command executions fail with 'access denied'. - # - # The Python documentation says that os.setgroups sets only - # the supplemental groups for a running process. On FreeBSD - # this does not appear to be strictly true. - group_list = get_group_dict(runas, include_default=True) - if sys.platform == 'darwin': - group_list = dict((k, v) for k, v in six.iteritems(group_list) - if not k.startswith('_')) - for group_name in group_list: - gid = group_list[group_name] - if (gid not in supgroups_seen - and not supgroups_seen.add(gid)): - supgroups.append(gid) - - if os.getgid() != uinfo.pw_gid: - try: - os.setgid(uinfo.pw_gid) - except OSError as err: - raise CommandExecutionError( - 'Failed to change from gid {0} to {1}. Error: {2}'.format( - os.getgid(), uinfo.pw_gid, err - ) - ) - - # Set supplemental groups - if sorted(os.getgroups()) != sorted(supgroups): - try: - os.setgroups(supgroups) - except OSError as err: - raise CommandExecutionError( - 'Failed to set supplemental groups to {0}. Error: {1}'.format( - supgroups, err - ) - ) - - if os.getuid() != uinfo.pw_uid: - try: - os.setuid(uinfo.pw_uid) - except OSError as err: - raise CommandExecutionError( - 'Failed to change from uid {0} to {1}. Error: {2}'.format( - os.getuid(), uinfo.pw_uid, err - ) - ) - - -def chugid_and_umask(runas, umask): - ''' - Helper method for for subprocess.Popen to initialise uid/gid and umask - for the new process. - ''' - if runas is not None and runas != getpass.getuser(): - chugid(runas) - if umask is not None: - os.umask(umask) - - def human_size_to_bytes(human_size): ''' Convert human-readable units to bytes @@ -3297,3 +2968,195 @@ def check_state_result(running, recurse=False, highstate=None): return salt.utils.state.check_result( running, recurse=recurse, highstate=highstate ) + + +def get_user(): + ''' + Returns the current user + + .. deprecated:: Oxygen + ''' + # Late import to avoid circular import. + import salt.utils.versions + import salt.utils.user + salt.utils.versions.warn_until( + 'Neon', + 'Use of \'salt.utils.get_user\' detected. This function ' + 'has been moved to \'salt.utils.user.get_user\' as of ' + 'Salt Oxygen. This warning will be removed in Salt Neon.' + ) + return salt.utils.user.get_user() + + +def get_uid(user=None): + ''' + Get the uid for a given user name. If no user given, the current euid will + be returned. If the user does not exist, None will be returned. On systems + which do not support pwd or os.geteuid, None will be returned. + + .. deprecated:: Oxygen + ''' + # Late import to avoid circular import. + import salt.utils.versions + import salt.utils.user + salt.utils.versions.warn_until( + 'Neon', + 'Use of \'salt.utils.get_uid\' detected. This function ' + 'has been moved to \'salt.utils.user.get_uid\' as of ' + 'Salt Oxygen. This warning will be removed in Salt Neon.' + ) + return salt.utils.user.get_uid(user) + + +def get_specific_user(): + ''' + Get a user name for publishing. If you find the user is "root" attempt to be + more specific by checking if Salt is being run as root via sudo. + + .. deprecated:: Oxygen + ''' + # Late import to avoid circular import. + import salt.utils.versions + import salt.utils.user + salt.utils.versions.warn_until( + 'Neon', + 'Use of \'salt.utils.get_specific_user\' detected. This function ' + 'has been moved to \'salt.utils.user.get_specific_user\' as of ' + 'Salt Oxygen. This warning will be removed in Salt Neon.' + ) + return salt.utils.user.get_specific_user() + + +def chugid(runas): + ''' + Change the current process to belong to the specified user (and the groups + to which it belongs) + + .. deprecated:: Oxygen + ''' + # Late import to avoid circular import. + import salt.utils.versions + import salt.utils.user + salt.utils.versions.warn_until( + 'Neon', + 'Use of \'salt.utils.chugid\' detected. This function ' + 'has been moved to \'salt.utils.user.chugid\' as of ' + 'Salt Oxygen. This warning will be removed in Salt Neon.' + ) + return salt.utils.user.chugid(runas) + + +def chugid_and_umask(runas, umask): + ''' + Helper method for for subprocess.Popen to initialise uid/gid and umask + for the new process. + + .. deprecated:: Oxygen + ''' + # Late import to avoid circular import. + import salt.utils.versions + import salt.utils.user + salt.utils.versions.warn_until( + 'Neon', + 'Use of \'salt.utils.chugid_and_umask\' detected. This function ' + 'has been moved to \'salt.utils.user.chugid_and_umask\' as of ' + 'Salt Oxygen. This warning will be removed in Salt Neon.' + ) + return salt.utils.user.chugid_and_umask(runas, umask) + + +def get_default_group(user): + ''' + Returns the specified user's default group. If the user doesn't exist, a + KeyError will be raised. + + .. deprecated:: Oxygen + ''' + # Late import to avoid circular import. + import salt.utils.versions + import salt.utils.user + salt.utils.versions.warn_until( + 'Neon', + 'Use of \'salt.utils.get_default_group\' detected. This function ' + 'has been moved to \'salt.utils.user.get_default_group\' as of ' + 'Salt Oxygen. This warning will be removed in Salt Neon.' + ) + return salt.utils.user.get_default_group(user) + + +def get_group_list(user, include_default=True): + ''' + Returns a list of all of the system group names of which the user + is a member. + + .. deprecated:: Oxygen + ''' + # Late import to avoid circular import. + import salt.utils.versions + import salt.utils.user + salt.utils.versions.warn_until( + 'Neon', + 'Use of \'salt.utils.get_group_list\' detected. This function ' + 'has been moved to \'salt.utils.user.get_group_list\' as of ' + 'Salt Oxygen. This warning will be removed in Salt Neon.' + ) + return salt.utils.user.get_group_list(user, include_default) + + +def get_group_dict(user=None, include_default=True): + ''' + Returns a dict of all of the system groups as keys, and group ids + as values, of which the user is a member. + E.g.: {'staff': 501, 'sudo': 27} + + .. deprecated:: Oxygen + ''' + # Late import to avoid circular import. + import salt.utils.versions + import salt.utils.user + salt.utils.versions.warn_until( + 'Neon', + 'Use of \'salt.utils.get_group_dict\' detected. This function ' + 'has been moved to \'salt.utils.user.get_group_dict\' as of ' + 'Salt Oxygen. This warning will be removed in Salt Neon.' + ) + return salt.utils.user.get_group_dict(user, include_default) + + +def get_gid_list(user, include_default=True): + ''' + Returns a list of all of the system group IDs of which the user + is a member. + + .. deprecated:: Oxygen + ''' + # Late import to avoid circular import. + import salt.utils.versions + import salt.utils.user + salt.utils.versions.warn_until( + 'Neon', + 'Use of \'salt.utils.get_gid_list\' detected. This function ' + 'has been moved to \'salt.utils.user.get_gid_list\' as of ' + 'Salt Oxygen. This warning will be removed in Salt Neon.' + ) + return salt.utils.user.get_gid_list(user, include_default) + + +def get_gid(group=None): + ''' + Get the gid for a given group name. If no group given, the current egid + will be returned. If the group does not exist, None will be returned. On + systems which do not support grp or os.getegid it will return None. + + .. deprecated:: Oxygen + ''' + # Late import to avoid circular import. + import salt.utils.versions + import salt.utils.user + salt.utils.versions.warn_until( + 'Neon', + 'Use of \'salt.utils.get_gid\' detected. This function ' + 'has been moved to \'salt.utils.user.get_gid\' as of ' + 'Salt Oxygen. This warning will be removed in Salt Neon.' + ) + return salt.utils.user.get_gid(group) diff --git a/salt/utils/gitfs.py b/salt/utils/gitfs.py index a0b0b20ca11..b38cce68570 100644 --- a/salt/utils/gitfs.py +++ b/salt/utils/gitfs.py @@ -20,14 +20,16 @@ import time from datetime import datetime # Import salt libs -import salt.utils +import salt.utils # Can be removed once check_whitelist_blacklist, get_hash, is_bin_file, repack_dictlist are moved import salt.utils.configparser import salt.utils.files +import salt.utils.gzip_util import salt.utils.itertools import salt.utils.path import salt.utils.platform import salt.utils.stringutils import salt.utils.url +import salt.utils.user import salt.utils.versions import salt.fileserver from salt.config import DEFAULT_MASTER_OPTS as _DEFAULT_MASTER_OPTS @@ -1494,7 +1496,7 @@ class Pygit2(GitProvider): # https://github.com/libgit2/libgit2/issues/2122 if "Error stat'ing config file" not in str(exc): raise - home = pwd.getpwnam(salt.utils.get_user()).pw_dir + home = pwd.getpwnam(salt.utils.user.get_user()).pw_dir pygit2.settings.search_path[pygit2.GIT_CONFIG_LEVEL_GLOBAL] = home self.repo = pygit2.Repository(self.cachedir) except KeyError: diff --git a/salt/utils/parsers.py b/salt/utils/parsers.py index cf926a015eb..7b2fca742ac 100644 --- a/salt/utils/parsers.py +++ b/salt/utils/parsers.py @@ -36,6 +36,7 @@ import salt.utils.files import salt.utils.jid import salt.utils.kinds as kinds import salt.utils.platform +import salt.utils.user import salt.utils.xdg from salt.defaults import DEFAULT_TARGET_DELIM from salt.utils.validate.path import is_writeable @@ -772,7 +773,7 @@ class LogLevelMixIn(six.with_metaclass(MixInMeta, object)): # Since we're not be able to write to the log file or its parent # directory (if the log file does not exit), are we the same user # as the one defined in the configuration file? - current_user = salt.utils.get_user() + current_user = salt.utils.user.get_user() if self.config['user'] != current_user: # Yep, not the same user! # Is the current user in ACL? diff --git a/salt/utils/user.py b/salt/utils/user.py new file mode 100644 index 00000000000..134a5658ecf --- /dev/null +++ b/salt/utils/user.py @@ -0,0 +1,341 @@ +# -*- coding: utf-8 -*- + +from __future__ import absolute_import + +# Import Python libs +import ctypes +import getpass +import logging +import os +import sys + +# Import Salt libs +import salt.utils.path +import salt.utils.platform +from salt.exceptions import CommandExecutionError +from salt.utils.decorators.jinja import jinja_filter + +# Import 3rd-party libs +from salt.ext import six + +# Conditional imports +try: + import pwd + HAS_PWD = True +except ImportError: + HAS_PWD = False + +try: + import grp + HAS_GRP = True +except ImportError: + HAS_GRP = False + +try: + import pysss + HAS_PYSSS = True +except ImportError: + HAS_PYSSS = False + +try: + import salt.utils.win_functions + HAS_WIN_FUNCTIONS = True +except ImportError: + HAS_WIN_FUNCTIONS = False + +log = logging.getLogger(__name__) + + +def get_user(): + ''' + Get the current user + ''' + if HAS_PWD: + return pwd.getpwuid(os.geteuid()).pw_name + elif HAS_WIN_FUNCTIONS and salt.utils.win_functions.HAS_WIN32: + return salt.utils.win_functions.get_current_user() + else: + raise CommandExecutionError( + 'Required external library (pwd or win32api) not installed') + + +@jinja_filter('get_uid') +def get_uid(user=None): + ''' + Get the uid for a given user name. If no user given, the current euid will + be returned. If the user does not exist, None will be returned. On systems + which do not support pwd or os.geteuid, None will be returned. + ''' + if not HAS_PWD: + return None + elif user is None: + try: + return os.geteuid() + except AttributeError: + return None + else: + try: + return pwd.getpwnam(user).pw_uid + except KeyError: + return None + + +def _win_user_token_is_admin(user_token): + ''' + Using the win32 api, determine if the user with token 'user_token' has + administrator rights. + + See MSDN entry here: + http://msdn.microsoft.com/en-us/library/aa376389(VS.85).aspx + ''' + class SID_IDENTIFIER_AUTHORITY(ctypes.Structure): + _fields_ = [ + ("byte0", ctypes.c_byte), + ("byte1", ctypes.c_byte), + ("byte2", ctypes.c_byte), + ("byte3", ctypes.c_byte), + ("byte4", ctypes.c_byte), + ("byte5", ctypes.c_byte), + ] + nt_authority = SID_IDENTIFIER_AUTHORITY() + nt_authority.byte5 = 5 + + SECURITY_BUILTIN_DOMAIN_RID = 0x20 + DOMAIN_ALIAS_RID_ADMINS = 0x220 + administrators_group = ctypes.c_void_p() + if ctypes.windll.advapi32.AllocateAndInitializeSid( + ctypes.byref(nt_authority), + 2, + SECURITY_BUILTIN_DOMAIN_RID, + DOMAIN_ALIAS_RID_ADMINS, + 0, 0, 0, 0, 0, 0, + ctypes.byref(administrators_group)) == 0: + raise Exception("AllocateAndInitializeSid failed") + + try: + is_admin = ctypes.wintypes.BOOL() + if ctypes.windll.advapi32.CheckTokenMembership( + user_token, + administrators_group, + ctypes.byref(is_admin)) == 0: + raise Exception("CheckTokenMembership failed") + return is_admin.value != 0 + + finally: + ctypes.windll.advapi32.FreeSid(administrators_group) + + +def _win_current_user_is_admin(): + ''' + ctypes.windll.shell32.IsUserAnAdmin() is intentionally avoided due to this + function being deprecated. + ''' + return _win_user_token_is_admin(0) + + +def get_specific_user(): + ''' + Get a user name for publishing. If you find the user is "root" attempt to be + more specific + ''' + user = get_user() + if salt.utils.platform.is_windows(): + if _win_current_user_is_admin(): + return 'sudo_{0}'.format(user) + else: + env_vars = ('SUDO_USER',) + if user == 'root': + for evar in env_vars: + if evar in os.environ: + return 'sudo_{0}'.format(os.environ[evar]) + return user + + +def chugid(runas): + ''' + Change the current process to belong to the specified user (and the groups + to which it belongs) + ''' + uinfo = pwd.getpwnam(runas) + supgroups = [] + supgroups_seen = set() + + # The line below used to exclude the current user's primary gid. + # However, when root belongs to more than one group + # this causes root's primary group of '0' to be dropped from + # his grouplist. On FreeBSD, at least, this makes some + # command executions fail with 'access denied'. + # + # The Python documentation says that os.setgroups sets only + # the supplemental groups for a running process. On FreeBSD + # this does not appear to be strictly true. + group_list = get_group_dict(runas, include_default=True) + if sys.platform == 'darwin': + group_list = dict((k, v) for k, v in six.iteritems(group_list) + if not k.startswith('_')) + for group_name in group_list: + gid = group_list[group_name] + if (gid not in supgroups_seen + and not supgroups_seen.add(gid)): + supgroups.append(gid) + + if os.getgid() != uinfo.pw_gid: + try: + os.setgid(uinfo.pw_gid) + except OSError as err: + raise CommandExecutionError( + 'Failed to change from gid {0} to {1}. Error: {2}'.format( + os.getgid(), uinfo.pw_gid, err + ) + ) + + # Set supplemental groups + if sorted(os.getgroups()) != sorted(supgroups): + try: + os.setgroups(supgroups) + except OSError as err: + raise CommandExecutionError( + 'Failed to set supplemental groups to {0}. Error: {1}'.format( + supgroups, err + ) + ) + + if os.getuid() != uinfo.pw_uid: + try: + os.setuid(uinfo.pw_uid) + except OSError as err: + raise CommandExecutionError( + 'Failed to change from uid {0} to {1}. Error: {2}'.format( + os.getuid(), uinfo.pw_uid, err + ) + ) + + +def chugid_and_umask(runas, umask): + ''' + Helper method for for subprocess.Popen to initialise uid/gid and umask + for the new process. + ''' + if runas is not None and runas != getpass.getuser(): + chugid(runas) + if umask is not None: + os.umask(umask) + + +def get_default_group(user): + ''' + Returns the specified user's default group. If the user doesn't exist, a + KeyError will be raised. + ''' + return grp.getgrgid(pwd.getpwnam(user).pw_gid).gr_name \ + if HAS_GRP and HAS_PWD \ + else None + + +def get_group_list(user, include_default=True): + ''' + Returns a list of all of the system group names of which the user + is a member. + ''' + if HAS_GRP is False or HAS_PWD is False: + return [] + group_names = None + ugroups = set() + if hasattr(os, 'getgrouplist'): + # Try os.getgrouplist, available in python >= 3.3 + log.trace('Trying os.getgrouplist for \'%s\'', user) + try: + group_names = [ + grp.getgrgid(grpid).gr_name for grpid in + os.getgrouplist(user, pwd.getpwnam(user).pw_gid) + ] + except Exception: + pass + elif HAS_PYSSS: + # Try pysss.getgrouplist + log.trace('Trying pysss.getgrouplist for \'%s\'', user) + try: + group_names = list(pysss.getgrouplist(user)) + except Exception: + pass + + if group_names is None: + # Fall back to generic code + # Include the user's default group to match behavior of + # os.getgrouplist() and pysss.getgrouplist() + log.trace('Trying generic group list for \'%s\'', user) + group_names = [g.gr_name for g in grp.getgrall() if user in g.gr_mem] + try: + default_group = get_default_group(user) + if default_group not in group_names: + group_names.append(default_group) + except KeyError: + # If for some reason the user does not have a default group + pass + + if group_names is not None: + ugroups.update(group_names) + + if include_default is False: + # Historically, saltstack code for getting group lists did not + # include the default group. Some things may only want + # supplemental groups, so include_default=False omits the users + # default group. + try: + default_group = grp.getgrgid(pwd.getpwnam(user).pw_gid).gr_name + ugroups.remove(default_group) + except KeyError: + # If for some reason the user does not have a default group + pass + log.trace('Group list for user \'%s\': %s', user, sorted(ugroups)) + return sorted(ugroups) + + +def get_group_dict(user=None, include_default=True): + ''' + Returns a dict of all of the system groups as keys, and group ids + as values, of which the user is a member. + E.g.: {'staff': 501, 'sudo': 27} + ''' + if HAS_GRP is False or HAS_PWD is False: + return {} + group_dict = {} + group_names = get_group_list(user, include_default=include_default) + for group in group_names: + group_dict.update({group: grp.getgrnam(group).gr_gid}) + return group_dict + + +def get_gid_list(user, include_default=True): + ''' + Returns a list of all of the system group IDs of which the user + is a member. + ''' + if HAS_GRP is False or HAS_PWD is False: + return [] + gid_list = list( + six.itervalues( + get_group_dict(user, include_default=include_default) + ) + ) + return sorted(set(gid_list)) + + +def get_gid(group=None): + ''' + Get the gid for a given group name. If no group given, the current egid + will be returned. If the group does not exist, None will be returned. On + systems which do not support grp or os.getegid it will return None. + ''' + if not HAS_GRP: + return None + if group is None: + try: + return os.getegid() + except AttributeError: + return None + else: + try: + return grp.getgrnam(group).gr_gid + except KeyError: + return None diff --git a/salt/utils/verify.py b/salt/utils/verify.py index 1559224e4d5..24e0decfc1f 100644 --- a/salt/utils/verify.py +++ b/salt/utils/verify.py @@ -27,9 +27,9 @@ from salt.log.setup import LOG_LEVELS from salt.exceptions import SaltClientError, SaltSystemExit, \ CommandExecutionError import salt.defaults.exitcodes -import salt.utils # Can be removed once get_jid_list and get_user are moved import salt.utils.files import salt.utils.platform +import salt.utils.user log = logging.getLogger(__name__) @@ -206,7 +206,7 @@ def verify_env(dirs, user, permissive=False, pki_dir='', skip_extra=False): pwnam = pwd.getpwnam(user) uid = pwnam[2] gid = pwnam[3] - groups = salt.utils.get_gid_list(user, include_default=False) + groups = salt.utils.user.get_gid_list(user, include_default=False) except KeyError: err = ('Failed to prepare the Salt environment for user ' @@ -302,7 +302,7 @@ def check_user(user): ''' if salt.utils.platform.is_windows(): return True - if user == salt.utils.get_user(): + if user == salt.utils.user.get_user(): return True import pwd # after confirming not running Windows try: @@ -311,7 +311,7 @@ def check_user(user): if hasattr(os, 'initgroups'): os.initgroups(user, pwuser.pw_gid) # pylint: disable=minimum-python-version else: - os.setgroups(salt.utils.get_gid_list(user, include_default=False)) + os.setgroups(salt.utils.user.get_gid_list(user, include_default=False)) os.setgid(pwuser.pw_gid) os.setuid(pwuser.pw_uid) @@ -383,7 +383,7 @@ def check_path_traversal(path, user='root', skip_perm_errors=False): if not os.path.exists(tpath): msg += ' Path does not exist.' else: - current_user = salt.utils.get_user() + current_user = salt.utils.user.get_user() # Make the error message more intelligent based on how # the user invokes salt-call or whatever other script. if user != current_user: diff --git a/tests/integration/modules/test_linux_acl.py b/tests/integration/modules/test_linux_acl.py index 8f4e448ed20..07b7f5142be 100644 --- a/tests/integration/modules/test_linux_acl.py +++ b/tests/integration/modules/test_linux_acl.py @@ -12,9 +12,8 @@ from tests.support.mixins import AdaptedConfigurationTestCaseMixin from tests.support.helpers import skip_if_binaries_missing # Import salt libs -import salt.utils import salt.utils.files -# from salt.modules import linux_acl as acl +import salt.utils.user # Acl package should be installed to test linux_acl module @@ -60,8 +59,8 @@ class LinuxAclModuleTest(ModuleCase, AdaptedConfigurationTestCaseMixin): def test_getfacl_w_single_file_without_acl(self): ret = self.run_function('acl.getfacl', arg=[self.myfile]) - user = salt.utils.get_user() - group = salt.utils.get_default_group(user) + user = salt.utils.user.get_user() + group = salt.utils.user.get_default_group(user) self.maxDiff = None self.assertEqual( ret, diff --git a/tests/unit/modules/test_pw_user.py b/tests/unit/modules/test_pw_user.py index b22196f3845..1b9fcc09035 100644 --- a/tests/unit/modules/test_pw_user.py +++ b/tests/unit/modules/test_pw_user.py @@ -316,7 +316,7 @@ class PwUserTestCase(TestCase, LoaderModuleMockMixin): ''' mock_group = 'saltgroup' - with patch('salt.utils.get_group_list', MagicMock(return_value=[mock_group])): + with patch('salt.utils.user.get_group_list', MagicMock(return_value=[mock_group])): self.assertEqual(pw_user.list_groups('name'), [mock_group]) def test_list_users(self): diff --git a/tests/unit/modules/test_useradd.py b/tests/unit/modules/test_useradd.py index 7fd457425fc..a7744899d22 100644 --- a/tests/unit/modules/test_useradd.py +++ b/tests/unit/modules/test_useradd.py @@ -355,7 +355,7 @@ class UserAddTestCase(TestCase, LoaderModuleMockMixin): ''' Test if it return a list of groups the named user belongs to ''' - with patch('salt.utils.get_group_list', MagicMock(return_value='Salt')): + with patch('salt.utils.user.get_group_list', MagicMock(return_value='Salt')): self.assertEqual(useradd.list_groups('name'), 'Salt') # 'list_users' function tests: 1