import abc from collections import namedtuple import typing import boto3 from celery import shared_task import structlog from atlas_um.consts import SystemEvents from atlas_um.settings import Settings from . import schemas class BaseAuditProcessor(abc.ABC): """ Interface for audit log processors. Processors are observers that intended to handle audit events in any needed way, e.g. storage backends, message busses etc. """ @abc.abstractmethod def process(self, event: SystemEvents, context: typing.Mapping, **kwargs): pass class StructlogProcessor(BaseAuditProcessor): """ Audit processor for writing events into system log via Structlog. """ def __init__(self, logger_name): self._logger = structlog.get_logger(logger_name) def process(self, event: SystemEvents, context: typing.Mapping, **kwargs): self._logger.bind(**context, **kwargs).info(event.value) BusConf = namedtuple("BusConf", ("detail_type", "detail_schema")) class EventBridgeAsyncProcessor(BaseAuditProcessor): """ Audit processor for pushing needed events to Amazon EventBridge. """ EVENTS_SETTINGS = { SystemEvents.creating_dna_account: BusConf( "dna_account.create", schemas.DNAAccountDetail ), SystemEvents.updating_dna_account: BusConf( "dna_account.update", schemas.DNAAccountDetail ), SystemEvents.updating_dna_account_claims: BusConf( "dna_account.update", schemas.DNAAccountDetail ), SystemEvents.disabling_dna_account_claims: BusConf( "dna_account.update", schemas.DNAAccountDetail ), SystemEvents.sending_dna_invitation: BusConf( "dna_account.update", schemas.DNAAccountDetail ), SystemEvents.suspending_dna_account: BusConf( "dna_account.delete", schemas.DNAAccountDetail ), } def process(self, event: SystemEvents, context: typing.Mapping, **kwargs): event_settings = self.EVENTS_SETTINGS.get(event) if not event_settings: return event_data = { "Source": Settings.EVENT_BUS_SOURCE, "EventBusName": Settings.EVENT_BUS_NAME, "DetailType": event_settings.detail_type, "Detail": event_settings.detail_schema().dumps(kwargs), } self.send_to_event_bridge.delay(event_data) @staticmethod @shared_task def send_to_event_bridge(event_data): client = boto3.client( "events", region_name=Settings.AWS_DEFAULT_REGION ) client.put_events(Entries=[event_data])