summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTim Burke <tim.burke@gmail.com>2018-09-07 11:31:50 -0700
committerTim Burke <tim.burke@gmail.com>2018-09-11 07:54:43 -0600
commitda5932affc253a8b50ba753c6f9fabde68410501 (patch)
tree5d662b74ca8167232136394184bb14b9a9440a1f
parent4fb7fef1eab2a981b8e366091073b6ee2d80ee6a (diff)
Respect delay_auth_decision when Keystone is unavailable
The delay_auth_decision option has two main uses: 1. Allow a service to provide its own auth mechanism, separate from auth tokens (like Swift's tempurl middleware). 2. Allow a service to integrate with multiple auth middlewares which may want to use the same X-Auth-Token header. The first case works fine even when the service has trouble talking to Keystone -- the client doesn't send an X-Auth-Token header, so we never even attempt to contact Keystone. The second case can be problematic, however. The client will provide some token, and we don't know whether it's valid for Keystone, the other auth system, or neither. We have to *try* contacting Keystone, but if that was down we'd previously return a 503 without ever trying the other auth system. As a result, a Keystone failure results in a total system failure. Now, when delay_auth_decision is True and we cannot determine whether a token is valid or invalid, we'll instead declare the token invalid and defer the rejection. As a result, Keystone failures only affect Keystone users, and tokens issued by the other auth system may still be validated and used. Change-Id: Ie4b3319862ba7fbd329dc6883ce837e894d5270c
Notes
Notes (review): Code-Review+2: wangxiyuan <wangxiyuan@huawei.com> Code-Review+2: Colleen Murphy <colleen@gazlene.net> Workflow+1: Colleen Murphy <colleen@gazlene.net> Verified+2: Zuul Submitted-by: Zuul Submitted-at: Fri, 05 Oct 2018 11:36:19 +0000 Reviewed-on: https://review.openstack.org/600862 Project: openstack/keystonemiddleware Branch: refs/heads/master
-rw-r--r--keystonemiddleware/auth_token/__init__.py5
-rw-r--r--keystonemiddleware/tests/unit/auth_token/test_auth_token_middleware.py66
-rw-r--r--releasenotes/notes/delay_auth_instead_of_503-f9b46bf4fbc11455.yaml9
3 files changed, 80 insertions, 0 deletions
diff --git a/keystonemiddleware/auth_token/__init__.py b/keystonemiddleware/auth_token/__init__.py
index 4ac7388..003f14e 100644
--- a/keystonemiddleware/auth_token/__init__.py
+++ b/keystonemiddleware/auth_token/__init__.py
@@ -768,6 +768,11 @@ class AuthProtocol(BaseAuthProtocol):
768 ksm_exceptions.RevocationListError, 768 ksm_exceptions.RevocationListError,
769 ksm_exceptions.ServiceError) as e: 769 ksm_exceptions.ServiceError) as e:
770 self.log.critical('Unable to validate token: %s', e) 770 self.log.critical('Unable to validate token: %s', e)
771 if self._delay_auth_decision:
772 self.log.debug('Keystone unavailable; marking token as '
773 'invalid and deferring auth decision.')
774 raise ksm_exceptions.InvalidToken(
775 'Keystone unavailable: %s' % e)
771 raise webob.exc.HTTPServiceUnavailable( 776 raise webob.exc.HTTPServiceUnavailable(
772 'The Keystone service is temporarily unavailable.') 777 'The Keystone service is temporarily unavailable.')
773 except ksm_exceptions.InvalidToken: 778 except ksm_exceptions.InvalidToken:
diff --git a/keystonemiddleware/tests/unit/auth_token/test_auth_token_middleware.py b/keystonemiddleware/tests/unit/auth_token/test_auth_token_middleware.py
index f9916cc..9051d71 100644
--- a/keystonemiddleware/tests/unit/auth_token/test_auth_token_middleware.py
+++ b/keystonemiddleware/tests/unit/auth_token/test_auth_token_middleware.py
@@ -2043,6 +2043,18 @@ class v3AuthTokenMiddlewareTest(BaseAuthTokenMiddlewareTest,
2043 2043
2044class DelayedAuthTests(BaseAuthTokenMiddlewareTest): 2044class DelayedAuthTests(BaseAuthTokenMiddlewareTest):
2045 2045
2046 def token_response(self, request, context):
2047 auth_id = request.headers.get('X-Auth-Token')
2048 self.assertEqual(auth_id, FAKE_ADMIN_TOKEN_ID)
2049
2050 if request.headers.get('X-Subject-Token') == ERROR_TOKEN:
2051 msg = 'Network connection refused.'
2052 raise ksc_exceptions.ConnectionRefused(msg)
2053
2054 # All others just fail
2055 context.status_code = 404
2056 return ''
2057
2046 def test_header_in_401(self): 2058 def test_header_in_401(self):
2047 body = uuid.uuid4().hex 2059 body = uuid.uuid4().hex
2048 www_authenticate_uri = 'http://local.test' 2060 www_authenticate_uri = 'http://local.test'
@@ -2101,6 +2113,60 @@ class DelayedAuthTests(BaseAuthTokenMiddlewareTest):
2101 self.assertFalse(token_auth.has_service_token) 2113 self.assertFalse(token_auth.has_service_token)
2102 self.assertIsNone(token_auth.service) 2114 self.assertIsNone(token_auth.service)
2103 2115
2116 def test_auth_plugin_with_token(self):
2117 self.requests_mock.get('%s/v3/auth/tokens' % BASE_URI,
2118 text=self.token_response,
2119 headers={'X-Subject-Token': uuid.uuid4().hex})
2120
2121 body = uuid.uuid4().hex
2122 www_authenticate_uri = 'http://local.test'
2123 conf = {
2124 'delay_auth_decision': 'True',
2125 'www_authenticate_uri': www_authenticate_uri,
2126 'auth_type': 'admin_token',
2127 'endpoint': '%s/v3' % BASE_URI,
2128 'token': FAKE_ADMIN_TOKEN_ID,
2129 }
2130
2131 middleware = self.create_simple_middleware(body=body, conf=conf)
2132 resp = self.call(middleware, headers={
2133 'X-Auth-Token': 'non-keystone-token'})
2134 self.assertEqual(six.b(body), resp.body)
2135
2136 token_auth = resp.request.environ['keystone.token_auth']
2137
2138 self.assertFalse(token_auth.has_user_token)
2139 self.assertIsNone(token_auth.user)
2140 self.assertFalse(token_auth.has_service_token)
2141 self.assertIsNone(token_auth.service)
2142
2143 def test_auth_plugin_with_token_keystone_down(self):
2144 self.requests_mock.get('%s/v3/auth/tokens' % BASE_URI,
2145 text=self.token_response,
2146 headers={'X-Subject-Token': ERROR_TOKEN})
2147
2148 body = uuid.uuid4().hex
2149 www_authenticate_uri = 'http://local.test'
2150 conf = {
2151 'delay_auth_decision': 'True',
2152 'www_authenticate_uri': www_authenticate_uri,
2153 'auth_type': 'admin_token',
2154 'endpoint': '%s/v3' % BASE_URI,
2155 'token': FAKE_ADMIN_TOKEN_ID,
2156 'http_request_max_retries': '0'
2157 }
2158
2159 middleware = self.create_simple_middleware(body=body, conf=conf)
2160 resp = self.call(middleware, headers={'X-Auth-Token': ERROR_TOKEN})
2161 self.assertEqual(six.b(body), resp.body)
2162
2163 token_auth = resp.request.environ['keystone.token_auth']
2164
2165 self.assertFalse(token_auth.has_user_token)
2166 self.assertIsNone(token_auth.user)
2167 self.assertFalse(token_auth.has_service_token)
2168 self.assertIsNone(token_auth.service)
2169
2104 2170
2105class CommonCompositeAuthTests(object): 2171class CommonCompositeAuthTests(object):
2106 """Test Composite authentication. 2172 """Test Composite authentication.
diff --git a/releasenotes/notes/delay_auth_instead_of_503-f9b46bf4fbc11455.yaml b/releasenotes/notes/delay_auth_instead_of_503-f9b46bf4fbc11455.yaml
new file mode 100644
index 0000000..11ce28a
--- /dev/null
+++ b/releasenotes/notes/delay_auth_instead_of_503-f9b46bf4fbc11455.yaml
@@ -0,0 +1,9 @@
1---
2fixes:
3 - |
4 When ``delay_auth_decision`` is enabled and a Keystone failure prevents
5 a final decision about whether a token is valid or invalid, it will be
6 marked invalid and the application will be responsible for a final auth
7 decision. This is similar to what happens when a token is confirmed *not*
8 valid. This allows a Keystone outage to only affect Keystone users in a
9 multi-auth system.