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:
Roxana Gherle 2015-08-06 09:46:58 -07:00
parent aac271e6f7
commit 9391f9307e
4 changed files with 40 additions and 33 deletions

View File

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

View File

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

View File

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

View File

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