def set_user_password(self, request, user_id, user): token_id = request.context_dict.get('token_id') original_password = user.get('original_password') token_data = self.token_provider_api.validate_token(token_id) token_ref = token_model.KeystoneToken(token_id=token_id, token_data=token_data) if token_ref.user_id != user_id: raise exception.Forbidden('Token belongs to another user') if original_password is None: raise exception.ValidationError(target='user', attribute='original password') try: user_ref = self.identity_api.authenticate( request, user_id=token_ref.user_id, password=original_password) if not user_ref.get('enabled', True): # NOTE(dolph): why can't you set a disabled user's password? raise exception.Unauthorized('User is disabled') except AssertionError: raise exception.Unauthorized( _('v2.0 password change call failed ' 'due to rejected authentication')) update_dict = {'password': user['password'], 'id': user_id} old_admin = request.context.is_admin request.context.is_admin = True super(UserController, self).set_user_password(request, user_id, update_dict) request.context.is_admin = old_admin # Issue a new token based upon the original token data. This will # always be a V2.0 token. # NOTE(lbragstad): Since we just updated the password and presisted a # revocation event for the user changing the password, it is necessary # to wait one second before authenticating. This ensures we are in the # threshold of a new second before getting a new token. import time time.sleep(1) new_token_id, new_token_data = self.token_provider_api.issue_token( token_ref.user_id, token_ref.methods, project_id=token_ref.project_id, parent_audit_id=token_ref.audit_chain_id) v2_helper = common.V2TokenDataHelper() v2_token_data = v2_helper.v3_to_v2_token(new_token_data, new_token_id) LOG.debug('TOKEN_REF %s', new_token_data) return v2_token_data
def validate_token(self, request, token_id): """Check that a token is valid. Optionally, also ensure that it is owned by a specific tenant. Returns metadata about the token along any associated roles. """ # TODO(ayoung) validate against revocation API v3_token_response = self.token_provider_api.validate_token(token_id) v2_helper = common.V2TokenDataHelper() token = v2_helper.v3_to_v2_token(v3_token_response, token_id) belongs_to = request.params.get('belongsTo') if belongs_to: self._token_belongs_to(token, belongs_to) return token
def validate_token_head(self, request, token_id): """Check that a token is valid. Optionally, also ensure that it is owned by a specific tenant. Identical to ``validate_token``, except does not return a response. The code in ``keystone.common.wsgi.render_response`` will remove the content body. """ v3_token_response = self.token_provider_api.validate_token(token_id) v2_helper = common.V2TokenDataHelper() token = v2_helper.v3_to_v2_token(v3_token_response, token_id) belongs_to = request.params.get('belongsTo') if belongs_to: self._token_belongs_to(token, belongs_to) return token
def _authenticate_token(self, request, auth): """Try to authenticate using an already existing token. Returns auth_token_data, (user_ref, tenant_ref, metadata_ref) """ if 'token' not in auth: raise exception.ValidationError(attribute='token', target='auth') if 'id' not in auth['token']: raise exception.ValidationError(attribute='id', target='token') old_token = auth['token']['id'] if len(old_token) > CONF.max_token_size: raise exception.ValidationSizeError(attribute='token', size=CONF.max_token_size) try: v3_token_data = self.token_provider_api.validate_token(old_token) # NOTE(lbragstad): Even though we are not using the v2.0 token # reference after we translate it in v3_to_v2_token(), we still # need to perform that check. We have to do this because # v3_to_v2_token will ensure we don't use specific tokens only # attainable via v3 to get new tokens on v2.0. For example, an # exception would be raised if we passed a federated token to # v3_to_v2_token, because federated tokens aren't supported by # v2.0 (the same applies to OAuth tokens, domain-scoped tokens, # etc..). v2_helper = common.V2TokenDataHelper() v2_helper.v3_to_v2_token(v3_token_data, old_token) token_model_ref = token_model.KeystoneToken( token_id=old_token, token_data=v3_token_data) except exception.NotFound as e: raise exception.Unauthorized(e) wsgi.validate_token_bind(request.context_dict, token_model_ref) self._restrict_scope(token_model_ref) user_id = token_model_ref.user_id tenant_id = self._get_project_id_from_auth(auth) if not CONF.trust.enabled and 'trust_id' in auth: raise exception.Forbidden('Trusts are disabled.') elif CONF.trust.enabled and 'trust_id' in auth: try: trust_ref = self.trust_api.get_trust(auth['trust_id']) except exception.TrustNotFound: raise exception.Forbidden() if user_id != trust_ref['trustee_user_id']: raise exception.Forbidden() if (trust_ref['project_id'] and tenant_id != trust_ref['project_id']): raise exception.Forbidden() if ('expires' in trust_ref) and (trust_ref['expires']): expiry = trust_ref['expires'] if expiry < timeutils.parse_isotime(utils.isotime()): raise exception.Forbidden() user_id = trust_ref['trustor_user_id'] trustor_user_ref = self.identity_api.get_user( trust_ref['trustor_user_id']) if not trustor_user_ref['enabled']: raise exception.Forbidden() trustee_user_ref = self.identity_api.get_user( trust_ref['trustee_user_id']) if not trustee_user_ref['enabled']: raise exception.Forbidden() if trust_ref['impersonation'] is True: current_user_ref = trustor_user_ref else: current_user_ref = trustee_user_ref else: current_user_ref = self.identity_api.get_user(user_id) metadata_ref = {} tenant_ref, metadata_ref['roles'] = self._get_project_roles_and_ref( user_id, tenant_id) expiry = token_model_ref.expires if CONF.trust.enabled and 'trust_id' in auth: trust_id = auth['trust_id'] trust_roles = [] for role in trust_ref['roles']: if 'roles' not in metadata_ref: raise exception.Forbidden() if role['id'] in metadata_ref['roles']: trust_roles.append(role['id']) else: raise exception.Forbidden() if 'expiry' in trust_ref and trust_ref['expiry']: trust_expiry = timeutils.parse_isotime(trust_ref['expiry']) if trust_expiry < expiry: expiry = trust_expiry metadata_ref['roles'] = trust_roles metadata_ref['trustee_user_id'] = trust_ref['trustee_user_id'] metadata_ref['trust_id'] = trust_id bind = token_model_ref.bind audit_id = token_model_ref.audit_chain_id return (current_user_ref, tenant_ref, metadata_ref, expiry, bind, audit_id)
def authenticate(self, request, auth=None): """Authenticate credentials and return a token. Accept auth as a dict that looks like:: { "auth":{ "passwordCredentials":{ "username":"******", "password":"******" }, "tenantName":"customer-x" } } In this case, tenant is optional, if not provided the token will be considered "unscoped" and can later be used to get a scoped token. Alternatively, this call accepts auth with only a token and tenant that will return a token that is scoped to that tenant. """ method = authentication_method_generator(request, auth) user_ref, project_id, expiry, bind, audit_id = ( method.authenticate(request, auth) ) # Ensure the entities provided in the authentication information are # valid and not disabled. try: self.identity_api.assert_user_enabled( user_id=user_ref['id'], user=user_ref) if project_id: try: self.resource_api.get_project(project_id) except exception.ProjectNotFound: msg = _( 'Project ID not found: %(p_id)s' ) % {'p_id': project_id} raise exception.Unauthorized(msg) self.resource_api.assert_project_enabled(project_id) except AssertionError as e: six.reraise(exception.Unauthorized, exception.Unauthorized(e), sys.exc_info()[2]) # NOTE(morganfainberg): Make sure the data is in correct form since it # might be consumed external to Keystone and this is a v2.0 controller. # The user_ref is encoded into the auth_token_data which is returned as # part of the token data. The token provider doesn't care about the # format. user_ref = self.v3_to_v2_user(user_ref) auth_context = {} if bind: auth_context['bind'] = bind trust_ref = None if CONF.trust.enabled and 'trust_id' in auth: trust_ref = self.trust_api.get_trust(auth['trust_id']) (token_id, token_data) = self.token_provider_api.issue_token( user_ref['id'], ['password'], expires_at=expiry, project_id=project_id, trust=trust_ref, parent_audit_id=audit_id, auth_context=auth_context) v2_helper = common.V2TokenDataHelper() token_data = v2_helper.v3_to_v2_token(token_data, token_id) # NOTE(wanghong): We consume a trust use only when we are using trusts # and have successfully issued a token. if CONF.trust.enabled and 'trust_id' in auth: self.trust_api.consume_use(auth['trust_id']) return token_data
def authenticate(self, request, auth): """Try to authenticate using an already existing token. :param request: A request object. :param auth: Dictionary representing the authentication request. :returns: A tuple containing the user reference, project identifier, token expiration, bind information, and original audit information. """ if 'token' not in auth: raise exception.ValidationError( attribute='token', target='auth') if 'id' not in auth['token']: raise exception.ValidationError( attribute='id', target='token') old_token = auth['token']['id'] if len(old_token) > CONF.max_token_size: raise exception.ValidationSizeError(attribute='token', size=CONF.max_token_size) try: v3_token_data = self.token_provider_api.validate_token( old_token ) # NOTE(lbragstad): Even though we are not using the v2.0 token # reference after we translate it in v3_to_v2_token(), we still # need to perform that check. We have to do this because # v3_to_v2_token will ensure we don't use specific tokens only # attainable via v3 to get new tokens on v2.0. For example, an # exception would be raised if we passed a federated token to # v3_to_v2_token, because federated tokens aren't supported by # v2.0 (the same applies to OAuth tokens, domain-scoped tokens, # etc..). v2_helper = common.V2TokenDataHelper() v2_helper.v3_to_v2_token(v3_token_data, old_token) token_model_ref = token_model.KeystoneToken( token_id=old_token, token_data=v3_token_data ) except exception.NotFound as e: raise exception.Unauthorized(e) wsgi.validate_token_bind(request.context_dict, token_model_ref) self._restrict_scope(token_model_ref) user_id = token_model_ref.user_id project_id = self._get_project_id_from_auth(auth) if not CONF.trust.enabled and 'trust_id' in auth: raise exception.Forbidden('Trusts are disabled.') elif CONF.trust.enabled and 'trust_id' in auth: try: trust_ref = self.trust_api.get_trust(auth['trust_id']) except exception.TrustNotFound: raise exception.Forbidden() # If a trust is being used to obtain access to another project and # the other project doesn't match the project in the trust, we need # to bail because trusts are only good up to a single project. if (trust_ref['project_id'] and project_id != trust_ref['project_id']): raise exception.Forbidden() expiry = token_model_ref.expires user_ref = self.identity_api.get_user(user_id) bind = token_model_ref.bind original_audit_id = token_model_ref.audit_chain_id return (user_ref, project_id, expiry, bind, original_audit_id)