Reuse token_ref fetched in AuthContextMiddleware.

All keystone middleware components should reuse the auth context prepared
by AuthContextMiddleware. Note that AuthContextMiddleware is listed in
etc/keystone-paste.ini as one of the earliest middleware components in the
keystone pipeline. This means that almost all other middleware components
can depend on the auth context being available for use.

Also added a test to check that a trust cannot be retrived in case of a
missing auth context. Such a request should throw Forbidden exception.

Change-Id: I1c08976cf4d175fa2cfe2e39fe55811f04f13243
Closes-Bug: #1433211
This commit is contained in:
Deepti Ramakrishna 2015-06-11 13:32:52 -07:00
parent c387a9353e
commit 288a05a4de
8 changed files with 50 additions and 86 deletions

View File

@ -26,10 +26,10 @@ from six.moves import urllib
from keystone.assignment import schema
from keystone.common import controller
from keystone.common import dependency
from keystone.common import utils
from keystone.common import validation
from keystone import exception
from keystone.i18n import _, _LW
from keystone.models import token_model
from keystone import notifications
@ -51,14 +51,7 @@ class TenantAssignment(controller.V2Controller):
Doesn't care about token scopedness.
"""
try:
token_data = self.token_provider_api.validate_token(
context['token_id'])
token_ref = token_model.KeystoneToken(token_id=context['token_id'],
token_data=token_data)
except exception.NotFound as e:
LOG.warning(_LW('Authentication failed: %s'), e)
raise exception.Unauthorized(e)
token_ref = utils.get_token_ref(context)
tenant_refs = (
self.assignment_api.list_projects_for_user(token_ref.user_id))

View File

@ -690,19 +690,7 @@ class V3Controller(wsgi.Application):
if context['query_string'].get('domain_id') is not None:
return context['query_string'].get('domain_id')
try:
token_ref = token_model.KeystoneToken(
token_id=context['token_id'],
token_data=self.token_provider_api.validate_token(
context['token_id']))
except KeyError:
raise exception.ValidationError(
_('domain_id is required as part of entity'))
except (exception.TokenNotFound,
exception.UnsupportedTokenVersionException):
LOG.warning(_LW('Invalid token found while getting domain ID '
'for list request'))
raise exception.Unauthorized()
token_ref = utils.get_token_ref(context)
if token_ref.domain_scoped:
return token_ref.domain_id
@ -719,25 +707,7 @@ class V3Controller(wsgi.Application):
being used.
"""
# We could make this more efficient by loading the domain_id
# into the context in the wrapper function above (since
# this version of normalize_domain will only be called inside
# a v3 protected call). However, this optimization is probably not
# worth the duplication of state
try:
token_ref = token_model.KeystoneToken(
token_id=context['token_id'],
token_data=self.token_provider_api.validate_token(
context['token_id']))
except KeyError:
# This might happen if we use the Admin token, for instance
raise exception.ValidationError(
_('A domain-scoped token must be used'))
except (exception.TokenNotFound,
exception.UnsupportedTokenVersionException):
LOG.warning(_LW('Invalid token found while getting domain ID '
'for create request'))
raise exception.Unauthorized()
token_ref = utils.get_token_ref(context)
if token_ref.domain_scoped:
return token_ref.domain_id

View File

@ -32,6 +32,7 @@ import passlib.hash
import six
from six import moves
from keystone.common import authorization
from keystone import exception
from keystone.i18n import _, _LE, _LW
@ -504,3 +505,20 @@ def isotime(at=None, subsecond=False):
def strtime():
at = timeutils.utcnow()
return at.strftime(timeutils.PERFECT_TIME_FORMAT)
def get_token_ref(context):
"""Retrieves KeystoneToken object from the auth context and returns it.
:param dict context: The request context.
:raises: exception.Unauthorized if auth context cannot be found.
:returns: The KeystoneToken object.
"""
try:
# Retrieve the auth context that was prepared by AuthContextMiddleware.
auth_context = (context['environment']
[authorization.AUTH_CONTEXT_ENV])
return auth_context['token']
except KeyError:
LOG.warning(_LW("Couldn't find the auth context."))
raise exception.Unauthorized()

View File

