From fff27cb949860bb9916c9ae039ddfaf7f0c69412 Mon Sep 17 00:00:00 2001 From: Steve Martinelli Date: Mon, 4 Aug 2014 02:42:48 -0400 Subject: [PATCH] Add notifications for identity authenticate events This patch adds support for notifications for when a user authenticates. For more information on what events keystone supports visit: http://docs.openstack.org/developer/keystone/event_notifications.html#auditing-with-cadf Change-Id: I73c6971902750b401a29bb21d41617b9775946a0 --- ceilometer/identity/notifications.py | 37 ++++++++-- .../tests/identity/test_notifications.py | 70 ++++++++++++++++++- setup.cfg | 1 + 3 files changed, 102 insertions(+), 6 deletions(-) diff --git a/ceilometer/identity/notifications.py b/ceilometer/identity/notifications.py index 45a98cd82..b84b425b9 100644 --- a/ceilometer/identity/notifications.py +++ b/ceilometer/identity/notifications.py @@ -46,6 +46,8 @@ class _Base(plugin.NotificationBase): exchange=conf.keystone_control_exchange) for topic in conf.notification_topics] + +class IdentityCRUD(_Base): def process_notification(self, message): yield sample.Sample.from_notification( name=message['event_type'], @@ -58,7 +60,7 @@ class _Base(plugin.NotificationBase): message=message) -class User(_Base): +class User(IdentityCRUD): resource_type = 'user' resource_name = '%s.%s' % (SERVICE, resource_type) @@ -68,7 +70,7 @@ class User(_Base): return ['%s.*' % (self.resource_name)] -class Group(_Base): +class Group(IdentityCRUD): resource_type = 'group' resource_name = '%s.%s' % (SERVICE, resource_type) @@ -78,7 +80,7 @@ class Group(_Base): return ['%s.*' % (self.resource_name)] -class Project(_Base): +class Project(IdentityCRUD): resource_type = 'project' resource_name = '%s.%s' % (SERVICE, resource_type) @@ -88,7 +90,7 @@ class Project(_Base): return ['%s.*' % (self.resource_name)] -class Role(_Base): +class Role(IdentityCRUD): resource_type = 'role' resource_name = '%s.%s' % (SERVICE, resource_type) @@ -98,7 +100,7 @@ class Role(_Base): return ['%s.*' % (self.resource_name)] -class Trust(_Base): +class Trust(IdentityCRUD): resource_type = 'trust' resource_name = '%s.%s' % (SERVICE, resource_type) @@ -109,3 +111,28 @@ class Trust(_Base): '%s.created' % (self.resource_name), '%s.deleted' % (self.resource_name) ] + + +class Authenticate(_Base): + """Convert identity authentication notifications into Samples.""" + + resource_type = 'authenticate' + event_name = '%s.%s' % (SERVICE, resource_type) + + def process_notification(self, message): + outcome = message['payload']['outcome'] + meter_name = '%s.%s.%s' % (SERVICE, self.resource_type, outcome) + + yield sample.Sample.from_notification( + name=meter_name, + type=sample.TYPE_DELTA, + unit='user', + volume=1, + resource_id=message['payload']['initiator']['id'], + user_id=message['payload']['initiator']['id'], + project_id=None, + message=message) + + @property + def event_types(self): + return [self.event_name] diff --git a/ceilometer/tests/identity/test_notifications.py b/ceilometer/tests/identity/test_notifications.py index c995aad9a..1bd72ec80 100644 --- a/ceilometer/tests/identity/test_notifications.py +++ b/ceilometer/tests/identity/test_notifications.py @@ -44,7 +44,43 @@ def notification_for(resource_type, operation, resource_id): } -class TestNotification(test.BaseTestCase): +def authn_notification_for(outcome): + + return { + u'event_type': u'identity.authenticate', + u'message_id': u'1371a590-d5fd-448f-b3bb-a14dead6f4cb', + u'payload': { + u'typeURI': u'http://schemas.dmtf.org/cloud/audit/1.0/event', + u'initiator': { + u'typeURI': u'service/security/account/user', + u'host': { + u'agent': u'python-keystoneclient', + u'address': u'10.0.2.15' + }, + u'id': USER_ID, + u'name': u'openstack:demo_user' + }, + u'target': { + u'typeURI': u'service/security/account/user', + u'id': u'openstack:44b3d8cb-5f16-46e9-9b1b-ac90b64c2530' + }, + u'observer': { + u'typeURI': u'service/security', + u'id': u'openstack:55a9e88c-a4b1-4864-9339-62b7e6ecb6a7' + }, + u'eventType': u'activity', + u'eventTime': u'2014-08-04T05:38:59.978898+0000', + u'action': u'authenticate', + u'outcome': outcome, + u'id': u'openstack:eca02fef-9394-4008-8fb3-c434133ca4b2' + }, + u'priority': u'INFO', + u'publisher_id': PUBLISHER_ID, + u'timestamp': NOW + } + + +class TestCRUDNotification(test.BaseTestCase): def _verify_common_sample(self, s): self.assertIsNotNone(s) @@ -110,3 +146,35 @@ class TestNotification(test.BaseTestCase): def test_delete_trust(self): self._test_operation('trust', 'deleted', TRUST_ID, notifications.Trust) + + +class TestAuthenticationNotification(test.BaseTestCase): + + def _verify_common_sample(self, s): + self.assertIsNotNone(s) + self.assertEqual(NOW, s.timestamp) + self.assertEqual(sample.TYPE_DELTA, s.type) + self.assertIsNone(s.project_id) + self.assertEqual(USER_ID, s.user_id) + self.assertEqual(USER_ID, s.resource_id) + self.assertEqual('user', s.unit) + metadata = s.resource_metadata + self.assertEqual(PUBLISHER_ID, metadata.get('host')) + + def _test_operation(self, outcome): + notif = authn_notification_for(outcome) + handler = notifications.Authenticate(mock.Mock()) + data = list(handler.process_notification(notif)) + self.assertEqual(1, len(data)) + name = '%s.%s.%s' % (notifications.SERVICE, 'authenticate', outcome) + self.assertEqual(name, data[0].name) + self._verify_common_sample(data[0]) + + def test_authn_success(self): + self._test_operation('success') + + def test_authn_failure(self): + self._test_operation('failure') + + def test_authn_pending(self): + self._test_operation('pending') diff --git a/setup.cfg b/setup.cfg index eb0bd189b..07b09535c 100644 --- a/setup.cfg +++ b/setup.cfg @@ -51,6 +51,7 @@ ceilometer.notification = volume_size = ceilometer.volume.notifications:VolumeSize snapshot = ceilometer.volume.notifications:Snapshot snapshot_size = ceilometer.volume.notifications:SnapshotSize + authenticate = ceilometer.identity.notifications:Authenticate user = ceilometer.identity.notifications:User group = ceilometer.identity.notifications:Group role = ceilometer.identity.notifications:Role