def wrapper(wrapped_self, context, user_id, *args, **kwargs): """Always send a notification.""" initiator = _get_request_audit_info(context, user_id) target = resource.Resource(typeURI=taxonomy.ACCOUNT_USER) try: result = f(wrapped_self, context, user_id, *args, **kwargs) except Exception: # For authentication failure send a cadf event as well _send_audit_notification(self.action, initiator, taxonomy.OUTCOME_FAILURE, target, self.event_type) raise else: _send_audit_notification(self.action, initiator, taxonomy.OUTCOME_SUCCESS, target, self.event_type) return result
def audit_initiator(self): """A pyCADF initiator describing the current authenticated context.""" pycadf_host = host.Host(address=self.remote_addr, agent=self.user_agent) initiator = resource.Resource(typeURI=taxonomy.ACCOUNT_USER, host=pycadf_host) if self.context.user_id: initiator.id = utils.resource_uuid(self.context.user_id) initiator.user_id = self.context.user_id if self.context.project_id: initiator.project_id = self.context.project_id if self.context.domain_id: initiator.domain_id = self.context.domain_id return initiator
def build_audit_initiator(): """A pyCADF initiator describing the current authenticated context.""" pycadf_host = host.Host(address=flask.request.remote_addr, agent=str(flask.request.user_agent)) initiator = resource.Resource(typeURI=taxonomy.ACCOUNT_USER, host=pycadf_host) oslo_context = flask.request.environ.get(context.REQUEST_CONTEXT_ENV) if oslo_context.user_id: initiator.id = utils.resource_uuid(oslo_context.user_id) initiator.user_id = oslo_context.user_id if oslo_context.project_id: initiator.project_id = oslo_context.project_id if oslo_context.domain_id: initiator.domain_id = oslo_context.domain_id return initiator
def _send_audit_notification(action, initiator, outcome, target, event_type, **kwargs): """Send CADF notification to inform observers about the affected resource. This method logs an exception when sending the notification fails. :param action: CADF action being audited (e.g., 'authenticate') :param initiator: CADF resource representing the initiator :param outcome: The CADF outcome (taxonomy.OUTCOME_PENDING, taxonomy.OUTCOME_SUCCESS, taxonomy.OUTCOME_FAILURE) :param target: CADF resource representing the target :param event_type: An OpenStack-ism, typically this is the meter name that Ceilometer uses to poll events. :param kwargs: Any additional arguments passed in will be added as key-value pairs to the CADF event. """ event = eventfactory.EventFactory().new_event( eventType=cadftype.EVENTTYPE_ACTIVITY, outcome=outcome, action=action, initiator=initiator, target=target, observer=resource.Resource(typeURI=taxonomy.SERVICE_SECURITY)) for key, value in kwargs.items(): setattr(event, key, value) context = {} payload = event.as_dict() notifier = _get_notifier() if notifier: try: notifier.info(context, event_type, payload) except Exception: # diaper defense: any exception that occurs while emitting the # notification should not interfere with the API request LOG.exception( _LE('Failed to send %(action)s %(event_type)s notification'), { 'action': action, 'event_type': event_type })
def test_resource(self): res = resource.Resource(typeURI='storage', name='res_name', domain='res_domain', ref='res_ref', credential=credential.Credential( token=identifier.generate_uuid()), host=host.Host(address='192.168.0.1'), geolocation=geolocation.Geolocation(), geolocationId=identifier.generate_uuid()) res.add_attachment(attachment.Attachment(typeURI='attachURI', content='content', name='attachment_name')) res.add_address(endpoint.Endpoint(url='http://192.168.0.1')) self.assertEqual(res.is_valid(), True) dict_res = res.as_dict() for key in resource.RESOURCE_KEYNAMES: self.assertIn(key, dict_res)
def fake_audit(action, initiator, outcome, target, event_type, **kwargs): service_security = cadftaxonomy.SERVICE_SECURITY event = eventfactory.EventFactory().new_event( eventType=cadftype.EVENTTYPE_ACTIVITY, outcome=outcome, action=action, initiator=initiator, target=target, observer=cadfresource.Resource(typeURI=service_security)) for key, value in kwargs.items(): setattr(event, key, value) audit = { 'payload': event.as_dict(), 'event_type': event_type, 'send_notification_called': True} self._audits.append(audit)
def test_event_unique(self): ev = event.Event(eventType='activity', initiator=resource.Resource(typeURI='storage'), action='read', target=resource.Resource(typeURI='storage'), observer=resource.Resource(id='target'), outcome='success') time.sleep(1) ev2 = event.Event(eventType='activity', initiator=resource.Resource(typeURI='storage'), action='read', target=resource.Resource(typeURI='storage'), observer=resource.Resource(id='target'), outcome='success') self.assertNotEqual(ev.id, ev2.id) self.assertNotEqual(ev.eventTime, ev2.eventTime)
def _get_request_audit_info(context, user_id=None): """Collect audit information about the request used for CADF. :param context: Request context :param user_id: Optional user ID, alternatively collected from context :returns: Auditing data about the request :rtype: :class:`pycadf.Resource` """ remote_addr = None http_user_agent = None project_id = None domain_id = None if context and 'environment' in context and context['environment']: environment = context['environment'] remote_addr = environment.get('REMOTE_ADDR') http_user_agent = environment.get('HTTP_USER_AGENT') if not user_id: user_id = environment.get('KEYSTONE_AUTH_CONTEXT', {}).get('user_id') project_id = environment.get('KEYSTONE_AUTH_CONTEXT', {}).get('project_id') domain_id = environment.get('KEYSTONE_AUTH_CONTEXT', {}).get('domain_id') host = pycadf.host.Host(address=remote_addr, agent=http_user_agent) initiator = resource.Resource(typeURI=taxonomy.ACCOUNT_USER, host=host) if user_id: initiator.user_id = user_id initiator.id = utils.resource_uuid(user_id) initiator = _add_username_to_initiator(initiator) if project_id: initiator.project_id = project_id if domain_id: initiator.domain_id = domain_id return initiator
def fake_notify(action, initiator, outcome, target, event_type, **kwargs): service_security = cadftaxonomy.SERVICE_SECURITY event = eventfactory.EventFactory().new_event( eventType=cadftype.EVENTTYPE_ACTIVITY, outcome=outcome, action=action, initiator=initiator, target=target, observer=cadfresource.Resource(typeURI=service_security)) for key, value in kwargs.items(): setattr(event, key, value) note = { 'action': action, 'initiator': initiator, 'event': event, 'send_notification_called': True } self._notifications.append(note)
def test_event_resource_shortform_not_self(self): self.assertRaises(ValueError, lambda: event.Event( eventType='activity', initiator=resource.Resource(typeURI='storage'), action='read', target=resource.Resource(id='target'), observer=resource.Resource(id='target'), outcome='success')) self.assertRaises(ValueError, lambda: event.Event( eventType='activity', initiator=resource.Resource(id='initiator'), action='read', target=resource.Resource(typeURI='storage'), observer=resource.Resource(id='target'), outcome='success'))
def _create_cadf_payload(operation, resource_type, resource_id, outcome, initiator, reason=None): """Prepare data for CADF audit notifier. Transform the arguments into content to be consumed by the function that emits CADF events (_send_audit_notification). Specifically the ``resource_type`` (role, user, etc) must be transformed into a CADF keyword, such as: ``data/security/role``. The ``resource_id`` is added as a top level value for the ``resource_info`` key. Lastly, the ``operation`` is used to create the CADF ``action``, and the ``event_type`` name. As per the CADF specification, the ``action`` must start with create, update, delete, etc... i.e.: created.user or deleted.role However the ``event_type`` is an OpenStack-ism that is typically of the form project.resource.operation. i.e.: identity.project.updated :param operation: operation being performed (created, updated, or deleted) :param resource_type: type of resource being operated on (role, user, etc) :param resource_id: ID of resource being operated on :param outcome: outcomes of the operation (SUCCESS, FAILURE, etc) :param initiator: CADF representation of the user that created the request :param reason: pycadf object containing the response code and message description """ if resource_type not in CADF_TYPE_MAP: target_uri = taxonomy.UNKNOWN else: target_uri = CADF_TYPE_MAP.get(resource_type) target = resource.Resource(typeURI=target_uri, id=resource_id) audit_kwargs = {'resource_info': resource_id} cadf_action = '%s.%s' % (operation, resource_type) event_type = '%s.%s.%s' % (SERVICE, resource_type, operation) _send_audit_notification(cadf_action, initiator, outcome, target, event_type, reason=reason, **audit_kwargs)
def send_saml_audit_notification(action, context, user_id, group_ids, identity_provider, protocol, token_id, outcome): """Send notification to inform observers about SAML events. :param action: Action being audited :type action: str :param context: Current request context to collect request info from :type context: dict :param user_id: User ID from Keystone token :type user_id: str :param group_ids: List of Group IDs from Keystone token :type group_ids: list :param identity_provider: ID of the IdP from the Keystone token :type identity_provider: str or None :param protocol: Protocol ID for IdP from the Keystone token :type protocol: str :param token_id: audit_id from Keystone token :type token_id: str or None :param outcome: One of :class:`pycadf.cadftaxonomy` :type outcome: str """ initiator = _get_request_audit_info(context) target = resource.Resource(typeURI=taxonomy.ACCOUNT_USER) audit_type = SAML_AUDIT_TYPE user_id = user_id or taxonomy.UNKNOWN token_id = token_id or taxonomy.UNKNOWN group_ids = group_ids or [] cred = credential.FederatedCredential(token=token_id, type=audit_type, identity_provider=identity_provider, user=user_id, groups=group_ids) initiator.credential = cred event_type = '%s.%s' % (SERVICE, action) _send_audit_notification(action, initiator, outcome, target, event_type)
def _process_response(self, request, response=None): # NOTE(gordc): handle case where error processing request if 'cadf_event' not in request.environ: self._create_event(request) event = request.environ['cadf_event'] if response: if response.status_int >= 200 and response.status_int < 400: result = taxonomy.OUTCOME_SUCCESS else: result = taxonomy.OUTCOME_FAILURE event.reason = reason.Reason(reasonType='HTTP', reasonCode=str(response.status_int)) else: result = taxonomy.UNKNOWN event.outcome = result event.add_reporterstep( reporterstep.Reporterstep(role=cadftype.REPORTER_ROLE_MODIFIER, reporter=resource.Resource(id='target'), reporterTime=timestamp.get_utc_now())) self._notifier.notify(request.context, 'audit.http.response', event.as_dict())
def wrapper(wrapped_self, role_id, *args, **kwargs): """Send a notification if the wrapped callable is successful.""" """ NOTE(stevemar): The reason we go through checking kwargs and args for possible target and actor values is because the create_grant() (and delete_grant()) method are called differently in various tests. Using named arguments, i.e.: create_grant(user_id=user['id'], domain_id=domain['id'], role_id=role['id']) Or, using positional arguments, i.e.: create_grant(role_id['id'], user['id'], None, domain_id=domain['id'], None) Or, both, i.e.: create_grant(role_id['id'], user_id=user['id'], domain_id=domain['id']) Checking the values for kwargs is easy enough, since it comes in as a dictionary The actual method signature is create_grant(role_id, user_id=None, group_id=None, domain_id=None, project_id=None, inherited_to_projects=False) So, if the values of actor or target are still None after checking kwargs, we can check the positional arguments, based on the method signature. """ call_args = inspect.getcallargs(f, wrapped_self, role_id, *args, **kwargs) inherited = call_args['inherited_to_projects'] context = call_args['context'] initiator = _get_request_audit_info(context) target = resource.Resource(typeURI=taxonomy.ACCOUNT_USER) audit_kwargs = {} if call_args['project_id']: audit_kwargs['project'] = call_args['project_id'] elif call_args['domain_id']: audit_kwargs['domain'] = call_args['domain_id'] if call_args['user_id']: audit_kwargs['user'] = call_args['user_id'] elif call_args['group_id']: audit_kwargs['group'] = call_args['group_id'] audit_kwargs['inherited_to_projects'] = inherited audit_kwargs['role'] = role_id # For backward compatability, send both old and new event_type. # Deprecate old format and remove it in the next release. event_types = [self.deprecated_event_type, self.event_type] versionutils.deprecated( as_of=versionutils.deprecated.KILO, remove_in=+1, what=('sending duplicate %s notification event type' % self.deprecated_event_type), in_favor_of='%s notification event type' % self.event_type) try: result = f(wrapped_self, role_id, *args, **kwargs) except Exception: for event_type in event_types: _send_audit_notification(self.action, initiator, taxonomy.OUTCOME_FAILURE, target, event_type, **audit_kwargs) raise else: for event_type in event_types: _send_audit_notification(self.action, initiator, taxonomy.OUTCOME_SUCCESS, target, event_type, **audit_kwargs) return result
def wrapper(wrapped_self, role_id, *args, **kwargs): """Send a notification if the wrapped callable is successful. NOTE(stevemar): The reason we go through checking kwargs and args for possible target and actor values is because the create_grant() (and delete_grant()) method are called differently in various tests. Using named arguments, i.e.:: create_grant(user_id=user['id'], domain_id=domain['id'], role_id=role['id']) Or, using positional arguments, i.e.:: create_grant(role_id['id'], user['id'], None, domain_id=domain['id'], None) Or, both, i.e.:: create_grant(role_id['id'], user_id=user['id'], domain_id=domain['id']) Checking the values for kwargs is easy enough, since it comes in as a dictionary The actual method signature is :: create_grant(role_id, user_id=None, group_id=None, domain_id=None, project_id=None, inherited_to_projects=False) So, if the values of actor or target are still None after checking kwargs, we can check the positional arguments, based on the method signature. """ call_args = inspect.getcallargs(f, wrapped_self, role_id, *args, **kwargs) inherited = call_args['inherited_to_projects'] context = call_args['context'] initiator = _get_request_audit_info(context) target = resource.Resource(typeURI=taxonomy.ACCOUNT_USER) audit_kwargs = {} if call_args['project_id']: audit_kwargs['project'] = call_args['project_id'] elif call_args['domain_id']: audit_kwargs['domain'] = call_args['domain_id'] if call_args['user_id']: audit_kwargs['user'] = call_args['user_id'] elif call_args['group_id']: audit_kwargs['group'] = call_args['group_id'] audit_kwargs['inherited_to_projects'] = inherited audit_kwargs['role'] = role_id try: result = f(wrapped_self, role_id, *args, **kwargs) except Exception: _send_audit_notification(self.action, initiator, taxonomy.OUTCOME_FAILURE, target, self.event_type, **audit_kwargs) raise else: _send_audit_notification(self.action, initiator, taxonomy.OUTCOME_SUCCESS, target, self.event_type, **audit_kwargs) return result
def _send_audit_notification(action, initiator, outcome, target, event_type, reason=None, **kwargs): """Send CADF notification to inform observers about the affected resource. This method logs an exception when sending the notification fails. :param action: CADF action being audited (e.g., 'authenticate') :param initiator: CADF resource representing the initiator :param outcome: The CADF outcome (taxonomy.OUTCOME_PENDING, taxonomy.OUTCOME_SUCCESS, taxonomy.OUTCOME_FAILURE) :param target: CADF resource representing the target :param event_type: An OpenStack-ism, typically this is the meter name that Ceilometer uses to poll events. :param kwargs: Any additional arguments passed in will be added as key-value pairs to the CADF event. :param reason: Reason for the notification which contains the response code and message description """ if _check_notification_opt_out(event_type, outcome): return global _CATALOG_HELPER_OBJ if _CATALOG_HELPER_OBJ is None: _CATALOG_HELPER_OBJ = _CatalogHelperObj() service_list = _CATALOG_HELPER_OBJ.catalog_api.list_services() service_id = None for i in service_list: if i['type'] == SERVICE: service_id = i['id'] break event = eventfactory.EventFactory().new_event( eventType=cadftype.EVENTTYPE_ACTIVITY, outcome=outcome, action=action, initiator=initiator, target=target, reason=reason, observer=resource.Resource(typeURI=taxonomy.SERVICE_SECURITY)) if service_id is not None: event.observer.id = service_id for key, value in kwargs.items(): setattr(event, key, value) context = {} payload = event.as_dict() notifier = _get_notifier() if notifier: try: notifier.info(context, event_type, payload) except Exception: # diaper defense: any exception that occurs while emitting the # notification should not interfere with the API request LOG.exception( 'Failed to send %(action)s %(event_type)s notification', { 'action': action, 'event_type': event_type })
def emit_event(self, env, bytes_received, bytes_sent, outcome='success'): path = urlparse.quote(env['PATH_INFO']) method = env['REQUEST_METHOD'] headers = {} for header in env: if header.startswith('HTTP_') and env[header]: key = header[5:] if isinstance(env[header], six.text_type): headers[key] = six.text_type(env[header]) else: headers[key] = str(env[header]) try: container = obj = None version, account, remainder = path.replace('/', '', 1).split('/', 2) if not version or not account: raise ValueError('Invalid path: %s' % path) if remainder: if '/' in remainder: container, obj = remainder.split('/', 1) else: container = remainder except ValueError: return now = timeutils.utcnow().isoformat() resource_metadata = { "path": path, "version": version, "container": container, "object": obj, } for header in self.metadata_headers: if header.upper() in headers: resource_metadata['http_header_%s' % header] = headers.get( header.upper()) # build object store details target = cadf_resource.Resource(typeURI='service/storage/object', id=account.partition( self.reseller_prefix)[2]) target.metadata = resource_metadata target.action = method.lower() # build user details initiator = cadf_resource.Resource( typeURI='service/security/account/user', id=env.get('HTTP_X_USER_ID')) initiator.project_id = env.get('HTTP_X_TENANT_ID') # build notification body event = cadf_event.Event(eventTime=now, outcome=outcome, initiator=initiator, target=target, observer=cadf_resource.Resource(id='target')) # measurements if bytes_received: event.add_measurement( cadf_measurement.Measurement( result=bytes_received, metric=cadf_metric.Metric( name='storage.objects.incoming.bytes', unit='B'))) if bytes_sent: event.add_measurement( cadf_measurement.Measurement( result=bytes_sent, metric=cadf_metric.Metric( name='storage.objects.outgoing.bytes', unit='B'))) self._notifier.info(context.get_admin_context().to_dict(), 'objectstore.http.request', event.as_dict())
def test_event(self): ev = event.Event(eventType='activity', id=identifier.generate_uuid(), eventTime=timestamp.get_utc_now(), initiator=resource.Resource(typeURI='storage'), initiatorId=identifier.generate_uuid(), action='read', target=resource.Resource(typeURI='storage'), targetId=identifier.generate_uuid(), observer=resource.Resource(id='target'), observerId=identifier.generate_uuid(), outcome='success', reason=reason.Reason(reasonType='HTTP', reasonCode='200'), severity='high') ev.add_measurement( measurement.Measurement(result='100', metricId=identifier.generate_uuid())), ev.add_tag(tag.generate_name_value_tag('name', 'val')) ev.add_attachment(attachment.Attachment(typeURI='attachURI', content='content', name='attachment_name')) ev.observer = resource.Resource(typeURI='service/security') ev.add_reporterstep(reporterstep.Reporterstep( role='observer', reporter=resource.Resource(typeURI='service/security'))) ev.add_reporterstep(reporterstep.Reporterstep( reporterId=identifier.generate_uuid())) self.assertEqual(ev.is_valid(), False) dict_ev = ev.as_dict() for key in event.EVENT_KEYNAMES: self.assertIn(key, dict_ev) ev = event.Event(eventType='activity', id=identifier.generate_uuid(), eventTime=timestamp.get_utc_now(), initiator=resource.Resource(typeURI='storage'), action='read', target=resource.Resource(typeURI='storage'), observer=resource.Resource(id='target'), outcome='success') self.assertEqual(ev.is_valid(), True) ev = event.Event(eventType='activity', id=identifier.generate_uuid(), eventTime=timestamp.get_utc_now(), initiatorId=identifier.generate_uuid(), action='read', targetId=identifier.generate_uuid(), observerId=identifier.generate_uuid(), outcome='success') self.assertEqual(ev.is_valid(), True) ev = event.Event(eventType='activity', id=identifier.generate_uuid(), eventTime=timestamp.get_utc_now(), initiator=resource.Resource(typeURI='storage'), action='read', targetId=identifier.generate_uuid(), observer=resource.Resource(id='target'), outcome='success') self.assertEqual(ev.is_valid(), True)
def emit_event(self, env, bytes_received, bytes_sent, outcome='success'): if ((env.get('HTTP_X_SERVICE_PROJECT_ID') or env.get('HTTP_X_PROJECT_ID') or env.get('HTTP_X_TENANT_ID')) in self.ignore_projects or env.get('swift.source') is not None): return path = urlparse.quote(env['PATH_INFO']) method = env['REQUEST_METHOD'] headers = {} for header in env: if header.startswith('HTTP_') and env[header]: key = header[5:] if isinstance(env[header], six.text_type): headers[key] = six.text_type(env[header]) else: headers[key] = str(env[header]) try: container = obj = None path = path.replace('/', '', 1) version, account, remainder = path.split('/', 2) except ValueError: try: version, account = path.split('/', 1) remainder = None except ValueError: return try: if not version or not account: raise ValueError('Invalid path: %s' % path) if remainder: if '/' in remainder: container, obj = remainder.split('/', 1) else: container = remainder except ValueError: return now = datetime.datetime.utcnow().isoformat() resource_metadata = { "path": path, "version": version, "container": container, "object": obj, } for header in self.metadata_headers: if header.upper() in headers: resource_metadata['http_header_%s' % header] = headers.get( header.upper()) # build object store details if self.reseller_prefix: target = cadf_resource.Resource( typeURI='service/storage/object', id=account.partition(self.reseller_prefix)[2] or path) else: target = cadf_resource.Resource( typeURI='service/storage/object', id=account) target.metadata = resource_metadata target.action = method.lower() # build user details initiator = cadf_resource.Resource( typeURI='service/security/account/user', id=env.get('HTTP_X_USER_ID')) initiator.project_id = (env.get('HTTP_X_PROJECT_ID') or env.get('HTTP_X_TENANT_ID')) # build notification body event = cadf_event.Event(eventTime=now, outcome=outcome, action=api.convert_req_action(method), initiator=initiator, target=target, observer=cadf_resource.Resource(id='target')) # measurements if bytes_received: event.add_measurement(cadf_measurement.Measurement( result=bytes_received, metric=cadf_metric.Metric( name='storage.objects.incoming.bytes', unit='B'))) if bytes_sent: event.add_measurement(cadf_measurement.Measurement( result=bytes_sent, metric=cadf_metric.Metric( name='storage.objects.outgoing.bytes', unit='B'))) # api call request_metric_name = self.get_request_metric_name(method.lower()) if request_metric_name: event.add_measurement(cadf_measurement.Measurement( result=1, metric=cadf_metric.Metric( name=request_metric_name, unit='request'))) if self.nonblocking_notify: try: Swift.event_queue.put(event, False) if not Swift.event_sender.is_alive(): Swift.threadLock.acquire() self.start_sender_thread() Swift.threadLock.release() except queue.Full: LOG.warning('Send queue FULL: Event %s not added', event.id) else: Swift.send_notification(self._notifier, event)
def _policy_resource(ra_name): return resource.Resource(id='anchor://certificates/policy', typeURI=cadftaxonomy.SECURITY_POLICY, domain=ra_name)
def _auth_resource(ra_name): return resource.Resource(id='anchor://authentication', typeURI=cadftaxonomy.SERVICE_SECURITY, domain=ra_name)
def create_event(self, req, correlation_id): action = self._get_action(req) initiator_host = host.Host(address=req.client_addr, agent=req.user_agent) catalog = ast.literal_eval(req.environ['HTTP_X_SERVICE_CATALOG']) service_info = self.Service(type=taxonomy.UNKNOWN, name=taxonomy.UNKNOWN, id=taxonomy.UNKNOWN, admin_endp=None, private_endp=None, public_endp=None) default_endpoint = None for endp in catalog: admin_urlparse = urlparse.urlparse( endp['endpoints'][0]['adminURL']) public_urlparse = urlparse.urlparse( endp['endpoints'][0]['publicURL']) req_url = urlparse.urlparse(req.host_url) if (req_url.netloc == admin_urlparse.netloc or req_url.netloc == public_urlparse.netloc): service_info = self._get_service_info(endp) break elif (self._MAP.default_target_endpoint_type and endp['type'] == self._MAP.default_target_endpoint_type): default_endpoint = endp else: if default_endpoint: service_info = self._get_service_info(default_endpoint) initiator = ClientResource( typeURI=taxonomy.ACCOUNT_USER, id=identifier.norm_ns(str(req.environ['HTTP_X_USER_ID'])), name=req.environ['HTTP_X_USER_NAME'], host=initiator_host, credential=KeystoneCredential( token=req.environ['HTTP_X_AUTH_TOKEN'], identity_status=req.environ['HTTP_X_IDENTITY_STATUS']), project_id=identifier.norm_ns(req.environ['HTTP_X_PROJECT_ID'])) target_typeURI = (self._build_typeURI(req, service_info.type) if service_info.type != taxonomy.UNKNOWN else service_info.type) target = resource.Resource(typeURI=target_typeURI, id=service_info.id, name=service_info.name) if service_info.admin_endp: target.add_address(service_info.admin_endp) if service_info.private_endp: target.add_address(service_info.private_endp) if service_info.public_endp: target.add_address(service_info.public_endp) event = factory.EventFactory().new_event( eventType=cadftype.EVENTTYPE_ACTIVITY, outcome=taxonomy.OUTCOME_PENDING, action=action, initiator=initiator, target=target, observer=resource.Resource(id='target')) event.requestPath = req.path_qs event.add_tag( tag.generate_name_value_tag('correlation_id', correlation_id)) return event