Merge "Emit CADF notifications on authentication for invalid users"
This commit is contained in:
commit
6c18a2c3a9
|
@ -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
|
||||
|
|
|
@ -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']
|
||||
|
|
Loading…
Reference in New Issue