def __init__(self, sensor_wrapper): self._sensor_wrapper = sensor_wrapper self._logger = self._sensor_wrapper._logger self._trigger_dispatcher_service = TriggerDispatcherService(logger=sensor_wrapper._logger) self._datastore_service = SensorDatastoreService( logger=self._logger, pack_name=self._sensor_wrapper._pack, class_name=self._sensor_wrapper._class_name, api_username='******') self._client = None
def __init__(self, *args, **kwargs): self._hooks = HooksHolder() self._base_url = '/webhooks/' self._trigger_types = list(WEBHOOK_TRIGGER_TYPES.keys()) self._trigger_dispatcher_service = TriggerDispatcherService(LOG) queue_suffix = self.__class__.__name__ self._trigger_watcher = TriggerWatcher( create_handler=self._handle_create_trigger, update_handler=self._handle_update_trigger, delete_handler=self._handle_delete_trigger, trigger_types=self._trigger_types, queue_suffix=queue_suffix, exclusive=True) self._trigger_watcher.start() self._register_webhook_trigger_types()
def __init__(self, sensor_wrapper): self._sensor_wrapper = sensor_wrapper self._logger = self._sensor_wrapper._logger self._trigger_dispatcher_service = TriggerDispatcherService(logger=sensor_wrapper._logger) self._datastore_service = SensorDatastoreService( logger=self._logger, pack_name=self._sensor_wrapper._pack, class_name=self._sensor_wrapper._class_name, api_username='******') self._client = None
def __init__(self, *args, **kwargs): self._hooks = HooksHolder() self._base_url = '/webhooks/' self._trigger_types = list(WEBHOOK_TRIGGER_TYPES.keys()) self._trigger_dispatcher_service = TriggerDispatcherService(LOG) queue_suffix = self.__class__.__name__ self._trigger_watcher = TriggerWatcher(create_handler=self._handle_create_trigger, update_handler=self._handle_update_trigger, delete_handler=self._handle_delete_trigger, trigger_types=self._trigger_types, queue_suffix=queue_suffix, exclusive=True) self._trigger_watcher.start() self._register_webhook_trigger_types()
class WebhooksController(object): def __init__(self, *args, **kwargs): self._hooks = HooksHolder() self._base_url = '/webhooks/' self._trigger_types = list(WEBHOOK_TRIGGER_TYPES.keys()) self._trigger_dispatcher_service = TriggerDispatcherService(LOG) queue_suffix = self.__class__.__name__ self._trigger_watcher = TriggerWatcher( create_handler=self._handle_create_trigger, update_handler=self._handle_update_trigger, delete_handler=self._handle_delete_trigger, trigger_types=self._trigger_types, queue_suffix=queue_suffix, exclusive=True) self._trigger_watcher.start() self._register_webhook_trigger_types() def get_all(self): # Return only the hooks known by this controller. return self._hooks.get_all() def get_one(self, url, requester_user): triggers = self._hooks.get_triggers_for_hook(url) if not triggers: abort(http_client.NOT_FOUND) return permission_type = PermissionType.WEBHOOK_VIEW rbac_utils = get_rbac_backend().get_utils_class() rbac_utils.assert_user_has_resource_db_permission( user_db=requester_user, resource_db=WebhookDB(name=url), permission_type=permission_type) # For demonstration purpose return 1st return triggers[0] def post(self, hook, webhook_body_api, headers, requester_user): body = webhook_body_api.data permission_type = PermissionType.WEBHOOK_SEND rbac_utils = get_rbac_backend().get_utils_class() rbac_utils.assert_user_has_resource_db_permission( user_db=requester_user, resource_db=WebhookDB(name=hook), permission_type=permission_type) headers = self._get_headers_as_dict(headers) headers = self._filter_authentication_headers(headers) # If webhook contains a trace-tag use that else create create a unique trace-tag. trace_context = self._create_trace_context(trace_tag=headers.pop( TRACE_TAG_HEADER, None), hook=hook) if hook == 'st2' or hook == 'st2/': # When using st2 or system webhook, body needs to always be a dict if not isinstance(body, dict): type_string = get_json_type_for_python_value(body) msg = ('Webhook body needs to be an object, got: %s' % (type_string)) raise ValueError(msg) trigger = body.get('trigger', None) payload = body.get('payload', None) if not trigger: msg = 'Trigger not specified.' return abort(http_client.BAD_REQUEST, msg) self._trigger_dispatcher_service.dispatch_with_context( trigger=trigger, payload=payload, trace_context=trace_context, throw_on_validation_error=True) else: if not self._is_valid_hook(hook): self._log_request('Invalid hook.', headers, body) msg = 'Webhook %s not registered with st2' % hook return abort(http_client.NOT_FOUND, msg) triggers = self._hooks.get_triggers_for_hook(hook) payload = {} payload['headers'] = headers payload['body'] = body # Dispatch trigger instance for each of the trigger found for trigger_dict in triggers: # TODO: Instead of dispatching the whole dict we should just # dispatch TriggerDB.ref or similar self._trigger_dispatcher_service.dispatch_with_context( trigger=trigger_dict, payload=payload, trace_context=trace_context, throw_on_validation_error=True) return Response(json=body, status=http_client.ACCEPTED) def _is_valid_hook(self, hook): # TODO: Validate hook payload with payload_schema. return hook in self._hooks def _register_webhook_trigger_types(self): for trigger_type in WEBHOOK_TRIGGER_TYPES.values(): trigger_service.create_trigger_type_db(trigger_type) def _create_trace_context(self, trace_tag, hook): # if no trace_tag then create a unique one if not trace_tag: trace_tag = 'webhook-%s-%s' % (hook, uuid.uuid4().hex) return TraceContext(trace_tag=trace_tag) def add_trigger(self, trigger): # NOTE: trigger is a dictionary # Note: Permission checking for creating and deleting a webhook is done during rule # creation url = self._get_normalized_url(trigger) LOG.info('Listening to endpoint: %s', urljoin(self._base_url, url)) self._hooks.add_hook(url, trigger) def update_trigger(self, trigger): pass def remove_trigger(self, trigger): # Note: Permission checking for creating and deleting a webhook is done during rule # creation url = self._get_normalized_url(trigger) removed = self._hooks.remove_hook(url, trigger) if removed: LOG.info('Stop listening to endpoint: %s', urljoin(self._base_url, url)) def _get_normalized_url(self, trigger): """ remove the trailing and leading / so that the hook url and those coming from trigger parameters end up being the same. """ return trigger['parameters']['url'].strip('/') def _get_headers_as_dict(self, headers): headers_dict = {} for key, value in headers.items(): headers_dict[key] = value return headers_dict def _filter_authentication_headers(self, headers): auth_headers = [ HEADER_API_KEY_ATTRIBUTE_NAME, HEADER_ATTRIBUTE_NAME, 'Cookie' ] return { key: value for key, value in headers.items() if key not in auth_headers } def _log_request(self, msg, headers, body, log_method=LOG.debug): headers = self._get_headers_as_dict(headers) body = str(body) log_method('%s\n\trequest.header: %s.\n\trequest.body: %s.', msg, headers, body) ############################################## # Event handler methods for the trigger events ############################################## def _handle_create_trigger(self, trigger): LOG.debug('Calling "add_trigger" method (trigger.type=%s)' % (trigger.type)) trigger = self._sanitize_trigger(trigger=trigger) self.add_trigger(trigger=trigger) def _handle_update_trigger(self, trigger): LOG.debug('Calling "update_trigger" method (trigger.type=%s)' % (trigger.type)) trigger = self._sanitize_trigger(trigger=trigger) self.update_trigger(trigger=trigger) def _handle_delete_trigger(self, trigger): LOG.debug('Calling "remove_trigger" method (trigger.type=%s)' % (trigger.type)) trigger = self._sanitize_trigger(trigger=trigger) self.remove_trigger(trigger=trigger) def _sanitize_trigger(self, trigger): sanitized = TriggerAPI.from_model(trigger).to_dict() return sanitized
class WebhooksController(object): def __init__(self, *args, **kwargs): self._hooks = HooksHolder() self._base_url = '/webhooks/' self._trigger_types = list(WEBHOOK_TRIGGER_TYPES.keys()) self._trigger_dispatcher_service = TriggerDispatcherService(LOG) queue_suffix = self.__class__.__name__ self._trigger_watcher = TriggerWatcher(create_handler=self._handle_create_trigger, update_handler=self._handle_update_trigger, delete_handler=self._handle_delete_trigger, trigger_types=self._trigger_types, queue_suffix=queue_suffix, exclusive=True) self._trigger_watcher.start() self._register_webhook_trigger_types() def get_all(self): # Return only the hooks known by this controller. return self._hooks.get_all() def get_one(self, url, requester_user): triggers = self._hooks.get_triggers_for_hook(url) if not triggers: abort(http_client.NOT_FOUND) return permission_type = PermissionType.WEBHOOK_VIEW rbac_utils.assert_user_has_resource_db_permission(user_db=requester_user, resource_db=WebhookDB(name=url), permission_type=permission_type) # For demonstration purpose return 1st return triggers[0] def post(self, hook, webhook_body_api, headers, requester_user): body = webhook_body_api.data permission_type = PermissionType.WEBHOOK_SEND rbac_utils.assert_user_has_resource_db_permission(user_db=requester_user, resource_db=WebhookDB(name=hook), permission_type=permission_type) headers = self._get_headers_as_dict(headers) # If webhook contains a trace-tag use that else create create a unique trace-tag. trace_context = self._create_trace_context(trace_tag=headers.pop(TRACE_TAG_HEADER, None), hook=hook) if hook == 'st2' or hook == 'st2/': # When using st2 or system webhook, body needs to always be a dict if not isinstance(body, dict): type_string = get_json_type_for_python_value(body) msg = ('Webhook body needs to be an object, got: %s' % (type_string)) raise ValueError(msg) trigger = body.get('trigger', None) payload = body.get('payload', None) if not trigger: msg = 'Trigger not specified.' return abort(http_client.BAD_REQUEST, msg) self._trigger_dispatcher_service.dispatch_with_context(trigger=trigger, payload=payload, trace_context=trace_context, throw_on_validation_error=True) else: if not self._is_valid_hook(hook): self._log_request('Invalid hook.', headers, body) msg = 'Webhook %s not registered with st2' % hook return abort(http_client.NOT_FOUND, msg) triggers = self._hooks.get_triggers_for_hook(hook) payload = {} payload['headers'] = headers payload['body'] = body # Dispatch trigger instance for each of the trigger found for trigger_dict in triggers: # TODO: Instead of dispatching the whole dict we should just # dispatch TriggerDB.ref or similar self._trigger_dispatcher_service.dispatch_with_context(trigger=trigger_dict, payload=payload, trace_context=trace_context, throw_on_validation_error=True) return Response(json=body, status=http_client.ACCEPTED) def _is_valid_hook(self, hook): # TODO: Validate hook payload with payload_schema. return hook in self._hooks def _register_webhook_trigger_types(self): for trigger_type in WEBHOOK_TRIGGER_TYPES.values(): trigger_service.create_trigger_type_db(trigger_type) def _create_trace_context(self, trace_tag, hook): # if no trace_tag then create a unique one if not trace_tag: trace_tag = 'webhook-%s-%s' % (hook, uuid.uuid4().hex) return TraceContext(trace_tag=trace_tag) def add_trigger(self, trigger): # NOTE: trigger is a dictionary # Note: Permission checking for creating and deleting a webhook is done during rule # creation url = self._get_normalized_url(trigger) LOG.info('Listening to endpoint: %s', urljoin(self._base_url, url)) self._hooks.add_hook(url, trigger) def update_trigger(self, trigger): pass def remove_trigger(self, trigger): # Note: Permission checking for creating and deleting a webhook is done during rule # creation url = self._get_normalized_url(trigger) removed = self._hooks.remove_hook(url, trigger) if removed: LOG.info('Stop listening to endpoint: %s', urljoin(self._base_url, url)) def _get_normalized_url(self, trigger): """ remove the trailing and leading / so that the hook url and those coming from trigger parameters end up being the same. """ return trigger['parameters']['url'].strip('/') def _get_headers_as_dict(self, headers): headers_dict = {} for key, value in headers.items(): headers_dict[key] = value return headers_dict def _log_request(self, msg, headers, body, log_method=LOG.debug): headers = self._get_headers_as_dict(headers) body = str(body) log_method('%s\n\trequest.header: %s.\n\trequest.body: %s.', msg, headers, body) ############################################## # Event handler methods for the trigger events ############################################## def _handle_create_trigger(self, trigger): LOG.debug('Calling "add_trigger" method (trigger.type=%s)' % (trigger.type)) trigger = self._sanitize_trigger(trigger=trigger) self.add_trigger(trigger=trigger) def _handle_update_trigger(self, trigger): LOG.debug('Calling "update_trigger" method (trigger.type=%s)' % (trigger.type)) trigger = self._sanitize_trigger(trigger=trigger) self.update_trigger(trigger=trigger) def _handle_delete_trigger(self, trigger): LOG.debug('Calling "remove_trigger" method (trigger.type=%s)' % (trigger.type)) trigger = self._sanitize_trigger(trigger=trigger) self.remove_trigger(trigger=trigger) def _sanitize_trigger(self, trigger): sanitized = TriggerAPI.from_model(trigger).to_dict() return sanitized
class SensorService(object): """ Instance of this class is passed to the sensor instance and exposes "public" methods which can be called by the sensor. """ def __init__(self, sensor_wrapper): self._sensor_wrapper = sensor_wrapper self._logger = self._sensor_wrapper._logger self._trigger_dispatcher_service = TriggerDispatcherService( logger=sensor_wrapper._logger) self._datastore_service = SensorDatastoreService( logger=self._logger, pack_name=self._sensor_wrapper._pack, class_name=self._sensor_wrapper._class_name, api_username='******') self._client = None @property def datastore_service(self): return self._datastore_service def get_logger(self, name): """ Retrieve an instance of a logger to be used by the sensor class. """ logger_name = '%s.%s' % (self._sensor_wrapper._logger.name, name) logger = logging.getLogger(logger_name) logger.propagate = True return logger ################################## # General methods ################################## def get_user_info(self): return self._datastore_service.get_user_info() ################################## # Sensor related methods ################################## def dispatch(self, trigger, payload=None, trace_tag=None): # Provided by the parent BaseTriggerDispatcherService class return self._trigger_dispatcher_service.dispatch( trigger=trigger, payload=payload, trace_tag=trace_tag, throw_on_validation_error=False) def dispatch_with_context(self, trigger, payload=None, trace_context=None): """ Method which dispatches the trigger. :param trigger: Full name / reference of the trigger. :type trigger: ``str`` :param payload: Trigger payload. :type payload: ``dict`` :param trace_context: Trace context to associate with Trigger. :type trace_context: ``st2common.api.models.api.trace.TraceContext`` """ # Provided by the parent BaseTriggerDispatcherService class return self._trigger_dispatcher_service.dispatch_with_context( trigger=trigger, payload=payload, trace_context=trace_context, throw_on_validation_error=False) ################################## # Methods for datastore management ################################## def list_values(self, local=True, prefix=None): return self.datastore_service.list_values(local=local, prefix=prefix) def get_value(self, name, local=True, scope=SYSTEM_SCOPE, decrypt=False): return self.datastore_service.get_value(name=name, local=local, scope=scope, decrypt=decrypt) def set_value(self, name, value, ttl=None, local=True, scope=SYSTEM_SCOPE, encrypt=False): return self.datastore_service.set_value(name=name, value=value, ttl=ttl, local=local, scope=scope, encrypt=encrypt) def delete_value(self, name, local=True, scope=SYSTEM_SCOPE): return self.datastore_service.delete_value(name=name, local=local, scope=scope)
class SensorService(object): """ Instance of this class is passed to the sensor instance and exposes "public" methods which can be called by the sensor. """ def __init__(self, sensor_wrapper): self._sensor_wrapper = sensor_wrapper self._logger = self._sensor_wrapper._logger self._trigger_dispatcher_service = TriggerDispatcherService(logger=sensor_wrapper._logger) self._datastore_service = SensorDatastoreService( logger=self._logger, pack_name=self._sensor_wrapper._pack, class_name=self._sensor_wrapper._class_name, api_username='******') self._client = None @property def datastore_service(self): return self._datastore_service def get_logger(self, name): """ Retrieve an instance of a logger to be used by the sensor class. """ logger_name = '%s.%s' % (self._sensor_wrapper._logger.name, name) logger = logging.getLogger(logger_name) logger.propagate = True return logger ################################## # General methods ################################## def get_user_info(self): return self._datastore_service.get_user_info() ################################## # Sensor related methods ################################## def dispatch(self, trigger, payload=None, trace_tag=None): # Provided by the parent BaseTriggerDispatcherService class return self._trigger_dispatcher_service.dispatch(trigger=trigger, payload=payload, trace_tag=trace_tag, throw_on_validation_error=False) def dispatch_with_context(self, trigger, payload=None, trace_context=None): """ Method which dispatches the trigger. :param trigger: Full name / reference of the trigger. :type trigger: ``str`` :param payload: Trigger payload. :type payload: ``dict`` :param trace_context: Trace context to associate with Trigger. :type trace_context: ``st2common.api.models.api.trace.TraceContext`` """ # Provided by the parent BaseTriggerDispatcherService class return self._trigger_dispatcher_service.dispatch_with_context(trigger=trigger, payload=payload, trace_context=trace_context, throw_on_validation_error=False) ################################## # Methods for datastore management ################################## def list_values(self, local=True, prefix=None): return self.datastore_service.list_values(local=local, prefix=prefix) def get_value(self, name, local=True, scope=SYSTEM_SCOPE, decrypt=False): return self.datastore_service.get_value(name=name, local=local, scope=scope, decrypt=decrypt) def set_value(self, name, value, ttl=None, local=True, scope=SYSTEM_SCOPE, encrypt=False): return self.datastore_service.set_value(name=name, value=value, ttl=ttl, local=local, scope=scope, encrypt=encrypt) def delete_value(self, name, local=True, scope=SYSTEM_SCOPE): return self.datastore_service.delete_value(name=name, local=local, scope=scope)