 aeb1b450eb
			
		
	
	aeb1b450eb
	
	
	
		
			
			* providers/google: initial account sync to google workspace Signed-off-by: Jens Langhammer <jens@goauthentik.io> * start separating scim sync client Signed-off-by: Jens Langhammer <jens@goauthentik.io> * generalize more...ish Signed-off-by: Jens Langhammer <jens@goauthentik.io> * set dispatch_uid Signed-off-by: Jens Langhammer <jens@goauthentik.io> * start generalizing task Signed-off-by: Jens Langhammer <jens@goauthentik.io> * fully separate tasks Signed-off-by: Jens Langhammer <jens@goauthentik.io> * fix more Signed-off-by: Jens Langhammer <jens@goauthentik.io> * fix signals...? Signed-off-by: Jens Langhammer <jens@goauthentik.io> * start google dedupe Signed-off-by: Jens Langhammer <jens@goauthentik.io> * drawing the rest of the owl Signed-off-by: Jens Langhammer <jens@goauthentik.io> * more Signed-off-by: Jens Langhammer <jens@goauthentik.io> * juse use a whole lot less magic Signed-off-by: Jens Langhammer <jens@goauthentik.io> * member sync, better implement conflict/retry-able exceptions Signed-off-by: Jens Langhammer <jens@goauthentik.io> * max wizards taller Signed-off-by: Jens Langhammer <jens@goauthentik.io> * gen api, basic UI Signed-off-by: Jens Langhammer <jens@goauthentik.io> * fix some bugs Signed-off-by: Jens Langhammer <jens@goauthentik.io> * fix a bunch more bugs Signed-off-by: Jens Langhammer <jens@goauthentik.io> * generalize sync status API Signed-off-by: Jens Langhammer <jens@goauthentik.io> * rework sync chart Signed-off-by: Jens Langhammer <jens@goauthentik.io> * add slugify to evaluator Signed-off-by: Jens Langhammer <jens@goauthentik.io> * add test property mappings Signed-off-by: Jens Langhammer <jens@goauthentik.io> * rename to google workspace Signed-off-by: Jens Langhammer <jens@goauthentik.io> * handle existing objects Signed-off-by: Jens Langhammer <jens@goauthentik.io> * fix credential render Signed-off-by: Jens Langhammer <jens@goauthentik.io> * verify email has correct domain before syncing user Signed-off-by: Jens Langhammer <jens@goauthentik.io> * fix missing docstring Signed-off-by: Jens Langhammer <jens@goauthentik.io> * fix lock not being used Signed-off-by: Jens Langhammer <jens@goauthentik.io> * abstract more common stuff away Signed-off-by: Jens Langhammer <jens@goauthentik.io> * backport time limit fix https://github.com/goauthentik/authentik/pull/9546 Signed-off-by: Jens Langhammer <jens@goauthentik.io> * start discovery Signed-off-by: Jens Langhammer <jens@goauthentik.io> * implement discover for google Signed-off-by: Jens Langhammer <jens@goauthentik.io> * prevent same issue as with https://github.com/goauthentik/authentik/pull/9557 Signed-off-by: Jens Langhammer <jens@goauthentik.io> * fix sync status Signed-off-by: Jens Langhammer <jens@goauthentik.io> * make group name unique in API Signed-off-by: Jens Langhammer <jens@goauthentik.io> * fix reference to old wrapper Signed-off-by: Jens Langhammer <jens@goauthentik.io> * start adding tests man this api client is awful Signed-off-by: Jens Langhammer <jens@goauthentik.io> * add SkipObject Signed-off-by: Jens Langhammer <jens@goauthentik.io> * dont use weak ref Signed-off-by: Jens Langhammer <jens@goauthentik.io> * add group tests Signed-off-by: Jens Langhammer <jens@goauthentik.io> * add user and group delete options Signed-off-by: Jens Langhammer <jens@goauthentik.io> * set user agent Signed-off-by: Jens Langhammer <jens@goauthentik.io> * if the api's testing tools are awful, let's just make our own Signed-off-by: Jens Langhammer <jens@goauthentik.io> * add more tests and already fix some more bugs Signed-off-by: Jens Langhammer <jens@goauthentik.io> * add discover Signed-off-by: Jens Langhammer <jens@goauthentik.io> * add preview banner Signed-off-by: Jens Langhammer <jens@goauthentik.io> * add group import test Signed-off-by: Jens Langhammer <jens@goauthentik.io> * only import users/groups in the correct parent group Signed-off-by: Jens Langhammer <jens@goauthentik.io> * fix conflicting args Signed-off-by: Jens Langhammer <jens@goauthentik.io> * fix missing schedule Signed-off-by: Jens Langhammer <jens@goauthentik.io> * fix web ui Signed-off-by: Jens Langhammer <jens@goauthentik.io> * add default_group_email_domain Signed-off-by: Jens Langhammer <jens@goauthentik.io> --------- Signed-off-by: Jens Langhammer <jens@goauthentik.io>
		
			
				
	
	
		
			145 lines
		
	
	
		
			5.1 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			145 lines
		
	
	
		
			5.1 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| """Event notification tasks"""
 | |
