Beispiel #1
0
    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
Beispiel #2
0
    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
Beispiel #3
0
    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
Beispiel #4
0
    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)
Beispiel #5
0
    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
Beispiel #6
0
    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)