def create_token(self, creds): ''' Create token with creds. Token authorizes salt access if successful authentication with the credentials in creds. creds format is as follows: { 'username': '******', 'password': '******', 'eauth': 'eauthtypestring', } examples of valid eauth type strings: 'pam' or 'ldap' Returns dictionary of token information with the following format: { 'token': 'tokenstring', 'start': starttimeinfractionalseconds, 'expire': expiretimeinfactionalseconds, 'name': 'usernamestring', 'user': '******', 'username': '******', 'eauth': 'eauthtypestring', 'perms: permslistofstrings, } The perms list provides those parts of salt for which the user is authorised to execute. example perms list: [ "grains.*", "status.*", "sys.*", "test.*" ] ''' try: tokenage = self.resolver.mk_token(creds) except Exception as ex: raise EauthAuthenticationError( "Authentication failed with {0}.".format(repr(ex))) if not 'token' in tokenage: raise EauthAuthenticationError( "Authentication failed with provided credentials.") # Grab eauth config for the current backend for the current user if tokenage['name'] in self.opts['external_auth'][tokenage['eauth']]: tokenage['perms'] = self.opts['external_auth'][tokenage['eauth']][ tokenage['name']] else: tokenage['perms'] = self.opts['external_auth'][ tokenage['eauth']]['*'] tokenage['user'] = tokenage['name'] tokenage['username'] = tokenage['name'] return tokenage
def _check_pub_data(self, pub_data): ''' Common checks on the pub_data data structure returned from running pub ''' if not pub_data: raise EauthAuthenticationError( 'Failed to authenticate, is this user permitted to execute ' 'commands?' ) # Failed to connect to the master and send the pub if 'jid' not in pub_data: return {} if pub_data['jid'] == '0': print('Failed to connect to the Master, ' 'is the Salt Master running?') return {} # If we order masters (via a syndic), don't short circuit if no minions # are found if not self.opts.get('order_masters'): # Check for no minions if not pub_data['minions']: print('No minions matched the target. ' 'No command was sent, no jid was assigned.') return {} return pub_data
def verify_token(self, token): ''' If token is valid Then returns user name associated with token Else False. ''' try: result = self.resolver.get_token(token) except Exception as ex: raise EauthAuthenticationError( "Token validation failed with {0}.".format(repr(ex))) return result
def mk_token(self, user, passwd, eauth='pam'): ''' Run time_auth and create a token. Return False or the token ''' options = salt.config.master_config( os.path.join(syspaths.CONFIG_DIR, 'master')) resolver = salt.auth.Resolver(options) try: creds = dict(username=user, password=passwd, eauth=eauth) tokenage = resolver.mk_token(creds) except Exception as ex: raise EauthAuthenticationError( "Authentication failed with {0}.".format(repr(ex))) if 'token' not in tokenage: raise EauthAuthenticationError( "Authentication failed with provided credentials.") return tokenage['token']
def verify_token(self, token): """ If token is valid Then returns user name associated with token Else False. """ try: result = self.resolver.get_token(token) except Exception as ex: # pylint: disable=broad-except raise EauthAuthenticationError( "Token validation failed with {0}.".format(repr(ex)) ) return result
def _check_pub_data(self, pub_data): ''' Common checks on the pub_data data structure returned from running pub ''' if not pub_data: raise EauthAuthenticationError( 'Failed to authenticate, is this user permitted to execute ' 'commands?') # Failed to connect to the master and send the pub if not 'jid' in pub_data or pub_data['jid'] == '0': return {} return pub_data
def run(self, low): ''' Execute the specified function in the specified client by passing the lowstate ''' if 'client' not in low: raise SaltException('No client specified') if not ('token' in low or 'eauth' in low) and low['client'] != 'ssh': raise EauthAuthenticationError( 'No authentication credentials given') l_fun = getattr(self, low['client']) f_call = salt.utils.format_call(l_fun, low) return l_fun(*f_call.get('args', ()), **f_call.get('kwargs', {}))
def test_run_bad_login(self): ''' Test the run URL with bad auth credentials ''' cmd = dict(self.low, **{'totally': 'invalid_creds'}) body = urllib.urlencode(cmd) # Mock the interaction with Salt so we can focus on the API. with mock.patch.object(self.app.salt.netapi.NetapiClient, 'run', side_effect=EauthAuthenticationError('Oh noes!')): request, response = self.request('/run', method='POST', body=body, headers={ 'content-type': 'application/x-www-form-urlencoded' }) self.assertEqual(response.status, '401 Unauthorized')
def run(self, cmd): ''' Execute the salt command given by cmd dict. cmd is a dictionary of the following form: { 'mode': 'modestring', 'fun' : 'modulefunctionstring', 'kwarg': functionkeywordargdictionary, 'tgt' : 'targetpatternstring', 'expr_form' : 'targetpatterntype', 'ret' : 'returner namestring', 'timeout': 'functiontimeout', 'arg' : 'functionpositionalarg sequence', 'token': 'salttokenstring', 'username': '******', 'password': '******', 'eauth': 'eauthtypestring', } Implied by the fun is which client is used to run the command, that is, either the master local minion client, the master runner client, or the master wheel client. The cmd dict items are as follows: mode: either 'sync' or 'async'. Defaults to 'async' if missing fun: required. If the function is to be run on the master using either a wheel or runner client then the fun: includes either 'wheel.' or 'runner.' as a prefix and has three parts separated by '.'. Otherwise the fun: specifies a module to be run on a minion via the local minion client. Example: fun of 'wheel.config.values' run with master wheel client fun of 'runner.manage.status' run with master runner client fun of 'test.ping' run with local minion client fun of 'wheel.foobar' run with with local minion client not wheel kwarg: A dictionary of keyword function parameters to be passed to the eventual salt function specified by fun: tgt: Pattern string specifying the targeted minions when the implied client is local expr_form: Optional target pattern type string when client is local minion. Defaults to 'glob' if missing ret: Optional name string of returner when local minion client. arg: Optional positional argument string when local minion client token: the salt token. Either token: is required or the set of username:, password: , and eauth: username: the salt username. Required if token is missing. password: the user's password. Required if token is missing. eauth: the authentication type such as 'pam' or 'ldap'. Required if token is missing ''' cmd = dict(cmd) # make copy client = 'minion' # default to local minion client mode = cmd.get('mode', 'async') # default to 'async' # check for wheel or runner prefix to fun name to use wheel or runner client funparts = cmd.get('fun', '').split('.') if len(funparts) > 2 and funparts[0] in ['wheel', 'runner']: # master client = funparts[0] cmd['fun'] = '.'.join(funparts[1:]) # strip prefix if not ('token' in cmd or ('eauth' in cmd and 'password' in cmd and 'username' in cmd)): raise EauthAuthenticationError( 'No authentication credentials given') executor = getattr(self, '{0}_{1}'.format(client, mode)) result = executor(**cmd) return result