diff options
author | Jenkins <jenkins@review.openstack.org> | 2015-08-12 06:56:40 +0000 |
---|---|---|
committer | Gerrit Code Review <review@openstack.org> | 2015-08-12 06:56:40 +0000 |
commit | 9db204e5c2d5632ee8c3f316bae83ba0234fadab (patch) | |
tree | d1afc4e04b5f768d486bb19c1d5932fdce310349 | |
parent | 64b6e3db7eac4d9c22db566e2c37b4fb27f517b6 (diff) | |
parent | 4fe8ae52a6d2fbccc876efd0573685d1ab20855c (diff) | |
download | keystonemiddleware-9db204e5c2d5632ee8c3f316bae83ba0234fadab.tar.gz |
Merge "Move common request processing to base class"
-rw-r--r-- | keystonemiddleware/auth_token/__init__.py | 115 | ||||
-rw-r--r-- | keystonemiddleware/auth_token/_user_plugin.py | 5 | ||||
-rw-r--r-- | keystonemiddleware/tests/unit/auth_token/test_base_middleware.py | 153 |
3 files changed, 214 insertions, 59 deletions
diff --git a/keystonemiddleware/auth_token/__init__.py b/keystonemiddleware/auth_token/__init__.py index 6babf50..930e847 100644 --- a/keystonemiddleware/auth_token/__init__.py +++ b/keystonemiddleware/auth_token/__init__.py @@ -457,6 +457,38 @@ class _BaseAuthProtocol(object): By default this method does not return a value. """ + request.remove_auth_headers() + + user_auth_ref = None + serv_auth_ref = None + + if request.user_token: + self.log.debug('Authenticating user token') + try: + data, user_auth_ref = self._do_fetch_token(request.user_token) + self._validate_token(user_auth_ref) + self._confirm_token_bind(user_auth_ref, request) + except exc.InvalidToken: + self.log.info(_LI('Invalid user token')) + request.user_token_valid = False + else: + request.user_token_valid = True + request.environ['keystone.token_info'] = data + + if request.service_token: + self.log.debug('Authenticating service token') + try: + _, serv_auth_ref = self._do_fetch_token(request.service_token) + self._validate_token(serv_auth_ref) + self._confirm_token_bind(serv_auth_ref, request) + except exc.InvalidToken: + self.log.info(_LI('Invalid service token')) + request.service_token_valid = False + else: + request.service_token_valid = True + + p = _user_plugin.UserAuthPlugin(user_auth_ref, serv_auth_ref) + request.environ['keystone.token_auth'] = p def _validate_token(self, auth_ref): """Perform the validation steps on the token. @@ -649,57 +681,39 @@ class AuthProtocol(_BaseAuthProtocol): depending on configuration. """ self._token_cache.initialize(request.environ) - request.remove_auth_headers() - user_auth_ref = None - serv_auth_ref = None + resp = super(AuthProtocol, self).process_request(request) + if resp: + return resp - self.log.debug('Authenticating user token') - try: - user_token = self._get_user_token_from_request(request) - data, user_auth_ref = self._do_fetch_token(user_token) - self._validate_token(user_auth_ref) - self._confirm_token_bind(user_auth_ref, request) - except exc.InvalidToken: + if not request.user_token: + # if no user token is present then that's an invalid request + request.user_token_valid = False + + # NOTE(jamielennox): The service status is allowed to be missing if a + # service token is not passed. If the service status is missing that's + # a valid request. We should find a better way to expose this from the + # request object. + user_status = request.user_token and request.user_token_valid + service_status = request.headers.get('X-Service-Identity-Status', + 'Confirmed') + + if not (user_status and service_status == 'Confirmed'): if self._delay_auth_decision: - self.log.info( - _LI('Invalid user token - deferring reject ' - 'downstream')) - request.user_token_valid = False + self.log.info(_LI('Deferring reject downstream')) else: - self.log.info( - _LI('Invalid user token - rejecting request')) + self.log.info(_LI('Rejecting request')) self._reject_request() - else: - request.environ['keystone.token_info'] = data - request.set_user_headers(user_auth_ref, - self._include_service_catalog) - if request.service_token is not None: - self.log.debug('Authenticating service token') - try: - _ignore, serv_auth_ref = self._do_fetch_token( - request.service_token) - self._validate_token(serv_auth_ref) - self._confirm_token_bind(serv_auth_ref, request) - except exc.InvalidToken: - if self._delay_auth_decision: - self.log.info( - _LI('Invalid service token - deferring reject ' - 'downstream')) - request.service_token_valid = False - else: - self.log.info( - _LI('Invalid service token - rejecting request')) - self._reject_request() - else: - request.set_service_headers(serv_auth_ref) + if request.user_token_valid: + request.set_user_headers(request.token_auth._user_auth_ref, + self._include_service_catalog) - request.token_auth = _user_plugin.UserAuthPlugin(user_auth_ref, - serv_auth_ref) + if request.service_token and request.service_token_valid: + request.set_service_headers(request.token_auth._serv_auth_ref) if self.log.isEnabledFor(logging.DEBUG): - self.log.debug('Received request from %s' % + self.log.debug('Received request from %s', request.token_auth._log_format) def process_response(self, response): @@ -713,23 +727,6 @@ class AuthProtocol(_BaseAuthProtocol): return response - def _get_user_token_from_request(self, request): - """Get token id from request. - - :param env: wsgi request environment - :returns: token id - :raises exc.InvalidToken: if no token is provided in request - - """ - token = request.user_token - - if token: - return token - else: - if not self._delay_auth_decision: - self.log.debug('Headers: %s', dict(request.headers)) - raise exc.InvalidToken(_('Unable to find token in headers')) - @property def _reject_auth_headers(self): header_val = 'Keystone uri=\'%s\'' % self._auth_uri diff --git a/keystonemiddleware/auth_token/_user_plugin.py b/keystonemiddleware/auth_token/_user_plugin.py index 9bdc400..93075c5 100644 --- a/keystonemiddleware/auth_token/_user_plugin.py +++ b/keystonemiddleware/auth_token/_user_plugin.py @@ -126,8 +126,13 @@ class UserAuthPlugin(base_identity.BaseIdentityPlugin): def __init__(self, user_auth_ref, serv_auth_ref): super(UserAuthPlugin, self).__init__(reauthenticate=False) + + # NOTE(jamielennox): _user_auth_ref and _serv_auth_ref are private + # because this object ends up in the environ that is passed to the + # service, however they are used within auth_token middleware. self._user_auth_ref = user_auth_ref self._serv_auth_ref = serv_auth_ref + self._user_data = None self._serv_data = None diff --git a/keystonemiddleware/tests/unit/auth_token/test_base_middleware.py b/keystonemiddleware/tests/unit/auth_token/test_base_middleware.py index cf7d812..b213f54 100644 --- a/keystonemiddleware/tests/unit/auth_token/test_base_middleware.py +++ b/keystonemiddleware/tests/unit/auth_token/test_base_middleware.py @@ -10,7 +10,12 @@ # License for the specific language governing permissions and limitations # under the License. +import datetime +import uuid + +from keystoneclient import fixture import mock +import six import testtools import webob @@ -25,6 +30,19 @@ class FakeApp(object): return webob.Response() +class FetchingMiddleware(auth_token._BaseAuthProtocol): + + def __init__(self, app, token_dict={}, **kwargs): + super(FetchingMiddleware, self).__init__(app, **kwargs) + self.token_dict = token_dict + + def _fetch_token(self, token): + try: + return self.token_dict[token] + except KeyError: + raise auth_token.InvalidToken() + + class BaseAuthProtocolTests(testtools.TestCase): @mock.patch.multiple(auth_token._BaseAuthProtocol, @@ -47,3 +65,138 @@ class BaseAuthProtocolTests(testtools.TestCase): self.assertEqual(1, process_response.call_count) self.assertIsInstance(process_response.call_args[0][0], webob.Response) + + @classmethod + def call(cls, middleware, method='GET', path='/', headers=None): + req = webob.Request.blank(path) + req.method = method + + for k, v in six.iteritems(headers or {}): + req.headers[k] = v + + resp = req.get_response(middleware) + resp.request = req + return resp + + def test_good_v3_user_token(self): + t = fixture.V3Token() + t.set_project_scope() + role = t.add_role() + + token_id = uuid.uuid4().hex + token_dict = {token_id: t} + + @webob.dec.wsgify + def _do_cb(req): + self.assertEqual(token_id, req.headers['X-Auth-Token']) + + self.assertEqual('Confirmed', req.headers['X-Identity-Status']) + self.assertNotIn('X-Service-Token', req.headers) + + p = req.environ['keystone.token_auth'] + + self.assertTrue(p.has_user_token) + self.assertFalse(p.has_service_token) + + self.assertEqual(t.project_id, p.user.project_id) + self.assertEqual(t.project_domain_id, p.user.project_domain_id) + self.assertEqual(t.user_id, p.user.user_id) + self.assertEqual(t.user_domain_id, p.user.user_domain_id) + self.assertIn(role['name'], p.user.role_names) + + return webob.Response() + + m = FetchingMiddleware(_do_cb, token_dict) + self.call(m, headers={'X-Auth-Token': token_id}) + + def test_invalid_user_token(self): + token_id = uuid.uuid4().hex + + @webob.dec.wsgify + def _do_cb(req): + self.assertEqual('Invalid', req.headers['X-Identity-Status']) + self.assertEqual(token_id, req.headers['X-Auth-Token']) + return webob.Response() + + m = FetchingMiddleware(_do_cb) + self.call(m, headers={'X-Auth-Token': token_id}) + + def test_expired_user_token(self): + t = fixture.V3Token() + t.set_project_scope() + t.expires = datetime.datetime.utcnow() - datetime.timedelta(minutes=10) + + token_id = uuid.uuid4().hex + token_dict = {token_id: t} + + @webob.dec.wsgify + def _do_cb(req): + self.assertEqual('Invalid', req.headers['X-Identity-Status']) + self.assertEqual(token_id, req.headers['X-Auth-Token']) + return webob.Response() + + m = FetchingMiddleware(_do_cb, token_dict=token_dict) + self.call(m, headers={'X-Auth-Token': token_id}) + + def test_good_v3_service_token(self): + t = fixture.V3Token() + t.set_project_scope() + role = t.add_role() + + token_id = uuid.uuid4().hex + token_dict = {token_id: t} + + @webob.dec.wsgify + def _do_cb(req): + self.assertEqual(token_id, req.headers['X-Service-Token']) + + self.assertEqual('Confirmed', + req.headers['X-Service-Identity-Status']) + self.assertNotIn('X-Auth-Token', req.headers) + + p = req.environ['keystone.token_auth'] + + self.assertFalse(p.has_user_token) + self.assertTrue(p.has_service_token) + + self.assertEqual(t.project_id, p.service.project_id) + self.assertEqual(t.project_domain_id, p.service.project_domain_id) + self.assertEqual(t.user_id, p.service.user_id) + self.assertEqual(t.user_domain_id, p.service.user_domain_id) + self.assertIn(role['name'], p.service.role_names) + + return webob.Response() + + m = FetchingMiddleware(_do_cb, token_dict) + self.call(m, headers={'X-Service-Token': token_id}) + + def test_invalid_service_token(self): + token_id = uuid.uuid4().hex + + @webob.dec.wsgify + def _do_cb(req): + self.assertEqual('Invalid', + req.headers['X-Service-Identity-Status']) + self.assertEqual(token_id, req.headers['X-Service-Token']) + return webob.Response() + + m = FetchingMiddleware(_do_cb) + self.call(m, headers={'X-Service-Token': token_id}) + + def test_expired_service_token(self): + t = fixture.V3Token() + t.set_project_scope() + t.expires = datetime.datetime.utcnow() - datetime.timedelta(minutes=10) + + token_id = uuid.uuid4().hex + token_dict = {token_id: t} + + @webob.dec.wsgify + def _do_cb(req): + self.assertEqual('Invalid', + req.headers['X-Service-Identity-Status']) + self.assertEqual(token_id, req.headers['X-Service-Token']) + return webob.Response() + + m = FetchingMiddleware(_do_cb, token_dict=token_dict) + self.call(m, headers={'X-Service-Token': token_id}) |