Ensure cache keys are a known/fixed length
Do not assume a token_id will result in a sane length for a memcache key length. In cases such as Fernet, these ids can easily exceed the limit on memcache key size. This change ensures we always use a SHA256 of the token id passed in, resulting in a fixed length cache key. Change-Id: I550e0a1b190047438756bbf40490815a5f177ea7 Closes-Bug: #1460225
This commit is contained in:
parent
6661501498
commit
2d4e19404a
@ -11,6 +11,7 @@
|
|||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
import contextlib
|
import contextlib
|
||||||
|
import hashlib
|
||||||
|
|
||||||
from oslo_serialization import jsonutils
|
from oslo_serialization import jsonutils
|
||||||
import six
|
import six
|
||||||
@ -21,6 +22,22 @@ from keystonemiddleware.i18n import _, _LE
|
|||||||
from keystonemiddleware.openstack.common import memorycache
|
from keystonemiddleware.openstack.common import memorycache
|
||||||
|
|
||||||
|
|
||||||
|
def _hash_key(key):
|
||||||
|
"""Turn a set of arguments into a SHA256 hash.
|
||||||
|
|
||||||
|
Using a known-length cache key is important to ensure that memcache
|
||||||
|
maximum key length is not exceeded causing failures to validate.
|
||||||
|
"""
|
||||||
|
if isinstance(key, six.text_type):
|
||||||
|
# NOTE(morganfainberg): Ensure we are always working with a bytes
|
||||||
|
# type required for the hasher. In python 2.7 it is possible to
|
||||||
|
# get a text_type (unicode). In python 3.4 all strings are
|
||||||
|
# text_type and not bytes by default. This encode coerces the
|
||||||
|
# text_type to the appropriate bytes values.
|
||||||
|
key = key.encode('utf-8')
|
||||||
|
return hashlib.sha256(key).hexdigest()
|
||||||
|
|
||||||
|
|
||||||
class _CachePool(list):
|
class _CachePool(list):
|
||||||
"""A lazy pool of cache references."""
|
"""A lazy pool of cache references."""
|
||||||
|
|
||||||
@ -178,7 +195,7 @@ class TokenCache(object):
|
|||||||
# NOTE(jamielennox): in the basic implementation there is no need for
|
# NOTE(jamielennox): in the basic implementation there is no need for
|
||||||
# a context so just pass None as it will only get passed back later.
|
# a context so just pass None as it will only get passed back later.
|
||||||
unused_context = None
|
unused_context = None
|
||||||
return self._CACHE_KEY_TEMPLATE % token_id, unused_context
|
return self._CACHE_KEY_TEMPLATE % _hash_key(token_id), unused_context
|
||||||
|
|
||||||
def _deserialize(self, data, context):
|
def _deserialize(self, data, context):
|
||||||
"""Deserialize data from the cache back into python objects.
|
"""Deserialize data from the cache back into python objects.
|
||||||
|
@ -500,6 +500,21 @@ class GeneralAuthTokenMiddlewareTest(BaseAuthTokenMiddlewareTest,
|
|||||||
token_response = self.examples.TOKEN_RESPONSES[token]
|
token_response = self.examples.TOKEN_RESPONSES[token]
|
||||||
self.assertTrue(auth_token._token_is_v3(token_response))
|
self.assertTrue(auth_token._token_is_v3(token_response))
|
||||||
|
|
||||||
|
def test_fixed_cache_key_length(self):
|
||||||
|
self.set_middleware()
|
||||||
|
short_string = uuid.uuid4().hex
|
||||||
|
long_string = 8 * uuid.uuid4().hex
|
||||||
|
|
||||||
|
token_cache = self.middleware._token_cache
|
||||||
|
hashed_short_string_key, context_ = token_cache._get_cache_key(
|
||||||
|
short_string)
|
||||||
|
hashed_long_string_key, context_ = token_cache._get_cache_key(
|
||||||
|
long_string)
|
||||||
|
|
||||||
|
# The hash keys should always match in length
|
||||||
|
self.assertThat(hashed_short_string_key,
|
||||||
|
matchers.HasLength(len(hashed_long_string_key)))
|
||||||
|
|
||||||
@testtools.skipUnless(memcached_available(), 'memcached not available')
|
@testtools.skipUnless(memcached_available(), 'memcached not available')
|
||||||
def test_encrypt_cache_data(self):
|
def test_encrypt_cache_data(self):
|
||||||
conf = {
|
conf = {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user