From 255685877ec54d1b9689b88cc5af8a5490d30c91 Mon Sep 17 00:00:00 2001 From: Fernando Diaz Date: Fri, 4 Dec 2015 22:23:15 -0600 Subject: [PATCH] Opt-out certain Keystone Notifications This patch will allow certain notifications for events in Keystone to be opted out. Opting out may be a desired way of doing this since most keystone deployers will likely like to by default have all audit traces. Change-Id: I86caf6e5f25cdd76121881813167c2144bf1d051 Closes-Bug: 1519210 --- doc/source/event_notifications.rst | 25 +++++- keystone/notifications.py | 40 ++++++++++ .../tests/unit/common/test_notifications.py | 76 +++++++++++++++++++ .../notes/bug-1519210-de76097c974f9c93.yaml | 7 ++ 4 files changed, 147 insertions(+), 1 deletion(-) create mode 100644 releasenotes/notes/bug-1519210-de76097c974f9c93.yaml diff --git a/doc/source/event_notifications.rst b/doc/source/event_notifications.rst index 7011e16c12..d9225c56ea 100644 --- a/doc/source/event_notifications.rst +++ b/doc/source/event_notifications.rst @@ -28,7 +28,7 @@ The supported operations between the two types of notification formats are documented below. Common Notification Structure -============================== +============================= Notifications generated by Keystone are generated in JSON format. An external application can format them into ATOM format and publish them as a feed. @@ -414,3 +414,26 @@ ensures this has an immediate impact on the accessibility of the project's resources by revoking tokens with authorization on the project, but should **not** have a direct impact on the projects resources (in other words, virtual machines should **not** be deleted). + +Opting out of certain notifications +=================================== + +There are many notifications that Keystone emits and some deployers may only +care about certain events. In Keystone there is a way to opt-out of certain +notifications. In ``/etc/keystone/keystone.conf`` you can set ``opt_out`` to +the event you wish to opt-out of. It is possible to opt-out of multiple events. + +Example: + +.. code-block:: ini + + [DEFAULT] + notification_opt_out = identity.user.created + notification_opt_out = identity.role_assignment.created + notification_opt_out = identity.authenticate.pending + +This will opt-out notifications for user creation, role assignment creation and +successful authentications. For a list of event types that can be used, refer +to: `Telemetry Measurements`_. + +.. _Telemetry Measurements: http://docs.openstack.org/admin-guide-cloud/telemetry-measurements.html#openstack-identity diff --git a/keystone/notifications.py b/keystone/notifications.py index ef56d2865f..7bd0e3d913 100644 --- a/keystone/notifications.py +++ b/keystone/notifications.py @@ -45,6 +45,14 @@ notifier_opts = [ 'the resource being operated on. A "cadf" notification ' 'has the same information, as well as information about ' 'the initiator of the event.'), + cfg.MultiStrOpt('notification_opt_out', default=[], + help='Define the notification options to opt-out from. ' + 'The value expected is: ' + 'identity... This field ' + 'can be set multiple times in order to add more ' + 'notifications to opt-out from. For example:\n ' + 'notification_opt_out=identity.user.created\n ' + 'notification_opt_out=identity.authenticate.success'), ] config_section = None @@ -470,6 +478,8 @@ def _send_notification(operation, resource_type, resource_id, public=True): 'service': SERVICE, 'resource_type': resource_type, 'operation': operation} + if _check_notification_opt_out(event_type, outcome=None): + return try: notifier.info(context, event_type, payload) except Exception: @@ -716,6 +726,9 @@ def _send_audit_notification(action, initiator, outcome, target, key-value pairs to the CADF event. """ + if _check_notification_opt_out(event_type, outcome): + return + event = eventfactory.EventFactory().new_event( eventType=cadftype.EVENTTYPE_ACTIVITY, outcome=outcome, @@ -742,6 +755,33 @@ def _send_audit_notification(action, initiator, outcome, target, {'action': action, 'event_type': event_type}) +def _check_notification_opt_out(event_type, outcome): + """Check if a particular event_type has been opted-out of. + + This method checks to see if an event should be sent to the messaging + service. Any event specified in the opt-out list will not be transmitted. + + :param event_type: This is the meter name that Ceilometer uses to poll + events. For example: identity.user.created, or + identity.authenticate.success, or identity.role_assignment.created + :param outcome: The CADF outcome (taxonomy.OUTCOME_PENDING, + taxonomy.OUTCOME_SUCCESS, taxonomy.OUTCOME_FAILURE) + + """ + # NOTE(stevemar): Special handling for authenticate, we look at the outcome + # as well when evaluating. For authN events, event_type is just + # idenitity.authenticate, which isn't fine enough to provide any opt-out + # value, so we attach the outcome to re-create the meter name used in + # ceilometer. + if 'authenticate' in event_type: + event_type = event_type + "." + outcome + + if event_type in CONF.notification_opt_out: + return True + + return False + + emit_event = CadfNotificationWrapper diff --git a/keystone/tests/unit/common/test_notifications.py b/keystone/tests/unit/common/test_notifications.py index 02706fb1f1..32da0e344d 100644 --- a/keystone/tests/unit/common/test_notifications.py +++ b/keystone/tests/unit/common/test_notifications.py @@ -201,6 +201,82 @@ class NotificationsTestCase(unit.BaseTestCase): resource) mocked.assert_called_once_with(*expected_args) + def test_send_notification_with_opt_out(self): + """Test the private method _send_notification with opt-out. + + Test that _send_notification does not notify when a valid + notification_opt_out configuration is provided. + """ + resource = uuid.uuid4().hex + resource_type = EXP_RESOURCE_TYPE + operation = CREATED_OPERATION + event_type = 'identity.%s.created' % resource_type + + # NOTE(diazjf): Here we add notification_opt_out to the + # configuration so that we should return before _get_notifer is + # called. This is because we are opting out notifications for the + # passed resource_type and operation. + conf = self.useFixture(config_fixture.Config(CONF)) + conf.config(notification_opt_out=event_type) + + with mock.patch.object(notifications._get_notifier(), + '_notify') as mocked: + + notifications._send_notification(operation, resource_type, + resource) + mocked.assert_not_called() + + def test_send_audit_notification_with_opt_out(self): + """Test the private method _send_audit_notification with opt-out. + + Test that _send_audit_notification does not notify when a valid + notification_opt_out configuration is provided. + """ + resource_type = EXP_RESOURCE_TYPE + + action = CREATED_OPERATION + '.' + resource_type + initiator = mock + target = mock + outcome = 'success' + event_type = 'identity.%s.created' % resource_type + + conf = self.useFixture(config_fixture.Config(CONF)) + conf.config(notification_opt_out=event_type) + + with mock.patch.object(notifications._get_notifier(), + '_notify') as mocked: + + notifications._send_audit_notification(action, + initiator, + outcome, + target, + event_type) + mocked.assert_not_called() + + def test_opt_out_authenticate_event(self): + """Test that authenticate events are successfully opted out.""" + resource_type = EXP_RESOURCE_TYPE + + action = CREATED_OPERATION + '.' + resource_type + initiator = mock + target = mock + outcome = 'success' + event_type = 'identity.authenticate' + meter_name = '%s.%s' % (event_type, outcome) + + conf = self.useFixture(config_fixture.Config(CONF)) + conf.config(notification_opt_out=meter_name) + + with mock.patch.object(notifications._get_notifier(), + '_notify') as mocked: + + notifications._send_audit_notification(action, + initiator, + outcome, + target, + event_type) + mocked.assert_not_called() + class BaseNotificationTest(test_v3.RestfulTestCase): diff --git a/releasenotes/notes/bug-1519210-de76097c974f9c93.yaml b/releasenotes/notes/bug-1519210-de76097c974f9c93.yaml new file mode 100644 index 0000000000..1a6006f7ee --- /dev/null +++ b/releasenotes/notes/bug-1519210-de76097c974f9c93.yaml @@ -0,0 +1,7 @@ +--- +features: + - > + [`bug 1519210 `_] + A user may now opt-out of notifications by specifying a list of + `event_types` in the ``notification_opt_out`` option in `keystone.conf`. + These events are never sent to a messaging service.