From ea6cf6eabf2a5b2eed42a1ebb06f19d1f0598bef Mon Sep 17 00:00:00 2001 From: "gcp-cherry-pick-bot[bot]" <98988430+gcp-cherry-pick-bot[bot]@users.noreply.github.com> Date: Thu, 16 Nov 2023 12:59:41 +0100 Subject: [PATCH] events: fix missing model_* events when not directly authenticated (cherry-pick #7588) (#7597) events: fix missing model_* events when not directly authenticated (#7588) * events: fix missing model_* events when not directly authenticated * defer accessing database --------- Signed-off-by: Jens Langhammer Co-authored-by: Jens L --- authentik/events/middleware.py | 23 ++++++++++++++------- authentik/stages/user_write/tests.py | 31 ++++++++++++++++++++++++---- 2 files changed, 43 insertions(+), 11 deletions(-) diff --git a/authentik/events/middleware.py b/authentik/events/middleware.py index a722d8e4a..d482bb21e 100644 --- a/authentik/events/middleware.py +++ b/authentik/events/middleware.py @@ -93,21 +93,30 @@ class AuditMiddleware: of models""" get_response: Callable[[HttpRequest], HttpResponse] + anonymous_user: User = None def __init__(self, get_response: Callable[[HttpRequest], HttpResponse]): self.get_response = get_response + def _ensure_fallback_user(self): + """Defer fetching anonymous user until we have to""" + if self.anonymous_user: + return + from guardian.shortcuts import get_anonymous_user + + self.anonymous_user = get_anonymous_user() + def connect(self, request: HttpRequest): """Connect signal for automatic logging""" - if not hasattr(request, "user"): - return - if not getattr(request.user, "is_authenticated", False): - return + self._ensure_fallback_user() + user = getattr(request, "user", self.anonymous_user) + if not user.is_authenticated: + user = self.anonymous_user if not hasattr(request, "request_id"): return - post_save_handler = partial(self.post_save_handler, user=request.user, request=request) - pre_delete_handler = partial(self.pre_delete_handler, user=request.user, request=request) - m2m_changed_handler = partial(self.m2m_changed_handler, user=request.user, request=request) + post_save_handler = partial(self.post_save_handler, user=user, request=request) + pre_delete_handler = partial(self.pre_delete_handler, user=user, request=request) + m2m_changed_handler = partial(self.m2m_changed_handler, user=user, request=request) post_save.connect( post_save_handler, dispatch_uid=request.request_id, diff --git a/authentik/stages/user_write/tests.py b/authentik/stages/user_write/tests.py index 66084f67e..0fe661e6b 100644 --- a/authentik/stages/user_write/tests.py +++ b/authentik/stages/user_write/tests.py @@ -6,6 +6,7 @@ from django.urls import reverse from authentik.core.models import USER_ATTRIBUTE_SOURCES, Group, Source, User, UserSourceConnection from authentik.core.sources.stage import PLAN_CONTEXT_SOURCES_CONNECTION from authentik.core.tests.utils import create_test_admin_user, create_test_flow +from authentik.events.models import Event, EventAction from authentik.flows.markers import StageMarker from authentik.flows.models import FlowStageBinding from authentik.flows.planner import PLAN_CONTEXT_PENDING_USER, FlowPlan @@ -58,11 +59,33 @@ class TestUserWriteStage(FlowTestCase): self.assertStageRedirects(response, reverse("authentik_core:root-redirect")) user_qs = User.objects.filter(username=plan.context[PLAN_CONTEXT_PROMPT]["username"]) self.assertTrue(user_qs.exists()) - self.assertTrue(user_qs.first().check_password(password)) - self.assertEqual( - list(user_qs.first().ak_groups.order_by("name")), [self.other_group, self.group] + user = user_qs.first() + self.assertTrue(user.check_password(password)) + self.assertEqual(list(user.ak_groups.order_by("name")), [self.other_group, self.group]) + self.assertEqual(user.attributes, {USER_ATTRIBUTE_SOURCES: [self.source.name]}) + + self.assertTrue( + Event.objects.filter( + action=EventAction.MODEL_CREATED, + context__model={ + "app": "authentik_core", + "model_name": "user", + "pk": user.pk, + "name": "name", + }, + ) + ) + self.assertTrue( + Event.objects.filter( + action=EventAction.MODEL_UPDATED, + context__model={ + "app": "authentik_core", + "model_name": "user", + "pk": user.pk, + "name": "name", + }, + ) ) - self.assertEqual(user_qs.first().attributes, {USER_ATTRIBUTE_SOURCES: [self.source.name]}) def test_user_update(self): """Test update of existing user"""