Ensure token validation works irrespective of padding

In change I674bad86ccc9027ac3b365c10b3b142fc9d73c17 we removed the padding from
Fernet tokens. This commit makes it so that we can validate Fernet tokens that
come in with and without padding.

Change-Id: I60e52af6c4cb39f2d136540a487bba8505a2c6b4
Closes-Bug: 1491926
(cherry picked from commit 90aca98058)
This commit is contained in:
Lance Bragstad 2015-09-09 14:09:06 +00:00
parent 4a239f23e3
commit 8dcd82fb9c
2 changed files with 53 additions and 2 deletions

View File

@ -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):

View File

@ -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)