| 
 | |
| from django.db.models.query_utils import Q
 | |
| from guardian.shortcuts import get_anonymous_user
 | |
| from structlog.stdlib import get_logger
 | |
| 
 | |
| from authentik.core.expression.exceptions import PropertyMappingExpressionException
 | |
| from authentik.core.models import User
 | |
| from authentik.events.models import (
 | |
|     Event,
 | |
|     Notification,
 | |
|     NotificationRule,
 | |
|     NotificationTransport,
 | |
|     NotificationTransportError,
 | |
|     TaskStatus,
 | |
| )
 | |
| from authentik.events.system_tasks import SystemTask, prefill_task
 | |
| from authentik.policies.engine import PolicyEngine
 | |
| from authentik.policies.models import PolicyBinding, PolicyEngineMode
 | |
| from authentik.root.celery import CELERY_APP
 | |
| 
 | |
| LOGGER = get_logger()
 | |
| 
 | |
| 
 | |
| @CELERY_APP.task()
 | |
| def event_notification_handler(event_uuid: str):
 | |
|     """Start task for each trigger definition"""
 | |
|     for trigger in NotificationRule.objects.all():
 | |
|         event_trigger_handler.apply_async(args=[event_uuid, trigger.name], queue="authentik_events")
 | |
| 
 | |
| 
 | |
| @CELERY_APP.task()
 | |
| def event_trigger_handler(event_uuid: str, trigger_name: str):
 | |
|     """Check if policies attached to NotificationRule match event"""
 | |
|     event: Event = Event.objects.filter(event_uuid=event_uuid).first()
 | |
|     if not event:
 | |
|         LOGGER.warning("event doesn't exist yet or anymore", event_uuid=event_uuid)
 | |
|         return
 | |
|     trigger: NotificationRule | None = NotificationRule.objects.filter(name=trigger_name).first()
 | |
|     if not trigger:
 | |
|         return
 | |
| 
 | |
|     if "policy_uuid" in event.context:
 | |
|         policy_uuid = event.context["policy_uuid"]
 | |
|         if PolicyBinding.objects.filter(
 | |
|             target__in=NotificationRule.objects.all().values_list("pbm_uuid", flat=True),
 | |
|             policy=policy_uuid,
 | |
|         ).exists():
 | |
|             # If policy that caused this event to be created is attached
 | |
|             # to *any* NotificationRule, we return early.
 | |
|             # This is the most effective way to prevent infinite loops.
 | |
|             LOGGER.debug("e(trigger): attempting to prevent infinite loop", trigger=trigger)
 | |
|             return
 | |
| 
 | |
|     LOGGER.debug("e(trigger): checking if trigger applies", trigger=trigger)
 | |
|     try:
 | |
|         user = User.objects.filter(pk=event.user.get("pk")).first() or get_anonymous_user()
 | |
