"Fossies" - the Fresh Open Source Software Archive

Member "keystone-18.0.0/keystone/tests/unit/common/test_notifications.py" (14 Oct 2020, 67317 Bytes) of package /linux/misc/openstack/keystone-18.0.0.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Python source code syntax highlighting (style: standard) with prefixed line numbers. Alternatively you can here view or download the uninterpreted source code file. See also the latest Fossies "Diffs" side-by-side code changes report for "test_notifications.py": 17.0.0_vs_18.0.0.

    1 # Copyright 2013 IBM Corp.
    2 #
    3 #   Licensed under the Apache License, Version 2.0 (the "License"); you may
    4 #   not use this file except in compliance with the License. You may obtain
    5 #   a copy of the License at
    6 #
    7 #     http://www.apache.org/licenses/LICENSE-2.0
    8 #
    9 #   Unless required by applicable law or agreed to in writing, software
   10 #   distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
   11 #   WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
   12 #   License for the specific language governing permissions and limitations
   13 #   under the License.
   14 
   15 import datetime
   16 from unittest import mock
   17 import uuid
   18 
   19 import fixtures
   20 import freezegun
   21 import http.client
   22 from oslo_config import fixture as config_fixture
   23 from oslo_log import log
   24 import oslo_messaging
   25 from pycadf import cadftaxonomy
   26 from pycadf import cadftype
   27 from pycadf import eventfactory
   28 from pycadf import resource as cadfresource
   29 
   30 from keystone.common import provider_api
   31 import keystone.conf
   32 from keystone import exception
   33 from keystone import notifications
   34 from keystone.tests import unit
   35 from keystone.tests.unit import test_v3
   36 
   37 
   38 CONF = keystone.conf.CONF
   39 PROVIDERS = provider_api.ProviderAPIs
   40 
   41 EXP_RESOURCE_TYPE = uuid.uuid4().hex
   42 CREATED_OPERATION = notifications.ACTIONS.created
   43 UPDATED_OPERATION = notifications.ACTIONS.updated
   44 DELETED_OPERATION = notifications.ACTIONS.deleted
   45 DISABLED_OPERATION = notifications.ACTIONS.disabled
   46 
   47 
   48 class ArbitraryException(Exception):
   49     pass
   50 
   51 
   52 def register_callback(operation, resource_type=EXP_RESOURCE_TYPE):
   53     """Helper for creating and registering a mock callback."""
   54     callback = mock.Mock(__name__='callback',
   55                          im_class=mock.Mock(__name__='class'))
   56     notifications.register_event_callback(operation, resource_type, callback)
   57     return callback
   58 
   59 
   60 class AuditNotificationsTestCase(unit.BaseTestCase):
   61     def setUp(self):
   62         super(AuditNotificationsTestCase, self).setUp()
   63         self.config_fixture = self.useFixture(config_fixture.Config(CONF))
   64         self.addCleanup(notifications.clear_subscribers)
   65 
   66     def _test_notification_operation_with_basic_format(self,
   67                                                        notify_function,
   68                                                        operation):
   69         self.config_fixture.config(notification_format='basic')
   70         exp_resource_id = uuid.uuid4().hex
   71         callback = register_callback(operation)
   72         notify_function(EXP_RESOURCE_TYPE, exp_resource_id)
   73         callback.assert_called_once_with('identity', EXP_RESOURCE_TYPE,
   74                                          operation,
   75                                          {'resource_info': exp_resource_id})
   76 
   77     def _test_notification_operation_with_cadf_format(self,
   78                                                       notify_function,
   79                                                       operation):
   80         self.config_fixture.config(notification_format='cadf')
   81         exp_resource_id = uuid.uuid4().hex
   82         with mock.patch(
   83                 'keystone.notifications._create_cadf_payload') as cadf_notify:
   84             notify_function(EXP_RESOURCE_TYPE, exp_resource_id)
   85             initiator = None
   86             reason = None
   87             cadf_notify.assert_called_once_with(
   88                 operation, EXP_RESOURCE_TYPE, exp_resource_id,
   89                 notifications.taxonomy.OUTCOME_SUCCESS, initiator, reason)
   90             notify_function(EXP_RESOURCE_TYPE, exp_resource_id, public=False)
   91             cadf_notify.assert_called_once_with(
   92                 operation, EXP_RESOURCE_TYPE, exp_resource_id,
   93                 notifications.taxonomy.OUTCOME_SUCCESS, initiator, reason)
   94 
   95     def test_resource_created_notification(self):
   96         self._test_notification_operation_with_basic_format(
   97             notifications.Audit.created, CREATED_OPERATION)
   98         self._test_notification_operation_with_cadf_format(
   99             notifications.Audit.created, CREATED_OPERATION)
  100 
  101     def test_resource_updated_notification(self):
  102         self._test_notification_operation_with_basic_format(
  103             notifications.Audit.updated, UPDATED_OPERATION)
  104         self._test_notification_operation_with_cadf_format(
  105             notifications.Audit.updated, UPDATED_OPERATION)
  106 
  107     def test_resource_deleted_notification(self):
  108         self._test_notification_operation_with_basic_format(
  109             notifications.Audit.deleted, DELETED_OPERATION)
  110         self._test_notification_operation_with_cadf_format(
  111             notifications.Audit.deleted, DELETED_OPERATION)
  112 
  113     def test_resource_disabled_notification(self):
  114         self._test_notification_operation_with_basic_format(
  115             notifications.Audit.disabled, DISABLED_OPERATION)
  116         self._test_notification_operation_with_cadf_format(
  117             notifications.Audit.disabled, DISABLED_OPERATION)
  118 
  119 
  120 class NotificationsTestCase(unit.BaseTestCase):
  121 
  122     def setUp(self):
  123         super(NotificationsTestCase, self).setUp()
  124         self.config_fixture = self.useFixture(config_fixture.Config(CONF))
  125         self.config_fixture.config(
  126             group='oslo_messaging_notifications', transport_url='rabbit://'
  127         )
  128 
  129     def test_send_notification(self):
  130         """Test _send_notification.
  131 
  132         Test the private method _send_notification to ensure event_type,
  133         payload, and context are built and passed properly.
  134 
  135         """
  136         resource = uuid.uuid4().hex
  137         resource_type = EXP_RESOURCE_TYPE
  138         operation = CREATED_OPERATION
  139 
  140         conf = self.useFixture(config_fixture.Config(CONF))
  141         conf.config(notification_format='basic')
  142 
  143         # NOTE(ldbragst): Even though notifications._send_notification doesn't
  144         # contain logic that creates cases, this is supposed to test that
  145         # context is always empty and that we ensure the resource ID of the
  146         # resource in the notification is contained in the payload. It was
  147         # agreed that context should be empty in Keystone's case, which is
  148         # also noted in the /keystone/notifications.py module. This test
  149         # ensures and maintains these conditions.
  150         expected_args = [
  151             {},  # empty context
  152             'identity.%s.created' % resource_type,  # event_type
  153             {'resource_info': resource}  # payload
  154         ]
  155 
  156         with mock.patch.object(notifications._get_notifier(),
  157                                'info') as mocked:
  158             notifications._send_notification(operation, resource_type,
  159                                              resource)
  160             mocked.assert_called_once_with(*expected_args)
  161 
  162     def test_send_notification_with_opt_out(self):
  163         """Test the private method _send_notification with opt-out.
  164 
  165         Test that _send_notification does not notify when a valid
  166         notification_opt_out configuration is provided.
  167         """
  168         resource = uuid.uuid4().hex
  169         resource_type = EXP_RESOURCE_TYPE
  170         operation = CREATED_OPERATION
  171         event_type = 'identity.%s.created' % resource_type
  172 
  173         # NOTE(diazjf): Here we add notification_opt_out to the
  174         # configuration so that we should return before _get_notifer is
  175         # called. This is because we are opting out notifications for the
  176         # passed resource_type and operation.
  177         conf = self.useFixture(config_fixture.Config(CONF))
  178         conf.config(notification_opt_out=[event_type])
  179 
  180         with mock.patch.object(notifications._get_notifier(),
  181                                'info') as mocked:
  182 
  183             notifications._send_notification(operation, resource_type,
  184                                              resource)
  185             mocked.assert_not_called()
  186 
  187     def test_send_audit_notification_with_opt_out(self):
  188         """Test the private method _send_audit_notification with opt-out.
  189 
  190         Test that _send_audit_notification does not notify when a valid
  191         notification_opt_out configuration is provided.
  192         """
  193         resource_type = EXP_RESOURCE_TYPE
  194 
  195         action = CREATED_OPERATION + '.' + resource_type
  196         initiator = mock
  197         target = mock
  198         outcome = 'success'
  199         event_type = 'identity.%s.created' % resource_type
  200 
  201         conf = self.useFixture(config_fixture.Config(CONF))
  202         conf.config(notification_opt_out=[event_type])
  203 
  204         with mock.patch.object(notifications._get_notifier(),
  205                                'info') as mocked:
  206 
  207             notifications._send_audit_notification(action,
  208                                                    initiator,
  209                                                    outcome,
  210                                                    target,
  211                                                    event_type)
  212             mocked.assert_not_called()
  213 
  214     def test_opt_out_authenticate_event(self):
  215         """Test that authenticate events are successfully opted out."""
  216         resource_type = EXP_RESOURCE_TYPE
  217 
  218         action = CREATED_OPERATION + '.' + resource_type
  219         initiator = mock
  220         target = mock
  221         outcome = 'success'
  222         event_type = 'identity.authenticate'
  223         meter_name = '%s.%s' % (event_type, outcome)
  224 
  225         conf = self.useFixture(config_fixture.Config(CONF))
  226         conf.config(notification_opt_out=[meter_name])
  227 
  228         with mock.patch.object(notifications._get_notifier(),
  229                                'info') as mocked:
  230 
  231             notifications._send_audit_notification(action,
  232                                                    initiator,
  233                                                    outcome,
  234                                                    target,
  235                                                    event_type)
  236             mocked.assert_not_called()
  237 
  238 
  239 class BaseNotificationTest(test_v3.RestfulTestCase):
  240 
  241     def setUp(self):
  242         super(BaseNotificationTest, self).setUp()
  243 
  244         self._notifications = []
  245         self._audits = []
  246 
  247         def fake_notify(operation, resource_type, resource_id, initiator=None,
  248                         actor_dict=None, public=True):
  249             note = {
  250                 'resource_id': resource_id,
  251                 'operation': operation,
  252                 'resource_type': resource_type,
  253                 'initiator': initiator,
  254                 'send_notification_called': True,
  255                 'public': public}
  256             if actor_dict:
  257                 note['actor_id'] = actor_dict.get('id')
  258                 note['actor_type'] = actor_dict.get('type')
  259                 note['actor_operation'] = actor_dict.get('actor_operation')
  260             self._notifications.append(note)
  261 
  262         self.useFixture(fixtures.MockPatchObject(
  263             notifications, '_send_notification', fake_notify))
  264 
  265         def fake_audit(action, initiator, outcome, target,
  266                        event_type, reason=None, **kwargs):
  267             service_security = cadftaxonomy.SERVICE_SECURITY
  268 
  269             event = eventfactory.EventFactory().new_event(
  270                 eventType=cadftype.EVENTTYPE_ACTIVITY,
  271                 outcome=outcome,
  272                 action=action,
  273                 initiator=initiator,
  274                 target=target,
  275                 reason=reason,
  276                 observer=cadfresource.Resource(typeURI=service_security))
  277 
  278             for key, value in kwargs.items():
  279                 setattr(event, key, value)
  280 
  281             payload = event.as_dict()
  282 
  283             audit = {
  284                 'payload': payload,
  285                 'event_type': event_type,
  286                 'send_notification_called': True}
  287             self._audits.append(audit)
  288 
  289         self.useFixture(fixtures.MockPatchObject(
  290             notifications, '_send_audit_notification', fake_audit))
  291 
  292     def _assert_last_note(self, resource_id, operation, resource_type,
  293                           actor_id=None, actor_type=None,
  294                           actor_operation=None):
  295         # NOTE(stevemar): If 'basic' format is not used, then simply
  296         # return since this assertion is not valid.
  297         if CONF.notification_format != 'basic':
  298             return
  299         self.assertGreater(len(self._notifications), 0)
  300         note = self._notifications[-1]
  301         self.assertEqual(operation, note['operation'])
  302         self.assertEqual(resource_id, note['resource_id'])
  303         self.assertEqual(resource_type, note['resource_type'])
  304         self.assertTrue(note['send_notification_called'])
  305         if actor_id:
  306             self.assertEqual(actor_id, note['actor_id'])
  307             self.assertEqual(actor_type, note['actor_type'])
  308             self.assertEqual(actor_operation, note['actor_operation'])
  309 
  310     def _assert_last_audit(self, resource_id, operation, resource_type,
  311                            target_uri, reason=None):
  312         # NOTE(stevemar): If 'cadf' format is not used, then simply
  313         # return since this assertion is not valid.
  314         if CONF.notification_format != 'cadf':
  315             return
  316         self.assertGreater(len(self._audits), 0)
  317         audit = self._audits[-1]
  318         payload = audit['payload']
  319         if 'resource_info' in payload:
  320             self.assertEqual(resource_id, payload['resource_info'])
  321         action = '.'.join(filter(None, [operation, resource_type]))
  322         self.assertEqual(action, payload['action'])
  323         self.assertEqual(target_uri, payload['target']['typeURI'])
  324         if resource_id:
  325             self.assertEqual(resource_id, payload['target']['id'])
  326         event_type = '.'.join(filter(None, ['identity',
  327                                             resource_type,
  328                                             operation]))
  329         self.assertEqual(event_type, audit['event_type'])
  330         if reason:
  331             self.assertEqual(reason['reasonCode'],
  332                              payload['reason']['reasonCode'])
  333             self.assertEqual(reason['reasonType'],
  334                              payload['reason']['reasonType'])
  335         self.assertTrue(audit['send_notification_called'])
  336 
  337     def _assert_initiator_data_is_set(self, operation, resource_type, typeURI):
  338         self.assertGreater(len(self._audits), 0)
  339         audit = self._audits[-1]
  340         payload = audit['payload']
  341         self.assertEqual(self.user_id, payload['initiator']['id'])
  342         self.assertEqual(self.project_id, payload['initiator']['project_id'])
  343         self.assertEqual(typeURI, payload['target']['typeURI'])
  344         self.assertIn('request_id', payload['initiator'])
  345         action = '%s.%s' % (operation, resource_type)
  346         self.assertEqual(action, payload['action'])
  347 
  348     def _assert_notify_not_sent(self, resource_id, operation, resource_type,
  349                                 public=True):
  350         unexpected = {
  351             'resource_id': resource_id,
  352             'operation': operation,
  353             'resource_type': resource_type,
  354             'send_notification_called': True,
  355             'public': public}
  356         for note in self._notifications:
  357             self.assertNotEqual(unexpected, note)
  358 
  359     def _assert_notify_sent(self, resource_id, operation, resource_type,
  360                             public=True):
  361         expected = {
  362             'resource_id': resource_id,
  363             'operation': operation,
  364             'resource_type': resource_type,
  365             'send_notification_called': True,
  366             'public': public}
  367         for note in self._notifications:
  368             # compare only expected fields
  369             if all(note.get(k) == v for k, v in expected.items()):
  370                 break
  371         else:
  372             self.fail("Notification not sent.")
  373 
  374 
  375 class NotificationsForEntities(BaseNotificationTest):
  376 
  377     def test_create_group(self):
  378         group_ref = unit.new_group_ref(domain_id=self.domain_id)
  379         group_ref = PROVIDERS.identity_api.create_group(group_ref)
  380         self._assert_last_note(group_ref['id'], CREATED_OPERATION, 'group')
  381         self._assert_last_audit(group_ref['id'], CREATED_OPERATION, 'group',
  382                                 cadftaxonomy.SECURITY_GROUP)
  383 
  384     def test_create_project(self):
  385         project_ref = unit.new_project_ref(domain_id=self.domain_id)
  386         PROVIDERS.resource_api.create_project(project_ref['id'], project_ref)
  387         self._assert_last_note(
  388             project_ref['id'], CREATED_OPERATION, 'project')
  389         self._assert_last_audit(project_ref['id'], CREATED_OPERATION,
  390                                 'project', cadftaxonomy.SECURITY_PROJECT)
  391 
  392     def test_create_role(self):
  393         role_ref = unit.new_role_ref()
  394         PROVIDERS.role_api.create_role(role_ref['id'], role_ref)
  395         self._assert_last_note(role_ref['id'], CREATED_OPERATION, 'role')
  396         self._assert_last_audit(role_ref['id'], CREATED_OPERATION, 'role',
  397                                 cadftaxonomy.SECURITY_ROLE)
  398 
  399     def test_create_user(self):
  400         user_ref = unit.new_user_ref(domain_id=self.domain_id)
  401         user_ref = PROVIDERS.identity_api.create_user(user_ref)
  402         self._assert_last_note(user_ref['id'], CREATED_OPERATION, 'user')
  403         self._assert_last_audit(user_ref['id'], CREATED_OPERATION, 'user',
  404                                 cadftaxonomy.SECURITY_ACCOUNT_USER)
  405 
  406     def test_create_trust(self):
  407         trustor = unit.new_user_ref(domain_id=self.domain_id)
  408         trustor = PROVIDERS.identity_api.create_user(trustor)
  409         trustee = unit.new_user_ref(domain_id=self.domain_id)
  410         trustee = PROVIDERS.identity_api.create_user(trustee)
  411         role_ref = unit.new_role_ref()
  412         PROVIDERS.role_api.create_role(role_ref['id'], role_ref)
  413         trust_ref = unit.new_trust_ref(trustor['id'],
  414                                        trustee['id'])
  415         PROVIDERS.trust_api.create_trust(
  416             trust_ref['id'], trust_ref, [role_ref]
  417         )
  418         self._assert_last_note(
  419             trust_ref['id'], CREATED_OPERATION, 'OS-TRUST:trust')
  420         self._assert_last_audit(trust_ref['id'], CREATED_OPERATION,
  421                                 'OS-TRUST:trust', cadftaxonomy.SECURITY_TRUST)
  422 
  423     def test_delete_group(self):
  424         group_ref = unit.new_group_ref(domain_id=self.domain_id)
  425         group_ref = PROVIDERS.identity_api.create_group(group_ref)
  426         PROVIDERS.identity_api.delete_group(group_ref['id'])
  427         self._assert_last_note(group_ref['id'], DELETED_OPERATION, 'group')
  428         self._assert_last_audit(group_ref['id'], DELETED_OPERATION, 'group',
  429                                 cadftaxonomy.SECURITY_GROUP)
  430 
  431     def test_delete_project(self):
  432         project_ref = unit.new_project_ref(domain_id=self.domain_id)
  433         PROVIDERS.resource_api.create_project(project_ref['id'], project_ref)
  434         PROVIDERS.resource_api.delete_project(project_ref['id'])
  435         self._assert_last_note(
  436             project_ref['id'], DELETED_OPERATION, 'project')
  437         self._assert_last_audit(project_ref['id'], DELETED_OPERATION,
  438                                 'project', cadftaxonomy.SECURITY_PROJECT)
  439 
  440     def test_delete_role(self):
  441         role_ref = unit.new_role_ref()
  442         PROVIDERS.role_api.create_role(role_ref['id'], role_ref)
  443         PROVIDERS.role_api.delete_role(role_ref['id'])
  444         self._assert_last_note(role_ref['id'], DELETED_OPERATION, 'role')
  445         self._assert_last_audit(role_ref['id'], DELETED_OPERATION, 'role',
  446                                 cadftaxonomy.SECURITY_ROLE)
  447 
  448     def test_delete_user(self):
  449         user_ref = unit.new_user_ref(domain_id=self.domain_id)
  450         user_ref = PROVIDERS.identity_api.create_user(user_ref)
  451         PROVIDERS.identity_api.delete_user(user_ref['id'])
  452         self._assert_last_note(user_ref['id'], DELETED_OPERATION, 'user')
  453         self._assert_last_audit(user_ref['id'], DELETED_OPERATION, 'user',
  454                                 cadftaxonomy.SECURITY_ACCOUNT_USER)
  455 
  456     def test_create_domain(self):
  457         domain_ref = unit.new_domain_ref()
  458         PROVIDERS.resource_api.create_domain(domain_ref['id'], domain_ref)
  459         self._assert_last_note(domain_ref['id'], CREATED_OPERATION, 'domain')
  460         self._assert_last_audit(domain_ref['id'], CREATED_OPERATION, 'domain',
  461                                 cadftaxonomy.SECURITY_DOMAIN)
  462 
  463     def test_update_domain(self):
  464         domain_ref = unit.new_domain_ref()
  465         PROVIDERS.resource_api.create_domain(domain_ref['id'], domain_ref)
  466         domain_ref['description'] = uuid.uuid4().hex
  467         PROVIDERS.resource_api.update_domain(domain_ref['id'], domain_ref)
  468         self._assert_last_note(domain_ref['id'], UPDATED_OPERATION, 'domain')
  469         self._assert_last_audit(domain_ref['id'], UPDATED_OPERATION, 'domain',
  470                                 cadftaxonomy.SECURITY_DOMAIN)
  471 
  472     def test_delete_domain(self):
  473         domain_ref = unit.new_domain_ref()
  474         PROVIDERS.resource_api.create_domain(domain_ref['id'], domain_ref)
  475         domain_ref['enabled'] = False
  476         PROVIDERS.resource_api.update_domain(domain_ref['id'], domain_ref)
  477         PROVIDERS.resource_api.delete_domain(domain_ref['id'])
  478         self._assert_last_note(domain_ref['id'], DELETED_OPERATION, 'domain')
  479         self._assert_last_audit(domain_ref['id'], DELETED_OPERATION, 'domain',
  480                                 cadftaxonomy.SECURITY_DOMAIN)
  481 
  482     def test_delete_trust(self):
  483         trustor = unit.new_user_ref(domain_id=self.domain_id)
  484         trustor = PROVIDERS.identity_api.create_user(trustor)
  485         trustee = unit.new_user_ref(domain_id=self.domain_id)
  486         trustee = PROVIDERS.identity_api.create_user(trustee)
  487         role_ref = unit.new_role_ref()
  488         trust_ref = unit.new_trust_ref(trustor['id'], trustee['id'])
  489         PROVIDERS.trust_api.create_trust(
  490             trust_ref['id'], trust_ref, [role_ref]
  491         )
  492         PROVIDERS.trust_api.delete_trust(trust_ref['id'])
  493         self._assert_last_note(
  494             trust_ref['id'], DELETED_OPERATION, 'OS-TRUST:trust')
  495         self._assert_last_audit(trust_ref['id'], DELETED_OPERATION,
  496                                 'OS-TRUST:trust', cadftaxonomy.SECURITY_TRUST)
  497 
  498     def test_create_endpoint(self):
  499         endpoint_ref = unit.new_endpoint_ref(service_id=self.service_id,
  500                                              interface='public',
  501                                              region_id=self.region_id)
  502         PROVIDERS.catalog_api.create_endpoint(endpoint_ref['id'], endpoint_ref)
  503         self._assert_notify_sent(endpoint_ref['id'], CREATED_OPERATION,
  504                                  'endpoint')
  505         self._assert_last_audit(endpoint_ref['id'], CREATED_OPERATION,
  506                                 'endpoint', cadftaxonomy.SECURITY_ENDPOINT)
  507 
  508     def test_update_endpoint(self):
  509         endpoint_ref = unit.new_endpoint_ref(service_id=self.service_id,
  510                                              interface='public',
  511                                              region_id=self.region_id)
  512         PROVIDERS.catalog_api.create_endpoint(endpoint_ref['id'], endpoint_ref)
  513         PROVIDERS.catalog_api.update_endpoint(endpoint_ref['id'], endpoint_ref)
  514         self._assert_notify_sent(endpoint_ref['id'], UPDATED_OPERATION,
  515                                  'endpoint')
  516         self._assert_last_audit(endpoint_ref['id'], UPDATED_OPERATION,
  517                                 'endpoint', cadftaxonomy.SECURITY_ENDPOINT)
  518 
  519     def test_delete_endpoint(self):
  520         endpoint_ref = unit.new_endpoint_ref(service_id=self.service_id,
  521                                              interface='public',
  522                                              region_id=self.region_id)
  523         PROVIDERS.catalog_api.create_endpoint(endpoint_ref['id'], endpoint_ref)
  524         PROVIDERS.catalog_api.delete_endpoint(endpoint_ref['id'])
  525         self._assert_notify_sent(endpoint_ref['id'], DELETED_OPERATION,
  526                                  'endpoint')
  527         self._assert_last_audit(endpoint_ref['id'], DELETED_OPERATION,
  528                                 'endpoint', cadftaxonomy.SECURITY_ENDPOINT)
  529 
  530     def test_create_service(self):
  531         service_ref = unit.new_service_ref()
  532         PROVIDERS.catalog_api.create_service(service_ref['id'], service_ref)
  533         self._assert_notify_sent(service_ref['id'], CREATED_OPERATION,
  534                                  'service')
  535         self._assert_last_audit(service_ref['id'], CREATED_OPERATION,
  536                                 'service', cadftaxonomy.SECURITY_SERVICE)
  537 
  538     def test_update_service(self):
  539         service_ref = unit.new_service_ref()
  540         PROVIDERS.catalog_api.create_service(service_ref['id'], service_ref)
  541         PROVIDERS.catalog_api.update_service(service_ref['id'], service_ref)
  542         self._assert_notify_sent(service_ref['id'], UPDATED_OPERATION,
  543                                  'service')
  544         self._assert_last_audit(service_ref['id'], UPDATED_OPERATION,
  545                                 'service', cadftaxonomy.SECURITY_SERVICE)
  546 
  547     def test_delete_service(self):
  548         service_ref = unit.new_service_ref()
  549         PROVIDERS.catalog_api.create_service(service_ref['id'], service_ref)
  550         PROVIDERS.catalog_api.delete_service(service_ref['id'])
  551         self._assert_notify_sent(service_ref['id'], DELETED_OPERATION,
  552                                  'service')
  553         self._assert_last_audit(service_ref['id'], DELETED_OPERATION,
  554                                 'service', cadftaxonomy.SECURITY_SERVICE)
  555 
  556     def test_create_region(self):
  557         region_ref = unit.new_region_ref()
  558         PROVIDERS.catalog_api.create_region(region_ref)
  559         self._assert_notify_sent(region_ref['id'], CREATED_OPERATION,
  560                                  'region')
  561         self._assert_last_audit(region_ref['id'], CREATED_OPERATION,
  562                                 'region', cadftaxonomy.SECURITY_REGION)
  563 
  564     def test_update_region(self):
  565         region_ref = unit.new_region_ref()
  566         PROVIDERS.catalog_api.create_region(region_ref)
  567         PROVIDERS.catalog_api.update_region(region_ref['id'], region_ref)
  568         self._assert_notify_sent(region_ref['id'], UPDATED_OPERATION,
  569                                  'region')
  570         self._assert_last_audit(region_ref['id'], UPDATED_OPERATION,
  571                                 'region', cadftaxonomy.SECURITY_REGION)
  572 
  573     def test_delete_region(self):
  574         region_ref = unit.new_region_ref()
  575         PROVIDERS.catalog_api.create_region(region_ref)
  576         PROVIDERS.catalog_api.delete_region(region_ref['id'])
  577         self._assert_notify_sent(region_ref['id'], DELETED_OPERATION,
  578                                  'region')
  579         self._assert_last_audit(region_ref['id'], DELETED_OPERATION,
  580                                 'region', cadftaxonomy.SECURITY_REGION)
  581 
  582     def test_create_policy(self):
  583         policy_ref = unit.new_policy_ref()
  584         PROVIDERS.policy_api.create_policy(policy_ref['id'], policy_ref)
  585         self._assert_notify_sent(policy_ref['id'], CREATED_OPERATION,
  586                                  'policy')
  587         self._assert_last_audit(policy_ref['id'], CREATED_OPERATION,
  588                                 'policy', cadftaxonomy.SECURITY_POLICY)
  589 
  590     def test_update_policy(self):
  591         policy_ref = unit.new_policy_ref()
  592         PROVIDERS.policy_api.create_policy(policy_ref['id'], policy_ref)
  593         PROVIDERS.policy_api.update_policy(policy_ref['id'], policy_ref)
  594         self._assert_notify_sent(policy_ref['id'], UPDATED_OPERATION,
  595                                  'policy')
  596         self._assert_last_audit(policy_ref['id'], UPDATED_OPERATION,
  597                                 'policy', cadftaxonomy.SECURITY_POLICY)
  598 
  599     def test_delete_policy(self):
  600         policy_ref = unit.new_policy_ref()
  601         PROVIDERS.policy_api.create_policy(policy_ref['id'], policy_ref)
  602         PROVIDERS.policy_api.delete_policy(policy_ref['id'])
  603         self._assert_notify_sent(policy_ref['id'], DELETED_OPERATION,
  604                                  'policy')
  605         self._assert_last_audit(policy_ref['id'], DELETED_OPERATION,
  606                                 'policy', cadftaxonomy.SECURITY_POLICY)
  607 
  608     def test_disable_domain(self):
  609         domain_ref = unit.new_domain_ref()
  610         PROVIDERS.resource_api.create_domain(domain_ref['id'], domain_ref)
  611         domain_ref['enabled'] = False
  612         PROVIDERS.resource_api.update_domain(domain_ref['id'], domain_ref)
  613         self._assert_notify_sent(domain_ref['id'], 'disabled', 'domain',
  614                                  public=False)
  615 
  616     def test_disable_of_disabled_domain_does_not_notify(self):
  617         domain_ref = unit.new_domain_ref(enabled=False)
  618         PROVIDERS.resource_api.create_domain(domain_ref['id'], domain_ref)
  619         # The domain_ref above is not changed during the create process. We
  620         # can use the same ref to perform the update.
  621         PROVIDERS.resource_api.update_domain(domain_ref['id'], domain_ref)
  622         self._assert_notify_not_sent(domain_ref['id'], 'disabled', 'domain',
  623                                      public=False)
  624 
  625     def test_update_group(self):
  626         group_ref = unit.new_group_ref(domain_id=self.domain_id)
  627         group_ref = PROVIDERS.identity_api.create_group(group_ref)
  628         PROVIDERS.identity_api.update_group(group_ref['id'], group_ref)
  629         self._assert_last_note(group_ref['id'], UPDATED_OPERATION, 'group')
  630         self._assert_last_audit(group_ref['id'], UPDATED_OPERATION, 'group',
  631                                 cadftaxonomy.SECURITY_GROUP)
  632 
  633     def test_update_project(self):
  634         project_ref = unit.new_project_ref(domain_id=self.domain_id)
  635         PROVIDERS.resource_api.create_project(project_ref['id'], project_ref)
  636         PROVIDERS.resource_api.update_project(project_ref['id'], project_ref)
  637         self._assert_notify_sent(
  638             project_ref['id'], UPDATED_OPERATION, 'project', public=True)
  639         self._assert_last_audit(project_ref['id'], UPDATED_OPERATION,
  640                                 'project', cadftaxonomy.SECURITY_PROJECT)
  641 
  642     def test_disable_project(self):
  643         project_ref = unit.new_project_ref(domain_id=self.domain_id)
  644         PROVIDERS.resource_api.create_project(project_ref['id'], project_ref)
  645         project_ref['enabled'] = False
  646         PROVIDERS.resource_api.update_project(project_ref['id'], project_ref)
  647         self._assert_notify_sent(project_ref['id'], 'disabled', 'project',
  648                                  public=False)
  649 
  650     def test_disable_of_disabled_project_does_not_notify(self):
  651         project_ref = unit.new_project_ref(domain_id=self.domain_id,
  652                                            enabled=False)
  653         PROVIDERS.resource_api.create_project(project_ref['id'], project_ref)
  654         # The project_ref above is not changed during the create process. We
  655         # can use the same ref to perform the update.
  656         PROVIDERS.resource_api.update_project(project_ref['id'], project_ref)
  657         self._assert_notify_not_sent(project_ref['id'], 'disabled', 'project',
  658                                      public=False)
  659 
  660     def test_update_project_does_not_send_disable(self):
  661         project_ref = unit.new_project_ref(domain_id=self.domain_id)
  662         PROVIDERS.resource_api.create_project(project_ref['id'], project_ref)
  663         project_ref['enabled'] = True
  664         PROVIDERS.resource_api.update_project(project_ref['id'], project_ref)
  665         self._assert_last_note(
  666             project_ref['id'], UPDATED_OPERATION, 'project')
  667         self._assert_notify_not_sent(project_ref['id'], 'disabled', 'project')
  668 
  669     def test_update_role(self):
  670         role_ref = unit.new_role_ref()
  671         PROVIDERS.role_api.create_role(role_ref['id'], role_ref)
  672         PROVIDERS.role_api.update_role(role_ref['id'], role_ref)
  673         self._assert_last_note(role_ref['id'], UPDATED_OPERATION, 'role')
  674         self._assert_last_audit(role_ref['id'], UPDATED_OPERATION, 'role',
  675                                 cadftaxonomy.SECURITY_ROLE)
  676 
  677     def test_update_user(self):
  678         user_ref = unit.new_user_ref(domain_id=self.domain_id)
  679         user_ref = PROVIDERS.identity_api.create_user(user_ref)
  680         PROVIDERS.identity_api.update_user(user_ref['id'], user_ref)
  681         self._assert_last_note(user_ref['id'], UPDATED_OPERATION, 'user')
  682         self._assert_last_audit(user_ref['id'], UPDATED_OPERATION, 'user',
  683                                 cadftaxonomy.SECURITY_ACCOUNT_USER)
  684 
  685     def test_config_option_no_events(self):
  686         self.config_fixture.config(notification_format='basic')
  687         role_ref = unit.new_role_ref()
  688         PROVIDERS.role_api.create_role(role_ref['id'], role_ref)
  689         # The regular notifications will still be emitted, since they are
  690         # used for callback handling.
  691         self._assert_last_note(role_ref['id'], CREATED_OPERATION, 'role')
  692         # No audit event should have occurred
  693         self.assertEqual(0, len(self._audits))
  694 
  695     def test_add_user_to_group(self):
  696         user_ref = unit.new_user_ref(domain_id=self.domain_id)
  697         user_ref = PROVIDERS.identity_api.create_user(user_ref)
  698         group_ref = unit.new_group_ref(domain_id=self.domain_id)
  699         group_ref = PROVIDERS.identity_api.create_group(group_ref)
  700         PROVIDERS.identity_api.add_user_to_group(
  701             user_ref['id'], group_ref['id']
  702         )
  703         self._assert_last_note(group_ref['id'], UPDATED_OPERATION, 'group',
  704                                actor_id=user_ref['id'], actor_type='user',
  705                                actor_operation='added')
  706 
  707     def test_remove_user_from_group(self):
  708         user_ref = unit.new_user_ref(domain_id=self.domain_id)
  709         user_ref = PROVIDERS.identity_api.create_user(user_ref)
  710         group_ref = unit.new_group_ref(domain_id=self.domain_id)
  711         group_ref = PROVIDERS.identity_api.create_group(group_ref)
  712         PROVIDERS.identity_api.add_user_to_group(
  713             user_ref['id'], group_ref['id']
  714         )
  715         PROVIDERS.identity_api.remove_user_from_group(
  716             user_ref['id'], group_ref['id']
  717         )
  718         self._assert_last_note(group_ref['id'], UPDATED_OPERATION, 'group',
  719                                actor_id=user_ref['id'], actor_type='user',
  720                                actor_operation='removed')
  721 
  722     def test_initiator_request_id(self):
  723         ref = unit.new_domain_ref()
  724         self.post('/domains', body={'domain': ref})
  725         note = self._notifications[-1]
  726         initiator = note['initiator']
  727         self.assertIsNotNone(initiator.request_id)
  728 
  729     def test_initiator_global_request_id(self):
  730         global_request_id = 'req-%s' % uuid.uuid4()
  731         ref = unit.new_domain_ref()
  732         self.post('/domains', body={'domain': ref},
  733                   headers={'X-OpenStack-Request-Id': global_request_id})
  734         note = self._notifications[-1]
  735         initiator = note['initiator']
  736         self.assertEqual(
  737             initiator.global_request_id, global_request_id)
  738 
  739     def test_initiator_global_request_id_not_set(self):
  740         ref = unit.new_domain_ref()
  741         self.post('/domains', body={'domain': ref})
  742         note = self._notifications[-1]
  743         initiator = note['initiator']
  744         self.assertFalse(hasattr(initiator, 'global_request_id'))
  745 
  746 
  747 class CADFNotificationsForPCIDSSEvents(BaseNotificationTest):
  748 
  749     def setUp(self):
  750         super(CADFNotificationsForPCIDSSEvents, self).setUp()
  751         conf = self.useFixture(config_fixture.Config(CONF))
  752         conf.config(notification_format='cadf')
  753         conf.config(group='security_compliance',
  754                     password_expires_days=2)
  755         conf.config(group='security_compliance',
  756                     lockout_failure_attempts=3)
  757         conf.config(group='security_compliance',
  758                     unique_last_password_count=2)
  759         conf.config(group='security_compliance',
  760                     minimum_password_age=2)
  761         conf.config(group='security_compliance',
  762                     password_regex=r'^(?=.*\d)(?=.*[a-zA-Z]).{7,}$')
  763         conf.config(group='security_compliance',
  764                     password_regex_description='1 letter, 1 digit, 7 chars')
  765 
  766     def test_password_expired_sends_notification(self):
  767         password = uuid.uuid4().hex
  768         password_creation_time = (
  769             datetime.datetime.utcnow() -
  770             datetime.timedelta(
  771                 days=CONF.security_compliance.password_expires_days + 1)
  772         )
  773         freezer = freezegun.freeze_time(password_creation_time)
  774 
  775         # NOTE(gagehugo): This part below uses freezegun to spoof
  776         # the time as being three days in the past from right now. We will
  777         # create a user and have that user successfully authenticate,
  778         # then stop the time machine and return to the present time,
  779         # where the user's password is now expired.
  780         freezer.start()
  781         user_ref = unit.new_user_ref(domain_id=self.domain_id,
  782                                      password=password)
  783         user_ref = PROVIDERS.identity_api.create_user(user_ref)
  784         with self.make_request():
  785             PROVIDERS.identity_api.authenticate(user_ref['id'], password)
  786         freezer.stop()
  787 
  788         reason_type = (exception.PasswordExpired.message_format %
  789                        {'user_id': user_ref['id']})
  790         expected_reason = {'reasonCode': '401',
  791                            'reasonType': reason_type}
  792         with self.make_request():
  793             self.assertRaises(exception.PasswordExpired,
  794                               PROVIDERS.identity_api.authenticate,
  795                               user_id=user_ref['id'],
  796                               password=password)
  797         self._assert_last_audit(None, 'authenticate', None,
  798                                 cadftaxonomy.ACCOUNT_USER,
  799                                 reason=expected_reason)
  800 
  801     def test_locked_out_user_sends_notification(self):
  802         password = uuid.uuid4().hex
  803         new_password = uuid.uuid4().hex
  804         expected_responses = [AssertionError, AssertionError, AssertionError,
  805                               exception.AccountLocked]
  806         user_ref = unit.new_user_ref(domain_id=self.domain_id,
  807                                      password=password)
  808         user_ref = PROVIDERS.identity_api.create_user(user_ref)
  809         reason_type = (exception.AccountLocked.message_format %
  810                        {'user_id': user_ref['id']})
  811         expected_reason = {'reasonCode': '401',
  812                            'reasonType': reason_type}
  813         for ex in expected_responses:
  814             with self.make_request():
  815                 self.assertRaises(ex,
  816                                   PROVIDERS.identity_api.change_password,
  817                                   user_id=user_ref['id'],
  818                                   original_password=new_password,
  819                                   new_password=new_password)
  820 
  821         self._assert_last_audit(None, 'authenticate', None,
  822                                 cadftaxonomy.ACCOUNT_USER,
  823                                 reason=expected_reason)
  824 
  825     def test_repeated_password_sends_notification(self):
  826         conf = self.useFixture(config_fixture.Config(CONF))
  827         conf.config(group='security_compliance',
  828                     minimum_password_age=0)
  829         password = uuid.uuid4().hex
  830         new_password = uuid.uuid4().hex
  831         count = CONF.security_compliance.unique_last_password_count
  832         reason_type = (exception.PasswordHistoryValidationError.message_format
  833                        % {'unique_count': count})
  834         expected_reason = {'reasonCode': '400',
  835                            'reasonType': reason_type}
  836         user_ref = unit.new_user_ref(domain_id=self.domain_id,
  837                                      password=password)
  838         user_ref = PROVIDERS.identity_api.create_user(user_ref)
  839         with self.make_request():
  840             PROVIDERS.identity_api.change_password(
  841                 user_id=user_ref['id'],
  842                 original_password=password, new_password=new_password
  843             )
  844         with self.make_request():
  845             self.assertRaises(exception.PasswordValidationError,
  846                               PROVIDERS.identity_api.change_password,
  847                               user_id=user_ref['id'],
  848                               original_password=new_password,
  849                               new_password=password)
  850 
  851         self._assert_last_audit(user_ref['id'], UPDATED_OPERATION, 'user',
  852                                 cadftaxonomy.SECURITY_ACCOUNT_USER,
  853                                 reason=expected_reason)
  854 
  855     def test_invalid_password_sends_notification(self):
  856         password = uuid.uuid4().hex
  857         invalid_password = '1'
  858         regex = CONF.security_compliance.password_regex_description
  859         reason_type = (exception.PasswordRequirementsValidationError
  860                        .message_format %
  861                        {'detail': regex})
  862         expected_reason = {'reasonCode': '400',
  863                            'reasonType': reason_type}
  864         user_ref = unit.new_user_ref(domain_id=self.domain_id,
  865                                      password=password)
  866         user_ref = PROVIDERS.identity_api.create_user(user_ref)
  867         with self.make_request():
  868             self.assertRaises(exception.PasswordValidationError,
  869                               PROVIDERS.identity_api.change_password,
  870                               user_id=user_ref['id'],
  871                               original_password=password,
  872                               new_password=invalid_password)
  873 
  874         self._assert_last_audit(user_ref['id'], UPDATED_OPERATION, 'user',
  875                                 cadftaxonomy.SECURITY_ACCOUNT_USER,
  876                                 reason=expected_reason)
  877 
  878     def test_changing_password_too_early_sends_notification(self):
  879         password = uuid.uuid4().hex
  880         new_password = uuid.uuid4().hex
  881         next_password = uuid.uuid4().hex
  882 
  883         user_ref = unit.new_user_ref(domain_id=self.domain_id,
  884                                      password=password,
  885                                      password_created_at=(
  886                                          datetime.datetime.utcnow()))
  887         user_ref = PROVIDERS.identity_api.create_user(user_ref)
  888 
  889         min_days = CONF.security_compliance.minimum_password_age
  890         min_age = (user_ref['password_created_at'] +
  891                    datetime.timedelta(days=min_days))
  892         days_left = (min_age - datetime.datetime.utcnow()).days
  893         reason_type = (exception.PasswordAgeValidationError.message_format %
  894                        {'min_age_days': min_days, 'days_left': days_left})
  895         expected_reason = {'reasonCode': '400',
  896                            'reasonType': reason_type}
  897         with self.make_request():
  898             PROVIDERS.identity_api.change_password(
  899                 user_id=user_ref['id'],
  900                 original_password=password, new_password=new_password
  901             )
  902         with self.make_request():
  903             self.assertRaises(exception.PasswordValidationError,
  904                               PROVIDERS.identity_api.change_password,
  905                               user_id=user_ref['id'],
  906                               original_password=new_password,
  907                               new_password=next_password)
  908 
  909         self._assert_last_audit(user_ref['id'], UPDATED_OPERATION, 'user',
  910                                 cadftaxonomy.SECURITY_ACCOUNT_USER,
  911                                 reason=expected_reason)
  912 
  913 
  914 class CADFNotificationsForEntities(NotificationsForEntities):
  915 
  916     def setUp(self):
  917         super(CADFNotificationsForEntities, self).setUp()
  918         self.config_fixture.config(notification_format='cadf')
  919 
  920     def test_initiator_data_is_set(self):
  921         ref = unit.new_domain_ref()
  922         resp = self.post('/domains', body={'domain': ref})
  923         resource_id = resp.result.get('domain').get('id')
  924         self._assert_last_audit(resource_id, CREATED_OPERATION, 'domain',
  925                                 cadftaxonomy.SECURITY_DOMAIN)
  926         self._assert_initiator_data_is_set(CREATED_OPERATION,
  927                                            'domain',
  928                                            cadftaxonomy.SECURITY_DOMAIN)
  929 
  930     def test_initiator_request_id(self):
  931         data = self.build_authentication_request(
  932             user_id=self.user_id,
  933             password=self.user['password'])
  934         self.post('/auth/tokens', body=data)
  935         audit = self._audits[-1]
  936         initiator = audit['payload']['initiator']
  937         self.assertIn('request_id', initiator)
  938 
  939     def test_initiator_global_request_id(self):
  940         global_request_id = 'req-%s' % uuid.uuid4()
  941         data = self.build_authentication_request(
  942             user_id=self.user_id,
  943             password=self.user['password'])
  944         self.post(
  945             '/auth/tokens', body=data,
  946             headers={'X-OpenStack-Request-Id': global_request_id})
  947         audit = self._audits[-1]
  948         initiator = audit['payload']['initiator']
  949         self.assertEqual(
  950             initiator['global_request_id'], global_request_id)
  951 
  952     def test_initiator_global_request_id_not_set(self):
  953         data = self.build_authentication_request(
  954             user_id=self.user_id,
  955             password=self.user['password'])
  956         self.post('/auth/tokens', body=data)
  957         audit = self._audits[-1]
  958         initiator = audit['payload']['initiator']
  959         self.assertNotIn('global_request_id', initiator)
  960 
  961 
  962 class TestEventCallbacks(test_v3.RestfulTestCase):
  963 
  964     class FakeManager(object):
  965 
  966         def _project_deleted_callback(self, service, resource_type, operation,
  967                                       payload):
  968             """Used just for the callback interface."""
  969 
  970     def test_notification_received(self):
  971         callback = register_callback(CREATED_OPERATION, 'project')
  972         project_ref = unit.new_project_ref(domain_id=self.domain_id)
  973         PROVIDERS.resource_api.create_project(project_ref['id'], project_ref)
  974         self.assertTrue(callback.called)
  975 
  976     def test_notification_method_not_callable(self):
  977         fake_method = None
  978         self.assertRaises(TypeError,
  979                           notifications.register_event_callback,
  980                           UPDATED_OPERATION,
  981                           'project',
  982                           [fake_method])
  983 
  984     def test_notification_event_not_valid(self):
  985         manager = self.FakeManager()
  986         self.assertRaises(ValueError,
  987                           notifications.register_event_callback,
  988                           uuid.uuid4().hex,
  989                           'project',
  990                           manager._project_deleted_callback)
  991 
  992     def test_event_registration_for_unknown_resource_type(self):
  993         # Registration for unknown resource types should succeed.  If no event
  994         # is issued for that resource type, the callback wont be triggered.
  995 
  996         manager = self.FakeManager()
  997 
  998         notifications.register_event_callback(
  999             DELETED_OPERATION,
 1000             uuid.uuid4().hex,
 1001             manager._project_deleted_callback)
 1002         resource_type = uuid.uuid4().hex
 1003         notifications.register_event_callback(
 1004             DELETED_OPERATION,
 1005             resource_type,
 1006             manager._project_deleted_callback)
 1007 
 1008     def test_provider_event_callback_subscription(self):
 1009         callback_called = []
 1010 
 1011         @notifications.listener
 1012         class Foo(object):
 1013             def __init__(self):
 1014                 self.event_callbacks = {
 1015                     CREATED_OPERATION: {'project': self.foo_callback}}
 1016 
 1017             def foo_callback(self, service, resource_type, operation,
 1018                              payload):
 1019                 # uses callback_called from the closure
 1020                 callback_called.append(True)
 1021 
 1022         Foo()
 1023         project_ref = unit.new_project_ref(domain_id=self.domain_id)
 1024         PROVIDERS.resource_api.create_project(project_ref['id'], project_ref)
 1025         self.assertEqual([True], callback_called)
 1026 
 1027     def test_provider_event_callbacks_subscription(self):
 1028         callback_called = []
 1029 
 1030         @notifications.listener
 1031         class Foo(object):
 1032             def __init__(self):
 1033                 self.event_callbacks = {
 1034                     CREATED_OPERATION: {
 1035                         'project': [self.callback_0, self.callback_1]}}
 1036 
 1037             def callback_0(self, service, resource_type, operation, payload):
 1038                 # uses callback_called from the closure
 1039                 callback_called.append('cb0')
 1040 
 1041             def callback_1(self, service, resource_type, operation, payload):
 1042                 # uses callback_called from the closure
 1043                 callback_called.append('cb1')
 1044 
 1045         Foo()
 1046         project_ref = unit.new_project_ref(domain_id=self.domain_id)
 1047         PROVIDERS.resource_api.create_project(project_ref['id'], project_ref)
 1048         self.assertItemsEqual(['cb1', 'cb0'], callback_called)
 1049 
 1050     def test_invalid_event_callbacks(self):
 1051         @notifications.listener
 1052         class Foo(object):
 1053             def __init__(self):
 1054                 self.event_callbacks = 'bogus'
 1055 
 1056         self.assertRaises(AttributeError, Foo)
 1057 
 1058     def test_invalid_event_callbacks_event(self):
 1059         @notifications.listener
 1060         class Foo(object):
 1061             def __init__(self):
 1062                 self.event_callbacks = {CREATED_OPERATION: 'bogus'}
 1063 
 1064         self.assertRaises(AttributeError, Foo)
 1065 
 1066     def test_using_an_unbound_method_as_a_callback_fails(self):
 1067         # NOTE(dstanek): An unbound method is when you reference a method
 1068         # from a class object. You'll get a method that isn't bound to a
 1069         # particular instance so there is no magic 'self'. You can call it,
 1070         # but you have to pass in the instance manually like: C.m(C()).
 1071         # If you reference the method from an instance then you get a method
 1072         # that effectively curries the self argument for you
 1073         # (think functools.partial). Obviously is we don't have an
 1074         # instance then we can't call the method.
 1075         @notifications.listener
 1076         class Foo(object):
 1077             def __init__(self):
 1078                 self.event_callbacks = {CREATED_OPERATION:
 1079                                         {'project': Foo.callback}}
 1080 
 1081             def callback(self, service, resource_type, operation, payload):
 1082                 pass
 1083 
 1084         # TODO(dstanek): it would probably be nice to fail early using
 1085         # something like:
 1086         #     self.assertRaises(TypeError, Foo)
 1087         Foo()
 1088         project_ref = unit.new_project_ref(domain_id=self.domain_id)
 1089         self.assertRaises(TypeError, PROVIDERS.resource_api.create_project,
 1090                           project_ref['id'], project_ref)
 1091 
 1092 
 1093 class CadfNotificationsWrapperTestCase(test_v3.RestfulTestCase):
 1094 
 1095     LOCAL_HOST = 'localhost'
 1096     ACTION = 'authenticate'
 1097     ROLE_ASSIGNMENT = 'role_assignment'
 1098 
 1099     def setUp(self):
 1100         super(CadfNotificationsWrapperTestCase, self).setUp()
 1101         self._notifications = []
 1102 
 1103         def fake_notify(action, initiator, outcome, target,
 1104                         event_type, reason=None, **kwargs):
 1105             service_security = cadftaxonomy.SERVICE_SECURITY
 1106 
 1107             event = eventfactory.EventFactory().new_event(
 1108                 eventType=cadftype.EVENTTYPE_ACTIVITY,
 1109                 outcome=outcome,
 1110                 action=action,
 1111                 initiator=initiator,
 1112                 target=target,
 1113                 reason=reason,
 1114                 observer=cadfresource.Resource(typeURI=service_security))
 1115 
 1116             for key, value in kwargs.items():
 1117                 setattr(event, key, value)
 1118 
 1119             note = {
 1120                 'action': action,
 1121                 'initiator': initiator,
 1122                 'event': event,
 1123                 'event_type': event_type,
 1124                 'send_notification_called': True}
 1125             self._notifications.append(note)
 1126 
 1127         self.useFixture(fixtures.MockPatchObject(
 1128             notifications, '_send_audit_notification', fake_notify))
 1129 
 1130     def _get_last_note(self):
 1131         self.assertTrue(self._notifications)
 1132         return self._notifications[-1]
 1133 
 1134     def _assert_last_note(self, action, user_id, event_type=None):
 1135         self.assertTrue(self._notifications)
 1136         note = self._notifications[-1]
 1137         self.assertEqual(action, note['action'])
 1138         initiator = note['initiator']
 1139         self.assertEqual(user_id, initiator.id)
 1140         self.assertEqual(self.LOCAL_HOST, initiator.host.address)
 1141         self.assertTrue(note['send_notification_called'])
 1142         if event_type:
 1143             self.assertEqual(event_type, note['event_type'])
 1144 
 1145     def _assert_event(self, role_id, project=None, domain=None,
 1146                       user=None, group=None, inherit=False):
 1147         """Assert that the CADF event is valid.
 1148 
 1149         In the case of role assignments, the event will have extra data,
 1150         specifically, the role, target, actor, and if the role is inherited.
 1151 
 1152         An example event, as a dictionary is seen below:
 1153             {
 1154                 'typeURI': 'http://schemas.dmtf.org/cloud/audit/1.0/event',
 1155                 'initiator': {
 1156                     'typeURI': 'service/security/account/user',
 1157                     'host': {'address': 'localhost'},
 1158                     'id': 'openstack:0a90d95d-582c-4efb-9cbc-e2ca7ca9c341',
 1159                     'username': u'admin'
 1160                 },
 1161                 'target': {
 1162                     'typeURI': 'service/security/account/user',
 1163                     'id': 'openstack:d48ea485-ef70-4f65-8d2b-01aa9d7ec12d'
 1164                 },
 1165                 'observer': {
 1166                     'typeURI': 'service/security',
 1167                     'id': 'openstack:d51dd870-d929-4aba-8d75-dcd7555a0c95'
 1168                 },
 1169                 'eventType': 'activity',
 1170                 'eventTime': '2014-08-21T21:04:56.204536+0000',
 1171                 'role': u'0e6b990380154a2599ce6b6e91548a68',
 1172                 'domain': u'24bdcff1aab8474895dbaac509793de1',
 1173                 'inherited_to_projects': False,
 1174                 'group': u'c1e22dc67cbd469ea0e33bf428fe597a',
 1175                 'action': 'created.role_assignment',
 1176                 'outcome': 'success',
 1177                 'id': 'openstack:782689dd-f428-4f13-99c7-5c70f94a5ac1'
 1178             }
 1179         """
 1180         note = self._notifications[-1]
 1181         event = note['event']
 1182         if project:
 1183             self.assertEqual(project, event.project)
 1184         if domain:
 1185             self.assertEqual(domain, event.domain)
 1186         if group:
 1187             self.assertEqual(group, event.group)
 1188         elif user:
 1189             self.assertEqual(user, event.user)
 1190         self.assertEqual(role_id, event.role)
 1191         self.assertEqual(inherit, event.inherited_to_projects)
 1192 
 1193     def test_initiator_id_always_matches_user_id(self):
 1194         # Clear notifications
 1195         while self._notifications:
 1196             self._notifications.pop()
 1197 
 1198         self.get_scoped_token()
 1199         self.assertEqual(len(self._notifications), 1)
 1200         note = self._notifications.pop()
 1201         initiator = note['initiator']
 1202         self.assertEqual(self.user_id, initiator.id)
 1203         self.assertEqual(self.user_id, initiator.user_id)
 1204 
 1205     def test_initiator_always_contains_username(self):
 1206         # Clear notifications
 1207         while self._notifications:
 1208             self._notifications.pop()
 1209 
 1210         self.get_scoped_token()
 1211         self.assertEqual(len(self._notifications), 1)
 1212         note = self._notifications.pop()
 1213         initiator = note['initiator']
 1214         self.assertEqual(self.user['name'], initiator.username)
 1215 
 1216     def test_v3_authenticate_user_name_and_domain_id(self):
 1217         user_id = self.user_id
 1218         user_name = self.user['name']
 1219         password = self.user['password']
 1220         domain_id = self.domain_id
 1221         data = self.build_authentication_request(username=user_name,
 1222                                                  user_domain_id=domain_id,
 1223                                                  password=password)
 1224         self.post('/auth/tokens', body=data)
 1225         self._assert_last_note(self.ACTION, user_id)
 1226 
 1227     def test_v3_authenticate_user_id(self):
 1228         user_id = self.user_id
 1229         password = self.user['password']
 1230         data = self.build_authentication_request(user_id=user_id,
 1231                                                  password=password)
 1232         self.post('/auth/tokens', body=data)
 1233         self._assert_last_note(self.ACTION, user_id)
 1234 
 1235     def test_v3_authenticate_with_invalid_user_id_sends_notification(self):
 1236         user_id = uuid.uuid4().hex
 1237         password = self.user['password']
 1238         data = self.build_authentication_request(user_id=user_id,
 1239                                                  password=password)
 1240         self.post('/auth/tokens', body=data,
 1241                   expected_status=http.client.UNAUTHORIZED)
 1242         note = self._get_last_note()
 1243         initiator = note['initiator']
 1244 
 1245         # Confirm user-name specific event was emitted.
 1246         self.assertEqual(self.ACTION, note['action'])
 1247         self.assertEqual(user_id, initiator.user_id)
 1248         self.assertTrue(note['send_notification_called'])
 1249         self.assertEqual(cadftaxonomy.OUTCOME_FAILURE, note['event'].outcome)
 1250         self.assertEqual(self.LOCAL_HOST, initiator.host.address)
 1251 
 1252     def test_v3_authenticate_with_invalid_user_name_sends_notification(self):
 1253         user_name = uuid.uuid4().hex
 1254         password = self.user['password']
 1255         domain_id = self.domain_id
 1256         data = self.build_authentication_request(username=user_name,
 1257                                                  user_domain_id=domain_id,
 1258                                                  password=password)
 1259         self.post('/auth/tokens', body=data,
 1260                   expected_status=http.client.UNAUTHORIZED)
 1261         note = self._get_last_note()
 1262         initiator = note['initiator']
 1263 
 1264         # Confirm user-name specific event was emitted.
 1265         self.assertEqual(self.ACTION, note['action'])
 1266         self.assertEqual(user_name, initiator.user_name)
 1267         self.assertEqual(domain_id, initiator.domain_id)
 1268         self.assertTrue(note['send_notification_called'])
 1269         self.assertEqual(cadftaxonomy.OUTCOME_FAILURE, note['event'].outcome)
 1270         self.assertEqual(self.LOCAL_HOST, initiator.host.address)
 1271 
 1272     def test_v3_authenticate_user_name_and_domain_name(self):
 1273         user_id = self.user_id
 1274         user_name = self.user['name']
 1275         password = self.user['password']
 1276         domain_name = self.domain['name']
 1277         data = self.build_authentication_request(username=user_name,
 1278                                                  user_domain_name=domain_name,
 1279                                                  password=password)
 1280         self.post('/auth/tokens', body=data)
 1281         self._assert_last_note(self.ACTION, user_id)
 1282 
 1283     def _test_role_assignment(self, url, role, project=None, domain=None,
 1284                               user=None, group=None):
 1285         self.put(url)
 1286         action = "%s.%s" % (CREATED_OPERATION, self.ROLE_ASSIGNMENT)
 1287         event_type = '%s.%s.%s' % (notifications.SERVICE,
 1288                                    self.ROLE_ASSIGNMENT, CREATED_OPERATION)
 1289         self._assert_last_note(action, self.user_id, event_type)
 1290         self._assert_event(role, project, domain, user, group)
 1291         self.delete(url)
 1292         action = "%s.%s" % (DELETED_OPERATION, self.ROLE_ASSIGNMENT)
 1293         event_type = '%s.%s.%s' % (notifications.SERVICE,
 1294                                    self.ROLE_ASSIGNMENT, DELETED_OPERATION)
 1295         self._assert_last_note(action, self.user_id, event_type)
 1296         self._assert_event(role, project, domain, user, None)
 1297 
 1298     def test_user_project_grant(self):
 1299         url = ('/projects/%s/users/%s/roles/%s' %
 1300                (self.project_id, self.user_id, self.role_id))
 1301         self._test_role_assignment(url, self.role_id,
 1302                                    project=self.project_id,
 1303                                    user=self.user_id)
 1304 
 1305     def test_group_domain_grant(self):
 1306         group_ref = unit.new_group_ref(domain_id=self.domain_id)
 1307         group = PROVIDERS.identity_api.create_group(group_ref)
 1308         PROVIDERS.identity_api.add_user_to_group(self.user_id, group['id'])
 1309         url = ('/domains/%s/groups/%s/roles/%s' %
 1310                (self.domain_id, group['id'], self.role_id))
 1311         self._test_role_assignment(url, self.role_id,
 1312                                    domain=self.domain_id,
 1313                                    group=group['id'])
 1314 
 1315     def test_add_role_to_user_and_project(self):
 1316         # A notification is sent when add_role_to_user_and_project is called on
 1317         # the assignment manager.
 1318 
 1319         project_ref = unit.new_project_ref(self.domain_id)
 1320         project = PROVIDERS.resource_api.create_project(
 1321             project_ref['id'], project_ref)
 1322         project_id = project['id']
 1323 
 1324         PROVIDERS.assignment_api.add_role_to_user_and_project(
 1325             self.user_id, project_id, self.role_id)
 1326 
 1327         self.assertTrue(self._notifications)
 1328         note = self._notifications[-1]
 1329         self.assertEqual('created.role_assignment', note['action'])
 1330         self.assertTrue(note['send_notification_called'])
 1331 
 1332         self._assert_event(self.role_id, project=project_id, user=self.user_id)
 1333 
 1334     def test_remove_role_from_user_and_project(self):
 1335         # A notification is sent when remove_role_from_user_and_project is
 1336         # called on the assignment manager.
 1337 
 1338         PROVIDERS.assignment_api.remove_role_from_user_and_project(
 1339             self.user_id, self.project_id, self.role_id)
 1340 
 1341         self.assertTrue(self._notifications)
 1342         note = self._notifications[-1]
 1343         self.assertEqual('deleted.role_assignment', note['action'])
 1344         self.assertTrue(note['send_notification_called'])
 1345 
 1346         self._assert_event(self.role_id, project=self.project_id,
 1347                            user=self.user_id)
 1348 
 1349 
 1350 class TestCallbackRegistration(unit.BaseTestCase):
 1351     def setUp(self):
 1352         super(TestCallbackRegistration, self).setUp()
 1353         self.mock_log = mock.Mock()
 1354         # Force the callback logging to occur
 1355         self.mock_log.logger.getEffectiveLevel.return_value = log.DEBUG
 1356 
 1357     def verify_log_message(self, data):
 1358         """Verify log message.
 1359 
 1360         Tests that use this are a little brittle because adding more
 1361         logging can break them.
 1362 
 1363         TODO(dstanek): remove the need for this in a future refactoring
 1364 
 1365         """
 1366         log_fn = self.mock_log.debug
 1367         self.assertEqual(len(data), log_fn.call_count)
 1368         for datum in data:
 1369             log_fn.assert_any_call(mock.ANY, datum)
 1370 
 1371     def test_a_function_callback(self):
 1372         def callback(*args, **kwargs):
 1373             pass
 1374 
 1375         resource_type = 'thing'
 1376         with mock.patch('keystone.notifications.LOG', self.mock_log):
 1377             notifications.register_event_callback(
 1378                 CREATED_OPERATION, resource_type, callback)
 1379 
 1380         callback = 'keystone.tests.unit.common.test_notifications.callback'
 1381         expected_log_data = {
 1382             'callback': callback,
 1383             'event': 'identity.%s.created' % resource_type
 1384         }
 1385         self.verify_log_message([expected_log_data])
 1386 
 1387     def test_a_method_callback(self):
 1388         class C(object):
 1389             def callback(self, *args, **kwargs):
 1390                 pass
 1391 
 1392         with mock.patch('keystone.notifications.LOG', self.mock_log):
 1393             notifications.register_event_callback(
 1394                 CREATED_OPERATION, 'thing', C().callback)
 1395 
 1396         callback = 'keystone.tests.unit.common.test_notifications.C.callback'
 1397         expected_log_data = {
 1398             'callback': callback,
 1399             'event': 'identity.thing.created'
 1400         }
 1401         self.verify_log_message([expected_log_data])
 1402 
 1403     def test_a_list_of_callbacks(self):
 1404         def callback(*args, **kwargs):
 1405             pass
 1406 
 1407         class C(object):
 1408             def callback(self, *args, **kwargs):
 1409                 pass
 1410 
 1411         with mock.patch('keystone.notifications.LOG', self.mock_log):
 1412             notifications.register_event_callback(
 1413                 CREATED_OPERATION, 'thing', [callback, C().callback])
 1414 
 1415         callback_1 = 'keystone.tests.unit.common.test_notifications.callback'
 1416         callback_2 = 'keystone.tests.unit.common.test_notifications.C.callback'
 1417         expected_log_data = [
 1418             {
 1419                 'callback': callback_1,
 1420                 'event': 'identity.thing.created'
 1421             },
 1422             {
 1423                 'callback': callback_2,
 1424                 'event': 'identity.thing.created'
 1425             },
 1426         ]
 1427         self.verify_log_message(expected_log_data)
 1428 
 1429     def test_an_invalid_callback(self):
 1430         self.assertRaises(TypeError,
 1431                           notifications.register_event_callback,
 1432                           (CREATED_OPERATION, 'thing', object()))
 1433 
 1434     def test_an_invalid_event(self):
 1435         def callback(*args, **kwargs):
 1436             pass
 1437 
 1438         self.assertRaises(ValueError,
 1439                           notifications.register_event_callback,
 1440                           uuid.uuid4().hex,
 1441                           'thing',
 1442                           callback)
 1443 
 1444 
 1445 class CADFNotificationsDataTestCase(test_v3.RestfulTestCase):
 1446 
 1447     def config_overrides(self):
 1448         super(CADFNotificationsDataTestCase, self).config_overrides()
 1449         # NOTE(lbragstad): This is a workaround since oslo.messaging version
 1450         # 9.0.0 had a broken default for transport_url. This makes it so that
 1451         # we are able to use version 9.0.0 in tests because we are supplying
 1452         # an override to use a sane default (rabbit://). The problem is that
 1453         # we can't update the config fixture until we call
 1454         # get_notification_transport since that method registers the
 1455         # configuration options for oslo.messaging, which fails since there
 1456         # isn't a default value for transport_url with version 9.0.0. All the
 1457         # next line is doing is bypassing the broken default logic by supplying
 1458         # a dummy url, which allows the options to be registered. After that,
 1459         # we can actually update the configuration option to override the
 1460         # transport_url option that was just registered before proceeding with
 1461         # the test.
 1462         oslo_messaging.get_notification_transport(CONF, url='rabbit://')
 1463         self.config_fixture.config(
 1464             group='oslo_messaging_notifications', transport_url='rabbit://'
 1465         )
 1466 
 1467     def test_receive_identityId_from_audit_notification(self):
 1468         observer = None
 1469         resource_type = EXP_RESOURCE_TYPE
 1470 
 1471         ref = unit.new_service_ref()
 1472         ref['type'] = 'identity'
 1473         PROVIDERS.catalog_api.create_service(ref['id'], ref.copy())
 1474 
 1475         action = CREATED_OPERATION + '.' + resource_type
 1476         initiator = notifications._get_request_audit_info(self.user_id)
 1477         target = cadfresource.Resource(typeURI=cadftaxonomy.ACCOUNT_USER)
 1478         outcome = 'success'
 1479         event_type = 'identity.authenticate.created'
 1480 
 1481         with mock.patch.object(notifications._get_notifier(),
 1482                                'info') as mocked:
 1483 
 1484             notifications._send_audit_notification(action,
 1485                                                    initiator,
 1486                                                    outcome,
 1487                                                    target,
 1488                                                    event_type)
 1489 
 1490             for mock_args_list in mocked.call_args:
 1491                 if len(mock_args_list) != 0:
 1492                     for mock_args in mock_args_list:
 1493                         if 'observer' in mock_args:
 1494                             observer = mock_args['observer']
 1495                             break
 1496 
 1497         self.assertEqual(ref['id'], observer['id'])