Merge "Don't cache tokens as invalid on network errors"
This commit is contained in:
@@ -318,6 +318,10 @@ class ConfigurationError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class NetworkError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class MiniResp(object):
|
||||
def __init__(self, error_message, env, headers=[]):
|
||||
# The HEAD method is unique: it must never return a body, even if
|
||||
@@ -424,6 +428,7 @@ class AuthProtocol(object):
|
||||
self.http_connect_timeout = (http_connect_timeout_cfg and
|
||||
int(http_connect_timeout_cfg))
|
||||
self.auth_version = None
|
||||
self.http_request_max_retries = 3
|
||||
|
||||
def _assert_valid_memcache_protection_config(self):
|
||||
if self._memcache_security_strategy:
|
||||
@@ -655,9 +660,8 @@ class AuthProtocol(object):
|
||||
"""
|
||||
conn = self._get_http_connection()
|
||||
|
||||
RETRIES = 3
|
||||
RETRIES = self.http_request_max_retries
|
||||
retry = 0
|
||||
|
||||
while True:
|
||||
try:
|
||||
conn.request(method, path, **kwargs)
|
||||
@@ -667,7 +671,7 @@ class AuthProtocol(object):
|
||||
except Exception as e:
|
||||
if retry == RETRIES:
|
||||
self.LOG.error('HTTP connection exception: %s' % e)
|
||||
raise ServiceError('Unable to communicate with keystone')
|
||||
raise NetworkError('Unable to communicate with keystone')
|
||||
# NOTE(vish): sleep 0.5, 1, 2
|
||||
self.LOG.warn('Retrying on HTTP connection exception: %s' % e)
|
||||
time.sleep(2.0 ** retry / 2)
|
||||
@@ -778,6 +782,10 @@ class AuthProtocol(object):
|
||||
expires = self._confirm_token_not_expired(data)
|
||||
self._cache_put(token_id, data, expires)
|
||||
return data
|
||||
except NetworkError as e:
|
||||
self.LOG.debug('Token validation failure.', exc_info=True)
|
||||
self.LOG.warn("Authorization failed for token %s", user_token)
|
||||
raise InvalidUserToken('Token authorization failed')
|
||||
except Exception as e:
|
||||
self.LOG.debug('Token validation failure.', exc_info=True)
|
||||
self._cache_store_invalid(user_token)
|
||||
|
@@ -558,6 +558,13 @@ class RaisingHTTPConnection(FakeHTTPConnection):
|
||||
raise AssertionError("HTTP request was called.")
|
||||
|
||||
|
||||
class RaisingHTTPNetworkError(FakeHTTPConnection):
|
||||
"""An HTTPConnection that always raises network error."""
|
||||
|
||||
def request(self, method, path, **kwargs):
|
||||
raise auth_token.NetworkError("Network connection error.")
|
||||
|
||||
|
||||
class FakeApp(object):
|
||||
"""This represents a WSGI app protected by the auth_token middleware."""
|
||||
def __init__(self, expected_env=None):
|
||||
@@ -1236,6 +1243,20 @@ class AuthTokenMiddlewareTest(BaseAuthTokenMiddlewareTest):
|
||||
self.assertEquals(middleware.token_revocation_list_cache_timeout,
|
||||
datetime.timedelta(seconds=24))
|
||||
|
||||
def test_http_error_not_cached_token(self):
|
||||
"""Test to don't cache token as invalid on network errors.
|
||||
|
||||
We use UUID tokens since they are the easiest one to reach
|
||||
get_http_connection.
|
||||
"""
|
||||
req = webob.Request.blank('/')
|
||||
token = self.token_dict['uuid_token_default']
|
||||
req.headers['X-Auth-Token'] = token
|
||||
self.set_fake_http(RaisingHTTPNetworkError)
|
||||
self.middleware.http_request_max_retries = 0
|
||||
self.middleware(req.environ, self.start_fake_response)
|
||||
self.assertEqual(self._get_cached_token(token), None)
|
||||
|
||||
|
||||
class CertDownloadMiddlewareTest(BaseAuthTokenMiddlewareTest):
|
||||
def setUp(self):
|
||||
|
Reference in New Issue
Block a user