From 25e5227d23d7b8e1b2969a6b7dd6da4fe50972a4 Mon Sep 17 00:00:00 2001 From: Dave Chen Date: Fri, 1 Jul 2016 16:40:18 +0800 Subject: [PATCH] Return `revoked_at` for list revoke events The field will help to figure out when the event is recorded, and thus give a clue on when the revocation was done. Change-Id: If5ba3bb9b68fff5f35be2b57af15584d69c6df8d Closes-Bug: #1598040 --- keystone/models/revoke_model.py | 3 +++ keystone/tests/unit/test_v3_auth.py | 4 ++++ keystone/tests/unit/test_v3_os_revoke.py | 28 ++++++++++++++++++++++++ 3 files changed, 35 insertions(+) diff --git a/keystone/models/revoke_model.py b/keystone/models/revoke_model.py index 790a2cb7f6..79f008d0fe 100644 --- a/keystone/models/revoke_model.py +++ b/keystone/models/revoke_model.py @@ -118,6 +118,9 @@ class RevokeEvent(object): if self.issued_before is not None: event['issued_before'] = utils.isotime(self.issued_before, subsecond=True) + if self.revoked_at is not None: + event['revoked_at'] = utils.isotime(self.revoked_at, + subsecond=True) return event def key_for_name(self, name): diff --git a/keystone/tests/unit/test_v3_auth.py b/keystone/tests/unit/test_v3_auth.py index 3abbe77912..a0c251ad7d 100644 --- a/keystone/tests/unit/test_v3_auth.py +++ b/keystone/tests/unit/test_v3_auth.py @@ -3342,6 +3342,7 @@ class TestTokenRevokeApi(TestTokenRevokeById): self.assertIsNotNone(events[0]['issued_before']) self.assertIsNotNone(events_response['links']) del (events_response['events'][0]['issued_before']) + del (events_response['events'][0]['revoked_at']) del (events_response['links']) expected_response = {'events': [{'project_id': project_id}]} self.assertEqual(expected_response, events_response) @@ -3356,6 +3357,8 @@ class TestTokenRevokeApi(TestTokenRevokeById): self.assertIsNotNone(events_response['links']) del (events_response['events'][0]['issued_before']) del (events_response['events'][1]['issued_before']) + del (events_response['events'][0]['revoked_at']) + del (events_response['events'][1]['revoked_at']) del (events_response['links']) expected_response = {'events': [{'project_id': domain_id}, {'domain_id': domain_id}]} @@ -3369,6 +3372,7 @@ class TestTokenRevokeApi(TestTokenRevokeById): self.assertIsNotNone(events[0]['issued_before']) self.assertIsNotNone(events_response['links']) del (events_response['events'][0]['issued_before']) + del (events_response['events'][0]['revoked_at']) del (events_response['links']) expected_response = {'events': [kwargs]} diff --git a/keystone/tests/unit/test_v3_os_revoke.py b/keystone/tests/unit/test_v3_os_revoke.py index 76a5d25e68..4a74026d52 100644 --- a/keystone/tests/unit/test_v3_os_revoke.py +++ b/keystone/tests/unit/test_v3_os_revoke.py @@ -38,6 +38,20 @@ class OSRevokeTests(test_v3.RestfulTestCase, test_v3.JsonHomeTestMixin): }, } + # TODO(davechen): This method is copied from `keystone.tests.unit. + # test_v3_auth.TokenAPITests`, move this method into utils.py to avoid + # the duplication? + def assertTimestampEqual(self, expected, value): + # Compare two timestamps but ignore the microseconds part + # of the expected timestamp. Keystone does not track microseconds and + # is working to eliminate microseconds from it's datetimes used. + expected = timeutils.parse_isotime(expected).replace(microsecond=0) + value = timeutils.parse_isotime(value).replace(microsecond=0) + self.assertEqual( + expected, + value, + "%s != %s" % (expected, value)) + def test_get_empty_list(self): resp = self.get('/OS-REVOKE/events') self.assertEqual([], resp.json_body['events']) @@ -62,6 +76,7 @@ class OSRevokeTests(test_v3.RestfulTestCase, test_v3.JsonHomeTestMixin): utils.isotime(event_issued_before, subsecond=True), utils.isotime(after_time, subsecond=True))) del (event['issued_before']) + del (event['revoked_at']) self.assertEqual(sample, event) def test_revoked_list_self_url(self): @@ -131,3 +146,16 @@ class OSRevokeTests(test_v3.RestfulTestCase, test_v3.JsonHomeTestMixin): resp = self.get('/OS-REVOKE/events?since=%s' % _future_time_string()) events = resp.json_body['events'] self.assertEqual([], events) + + def test_revoked_at_in_list(self): + revoked_at = timeutils.utcnow() + # Given or not, `revoked_at` will always be set in the backend. + self.revoke_api.revoke( + revoke_model.RevokeEvent(revoked_at=revoked_at)) + + resp = self.get('/OS-REVOKE/events') + events = resp.json_body['events'] + self.assertThat(events, matchers.HasLength(1)) + # Strip off the microseconds from `revoked_at`. + self.assertTimestampEqual(utils.isotime(revoked_at), + events[0]['revoked_at'])