@ -299,13 +299,7 @@ class Application(BaseApplication):
"""
if not context['is_admin']:
try:
user_token_ref = token_model.KeystoneToken(
token_id=context['token_id'],
token_data=self.token_provider_api.validate_token(
context['token_id']))
except exception.TokenNotFound as e:
raise exception.Unauthorized(e)
user_token_ref = utils.get_token_ref(context)
validate_token_bind(context, user_token_ref)
creds = copy.deepcopy(user_token_ref.metadata)
@ -364,16 +358,7 @@ class Application(BaseApplication):
LOG.debug(('will not lookup trust as the request auth token is '
'either absent or it is the system admin token'))
return None
try:
token_data = self.token_provider_api.validate_token(
context['token_id'])
except exception.TokenNotFound:
LOG.warning(_LW('Invalid token in _get_trust_id_for_request'))
raise exception.Unauthorized()
token_ref = token_model.KeystoneToken(token_id=context['token_id'],
token_data=token_data)
token_ref = utils.get_token_ref(context)
return token_ref.trust_id
@classmethod

View File

@ -46,7 +46,6 @@ from keystone.common import utils
from keystone.common import wsgi
from keystone import exception
from keystone.i18n import _
from keystone.models import token_model
@dependency.requires('assignment_api', 'catalog_api', 'credential_api',
@ -319,14 +318,7 @@ class Ec2Controller(Ec2ControllerCommon, controller.V2Controller):
:raises exception.Forbidden: when token is invalid
"""
try:
token_data = self.token_provider_api.validate_token(
context['token_id'])
except exception.TokenNotFound as e:
raise exception.Unauthorized(e)
token_ref = token_model.KeystoneToken(token_id=context['token_id'],
token_data=token_data)
token_ref = utils.get_token_ref(context)
if token_ref.user_id != user_id:
raise exception.Forbidden(_('Token belongs to another user'))

View File

@ -20,12 +20,12 @@ from oslo_utils import timeutils
from keystone.common import controller
from keystone.common import dependency
from keystone.common import utils
from keystone.common import wsgi
from keystone.contrib.oauth1 import core as oauth1
from keystone.contrib.oauth1 import validator
from keystone import exception
from keystone.i18n import _
from keystone.models import token_model
from keystone import notifications
@ -84,10 +84,7 @@ class ConsumerCrudV3(controller.V3Controller):
@controller.protected()
def delete_consumer(self, context, consumer_id):
user_token_ref = token_model.KeystoneToken(
token_id=context['token_id'],
token_data=self.token_provider_api.validate_token(
context['token_id']))
user_token_ref = utils.get_token_ref(context)
payload = {'user_id': user_token_ref.user_id,
'consumer_id': consumer_id}
_emit_user_oauth_consumer_token_invalidate(payload)
@ -382,10 +379,7 @@ class OAuthControllerV3(controller.V3Controller):
authed_roles.add(role['id'])
# verify the authorizing user has the roles
user_token = token_model.KeystoneToken(
token_id=context['token_id'],
token_data=self.token_provider_api.validate_token(
context['token_id']))
user_token = utils.get_token_ref(context)
user_id = user_token.user_id
project_id = req_token['requested_project_id']
user_roles = self.assignment_api.get_roles_for_user_and_project(

View File

@ -933,8 +933,8 @@ class AuthWithTrust(AuthTest):
def test_get_trust(self):
unscoped_token = self.get_unscoped_token(self.trustor['name'])
context = {'token_id': unscoped_token['access']['token']['id'],
'host_url': HOST_URL}
context = self._create_auth_context(
unscoped_token['access']['token']['id'])
new_trust = self.trust_controller.create_trust(
context, trust=self.sample_data)['trust']
trust = self.trust_controller.get_trust(context,
@ -945,6 +945,21 @@ class AuthWithTrust(AuthTest):
for role in new_trust['roles']:
self.assertIn(role['id'], role_ids)
def test_get_trust_without_auth_context(self):
"""Verify that a trust cannot be retrieved when the auth context is
missing.
"""
unscoped_token = self.get_unscoped_token(self.trustor['name'])
context = self._create_auth_context(
unscoped_token['access']['token']['id'])
new_trust = self.trust_controller.create_trust(
context, trust=self.sample_data)['trust']
# Delete the auth context before calling get_trust().
del context['environment'][authorization.AUTH_CONTEXT_ENV]
self.assertRaises(exception.Forbidden,
self.trust_controller.get_trust, context,
new_trust['id'])
def test_create_trust_no_impersonation(self):
new_trust = self.create_trust(self.sample_data, self.trustor['name'],
expires_at=None, impersonation=False)

View File

@ -27,7 +27,6 @@ from keystone.common import utils
from keystone.common import validation
from keystone import exception
from keystone.i18n import _
from keystone.models import token_model
from keystone import notifications
from keystone.trust import schema
@ -64,13 +63,11 @@ class TrustV3(controller.V3Controller):
return super(TrustV3, cls).base_url(context, path=path)
def _get_user_id(self, context):
if 'token_id' in context:
token_id = context['token_id']
token_data = self.token_provider_api.validate_token(token_id)
token_ref = token_model.KeystoneToken(token_id=token_id,
token_data=token_data)
return token_ref.user_id
return None
try:
token_ref = utils.get_token_ref(context)
except exception.Unauthorized:
return None
return token_ref.user_id
def get_trust(self, context, trust_id):
user_id = self._get_user_id(context)