URL quote Fernet tokens

The padding in base64.urlsafe_b64encode()'s output is not actually URL
safe, so you have to quote the result when it's of variable length.

In addition, Fernet tokens can always be handled as bytes, despite being
passed in from json.loads() as Unicode.

Change-Id: I72dbd4ddc066706f6af6ea2f2bcd5f0a6cb9b30c
Closes-Bug: 1433372
Closes-Bug: 1431669
This commit is contained in:
Dolph Mathews 2015-03-18 03:18:31 +00:00
parent a998ead704
commit 68a54c2cf1
2 changed files with 15 additions and 4 deletions

View File

@ -2402,7 +2402,7 @@ class FernetFederatedTokenTests(FederationTests, FederatedSetupMixin):
def test_federated_unscoped_token(self):
resp = self._issue_unscoped_token()
self.assertEqual(184, len(resp.headers['X-Subject-Token']))
self.assertEqual(186, len(resp.headers['X-Subject-Token']))
def test_federated_unscoped_token_with_multiple_groups(self):
assertion = 'ANOTHER_CUSTOMER_ASSERTION'

View File

@ -21,6 +21,7 @@ from oslo_config import cfg
from oslo_log import log
from oslo_utils import timeutils
import six
from six.moves import urllib
from keystone.auth import plugins as auth_plugins
from keystone import exception
@ -64,19 +65,29 @@ class TokenFormatter(object):
def pack(self, payload):
"""Pack a payload for transport as a token."""
return self.crypto.encrypt(payload)
# base64 padding (if any) is not URL-safe
return urllib.parse.quote(self.crypto.encrypt(payload))
def unpack(self, token):
"""Unpack a token, and validate the payload."""
token = urllib.parse.unquote(six.binary_type(token))
try:
return self.crypto.decrypt(token, ttl=CONF.token.expiration)
return self.crypto.decrypt(token)
except fernet.InvalidToken as e:
raise exception.Unauthorized(six.text_type(e))
@classmethod
def creation_time(cls, fernet_token):
"""Returns the creation time of a valid Fernet token."""
# fernet tokens are base64 encoded, so we need to unpack them first
# tokens may be transmitted as Unicode, but they're just ASCII
# (pypi/cryptography will refuse to operate on Unicode input)
fernet_token = six.binary_type(fernet_token)
# the base64 padding on fernet tokens is made URL-safe
fernet_token = urllib.parse.unquote(fernet_token)
# fernet tokens are base64 encoded and the padding made URL-safe
token_bytes = base64.urlsafe_b64decode(fernet_token)
# slice into the byte array to get just the timestamp