Fernet 'expires' value loses 'ms' after validation
When we create a Fernet token we get a token with an expiration
time which contains microseconds. When we validate a Fernet token,
the expiration time changes and comes back missing the microseconds.
We need to make the expiration time the same between creation and
validation time, having microseconds in both cases. This applies to
both v2 and v3 API.
Conflicts:
keystone/tests/unit/test_v3_auth.py
keystone/tests/unit/token/test_fernet_provider.py
keystone/token/providers/fernet/core.py
keystone/token/providers/fernet/token_formatters.py
NOTE: This backport differs from the patch to master to handle the
difference in oslo.timeutils versus the move to keystone's own
implementation of isotime in master, and to move the fix in
keystone/token/providers/fernet/core.py to avoid having to backport a
significant refactor Fernet's implementation.
Closes-Bug: #1459790
Change-Id: Ic6adbc2d69af03519a2c4ef66d558bcff9022049
(cherry picked from commit 9391f9307e
)
This commit is contained in:
parent
0ed33e3d25
commit
aa3354e73b
|
@ -267,8 +267,8 @@ class TokenAPITests(object):
|
|||
# just need to make sure the non fraction part agrees
|
||||
self.assertIn(v2_token['access']['token']['expires'][:-1],
|
||||
token_data['token']['expires_at'])
|
||||
self.assertEqual(v2_token['access']['user']['roles'][0]['id'],
|
||||
token_data['token']['roles'][0]['id'])
|
||||
self.assertEqual(v2_token['access']['user']['roles'][0]['name'],
|
||||
token_data['token']['roles'][0]['name'])
|
||||
|
||||
def test_v2_v3_unscoped_token_intermix(self):
|
||||
body = {
|
||||
|
@ -540,22 +540,6 @@ class TestFernetTokenAPIs(test_v3.RestfulTestCase, TokenAPITests):
|
|||
super(TestFernetTokenAPIs, self).setUp()
|
||||
self.doSetUp()
|
||||
|
||||
@test_utils.wip('Failing due to bug 1459790.')
|
||||
def test_v3_v2_token_intermix(self):
|
||||
super(TestFernetTokenAPIs, self).test_v3_v2_token_intermix()
|
||||
|
||||
@test_utils.wip('Failing due to bug 1459790.')
|
||||
def test_v3_v2_unscoped_token_intermix(self):
|
||||
super(TestFernetTokenAPIs, self).test_v3_v2_unscoped_token_intermix()
|
||||
|
||||
@test_utils.wip('Failing due to bug 1459790.')
|
||||
def test_v2_v3_token_intermix(self):
|
||||
super(TestFernetTokenAPIs, self).test_v2_v3_token_intermix()
|
||||
|
||||
@test_utils.wip('Failing due to bug 1459790.')
|
||||
def test_rescoping_token(self):
|
||||
super(TestFernetTokenAPIs, self).test_rescoping_token()
|
||||
|
||||
@test_utils.wip('Failing due to bug 1475762.')
|
||||
def test_v3_v2_intermix_non_default_project_failed(self):
|
||||
super(TestFernetTokenAPIs,
|
||||
|
|
|
@ -75,7 +75,7 @@ class TestPayloads(tests.TestCase):
|
|||
def test_time_string_to_int_conversions(self):
|
||||
payload_cls = token_formatters.BasePayload
|
||||
|
||||
expected_time_str = timeutils.isotime()
|
||||
expected_time_str = timeutils.isotime(subsecond=True)
|
||||
time_obj = timeutils.parse_isotime(expected_time_str)
|
||||
expected_time_int = (
|
||||
(timeutils.normalize_time(time_obj) -
|
||||
|
@ -92,7 +92,7 @@ class TestPayloads(tests.TestCase):
|
|||
def test_unscoped_payload(self):
|
||||
exp_user_id = uuid.uuid4().hex
|
||||
exp_methods = ['password']
|
||||
exp_expires_at = timeutils.isotime(timeutils.utcnow())
|
||||
exp_expires_at = timeutils.isotime(timeutils.utcnow(), subsecond=True)
|
||||
exp_audit_ids = [provider.random_urlsafe_str()]
|
||||
|
||||
payload = token_formatters.UnscopedPayload.assemble(
|
||||
|
@ -110,7 +110,7 @@ class TestPayloads(tests.TestCase):
|
|||
exp_user_id = uuid.uuid4().hex
|
||||
exp_methods = ['password']
|
||||
exp_project_id = uuid.uuid4().hex
|
||||
exp_expires_at = timeutils.isotime(timeutils.utcnow())
|
||||
exp_expires_at = timeutils.isotime(timeutils.utcnow(), subsecond=True)
|
||||
exp_audit_ids = [provider.random_urlsafe_str()]
|
||||
|
||||
payload = token_formatters.ProjectScopedPayload.assemble(
|
||||
|
@ -130,7 +130,7 @@ class TestPayloads(tests.TestCase):
|
|||
exp_user_id = uuid.uuid4().hex
|
||||
exp_methods = ['password']
|
||||
exp_domain_id = uuid.uuid4().hex
|
||||
exp_expires_at = timeutils.isotime(timeutils.utcnow())
|
||||
exp_expires_at = timeutils.isotime(timeutils.utcnow(), subsecond=True)
|
||||
exp_audit_ids = [provider.random_urlsafe_str()]
|
||||
|
||||
payload = token_formatters.DomainScopedPayload.assemble(
|
||||
|
@ -150,7 +150,7 @@ class TestPayloads(tests.TestCase):
|
|||
exp_user_id = uuid.uuid4().hex
|
||||
exp_methods = ['password']
|
||||
exp_domain_id = CONF.identity.default_domain_id
|
||||
exp_expires_at = timeutils.isotime(timeutils.utcnow())
|
||||
exp_expires_at = timeutils.isotime(timeutils.utcnow(), subsecond=True)
|
||||
exp_audit_ids = [provider.random_urlsafe_str()]
|
||||
|
||||
payload = token_formatters.DomainScopedPayload.assemble(
|
||||
|
@ -170,7 +170,7 @@ class TestPayloads(tests.TestCase):
|
|||
exp_user_id = uuid.uuid4().hex
|
||||
exp_methods = ['password']
|
||||
exp_project_id = uuid.uuid4().hex
|
||||
exp_expires_at = timeutils.isotime(timeutils.utcnow())
|
||||
exp_expires_at = timeutils.isotime(timeutils.utcnow(), subsecond=True)
|
||||
exp_audit_ids = [provider.random_urlsafe_str()]
|
||||
exp_trust_id = uuid.uuid4().hex
|
||||
|
||||
|
@ -191,7 +191,7 @@ class TestPayloads(tests.TestCase):
|
|||
def test_unscoped_payload_with_non_uuid_user_id(self):
|
||||
exp_user_id = 'someNonUuidUserId'
|
||||
exp_methods = ['password']
|
||||
exp_expires_at = timeutils.isotime(timeutils.utcnow())
|
||||
exp_expires_at = timeutils.isotime(timeutils.utcnow(), subsecond=True)
|
||||
exp_audit_ids = [provider.random_urlsafe_str()]
|
||||
|
||||
payload = token_formatters.UnscopedPayload.assemble(
|
||||
|
@ -209,7 +209,7 @@ class TestPayloads(tests.TestCase):
|
|||
exp_user_id = 'someNonUuidUserId'
|
||||
exp_methods = ['password']
|
||||
exp_project_id = uuid.uuid4().hex
|
||||
exp_expires_at = timeutils.isotime(timeutils.utcnow())
|
||||
exp_expires_at = timeutils.isotime(timeutils.utcnow(), subsecond=True)
|
||||
exp_audit_ids = [provider.random_urlsafe_str()]
|
||||
|
||||
payload = token_formatters.ProjectScopedPayload.assemble(
|
||||
|
@ -229,7 +229,7 @@ class TestPayloads(tests.TestCase):
|
|||
exp_user_id = uuid.uuid4().hex
|
||||
exp_methods = ['password']
|
||||
exp_project_id = 'someNonUuidProjectId'
|
||||
exp_expires_at = timeutils.isotime(timeutils.utcnow())
|
||||
exp_expires_at = timeutils.isotime(timeutils.utcnow(), subsecond=True)
|
||||
exp_audit_ids = [provider.random_urlsafe_str()]
|
||||
|
||||
payload = token_formatters.ProjectScopedPayload.assemble(
|
||||
|
@ -249,7 +249,7 @@ class TestPayloads(tests.TestCase):
|
|||
exp_user_id = 'someNonUuidUserId'
|
||||
exp_methods = ['password']
|
||||
exp_domain_id = uuid.uuid4().hex
|
||||
exp_expires_at = timeutils.isotime(timeutils.utcnow())
|
||||
exp_expires_at = timeutils.isotime(timeutils.utcnow(), subsecond=True)
|
||||
exp_audit_ids = [provider.random_urlsafe_str()]
|
||||
|
||||
payload = token_formatters.DomainScopedPayload.assemble(
|
||||
|
@ -269,7 +269,7 @@ class TestPayloads(tests.TestCase):
|
|||
exp_user_id = 'someNonUuidUserId'
|
||||
exp_methods = ['password']
|
||||
exp_project_id = uuid.uuid4().hex
|
||||
exp_expires_at = timeutils.isotime(timeutils.utcnow())
|
||||
exp_expires_at = timeutils.isotime(timeutils.utcnow(), subsecond=True)
|
||||
exp_audit_ids = [provider.random_urlsafe_str()]
|
||||
exp_trust_id = uuid.uuid4().hex
|
||||
|
||||
|
@ -291,7 +291,7 @@ class TestPayloads(tests.TestCase):
|
|||
exp_user_id = uuid.uuid4().hex
|
||||
exp_methods = ['password']
|
||||
exp_project_id = 'someNonUuidProjectId'
|
||||
exp_expires_at = timeutils.isotime(timeutils.utcnow())
|
||||
exp_expires_at = timeutils.isotime(timeutils.utcnow(), subsecond=True)
|
||||
exp_audit_ids = [provider.random_urlsafe_str()]
|
||||
exp_trust_id = uuid.uuid4().hex
|
||||
|
||||
|
@ -312,7 +312,7 @@ class TestPayloads(tests.TestCase):
|
|||
def test_federated_payload_with_non_uuid_ids(self):
|
||||
exp_user_id = 'someNonUuidUserId'
|
||||
exp_methods = ['password']
|
||||
exp_expires_at = timeutils.isotime(timeutils.utcnow())
|
||||
exp_expires_at = timeutils.isotime(timeutils.utcnow(), subsecond=True)
|
||||
exp_audit_ids = [provider.random_urlsafe_str()]
|
||||
exp_federated_info = {'group_ids': [{'id': 'someNonUuidGroupId'}],
|
||||
'idp_id': uuid.uuid4().hex,
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log
|
||||
from oslo_utils import timeutils
|
||||
|
||||
from keystone.common import dependency
|
||||
from keystone.contrib import federation
|
||||
|
@ -87,12 +88,28 @@ class Provider(common.BaseProvider):
|
|||
audit_ids,
|
||||
methods=method_names,
|
||||
project_id=project_id)
|
||||
self._build_issued_at_info(token_id, v3_token_data)
|
||||
# Convert v3 to v2 token data and build v2 catalog
|
||||
token_data = self.v2_token_data_helper.v3_to_v2_token(token_id,
|
||||
v3_token_data)
|
||||
|
||||
return token_id, token_data
|
||||
|
||||
def _build_issued_at_info(self, token_id, token_data):
|
||||
# NOTE(roxanaghe, lbragstad): We must use the creation time that
|
||||
# Fernet builds into it's token. The Fernet spec details that the
|
||||
# token creation time is built into the token, outside of the payload
|
||||
# provided by Keystone. This is the reason why we don't pass the
|
||||
# issued_at time in the payload. This also means that we shouldn't
|
||||
# return a token reference with a creation time that we created
|
||||
# when Fernet uses a different creation time. We should use the
|
||||
# creation time provided by Fernet because it's the creation time
|
||||
# that we have to rely on when we validate the token.
|
||||
fernet_creation_datetime_obj = self.token_formatter.creation_time(
|
||||
token_id)
|
||||
token_data['token']['issued_at'] = timeutils.isotime(
|
||||
at=fernet_creation_datetime_obj, subsecond=True)
|
||||
|
||||
def _build_federated_info(self, token_data):
|
||||
"""Extract everything needed for federated tokens.
|
||||
|
||||
|
@ -195,6 +212,7 @@ class Provider(common.BaseProvider):
|
|||
project_id=project_id,
|
||||
trust_id=token_data['token'].get('OS-TRUST:trust', {}).get('id'),
|
||||
federated_info=federated_dict)
|
||||
self._build_issued_at_info(token, token_data)
|
||||
return token, token_data
|
||||
|
||||
def validate_v2_token(self, token_ref):
|
||||
|
|
|
@ -274,8 +274,8 @@ class BasePayload(object):
|
|||
:returns: a time formatted strings
|
||||
|
||||
"""
|
||||
time_object = datetime.datetime.utcfromtimestamp(int(time_int))
|
||||
return timeutils.isotime(time_object)
|
||||
time_object = datetime.datetime.utcfromtimestamp(time_int)
|
||||
return timeutils.isotime(time_object, subsecond=True)
|
||||
|
||||
@classmethod
|
||||
def attempt_convert_uuid_hex_to_bytes(cls, value):
|
||||
|
|
Loading…
Reference in New Issue