Merge "Don't cache tokens as invalid on network errors"
This commit is contained in:
@@ -318,6 +318,10 @@ class ConfigurationError(Exception):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class NetworkError(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class MiniResp(object):
|
class MiniResp(object):
|
||||||
def __init__(self, error_message, env, headers=[]):
|
def __init__(self, error_message, env, headers=[]):
|
||||||
# The HEAD method is unique: it must never return a body, even if
|
# 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
|
self.http_connect_timeout = (http_connect_timeout_cfg and
|
||||||
int(http_connect_timeout_cfg))
|
int(http_connect_timeout_cfg))
|
||||||
self.auth_version = None
|
self.auth_version = None
|
||||||
|
self.http_request_max_retries = 3
|
||||||
|
|
||||||
def _assert_valid_memcache_protection_config(self):
|
def _assert_valid_memcache_protection_config(self):
|
||||||
if self._memcache_security_strategy:
|
if self._memcache_security_strategy:
|
||||||
@@ -655,9 +660,8 @@ class AuthProtocol(object):
|
|||||||
"""
|
"""
|
||||||
conn = self._get_http_connection()
|
conn = self._get_http_connection()
|
||||||
|
|
||||||
RETRIES = 3
|
RETRIES = self.http_request_max_retries
|
||||||
retry = 0
|
retry = 0
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
conn.request(method, path, **kwargs)
|
conn.request(method, path, **kwargs)
|
||||||
@@ -667,7 +671,7 @@ class AuthProtocol(object):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
if retry == RETRIES:
|
if retry == RETRIES:
|
||||||
self.LOG.error('HTTP connection exception: %s' % e)
|
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
|
# NOTE(vish): sleep 0.5, 1, 2
|
||||||
self.LOG.warn('Retrying on HTTP connection exception: %s' % e)
|
self.LOG.warn('Retrying on HTTP connection exception: %s' % e)
|
||||||
time.sleep(2.0 ** retry / 2)
|
time.sleep(2.0 ** retry / 2)
|
||||||
@@ -778,6 +782,10 @@ class AuthProtocol(object):
|
|||||||
expires = self._confirm_token_not_expired(data)
|
expires = self._confirm_token_not_expired(data)
|
||||||
self._cache_put(token_id, data, expires)
|
self._cache_put(token_id, data, expires)
|
||||||
return data
|
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:
|
except Exception as e:
|
||||||
self.LOG.debug('Token validation failure.', exc_info=True)
|
self.LOG.debug('Token validation failure.', exc_info=True)
|
||||||
self._cache_store_invalid(user_token)
|
self._cache_store_invalid(user_token)
|
||||||
|
@@ -558,6 +558,13 @@ class RaisingHTTPConnection(FakeHTTPConnection):
|
|||||||
raise AssertionError("HTTP request was called.")
|
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):
|
class FakeApp(object):
|
||||||
"""This represents a WSGI app protected by the auth_token middleware."""
|
"""This represents a WSGI app protected by the auth_token middleware."""
|
||||||
def __init__(self, expected_env=None):
|
def __init__(self, expected_env=None):
|
||||||
@@ -1236,6 +1243,20 @@ class AuthTokenMiddlewareTest(BaseAuthTokenMiddlewareTest):
|
|||||||
self.assertEquals(middleware.token_revocation_list_cache_timeout,
|
self.assertEquals(middleware.token_revocation_list_cache_timeout,
|
||||||
datetime.timedelta(seconds=24))
|
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):
|
class CertDownloadMiddlewareTest(BaseAuthTokenMiddlewareTest):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
Reference in New Issue
Block a user