Browse Source

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
Dolph Mathews 4 years ago
parent
commit
68a54c2cf1

+ 1
- 1
keystone/tests/unit/test_v3_federation.py View File

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

+ 14
- 3
keystone/token/providers/fernet/token_formatters.py View File

@@ -21,6 +21,7 @@ from oslo_config import cfg
21 21
 from oslo_log import log
22 22
 from oslo_utils import timeutils
23 23
 import six
24
+from six.moves import urllib
24 25
 
25 26
 from keystone.auth import plugins as auth_plugins
26 27
 from keystone import exception
@@ -64,19 +65,29 @@ class TokenFormatter(object):
64 65
 
65 66
     def pack(self, payload):
66 67
         """Pack a payload for transport as a token."""
67
-        return self.crypto.encrypt(payload)
68
+        # base64 padding (if any) is not URL-safe
69
+        return urllib.parse.quote(self.crypto.encrypt(payload))
68 70
 
69 71
     def unpack(self, token):
70 72
         """Unpack a token, and validate the payload."""
73
+        token = urllib.parse.unquote(six.binary_type(token))
74
+
71 75
         try:
72
-            return self.crypto.decrypt(token, ttl=CONF.token.expiration)
76
+            return self.crypto.decrypt(token)
73 77
         except fernet.InvalidToken as e:
74 78
             raise exception.Unauthorized(six.text_type(e))
75 79
 
76 80
     @classmethod
77 81
     def creation_time(cls, fernet_token):
78 82
         """Returns the creation time of a valid Fernet token."""
79
-        # fernet tokens are base64 encoded, so we need to unpack them first
83
+        # tokens may be transmitted as Unicode, but they're just ASCII
84
+        # (pypi/cryptography will refuse to operate on Unicode input)
85
+        fernet_token = six.binary_type(fernet_token)
86
+
87
+        # the base64 padding on fernet tokens is made URL-safe
88
+        fernet_token = urllib.parse.unquote(fernet_token)
89
+
90
+        # fernet tokens are base64 encoded and the padding made URL-safe
80 91
         token_bytes = base64.urlsafe_b64decode(fernet_token)
81 92
 
82 93
         # slice into the byte array to get just the timestamp

Loading…
Cancel
Save