summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJenkins <jenkins@review.openstack.org>2015-02-10 20:13:25 +0000
committerGerrit Code Review <review@openstack.org>2015-02-10 20:13:25 +0000
commitb562b04ee5db309268716e0e1b8270f30bdf1a76 (patch)
tree56264c010cd491a1e56a80616dbdca74544c148b
parentfb306836b411546db385274996a8cdaf6154aaf7 (diff)
parentaebc5df958974d34c49a2a62da7bb124eafca4bd (diff)
downloadkeystonemiddleware-b562b04ee5db309268716e0e1b8270f30bdf1a76.tar.gz
Merge "Turn our auth plugin into a token interface"
-rw-r--r--keystonemiddleware/auth_token.py161
-rw-r--r--keystonemiddleware/tests/test_auth_token_middleware.py81
2 files changed, 233 insertions, 9 deletions
diff --git a/keystonemiddleware/auth_token.py b/keystonemiddleware/auth_token.py
index 03dc5dd..ea3b80e 100644
--- a/keystonemiddleware/auth_token.py
+++ b/keystonemiddleware/auth_token.py
@@ -667,6 +667,99 @@ class _AuthTokenPlugin(auth.BaseAuthPlugin):
_AuthTokenPlugin.register_conf_options(CONF, _AUTHTOKEN_GROUP)
+class _TokenData(object):
+ """An abstraction to show auth_token consumers some of the token contents.
+
+ This is a simplified and cleaned up keystoneclient.access.AccessInfo object
+ with which services relying on auth_token middleware can find details of
+ the current token.
+ """
+
+ def __init__(self, auth_ref):
+ self._stored_auth_ref = auth_ref
+
+ @property
+ def _is_v2(self):
+ return self._stored_auth_ref.version == 'v2.0'
+
+ @property
+ def auth_token(self):
+ """The token data used to authenticate requests.
+
+ :returns: token data.
+ :rtype: str
+ """
+ return self._stored_auth_ref.auth_token
+
+ @property
+ def user_id(self):
+ """The user id associated with the authentication request.
+
+ :rtype: str
+ """
+ return self._stored_auth_ref.user_id
+
+ @property
+ def user_domain_id(self):
+ """Returns the domain id of the user associated with the authentication
+ request.
+
+ :returns: str
+ """
+ # NOTE(jamielennox): v2 AccessInfo returns 'default' for domain_id
+ # because it can't know that value. We want to return None instead.
+ if self._is_v2:
+ return None
+
+ return self._stored_auth_ref.user_domain_id
+
+ @property
+ def project_id(self):
+ """The project ID associated with the authentication.
+
+ :rtype: str
+ """
+ return self._stored_auth_ref.project_id
+
+ @property
+ def project_domain_id(self):
+ """The domain id of the project associated with the authentication
+ request.
+
+ :rtype: str
+ """
+ # NOTE(jamielennox): v2 AccessInfo returns 'default' for domain_id
+ # because it can't know that value. We want to return None instead.
+ if self._is_v2:
+ return None
+
+ return self._stored_auth_ref.project_domain_id
+
+ @property
+ def trust_id(self):
+ """Returns the trust id associated with the authentication request..
+
+ :rtype: str
+ """
+ return self._stored_auth_ref.trust_id
+
+ @property
+ def role_ids(self):
+ """Role ids of the user associated with the authentication request.
+
+ :rtype: set(str)
+ """
+ return frozenset(self._stored_auth_ref.role_ids or [])
+
+ @property
+ def role_names(self):
+ """Role names of the user associated with the authentication request.
+
+ :rtype: set(str)
+ """
+ return frozenset(self._stored_auth_ref.role_names or [])
+
+
class _UserAuthPlugin(base_identity.BaseIdentityPlugin):
"""The incoming authentication credentials.
@@ -678,15 +771,56 @@ class _UserAuthPlugin(base_identity.BaseIdentityPlugin):
authentication plugin when communicating via a session.
"""
- def __init__(self, auth_ref):
+ def __init__(self, user_auth_ref, serv_auth_ref):
super(_UserAuthPlugin, self).__init__(reauthenticate=False)
- self._stored_auth_ref = auth_ref
+ self._user_auth_ref = user_auth_ref
+ self._serv_auth_ref = serv_auth_ref
+ self._user_data = None
+ self._serv_data = None
+
+ @property
+ def has_user_token(self):
+ """Did this authentication request contained a user auth token."""
+ return self._user_auth_ref is not None
+
+ @property
+ def user(self):
+ """Authentication information about the user token.
+
+ Will return None if a user token was not passed with this request.
+ """
+ if not self.has_user_token:
+ return None
+
+ if not self._user_data:
+ self._user_data = _TokenData(self._user_auth_ref)
+
+ return self._user_data
+
+ @property
+ def has_service_token(self):
+ """Did this authentication request contained a service token."""
+ return self._serv_auth_ref is not None
+
+ @property
+ def service(self):
+ """Authentication information about the service token.
+
+ Will return None if a user token was not passed with this request.
+ """
+ if not self.has_service_token:
+ return None
+
+ if not self._serv_data:
+ self._serv_data = _TokenData(self._serv_auth_ref)
+
+ return self._serv_data
def get_auth_ref(self, session, **kwargs):
# NOTE(jamielennox): We will always use the auth_ref that was
# calculated by the middleware. reauthenticate=False in __init__ should
# ensure that this function is only called on the first access.
- return self._stored_auth_ref
+ return self._user_auth_ref
class AuthProtocol(object):
@@ -788,16 +922,19 @@ class AuthProtocol(object):
self._remove_auth_headers(env)
try:
+ user_auth_ref = None
+ serv_auth_ref = None
try:
self._LOG.debug('Authenticating user token')
user_token = self._get_user_token_from_header(env)
- token_info = self._validate_token(user_token, env)
- auth_ref = access.AccessInfo.factory(body=token_info,
- auth_token=user_token)
- env['keystone.token_info'] = token_info
- env['keystone.token_auth'] = _UserAuthPlugin(auth_ref)
- user_headers = self._build_user_headers(auth_ref, token_info)
+ user_token_info = self._validate_token(user_token, env)
+ user_auth_ref = access.AccessInfo.factory(
+ body=user_token_info,
+ auth_token=user_token)
+ env['keystone.token_info'] = user_token_info
+ user_headers = self._build_user_headers(user_auth_ref,
+ user_token_info)
self._add_headers(env, user_headers)
except InvalidToken:
if self._delay_auth_decision:
@@ -816,6 +953,9 @@ class AuthProtocol(object):
if serv_token is not None:
serv_token_info = self._validate_token(
serv_token, env)
+ serv_auth_ref = access.AccessInfo.factory(
+ body=serv_token_info,
+ auth_token=serv_token)
serv_headers = self._build_service_headers(serv_token_info)
self._add_headers(env, serv_headers)
except InvalidToken:
@@ -825,6 +965,9 @@ class AuthProtocol(object):
_LI('Invalid service token - rejecting request'))
return self._reject_request(env, start_response)
+ env['keystone.token_auth'] = _UserAuthPlugin(user_auth_ref,
+ serv_auth_ref)
+
except ServiceError as e:
self._LOG.critical(_LC('Unable to obtain admin token: %s'), e)
return self._do_503_error(env, start_response)
diff --git a/keystonemiddleware/tests/test_auth_token_middleware.py b/keystonemiddleware/tests/test_auth_token_middleware.py
index 0927abe..fce882a 100644
--- a/keystonemiddleware/tests/test_auth_token_middleware.py
+++ b/keystonemiddleware/tests/test_auth_token_middleware.py
@@ -1373,6 +1373,10 @@ class CommonAuthTokenMiddlewareTest(object):
url = token_auth.get_endpoint(session.Session(), **endpoint_filter)
self.assertEqual('%s/v3' % BASE_URI, url)
+ self.assertTrue(token_auth.has_user_token)
+ self.assertFalse(token_auth.has_service_token)
+ self.assertIsNone(token_auth.service)
+
class V2CertDownloadMiddlewareTest(BaseAuthTokenMiddlewareTest,
testresources.ResourcedTestCase):
@@ -1629,6 +1633,35 @@ class v2AuthTokenMiddlewareTest(BaseAuthTokenMiddlewareTest,
self.assertFalse(req.headers.get('X-Service-Catalog'))
self.assertEqual(body, [FakeApp.SUCCESS])
+ def test_user_plugin_token_properties(self):
+ req = webob.Request.blank('/')
+ req.headers['X-Service-Catalog'] = '[]'
+ token = self.examples.UUID_TOKEN_DEFAULT
+ token_data = self.examples.TOKEN_RESPONSES[token]
+ req.headers['X-Auth-Token'] = token
+ req.headers['X-Service-Token'] = token
+
+ body = self.middleware(req.environ, self.start_fake_response)
+ self.assertEqual(self.response_status, 200)
+ self.assertEqual([FakeApp.SUCCESS], body)
+
+ token_auth = req.environ['keystone.token_auth']
+
+ self.assertTrue(token_auth.has_user_token)
+ self.assertTrue(token_auth.has_service_token)
+
+ for t in [token_auth.user, token_auth.service]:
+ self.assertEqual(token_data.user_id, t.user_id)
+ self.assertEqual(token_data.tenant_id, t.project_id)
+
+ self.assertThat(t.role_names, matchers.HasLength(2))
+ self.assertIn('role1', t.role_names)
+ self.assertIn('role2', t.role_names)
+
+ self.assertIsNone(t.trust_id)
+ self.assertIsNone(t.user_domain_id)
+ self.assertIsNone(t.project_domain_id)
+
class CrossVersionAuthTokenMiddlewareTest(BaseAuthTokenMiddlewareTest,
testresources.ResourcedTestCase):
@@ -1842,6 +1875,35 @@ class v3AuthTokenMiddlewareTest(BaseAuthTokenMiddlewareTest,
self.assert_valid_request_200(
self.token_dict['signed_token_scoped_pkiz'])
+ def test_user_plugin_token_properties(self):
+ req = webob.Request.blank('/')
+ req.headers['X-Service-Catalog'] = '[]'
+ token = self.examples.v3_UUID_TOKEN_DEFAULT
+ token_data = self.examples.TOKEN_RESPONSES[token]
+ req.headers['X-Auth-Token'] = token
+ req.headers['X-Service-Token'] = token
+
+ body = self.middleware(req.environ, self.start_fake_response)
+ self.assertEqual(self.response_status, 200)
+ self.assertEqual([FakeApp.SUCCESS], body)
+
+ token_auth = req.environ['keystone.token_auth']
+
+ self.assertTrue(token_auth.has_user_token)
+ self.assertTrue(token_auth.has_service_token)
+
+ for t in [token_auth.user, token_auth.service]:
+ self.assertEqual(token_data.user_id, t.user_id)
+ self.assertEqual(token_data.project_id, t.project_id)
+ self.assertEqual(token_data.user_domain_id, t.user_domain_id)
+ self.assertEqual(token_data.project_domain_id, t.project_domain_id)
+
+ self.assertThat(t.role_names, matchers.HasLength(2))
+ self.assertIn('role1', t.role_names)
+ self.assertIn('role2', t.role_names)
+
+ self.assertIsNone(t.trust_id)
+
class TokenEncodingTest(testtools.TestCase):
def test_unquoted_token(self):
@@ -2180,6 +2242,25 @@ class DelayedAuthTests(BaseAuthTokenMiddlewareTest):
middleware = auth_token.AuthProtocol(fake_app, conf)
self.assertFalse(middleware._delay_auth_decision)
+ def test_auth_plugin_with_no_tokens(self):
+ body = uuid.uuid4().hex
+ auth_uri = 'http://local.test'
+ conf = {'delay_auth_decision': True, 'auth_uri': auth_uri}
+ self.fake_app = new_app('200 OK', body)
+ self.set_middleware(conf=conf)
+
+ req = webob.Request.blank('/')
+ resp = self.middleware(req.environ, self.start_fake_response)
+
+ self.assertEqual([six.b(body)], resp)
+
+ token_auth = req.environ['keystone.token_auth']
+
+ self.assertFalse(token_auth.has_user_token)
+ self.assertIsNone(token_auth.user)
+ self.assertFalse(token_auth.has_service_token)
+ self.assertIsNone(token_auth.service)
+
class CommonCompositeAuthTests(object):
"""Test Composite authentication.