Merge "Emit CADF notifications on authentication for invalid users"

This commit is contained in:
Zuul 2018-11-09 23:18:14 +00:00 committed by Gerrit Code Review
commit 6c18a2c3a9
2 changed files with 73 additions and 0 deletions

View File

@ -15,17 +15,24 @@
import sys
from oslo_log import log
from pycadf import cadftaxonomy as taxonomy
from pycadf import reason
from pycadf import resource
import six
from keystone.common import driver_hints
from keystone.common import provider_api
import keystone.conf
from keystone import exception
from keystone import notifications
CONF = keystone.conf.CONF
LOG = log.getLogger(__name__)
PROVIDERS = provider_api.ProviderAPIs
_NOTIFY_OP = 'authenticate'
_NOTIFY_EVENT = '{service}.{event}'.format(service=notifications.SERVICE,
event=_NOTIFY_OP)
def construct_method_map_from_config():
@ -153,6 +160,7 @@ class BaseUserInfo(provider_api.ProviderAPIMixin, object):
user_info = auth_payload['user']
user_id = user_info.get('id')
user_name = user_info.get('name')
domain_ref = {}
if not user_id and not user_name:
raise exception.ValidationError(attribute='id or name',
target='user')
@ -171,6 +179,29 @@ class BaseUserInfo(provider_api.ProviderAPIMixin, object):
self._assert_domain_is_enabled(domain_ref)
except exception.UserNotFound as e:
LOG.warning(six.text_type(e))
# We need to special case USER NOT FOUND here for CADF
# notifications as the normal path for notification(s) come from
# `identity_api.authenticate` and we are a bit before dropping into
# that method.
audit_reason = reason.Reason(str(e), str(e.code))
audit_initiator = notifications.build_audit_initiator()
# build an appropriate audit initiator with relevant information
# for the failed request. This will catch invalid user_name and
# invalid user_id.
if user_name:
audit_initiator.user_name = user_name
else:
audit_initiator.user_id = user_id
audit_initiator.domain_id = domain_ref.get('id')
audit_initiator.domain_name = domain_ref.get('name')
notifications._send_audit_notification(
action=_NOTIFY_OP,
initiator=audit_initiator,
outcome=taxonomy.OUTCOME_FAILURE,
target=resource.Resource(typeURI=taxonomy.ACCOUNT_USER),
event_type=_NOTIFY_EVENT,
reason=audit_reason)
raise exception.Unauthorized(e)
self._assert_user_is_enabled(user_ref)
self.user_ref = user_ref

View File

@ -25,6 +25,7 @@ from pycadf import cadftaxonomy
from pycadf import cadftype
from pycadf import eventfactory
from pycadf import resource as cadfresource
from six.moves import http_client
from keystone.common import provider_api
import keystone.conf
@ -1068,6 +1069,10 @@ class CadfNotificationsWrapperTestCase(test_v3.RestfulTestCase):
self.useFixture(fixtures.MockPatchObject(
notifications, '_send_audit_notification', fake_notify))
def _get_last_note(self):
self.assertTrue(self._notifications)
return self._notifications[-1]
def _assert_last_note(self, action, user_id, event_type=None):
self.assertTrue(self._notifications)
note = self._notifications[-1]
@ -1158,6 +1163,43 @@ class CadfNotificationsWrapperTestCase(test_v3.RestfulTestCase):
self.post('/auth/tokens', body=data)
self._assert_last_note(self.ACTION, user_id)
def test_v3_authenticate_with_invalid_user_id_sends_notification(self):
user_id = uuid.uuid4().hex
password = self.user['password']
data = self.build_authentication_request(user_id=user_id,
password=password)
self.post('/auth/tokens', body=data,
expected_status=http_client.UNAUTHORIZED)
note = self._get_last_note()
initiator = note['initiator']
# Confirm user-name specific event was emitted.
self.assertEqual(self.ACTION, note['action'])
self.assertEqual(user_id, initiator.user_id)
self.assertTrue(note['send_notification_called'])
self.assertEqual(cadftaxonomy.OUTCOME_FAILURE, note['event'].outcome)
self.assertEqual(self.LOCAL_HOST, initiator.host.address)
def test_v3_authenticate_with_invalid_user_name_sends_notification(self):
user_name = uuid.uuid4().hex
password = self.user['password']
domain_id = self.domain_id
data = self.build_authentication_request(username=user_name,
user_domain_id=domain_id,
password=password)
self.post('/auth/tokens', body=data,
expected_status=http_client.UNAUTHORIZED)
note = self._get_last_note()
initiator = note['initiator']
# Confirm user-name specific event was emitted.
self.assertEqual(self.ACTION, note['action'])
self.assertEqual(user_name, initiator.user_name)
self.assertEqual(domain_id, initiator.domain_id)
self.assertTrue(note['send_notification_called'])
self.assertEqual(cadftaxonomy.OUTCOME_FAILURE, note['event'].outcome)
self.assertEqual(self.LOCAL_HOST, initiator.host.address)
def test_v3_authenticate_user_name_and_domain_name(self):
user_id = self.user_id
user_name = self.user['name']