diff options
-rw-r--r-- | keystone/tests/unit/token/test_fernet_provider.py | 39 | ||||
-rw-r--r-- | keystone/token/providers/fernet/token_formatters.py | 16 |
2 files changed, 53 insertions, 2 deletions
diff --git a/keystone/tests/unit/token/test_fernet_provider.py b/keystone/tests/unit/token/test_fernet_provider.py index 5f74b430e..a909ee276 100644 --- a/keystone/tests/unit/token/test_fernet_provider.py +++ b/keystone/tests/unit/token/test_fernet_provider.py @@ -16,7 +16,9 @@ import hashlib import os import uuid +import msgpack from oslo_utils import timeutils +from six.moves import urllib from keystone.common import config from keystone.common import utils @@ -58,6 +60,10 @@ class TestFernetTokenProvider(unit.TestCase): class TestTokenFormatter(unit.TestCase): + def setUp(self): + super(TestTokenFormatter, self).setUp() + self.useFixture(ksfixtures.KeyRepository(self.config_fixture)) + def test_restore_padding(self): # 'a' will result in '==' padding, 'aa' will result in '=' padding, and # 'aaa' will result in no padding. @@ -73,6 +79,39 @@ class TestTokenFormatter(unit.TestCase): ) self.assertEqual(encoded_string, encoded_str_with_padding_restored) + def test_legacy_padding_validation(self): + first_value = uuid.uuid4().hex + second_value = uuid.uuid4().hex + payload = (first_value, second_value) + msgpack_payload = msgpack.packb(payload) + + # NOTE(lbragstad): This method perserves the way that keystone used to + # percent encode the tokens, prior to bug #1491926. + def legacy_pack(payload): + tf = token_formatters.TokenFormatter() + encrypted_payload = tf.crypto.encrypt(payload) + + # the encrypted_payload is returned with padding appended + self.assertTrue(encrypted_payload.endswith('=')) + + # using urllib.parse.quote will percent encode the padding, like + # keystone did in Kilo. + percent_encoded_payload = urllib.parse.quote(encrypted_payload) + + # ensure that the padding was actaully percent encoded + self.assertTrue(percent_encoded_payload.endswith('%3D')) + return percent_encoded_payload + + token_with_legacy_padding = legacy_pack(msgpack_payload) + tf = token_formatters.TokenFormatter() + + # demonstrate the we can validate a payload that has been percent + # encoded with the Fernet logic that existed in Kilo + serialized_payload = tf.unpack(token_with_legacy_padding) + returned_payload = msgpack.unpackb(serialized_payload) + self.assertEqual(first_value, returned_payload[0]) + self.assertEqual(second_value, returned_payload[1]) + class TestPayloads(unit.TestCase): def test_uuid_hex_to_byte_conversions(self): diff --git a/keystone/token/providers/fernet/token_formatters.py b/keystone/token/providers/fernet/token_formatters.py index f0b6271d1..a6b42197f 100644 --- a/keystone/token/providers/fernet/token_formatters.py +++ b/keystone/token/providers/fernet/token_formatters.py @@ -22,6 +22,7 @@ from oslo_log import log from oslo_utils import timeutils import six from six.moves import map +from six.moves import urllib from keystone.auth import plugins as auth_plugins from keystone.common import utils as ks_utils @@ -73,8 +74,19 @@ class TokenFormatter(object): """Unpack a token, and validate the payload.""" token = six.binary_type(token) - # Restore padding on token before decoding it - token = TokenFormatter.restore_padding(token) + # TODO(lbragstad): Restore padding on token before decoding it. + # Initially in Kilo, Fernet tokens were returned to the user with + # padding appended to the token. Later in Liberty this padding was + # removed and restored in the Fernet provider. The following if + # statement ensures that we can validate tokens with and without token + # padding, in the event of an upgrade and the tokens that are issued + # throughout the upgrade. Remove this if statement when Mitaka opens + # for development and exclusively use the restore_padding() class + # method. + if token.endswith('%3D'): + token = urllib.parse.unquote(token) + else: + token = TokenFormatter.restore_padding(token) try: return self.crypto.decrypt(token) |