Consistently round down timestamps
This is one of the ways we can prevent race conditions with backends that round datetime objects or strings before persisting them. Change-Id: Iaee0ec8c7acd512b9d93096ce8306a2952061c7a Closes-Bug: 1622010
This commit is contained in:
parent
d1a761293a
commit
301b6a7bc7
|
@ -527,7 +527,7 @@ def isotime(at=None, subsecond=False):
|
|||
# parse correctly most of the time.
|
||||
|
||||
if not at:
|
||||
at = timeutils.utcnow()
|
||||
at = timeutils.utcnow().replace(microsecond=0)
|
||||
st = at.strftime(_ISO8601_TIME_FORMAT
|
||||
if not subsecond
|
||||
else _ISO8601_TIME_FORMAT_SUBSECOND)
|
||||
|
|
|
@ -94,7 +94,7 @@ class RevokeEvent(object):
|
|||
self.expires_at = self.expires_at.replace(microsecond=0)
|
||||
|
||||
if self.revoked_at is None:
|
||||
self.revoked_at = timeutils.utcnow()
|
||||
self.revoked_at = timeutils.utcnow().replace(microsecond=0)
|
||||
if self.issued_before is None:
|
||||
self.issued_before = self.revoked_at
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@ import operator
|
|||
import re
|
||||
import uuid
|
||||
|
||||
import freezegun
|
||||
from keystoneclient.common import cms
|
||||
import mock
|
||||
from oslo_log import versionutils
|
||||
|
@ -3161,51 +3162,62 @@ class TestTokenRevokeById(test_v3.RestfulTestCase):
|
|||
|
||||
def test_removing_role_assignment_does_not_affect_other_users(self):
|
||||
"""Revoking a role from one user should not affect other users."""
|
||||
# This group grant is not needed for the test
|
||||
self.delete(
|
||||
'/projects/%(project_id)s/groups/%(group_id)s/roles/%(role_id)s' %
|
||||
{'project_id': self.projectA['id'],
|
||||
'group_id': self.group1['id'],
|
||||
'role_id': self.role1['id']})
|
||||
time = datetime.datetime.utcnow()
|
||||
with freezegun.freeze_time(time) as frozen_datetime:
|
||||
# This group grant is not needed for the test
|
||||
self.delete(
|
||||
'/projects/%(p_id)s/groups/%(g_id)s/roles/%(r_id)s' %
|
||||
{'p_id': self.projectA['id'],
|
||||
'g_id': self.group1['id'],
|
||||
'r_id': self.role1['id']})
|
||||
|
||||
user1_token = self.get_requested_token(
|
||||
self.build_authentication_request(
|
||||
user_id=self.user1['id'],
|
||||
password=self.user1['password'],
|
||||
project_id=self.projectA['id']))
|
||||
# NOTE(lbragstad): Here we advance the clock one second to pass
|
||||
# into the threshold of a new second because we just presisted a
|
||||
# revocation event for removing a role from a group on a project.
|
||||
# One thing to note about that revocation event is that it has no
|
||||
# context about the group, so even though user3 might not be in
|
||||
# group1, they could have their token revoked because the
|
||||
# revocation event is very general.
|
||||
frozen_datetime.tick(delta=datetime.timedelta(seconds=1))
|
||||
|
||||
user3_token = self.get_requested_token(
|
||||
self.build_authentication_request(
|
||||
user_id=self.user3['id'],
|
||||
password=self.user3['password'],
|
||||
project_id=self.projectA['id']))
|
||||
user1_token = self.get_requested_token(
|
||||
self.build_authentication_request(
|
||||
user_id=self.user1['id'],
|
||||
password=self.user1['password'],
|
||||
project_id=self.projectA['id']))
|
||||
|
||||
# delete relationships between user1 and projectA from setUp
|
||||
self.delete(
|
||||
'/projects/%(project_id)s/users/%(user_id)s/roles/%(role_id)s' % {
|
||||
'project_id': self.projectA['id'],
|
||||
'user_id': self.user1['id'],
|
||||
'role_id': self.role1['id']})
|
||||
# authorization for the first user should now fail
|
||||
self.head('/auth/tokens',
|
||||
headers={'X-Subject-Token': user1_token},
|
||||
expected_status=http_client.NOT_FOUND)
|
||||
self.v3_create_token(
|
||||
self.build_authentication_request(
|
||||
user_id=self.user1['id'],
|
||||
password=self.user1['password'],
|
||||
project_id=self.projectA['id']),
|
||||
expected_status=http_client.UNAUTHORIZED)
|
||||
user3_token = self.get_requested_token(
|
||||
self.build_authentication_request(
|
||||
user_id=self.user3['id'],
|
||||
password=self.user3['password'],
|
||||
project_id=self.projectA['id']))
|
||||
|
||||
# authorization for the second user should still succeed
|
||||
self.head('/auth/tokens',
|
||||
headers={'X-Subject-Token': user3_token},
|
||||
expected_status=http_client.OK)
|
||||
self.v3_create_token(
|
||||
self.build_authentication_request(
|
||||
user_id=self.user3['id'],
|
||||
password=self.user3['password'],
|
||||
project_id=self.projectA['id']))
|
||||
# delete relationships between user1 and projectA from setUp
|
||||
self.delete(
|
||||
'/projects/%(p_id)s/users/%(u_id)s/roles/%(r_id)s' % {
|
||||
'p_id': self.projectA['id'],
|
||||
'u_id': self.user1['id'],
|
||||
'r_id': self.role1['id']})
|
||||
# authorization for the first user should now fail
|
||||
self.head('/auth/tokens',
|
||||
headers={'X-Subject-Token': user1_token},
|
||||
expected_status=http_client.NOT_FOUND)
|
||||
self.v3_create_token(
|
||||
self.build_authentication_request(
|
||||
user_id=self.user1['id'],
|
||||
password=self.user1['password'],
|
||||
project_id=self.projectA['id']),
|
||||
expected_status=http_client.UNAUTHORIZED)
|
||||
|
||||
# authorization for the second user should still succeed
|
||||
self.head('/auth/tokens',
|
||||
headers={'X-Subject-Token': user3_token},
|
||||
expected_status=http_client.OK)
|
||||
self.v3_create_token(
|
||||
self.build_authentication_request(
|
||||
user_id=self.user3['id'],
|
||||
password=self.user3['password'],
|
||||
project_id=self.projectA['id']))
|
||||
|
||||
def test_deleting_project_deletes_grants(self):
|
||||
# This is to make it a little bit more pretty with PEP8
|
||||
|
|
|
@ -78,7 +78,7 @@ class OSRevokeTests(test_v3.RestfulTestCase, test_v3.JsonHomeTestMixin):
|
|||
audit_id = uuid.uuid4().hex
|
||||
sample = self._blank_event()
|
||||
sample['audit_id'] = six.text_type(audit_id)
|
||||
before_time = timeutils.utcnow()
|
||||
before_time = timeutils.utcnow().replace(microsecond=0)
|
||||
self.revoke_api.revoke_by_audit_id(audit_id)
|
||||
resp = self.get('/OS-REVOKE/events')
|
||||
events = resp.json_body['events']
|
||||
|
@ -89,7 +89,7 @@ class OSRevokeTests(test_v3.RestfulTestCase, test_v3.JsonHomeTestMixin):
|
|||
project_id = uuid.uuid4().hex
|
||||
sample = dict()
|
||||
sample['project_id'] = six.text_type(project_id)
|
||||
before_time = timeutils.utcnow()
|
||||
before_time = timeutils.utcnow().replace(microsecond=0)
|
||||
self.revoke_api.revoke(
|
||||
revoke_model.RevokeEvent(project_id=project_id))
|
||||
|
||||
|
@ -102,7 +102,7 @@ class OSRevokeTests(test_v3.RestfulTestCase, test_v3.JsonHomeTestMixin):
|
|||
domain_id = uuid.uuid4().hex
|
||||
sample = dict()
|
||||
sample['domain_id'] = six.text_type(domain_id)
|
||||
before_time = timeutils.utcnow()
|
||||
before_time = timeutils.utcnow().replace(microsecond=0)
|
||||
self.revoke_api.revoke(
|
||||
revoke_model.RevokeEvent(domain_id=domain_id))
|
||||
|
||||
|
|
|
@ -101,7 +101,8 @@ def default_expire_time():
|
|||
|
||||
"""
|
||||
expire_delta = datetime.timedelta(seconds=CONF.token.expiration)
|
||||
return timeutils.utcnow() + expire_delta
|
||||
expires_at = timeutils.utcnow() + expire_delta
|
||||
return expires_at.replace(microsecond=0)
|
||||
|
||||
|
||||
def audit_info(parent_audit_id):
|
||||
|
|
Loading…
Reference in New Issue