Use hashed token for invalid PKI token cache key
Invalid PKI tokens are cached in memcache using the entire token as key. This triggers the familiar memcache key length error since a PKI token is much longer than 250 characters. This change hashes the token before using it as a key, and also changes instances of "token" to "token_id" where appropriate so it's clear that we're passing around a hashed token rather than the token data itself. Change-Id: I765b209578d60266da706094f61d8d9b15cfb6de Closes-bug: 1206347
This commit is contained in:
@@ -769,6 +769,8 @@ class AuthProtocol(object):
|
|||||||
:no longer raises ServiceError since it no longer makes RPC
|
:no longer raises ServiceError since it no longer makes RPC
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
token_id = None
|
||||||
|
|
||||||
try:
|
try:
|
||||||
token_id = cms.cms_hash_token(user_token)
|
token_id = cms.cms_hash_token(user_token)
|
||||||
cached = self._cache_get(token_id)
|
cached = self._cache_get(token_id)
|
||||||
@@ -784,12 +786,13 @@ class AuthProtocol(object):
|
|||||||
return data
|
return data
|
||||||
except NetworkError as e:
|
except NetworkError as e:
|
||||||
self.LOG.debug('Token validation failure.', exc_info=True)
|
self.LOG.debug('Token validation failure.', exc_info=True)
|
||||||
self.LOG.warn("Authorization failed for token %s", user_token)
|
self.LOG.warn("Authorization failed for token %s", token_id)
|
||||||
raise InvalidUserToken('Token authorization failed')
|
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)
|
if token_id:
|
||||||
self.LOG.warn("Authorization failed for token %s", user_token)
|
self._cache_store_invalid(token_id)
|
||||||
|
self.LOG.warn("Authorization failed for token %s", token_id)
|
||||||
raise InvalidUserToken('Token authorization failed')
|
raise InvalidUserToken('Token authorization failed')
|
||||||
|
|
||||||
def _token_is_v2(self, token_info):
|
def _token_is_v2(self, token_info):
|
||||||
@@ -930,20 +933,20 @@ class AuthProtocol(object):
|
|||||||
env_key = self._header_to_env_var(key)
|
env_key = self._header_to_env_var(key)
|
||||||
return env.get(env_key, default)
|
return env.get(env_key, default)
|
||||||
|
|
||||||
def _cache_get(self, token, ignore_expires=False):
|
def _cache_get(self, token_id, ignore_expires=False):
|
||||||
"""Return token information from cache.
|
"""Return token information from cache.
|
||||||
|
|
||||||
If token is invalid raise InvalidUserToken
|
If token is invalid raise InvalidUserToken
|
||||||
return token only if fresh (not expired).
|
return token only if fresh (not expired).
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if self._cache and token:
|
if self._cache and token_id:
|
||||||
if self._memcache_security_strategy is None:
|
if self._memcache_security_strategy is None:
|
||||||
key = CACHE_KEY_TEMPLATE % token
|
key = CACHE_KEY_TEMPLATE % token_id
|
||||||
serialized = self._cache.get(key)
|
serialized = self._cache.get(key)
|
||||||
else:
|
else:
|
||||||
keys = memcache_crypt.derive_keys(
|
keys = memcache_crypt.derive_keys(
|
||||||
token,
|
token_id,
|
||||||
self._memcache_secret_key,
|
self._memcache_secret_key,
|
||||||
self._memcache_security_strategy)
|
self._memcache_security_strategy)
|
||||||
cache_key = CACHE_KEY_TEMPLATE % (
|
cache_key = CACHE_KEY_TEMPLATE % (
|
||||||
@@ -968,17 +971,18 @@ class AuthProtocol(object):
|
|||||||
# a collision with json.loads(serialized) == None.
|
# a collision with json.loads(serialized) == None.
|
||||||
cached = json.loads(serialized)
|
cached = json.loads(serialized)
|
||||||
if cached == 'invalid':
|
if cached == 'invalid':
|
||||||
self.LOG.debug('Cached Token %s is marked unauthorized', token)
|
self.LOG.debug('Cached Token %s is marked unauthorized',
|
||||||
|
token_id)
|
||||||
raise InvalidUserToken('Token authorization failed')
|
raise InvalidUserToken('Token authorization failed')
|
||||||
|
|
||||||
data, expires = cached
|
data, expires = cached
|
||||||
if ignore_expires or time.time() < float(expires):
|
if ignore_expires or time.time() < float(expires):
|
||||||
self.LOG.debug('Returning cached token %s', token)
|
self.LOG.debug('Returning cached token %s', token_id)
|
||||||
return data
|
return data
|
||||||
else:
|
else:
|
||||||
self.LOG.debug('Cached Token %s seems expired', token)
|
self.LOG.debug('Cached Token %s seems expired', token_id)
|
||||||
|
|
||||||
def _cache_store(self, token, data):
|
def _cache_store(self, token_id, data):
|
||||||
"""Store value into memcache.
|
"""Store value into memcache.
|
||||||
|
|
||||||
data may be the string 'invalid' or a tuple like (data, expires)
|
data may be the string 'invalid' or a tuple like (data, expires)
|
||||||
@@ -986,11 +990,11 @@ class AuthProtocol(object):
|
|||||||
"""
|
"""
|
||||||
serialized_data = json.dumps(data)
|
serialized_data = json.dumps(data)
|
||||||
if self._memcache_security_strategy is None:
|
if self._memcache_security_strategy is None:
|
||||||
cache_key = CACHE_KEY_TEMPLATE % token
|
cache_key = CACHE_KEY_TEMPLATE % token_id
|
||||||
data_to_store = serialized_data
|
data_to_store = serialized_data
|
||||||
else:
|
else:
|
||||||
keys = memcache_crypt.derive_keys(
|
keys = memcache_crypt.derive_keys(
|
||||||
token,
|
token_id,
|
||||||
self._memcache_secret_key,
|
self._memcache_secret_key,
|
||||||
self._memcache_security_strategy)
|
self._memcache_security_strategy)
|
||||||
cache_key = CACHE_KEY_TEMPLATE % memcache_crypt.get_cache_key(keys)
|
cache_key = CACHE_KEY_TEMPLATE % memcache_crypt.get_cache_key(keys)
|
||||||
@@ -1025,7 +1029,7 @@ class AuthProtocol(object):
|
|||||||
raise InvalidUserToken('Token authorization failed')
|
raise InvalidUserToken('Token authorization failed')
|
||||||
return expires
|
return expires
|
||||||
|
|
||||||
def _cache_put(self, token, data, expires):
|
def _cache_put(self, token_id, data, expires):
|
||||||
"""Put token data into the cache.
|
"""Put token data into the cache.
|
||||||
|
|
||||||
Stores the parsed expire date in cache allowing
|
Stores the parsed expire date in cache allowing
|
||||||
@@ -1033,15 +1037,15 @@ class AuthProtocol(object):
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
if self._cache:
|
if self._cache:
|
||||||
self.LOG.debug('Storing %s token in memcache', token)
|
self.LOG.debug('Storing %s token in memcache', token_id)
|
||||||
self._cache_store(token, (data, expires))
|
self._cache_store(token_id, (data, expires))
|
||||||
|
|
||||||
def _cache_store_invalid(self, token):
|
def _cache_store_invalid(self, token_id):
|
||||||
"""Store invalid token in cache."""
|
"""Store invalid token in cache."""
|
||||||
if self._cache:
|
if self._cache:
|
||||||
self.LOG.debug(
|
self.LOG.debug(
|
||||||
'Marking token %s as unauthorized in memcache', token)
|
'Marking token %s as unauthorized in memcache', token_id)
|
||||||
self._cache_store(token, 'invalid')
|
self._cache_store(token_id, 'invalid')
|
||||||
|
|
||||||
def cert_file_missing(self, proc_output, file_name):
|
def cert_file_missing(self, proc_output, file_name):
|
||||||
return (file_name in proc_output and not os.path.exists(file_name))
|
return (file_name in proc_output and not os.path.exists(file_name))
|
||||||
|
@@ -1076,9 +1076,17 @@ class AuthTokenMiddlewareTest(BaseAuthTokenMiddlewareTest):
|
|||||||
self.middleware(req.environ, self.start_fake_response)
|
self.middleware(req.environ, self.start_fake_response)
|
||||||
self.assertEqual(self.response_status, 401)
|
self.assertEqual(self.response_status, 401)
|
||||||
|
|
||||||
def test_memcache_set_invalid(self):
|
def test_memcache_set_invalid_uuid(self):
|
||||||
req = webob.Request.blank('/')
|
req = webob.Request.blank('/')
|
||||||
token = 'invalid-token'
|
token = uuid.uuid4().hex
|
||||||
|
req.headers['X-Auth-Token'] = token
|
||||||
|
self.middleware(req.environ, self.start_fake_response)
|
||||||
|
self.assertRaises(auth_token.InvalidUserToken,
|
||||||
|
self._get_cached_token, token)
|
||||||
|
|
||||||
|
def test_memcache_set_invalid_signed(self):
|
||||||
|
req = webob.Request.blank('/')
|
||||||
|
token = self.token_dict['signed_token_scoped_expired']
|
||||||
req.headers['X-Auth-Token'] = token
|
req.headers['X-Auth-Token'] = token
|
||||||
self.middleware(req.environ, self.start_fake_response)
|
self.middleware(req.environ, self.start_fake_response)
|
||||||
self.assertRaises(auth_token.InvalidUserToken,
|
self.assertRaises(auth_token.InvalidUserToken,
|
||||||
|
Reference in New Issue
Block a user