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. Closes-Bug: #1459790 Change-Id: Ic6adbc2d69af03519a2c4ef66d558bcff9022049
This commit is contained in:
parent
aac271e6f7
commit
9391f9307e
|
@ -262,8 +262,8 @@ class TokenAPITests(object):
|
|||
# just need to make sure the non fraction part agrees
|
||||
self.assertIn(v2_token_data['access']['token']['expires'][:-1],
|
||||
v3_token_data['token']['expires_at'])
|
||||
self.assertEqual(v2_token_data['access']['user']['roles'][0]['id'],
|
||||
v3_token_data['token']['roles'][0]['id'])
|
||||
self.assertEqual(v2_token_data['access']['user']['roles'][0]['name'],
|
||||
v3_token_data['token']['roles'][0]['name'])
|
||||
|
||||
def test_v2_v3_unscoped_token_intermix(self):
|
||||
r = self.admin_request(
|
||||
|
@ -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,
|
||||
|
|
|
@ -72,7 +72,7 @@ class TestPayloads(tests.TestCase):
|
|||
def test_time_string_to_int_conversions(self):
|
||||
payload_cls = token_formatters.BasePayload
|
||||
|
||||
expected_time_str = utils.isotime()
|
||||
expected_time_str = utils.isotime(subsecond=True)
|
||||
time_obj = timeutils.parse_isotime(expected_time_str)
|
||||
expected_time_int = (
|
||||
(timeutils.normalize_time(time_obj) -
|
||||
|
@ -89,7 +89,7 @@ class TestPayloads(tests.TestCase):
|
|||
def test_unscoped_payload(self):
|
||||
exp_user_id = uuid.uuid4().hex
|
||||
exp_methods = ['password']
|
||||
exp_expires_at = utils.isotime(timeutils.utcnow())
|
||||
exp_expires_at = utils.isotime(timeutils.utcnow(), subsecond=True)
|
||||
exp_audit_ids = [provider.random_urlsafe_str()]
|
||||
|
||||
payload = token_formatters.UnscopedPayload.assemble(
|
||||
|
@ -107,7 +107,7 @@ class TestPayloads(tests.TestCase):
|
|||
exp_user_id = uuid.uuid4().hex
|
||||
exp_methods = ['password']
|
||||
exp_project_id = uuid.uuid4().hex
|
||||
exp_expires_at = utils.isotime(timeutils.utcnow())
|
||||
exp_expires_at = utils.isotime(timeutils.utcnow(), subsecond=True)
|
||||
exp_audit_ids = [provider.random_urlsafe_str()]
|
||||
|
||||
payload = token_formatters.ProjectScopedPayload.assemble(
|
||||
|
@ -127,7 +127,7 @@ class TestPayloads(tests.TestCase):
|
|||
exp_user_id = uuid.uuid4().hex
|
||||
exp_methods = ['password']
|
||||
exp_domain_id = uuid.uuid4().hex
|
||||
exp_expires_at = utils.isotime(timeutils.utcnow())
|
||||
exp_expires_at = utils.isotime(timeutils.utcnow(), subsecond=True)
|
||||
exp_audit_ids = [provider.random_urlsafe_str()]
|
||||
|
||||
payload = token_formatters.DomainScopedPayload.assemble(
|
||||
|
@ -147,7 +147,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 = utils.isotime(timeutils.utcnow())
|
||||
exp_expires_at = utils.isotime(timeutils.utcnow(), subsecond=True)
|
||||
exp_audit_ids = [provider.random_urlsafe_str()]
|
||||
|
||||
payload = token_formatters.DomainScopedPayload.assemble(
|
||||
|
@ -167,7 +167,7 @@ class TestPayloads(tests.TestCase):
|
|||
exp_user_id = uuid.uuid4().hex
|
||||
exp_methods = ['password']
|
||||
exp_project_id = uuid.uuid4().hex
|
||||
exp_expires_at = utils.isotime(timeutils.utcnow())
|
||||
exp_expires_at = utils.isotime(timeutils.utcnow(), subsecond=True)
|
||||
exp_audit_ids = [provider.random_urlsafe_str()]
|
||||
exp_trust_id = uuid.uuid4().hex
|
||||
|
||||
|
@ -188,7 +188,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 = utils.isotime(timeutils.utcnow())
|
||||
exp_expires_at = utils.isotime(timeutils.utcnow(), subsecond=True)
|
||||
exp_audit_ids = [provider.random_urlsafe_str()]
|
||||
|
||||
payload = token_formatters.UnscopedPayload.assemble(
|
||||
|
@ -206,7 +206,7 @@ class TestPayloads(tests.TestCase):
|
|||
exp_user_id = 'someNonUuidUserId'
|
||||
exp_methods = ['password']
|
||||
exp_project_id = uuid.uuid4().hex
|
||||
exp_expires_at = utils.isotime(timeutils.utcnow())
|
||||
exp_expires_at = utils.isotime(timeutils.utcnow(), subsecond=True)
|
||||
exp_audit_ids = [provider.random_urlsafe_str()]
|
||||
|
||||
payload = token_formatters.ProjectScopedPayload.assemble(
|
||||
|
@ -226,7 +226,7 @@ class TestPayloads(tests.TestCase):
|
|||
exp_user_id = uuid.uuid4().hex
|
||||
exp_methods = ['password']
|
||||
exp_project_id = 'someNonUuidProjectId'
|
||||
exp_expires_at = utils.isotime(timeutils.utcnow())
|
||||
exp_expires_at = utils.isotime(timeutils.utcnow(), subsecond=True)
|
||||
exp_audit_ids = [provider.random_urlsafe_str()]
|
||||
|
||||
payload = token_formatters.ProjectScopedPayload.assemble(
|
||||
|
@ -246,7 +246,7 @@ class TestPayloads(tests.TestCase):
|
|||
exp_user_id = 'someNonUuidUserId'
|
||||
exp_methods = ['password']
|
||||
exp_domain_id = uuid.uuid4().hex
|
||||
exp_expires_at = utils.isotime(timeutils.utcnow())
|
||||
exp_expires_at = utils.isotime(timeutils.utcnow(), subsecond=True)
|
||||
exp_audit_ids = [provider.random_urlsafe_str()]
|
||||
|
||||
payload = token_formatters.DomainScopedPayload.assemble(
|
||||
|
@ -266,7 +266,7 @@ class TestPayloads(tests.TestCase):
|
|||
exp_user_id = 'someNonUuidUserId'
|
||||
exp_methods = ['password']
|
||||
exp_project_id = uuid.uuid4().hex
|
||||
exp_expires_at = utils.isotime(timeutils.utcnow())
|
||||
exp_expires_at = utils.isotime(timeutils.utcnow(), subsecond=True)
|
||||
exp_audit_ids = [provider.random_urlsafe_str()]
|
||||
exp_trust_id = uuid.uuid4().hex
|
||||
|
||||
|
@ -288,7 +288,7 @@ class TestPayloads(tests.TestCase):
|
|||
exp_user_id = uuid.uuid4().hex
|
||||
exp_methods = ['password']
|
||||
exp_project_id = 'someNonUuidProjectId'
|
||||
exp_expires_at = utils.isotime(timeutils.utcnow())
|
||||
exp_expires_at = utils.isotime(timeutils.utcnow(), subsecond=True)
|
||||
exp_audit_ids = [provider.random_urlsafe_str()]
|
||||
exp_trust_id = uuid.uuid4().hex
|
||||
|
||||
|
@ -309,7 +309,7 @@ class TestPayloads(tests.TestCase):
|
|||
def test_federated_payload_with_non_uuid_ids(self):
|
||||
exp_user_id = 'someNonUuidUserId'
|
||||
exp_methods = ['password']
|
||||
exp_expires_at = utils.isotime(timeutils.utcnow())
|
||||
exp_expires_at = utils.isotime(timeutils.utcnow(), subsecond=True)
|
||||
exp_audit_ids = [provider.random_urlsafe_str()]
|
||||
exp_federated_info = {'group_ids': [{'id': 'someNonUuidGroupId'}],
|
||||
'idp_id': uuid.uuid4().hex,
|
||||
|
|
|
@ -14,6 +14,7 @@ from oslo_config import cfg
|
|||
from oslo_log import log
|
||||
|
||||
from keystone.common import dependency
|
||||
from keystone.common import utils as ks_utils
|
||||
from keystone.contrib.federation import constants as federation_constants
|
||||
from keystone import exception
|
||||
from keystone.i18n import _
|
||||
|
@ -87,12 +88,34 @@ 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 issue_v3_token(self, *args, **kwargs):
|
||||
token_id, token_data = super(Provider, self).issue_v3_token(
|
||||
*args, **kwargs)
|
||||
self._build_issued_at_info(token_id, 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'] = ks_utils.isotime(
|
||||
at=fernet_creation_datetime_obj, subsecond=True)
|
||||
|
||||
def _build_federated_info(self, token_data):
|
||||
"""Extract everything needed for federated tokens.
|
||||
|
||||
|
|
|
@ -286,8 +286,8 @@ class BasePayload(object):
|
|||
:returns: a time formatted strings
|
||||
|
||||
"""
|
||||
time_object = datetime.datetime.utcfromtimestamp(int(time_int))
|
||||
return ks_utils.isotime(time_object)
|
||||
time_object = datetime.datetime.utcfromtimestamp(time_int)
|
||||
return ks_utils.isotime(time_object, subsecond=True)
|
||||
|
||||
@classmethod
|
||||
def attempt_convert_uuid_hex_to_bytes(cls, value):
|
||||
|
|
Loading…
Reference in New Issue