Enforce policy on oslo-context

Up to now, middleware/auth.py was overwriting the oslo
context generated from keystonemiddleware with a
dictionary that simulated the values of
oslo-context directly pulled out of the token.

As oslo-context gains logic, Keystone has started
to diverge from the rest of the projects that use
oslo-context.

Change-Id: Ie93392f8aabddf8c963b9cb08aaa006d28d37e22
This commit is contained in:
Adam Young 2017-11-29 17:36:48 -05:00 committed by ayoung
parent fd8f424481
commit f71a78db86
2 changed files with 52 additions and 39 deletions

View File

@ -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

View File

@ -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()