diff --git a/keystone/middleware/auth.py b/keystone/middleware/auth.py index ccf5980cb6..6f8b7d440a 100644 --- a/keystone/middleware/auth.py +++ b/keystone/middleware/auth.py @@ -156,6 +156,8 @@ class AuthContextMiddleware(auth_token.BaseAuthProtocol): if resp: return resp + if request.token_auth.user is not None: + request.set_user_headers(request.token_auth.user) # NOTE(jamielennox): function is split so testing can check errors from # fill_context. There is no actual reason for fill_context to raise @@ -164,13 +166,28 @@ class AuthContextMiddleware(auth_token.BaseAuthProtocol): # and the middleware_exceptions helper removed. self.fill_context(request) + def _keystone_specific_values(self, token, request_context): + if token.domain_scoped: + # Domain scoped tokens should never have is_admin_project set + # Even if KSA defaults it otherwise. The two mechanisms are + # parallel; only ione or the other should be used for access. + request_context.is_admin_project = False + request_context.domain_id = token.domain_id + request_context.domain_name = token.domain_name + if token.oauth_scoped: + request_context.is_delegated_auth = True + request_context.oauth_consumer_id = token.oauth_consumer_id + request_context.oauth_access_token_id = token.oauth_access_token_id + if token.trust_scoped: + request_context.is_delegated_auth = True + request_context.trust_id = token.trust_id + if token.is_federated_user: + request_context.group_ids = token.federation_group_ids + else: + request_context.group_ids = [] + def fill_context(self, request): # The request context stores itself in thread-local memory for logging. - request_context = context.RequestContext( - request_id=request.environ.get('openstack.request_id'), - authenticated=False, - overwrite=True) - request.environ[context.REQUEST_CONTEXT_ENV] = request_context if authorization.AUTH_CONTEXT_ENV in request.environ: msg = ('Auth context already exists in the request ' @@ -179,6 +196,13 @@ class AuthContextMiddleware(auth_token.BaseAuthProtocol): LOG.warning(msg) return + kwargs = { + 'authenticated': False, + 'overwrite': True} + request_context = context.RequestContext.from_environ( + request.environ, **kwargs) + request.environ[context.REQUEST_CONTEXT_ENV] = request_context + # NOTE(gyee): token takes precedence over SSL client certificates. # This will preserve backward compatibility with the existing # behavior. Tokenless authorization with X.509 SSL client @@ -190,10 +214,23 @@ class AuthContextMiddleware(auth_token.BaseAuthProtocol): auth_context = {} elif request.token_auth.has_user_token: + # Keystone enforces policy on some values that other services + # do not, and should not, use. This adds them in to the context. + token = token_model.KeystoneToken(token_id=request.user_token, + token_data=request.token_info) + self._keystone_specific_values(token, request_context) request_context.auth_token = request.user_token - ref = token_model.KeystoneToken(token_id=request.user_token, - token_data=request.token_info) - auth_context = authorization.token_to_auth_context(ref) + auth_context = request_context.to_policy_values() + additional = { + 'trust_id': request_context.trust_id, + 'trustor_id': request_context.trustor_id, + 'trustee_id': request_context.trustee_id, + 'domain_id': request_context._domain_id, + 'domain_name': request_context.domain_name, + 'group_ids': request_context.group_ids, + 'token': token + } + auth_context.update(additional) elif self._validate_trusted_issuer(request): auth_context = self._build_tokenless_auth_context(request) @@ -207,34 +244,6 @@ class AuthContextMiddleware(auth_token.BaseAuthProtocol): # set authenticated to flag to keystone that a token has been validated request_context.authenticated = True - # The attributes of request_context are put into the logs. This is a - # common pattern for all the OpenStack services. In all the other - # projects these are IDs, so set the attributes to IDs here rather than - # the name. - request_context.user_id = auth_context.get('user_id') - request_context.project_id = auth_context.get('project_id') - request_context.domain_id = auth_context.get('domain_id') - request_context.domain_name = auth_context.get('domain_name') - request_context.user_domain_id = auth_context.get('user_domain_id') - request_context.roles = auth_context.get('roles') - - is_admin_project = auth_context.get('is_admin_project', True) - request_context.is_admin_project = is_admin_project - - project_domain_id = auth_context.get('project_domain_id') - request_context.project_domain_id = project_domain_id - - is_delegated_auth = auth_context.get('is_delegated_auth', False) - request_context.is_delegated_auth = is_delegated_auth - - request_context.trust_id = auth_context.get('trust_id') - request_context.trustor_id = auth_context.get('trustor_id') - request_context.trustee_id = auth_context.get('trustee_id') - - access_token_id = auth_context.get('access_token_id') - request_context.oauth_consumer_id = auth_context.get('consumer_id') - request_context.oauth_acess_token_id = access_token_id - LOG.debug('RBAC: auth_context: %s', auth_context) request.environ[authorization.AUTH_CONTEXT_ENV] = auth_context diff --git a/keystone/tests/unit/test_v3.py b/keystone/tests/unit/test_v3.py index 64a074eef6..bffe311e1e 100644 --- a/keystone/tests/unit/test_v3.py +++ b/keystone/tests/unit/test_v3.py @@ -1300,10 +1300,14 @@ class AuthContextMiddlewareTestCase(RestfulTestCase): def test_unscoped_token_auth_context(self): unscoped_token = self.get_unscoped_token() req = self._middleware_request(unscoped_token) + # This check originally looked that the value was unset + # but that was an artifact of the custom context keystone + # used to create. Oslo-context will always provide the + # same set of keys, but the values will be None in an + # unscoped token for key in ['project_id', 'domain_id', 'domain_name']: - self.assertNotIn( - key, - req.environ.get(authorization.AUTH_CONTEXT_ENV)) + self.assertIsNone( + req.environ.get(authorization.AUTH_CONTEXT_ENV)[key]) def test_project_scoped_token_auth_context(self): project_scoped_token = self.get_scoped_token()