summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMorgan Fainberg <morgan.fainberg@gmail.com>2015-05-29 17:43:31 -0700
committerMorgan Fainberg <morgan.fainberg@gmail.com>2015-06-12 10:36:56 +0200
commit2d4e19404aa33200767dab86956608878876b03a (patch)
tree279b637e80a03269a27f7ea4570d21e643b471a8
parent666150149889d1d167fb3fba06d43f4b07809503 (diff)
downloadkeystonemiddleware-2d4e19404aa33200767dab86956608878876b03a.tar.gz
Ensure cache keys are a known/fixed length2.0.0
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
-rw-r--r--keystonemiddleware/auth_token/_cache.py19
-rw-r--r--keystonemiddleware/tests/unit/auth_token/test_auth_token_middleware.py15
2 files changed, 33 insertions, 1 deletions
diff --git a/keystonemiddleware/auth_token/_cache.py b/keystonemiddleware/auth_token/_cache.py
index dbcdebf..ce5faf6 100644
--- a/keystonemiddleware/auth_token/_cache.py
+++ b/keystonemiddleware/auth_token/_cache.py
@@ -11,6 +11,7 @@
# under the License.
import contextlib
+import hashlib
from oslo_serialization import jsonutils
import six
@@ -21,6 +22,22 @@ from keystonemiddleware.i18n import _, _LE
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):
"""A lazy pool of cache references."""
@@ -178,7 +195,7 @@ class TokenCache(object):
# 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.
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):
"""Deserialize data from the cache back into python objects.
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 7b3d574..8657deb 100644
--- a/keystonemiddleware/tests/unit/auth_token/test_auth_token_middleware.py
+++ b/keystonemiddleware/tests/unit/auth_token/test_auth_token_middleware.py
@@ -500,6 +500,21 @@ class GeneralAuthTokenMiddlewareTest(BaseAuthTokenMiddlewareTest,
token_response = self.examples.TOKEN_RESPONSES[token]
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')
def test_encrypt_cache_data(self):
conf = {