|     except User.DoesNotExist:
 | |
|         LOGGER.warning("e(trigger): failed to get user", trigger=trigger)
 | |
|         return
 | |
|     policy_engine = PolicyEngine(trigger, user)
 | |
|     policy_engine.mode = PolicyEngineMode.MODE_ANY
 | |
|     policy_engine.empty_result = False
 | |
|     policy_engine.use_cache = False
 | |
|     policy_engine.request.context["event"] = event
 | |
|     policy_engine.build()
 | |
|     result = policy_engine.result
 | |
|     if not result.passing:
 | |
|         return
 | |
| 
 | |
|     if not trigger.group:
 | |
|         LOGGER.debug("e(trigger): trigger has no group", trigger=trigger)
 | |
|         return
 | |
| 
 | |
|     LOGGER.debug("e(trigger): event trigger matched", trigger=trigger)
 | |
|     # Create the notification objects
 | |
|     for transport in trigger.transports.all():
 | |
|         for user in trigger.group.users.all():
 | |
|             LOGGER.debug("created notification")
 | |
|             notification_transport.apply_async(
 | |
|                 args=[
 | |
|                     transport.pk,
 | |
|                     str(event.pk),
 | |
|                     user.pk,
 | |
|                     str(trigger.pk),
 | |
|                 ],
 | |
|                 queue="authentik_events",
 | |
|             )
 | |
|             if transport.send_once:
 | |
|                 break
 | |
| 
 | |
| 
 | |
| @CELERY_APP.task(
 | |
|     bind=True,
 | |
|     autoretry_for=(NotificationTransportError,),
 | |
|     retry_backoff=True,
 | |
|     base=SystemTask,
 | |
| )
 | |
| def notification_transport(
 | |
|     self: SystemTask, transport_pk: int, event_pk: str, user_pk: int, trigger_pk: str
 | |
| ):
 | |
|     """Send notification over specified transport"""
 | |
|     self.save_on_success = False
 | |
|     try:
 | |
|         event = Event.objects.filter(pk=event_pk).first()
 | |
|         if not event:
 | |
|             return
 | |
|         user = User.objects.filter(pk=user_pk).first()
 | |
|         if not user:
 | |
|             return
 | |
|         trigger = NotificationRule.objects.filter(pk=trigger_pk).first()
 | |
|         if not trigger:
 | |
|             return
 | |
|         notification = Notification(
 | |
|             severity=trigger.severity, body=event.summary, event=event, user=user
 | |
|         )
 | |
|         transport = NotificationTransport.objects.filter(pk=transport_pk).first()
 | |
|         if not transport:
 | |
|             return
 | |
|         transport.send(notification)
 | |
|         self.set_status(TaskStatus.SUCCESSFUL)
 | |
|     except (NotificationTransportError, PropertyMappingExpressionException) as exc:
 | |
|         self.set_error(exc)
 | |
|         raise exc
 | |
| 
 | |
| 
 | |
| @CELERY_APP.task()
 | |
| def gdpr_cleanup(user_pk: int):
 | |
|     """cleanup events from gdpr_compliance"""
 | |
|     events = Event.objects.filter(user__pk=user_pk)
 | |
|     LOGGER.debug("GDPR cleanup, removing events from user", events=events.count())
 | |
|     events.delete()
 | |
| 
 | |
| 
 | |
| @CELERY_APP.task(bind=True, base=SystemTask)
 | |
| @prefill_task
 | |
| def notification_cleanup(self: SystemTask):
 | |
|     """Cleanup seen notifications and notifications whose event expired."""
 | |
|     notifications = Notification.objects.filter(Q(event=None) | Q(seen=True))
 | |
|     amount = notifications.count()
 | |
|     for notification in notifications:
 | |
|         notification.delete()
 | |
|     LOGGER.debug("Expired notifications", amount=amount)
 | |
|     self.set_status(TaskStatus.SUCCESSFUL, f"Expired {amount} Notifications")
 |