def test_list_revoked_project(self): revocation_backend = sql.Revoke() token = _sample_blank_token() # Create a token for a project, revoke token, check the token we # created has been revoked, and check the list returned a match for # the token when passed in. first_token = _sample_blank_token() first_token['project_id'] = uuid.uuid4().hex revocation_backend.revoke( revoke_model.RevokeEvent(project_id=first_token['project_id'])) self._assertTokenRevoked(first_token) self.assertEqual( 1, len(revocation_backend.list_events(token=first_token))) # Create a second token, revoke it, check the token has been revoked, # and check the list to make sure that even though we now have 2 # revoked events in the revocation list, it will only return 1 because # only one match for our second_token should exist second_token = _sample_blank_token() second_token['project_id'] = uuid.uuid4().hex revocation_backend.revoke( revoke_model.RevokeEvent(project_id=second_token['project_id'])) self._assertTokenRevoked(second_token) self.assertEqual( 1, len(revocation_backend.list_events(token=second_token))) # This gets a token but overrides project_id of the token to be None. # We expect that since there are two events which both have populated # project_ids, this should not match this third_token with any other # event in the list so we should receive 0. third_token = _sample_blank_token() third_token['project_id'] = None self._assertTokenNotRevoked(token) self.assertEqual(0, len(revocation_backend.list_events(token=token)))
def test_list_revoked_user(self): revocation_backend = sql.Revoke() events = [] # This simulates creating a token for a specific user. When we revoke # the token we should have a single revocation event in the list. We # are going to assert that the token values match the only revocation # event in the backend. first_token = _sample_blank_token() first_token['user_id'] = uuid.uuid4().hex add_event( events, revoke_model.RevokeEvent(user_id=first_token['user_id']) ) self.revoke_api.revoke_by_user(user_id=first_token['user_id']) self._assertTokenRevoked(events, first_token) self.assertEqual( 1, len(revocation_backend.list_events(token=first_token)) ) # This simulates creating a separate token for a separate user. We are # going to revoke the token just like we did for the previous token. # We should have two revocation events stored in the backend but only # one should match the values of the second token. second_token = _sample_blank_token() second_token['user_id'] = uuid.uuid4().hex add_event( events, revoke_model.RevokeEvent(user_id=second_token['user_id']) ) self.revoke_api.revoke_by_user(user_id=second_token['user_id']) self._assertTokenRevoked(events, second_token) self.assertEqual( 1, len(revocation_backend.list_events(token=second_token)) ) # This simulates creating another separate token for a separate user, # but we're not going to issue a revocation event. Even though we have # two revocation events persisted in the backend, neither of them # should match the values of the third token. If they did - our # revocation event matching would be too heavy handed, which would # result in over-generalized revocation patterns. third_token = _sample_blank_token() third_token['user_id'] = uuid.uuid4().hex self._assertTokenNotRevoked(events, third_token) self.assertEqual( 0, len(revocation_backend.list_events(token=third_token)) ) # This gets a token but overrides the user_id of the token to be None. # Technically this should never happen because tokens must belong to # a user. What we're testing here is that the two revocation events # we've created won't match None values for the user_id. fourth_token = _sample_blank_token() fourth_token['user_id'] = None self._assertTokenNotRevoked(events, fourth_token) self.assertEqual( 0, len(revocation_backend.list_events(token=fourth_token)) )
def test_list_revoked_multiple_filters(self): revocation_backend = sql.Revoke() events = [] # create token that sets key/value filters in list_revoked first_token = _sample_blank_token() first_token['user_id'] = uuid.uuid4().hex first_token['project_id'] = uuid.uuid4().hex first_token['audit_id'] = common.random_urlsafe_str() # revoke event and then verify that there is only one revocation # and verify the only revoked event is the token add_event( events, revoke_model.RevokeEvent(user_id=first_token['user_id'], project_id=first_token['project_id'], audit_id=first_token['audit_id'])) self.revoke_api.revoke( revoke_model.RevokeEvent(user_id=first_token['user_id'], project_id=first_token['project_id'], audit_id=first_token['audit_id'])) self._assertTokenRevoked(events, first_token) self.assertEqual( 1, len(revocation_backend.list_events(token=first_token))) # If a token has None values which the event contains it shouldn't # match and not be revoked second_token = _sample_blank_token() self._assertTokenNotRevoked(events, second_token) self.assertEqual( 0, len(revocation_backend.list_events(token=second_token))) # If an event column and corresponding dict value don't match, Then # it should not add the event in the list. Demonstrate for project third_token = _sample_blank_token() third_token['project_id'] = uuid.uuid4().hex self._assertTokenNotRevoked(events, third_token) self.assertEqual( 0, len(revocation_backend.list_events(token=third_token))) # A revoked event with user_id as null and token user_id non null # should still be return an event and be revoked if other non null # event fields match non null token fields fourth_token = _sample_blank_token() fourth_token['user_id'] = uuid.uuid4().hex fourth_token['project_id'] = uuid.uuid4().hex fourth_token['audit_id'] = common.random_urlsafe_str() add_event( events, revoke_model.RevokeEvent(project_id=fourth_token['project_id'], audit_id=fourth_token['audit_id'])) self.revoke_api.revoke( revoke_model.RevokeEvent(project_id=fourth_token['project_id'], audit_id=fourth_token['audit_id'])) self._assertTokenRevoked(events, fourth_token) self.assertEqual( 1, len(revocation_backend.list_events(token=fourth_token)))
def revoke_by_grant(self, role_id, user_id=None, domain_id=None, project_id=None): self.revoke( revoke_model.RevokeEvent(user_id=user_id, role_id=role_id, domain_id=domain_id, project_id=project_id))
def test_past_expiry_are_removed(self): user_id = 1 self.revoke_api.revoke_by_expiration(user_id, _future_time()) self.assertEqual(1, len(self.revoke_api.list_events())) event = revoke_model.RevokeEvent() event.revoked_at = _past_time() self.revoke_api.revoke(event) self.assertEqual(1, len(self.revoke_api.list_events()))
def revoke_by_audit_chain_id(self, audit_chain_id, project_id=None, domain_id=None): self._assert_not_domain_and_project_scoped(domain_id=domain_id, project_id=project_id) self.revoke(revoke_model.RevokeEvent(audit_chain_id=audit_chain_id, domain_id=domain_id, project_id=project_id))
def _revoke_by_audit_chain_id(self, audit_chain_id, project_id=None, domain_id=None): event = add_event( self.revoke_events, revoke_model.RevokeEvent(audit_chain_id=audit_chain_id, project_id=project_id, domain_id=domain_id) ) self.events.append(event) return event
def _revoke_by_expiration(self, user_id, expires_at, project_id=None, domain_id=None): event = add_event( self.revoke_events, revoke_model.RevokeEvent(user_id=user_id, expires_at=expires_at, project_id=project_id, domain_id=domain_id)) self.events.append(event) return event
def _revoke_by_grant(self, role_id, user_id=None, domain_id=None, project_id=None): event = add_event( self.revoke_events, revoke_model.RevokeEvent(user_id=user_id, role_id=role_id, domain_id=domain_id, project_id=project_id)) self.events.append(event) return event
def _list_last_fetch_events(self, last_fetch=None): with sql.session_for_read() as session: query = session.query(RevocationEvent).order_by( RevocationEvent.revoked_at) if last_fetch: query = query.filter(RevocationEvent.revoked_at > last_fetch) events = [revoke_model.RevokeEvent(**e.to_dict()) for e in query] return events
def test_disabled_domain_in_list(self): domain_id = uuid.uuid4().hex sample = dict() sample['domain_id'] = six.text_type(domain_id) before_time = timeutils.utcnow().replace(microsecond=0) self.revoke_api.revoke(revoke_model.RevokeEvent(domain_id=domain_id)) resp = self.get('/OS-REVOKE/events') events = resp.json_body['events'] self.assertEqual(1, len(events)) self.assertReportedEventMatchesRecorded(events[0], sample, before_time)
def test_list_revoked_audit(self): revocation_backend = sql.Revoke() events = [] # Create a token with audit_id set, revoke it, check it is revoked, # check to make sure that list_events matches the token to the event we # just revoked. first_token = _sample_blank_token() first_token['audit_id'] = common.random_urlsafe_str() add_event(events, revoke_model.RevokeEvent( audit_id=first_token['audit_id'])) self.revoke_api.revoke_by_audit_id( audit_id=first_token['audit_id']) self._assertTokenRevoked(events, first_token) self.assertEqual( 1, len(revocation_backend.list_events(token=first_token))) # Create a second token, revoke it, check it is revoked, check to make # sure that list events only finds 1 match since there are 2 and they # dont both have different populated audit_id fields second_token = _sample_blank_token() second_token['audit_id'] = common.random_urlsafe_str() add_event(events, revoke_model.RevokeEvent( audit_id=second_token['audit_id'])) self.revoke_api.revoke_by_audit_id( audit_id=second_token['audit_id']) self._assertTokenRevoked(events, second_token) self.assertEqual( 1, len(revocation_backend.list_events(token=second_token))) # Create a third token with audit_id set to None to make sure that # since there are no events currently revoked with audit_id None this # finds no matches third_token = _sample_blank_token() third_token['audit_id'] = None self._assertTokenNotRevoked(events, third_token) self.assertEqual( 0, len(revocation_backend.list_events(token=third_token)))
def revoke_by_expiration(self, user_id, expires_at, domain_id=None, project_id=None): self._assert_not_domain_and_project_scoped(domain_id=domain_id, project_id=project_id) self.revoke( revoke_model.RevokeEvent(user_id=user_id, expires_at=expires_at, domain_id=domain_id, project_id=project_id))
def test_since_future_time_no_events(self): domain_id = uuid.uuid4().hex sample = dict() sample['domain_id'] = six.text_type(domain_id) self.revoke_api.revoke(revoke_model.RevokeEvent(domain_id=domain_id)) resp = self.get('/OS-REVOKE/events') events = resp.json_body['events'] self.assertEqual(1, len(events)) 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): time = datetime.datetime.utcnow() with freezegun.freeze_time(time) as frozen_datetime: 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)) frozen_datetime.tick(delta=datetime.timedelta(seconds=1)) 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'])
def test_by_domain_domain(self): revocation_backend = sql.Revoke() token_data = _sample_blank_token() token_data['user_id'] = uuid.uuid4().hex token_data['identity_domain_id'] = uuid.uuid4().hex token_data['assignment_domain_id'] = uuid.uuid4().hex self._assertTokenNotRevoked(token_data) self.assertEqual( 0, len(revocation_backend.list_events(token=token_data))) # If revoke a domain, then a token scoped to the domain is revoked. PROVIDERS.revoke_api.revoke(revoke_model.RevokeEvent( domain_id=token_data['assignment_domain_id'])) self._assertTokenRevoked(token_data) self.assertEqual( 1, len(revocation_backend.list_events(token=token_data)))
def test_by_domain_user(self): revocation_backend = sql.Revoke() # If revoke a domain, then a token for a user in the domain is revoked user_id = uuid.uuid4().hex domain_id = uuid.uuid4().hex token_data = _sample_blank_token() token_data['user_id'] = user_id token_data['identity_domain_id'] = domain_id self._assertTokenNotRevoked(token_data) self.assertEqual(0, len(revocation_backend.list_events(token=token_data))) self.revoke_api.revoke(revoke_model.RevokeEvent(domain_id=domain_id)) self._assertTokenRevoked(token_data) self.assertEqual(1, len(revocation_backend.list_events(token=token_data)))
def _list_token_events(self, token): with sql.session_for_read() as session: query = session.query(RevocationEvent).filter( RevocationEvent.issued_before >= token['issued_at']) user = [RevocationEvent.user_id.is_(None)] proj = [RevocationEvent.project_id.is_(None)] audit = [RevocationEvent.audit_id.is_(None)] if token['user_id']: user.append(RevocationEvent.user_id == token['user_id']) if token['trustor_id']: user.append(RevocationEvent.user_id == token['trustor_id']) if token['trustee_id']: user.append(RevocationEvent.user_id == token['trustee_id']) if token['project_id']: proj.append(RevocationEvent.project_id == token['project_id']) if token['audit_id']: audit.append(RevocationEvent.audit_id == token['audit_id']) query = query.filter( sqlalchemy.and_(sqlalchemy.or_(*user), sqlalchemy.or_(*proj), sqlalchemy.or_(*audit))) events = [revoke_model.RevokeEvent(**e.to_dict()) for e in query] return events
def test_retries_on_deadlock(self): patcher = mock.patch('sqlalchemy.orm.query.Query.delete', autospec=True) # NOTE(mnikolaenko): raise 2 deadlocks and back to normal work of # method. Two attempts is enough to check that retry decorator works. # Otherwise it will take very much time to pass this test class FakeDeadlock(object): def __init__(self, mock_patcher): self.deadlock_count = 2 self.mock_patcher = mock_patcher self.patched = True def __call__(self, *args, **kwargs): if self.deadlock_count > 1: self.deadlock_count -= 1 else: self.mock_patcher.stop() self.patched = False raise oslo_db_exception.DBDeadlock sql_delete_mock = patcher.start() side_effect = FakeDeadlock(patcher) sql_delete_mock.side_effect = side_effect try: self.revoke_api.revoke( revoke_model.RevokeEvent(user_id=uuid.uuid4().hex)) finally: if side_effect.patched: patcher.stop() call_count = sql_delete_mock.call_count # initial attempt + 1 retry revoke_attempt_count = 2 self.assertEqual(call_count, revoke_attempt_count)
def _consumer_callback(self, service, resource_type, operation, payload): self.revoke( revoke_model.RevokeEvent(consumer_id=payload['resource_info']))
def _revoke_by_user(self, user_id): return add_event( self.revoke_events, revoke_model.RevokeEvent(user_id=user_id))
def _revoke_by_domain(self, domain_id): event = add_event(self.revoke_events, revoke_model.RevokeEvent(domain_id=domain_id)) self.events.append(event)
def _revoke_by_domain_role_assignment(self, domain_id, role_id): event = add_event(self.revoke_events, revoke_model.RevokeEvent(domain_id=domain_id, role_id=role_id)) self.events.append(event) return event
def _revoke_by_project_role_assignment(self, project_id, role_id): event = add_event(self.revoke_events, revoke_model.RevokeEvent(project_id=project_id, role_id=role_id)) self.events.append(event) return event
def _revoke_by_audit_id(self, audit_id): event = add_event( self.revoke_events, revoke_model.RevokeEvent(audit_id=audit_id)) self.events.append(event) return event
def revoke_by_audit_id(self, audit_id): self.revoke(revoke_model.RevokeEvent(audit_id=audit_id))
def revoke_by_user_and_project(self, user_id, project_id): self.revoke( revoke_model.RevokeEvent(project_id=project_id, user_id=user_id))
def _revoke_by_user_and_project(self, user_id, project_id): event = add_event(self.revoke_events, revoke_model.RevokeEvent(project_id=project_id, user_id=user_id)) self.events.append(event) return event
def revoke_by_user(self, user_id): return self.revoke(revoke_model.RevokeEvent(user_id=user_id))
def deserialize(self, data): revoke_event_data = msgpackutils.loads(data, registry=self._registry) revoke_event = revoke_model.RevokeEvent(**revoke_event_data) return revoke_event