This commit is contained in:
John W. 2026-04-09 11:23:01 -03:00 committed by GitHub
commit 2809fde619
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 32 additions and 1 deletions

View file

@ -1,5 +1,6 @@
from django.conf import settings
from django.contrib.auth import get_user_model
from django.utils.functional import SimpleLazyObject
from auditlog.cid import set_cid
from auditlog.context import set_extra_data
@ -59,7 +60,9 @@ class AuditlogMiddleware:
context_data["remote_addr"] = self._get_remote_addr(request)
context_data["remote_port"] = self._get_remote_port(request)
context_data["actor"] = self._get_actor(request)
# SimpleLazyObject defers evaluating the user in the request until it is accessed
# so it prevents a bug where the default anonymous user is used instead of the authenticated user
context_data["actor"] = SimpleLazyObject(lambda: self._get_actor(request))
return context_data

View file

@ -723,6 +723,34 @@ class MiddlewareTest(TestCase):
self.assertEqual(self.middleware._get_actor(request), actor)
def test_lazy_actor_resolution_with_deferred_auth(self):
"""
When authentication is deferred (e.g. DRF token auth), request.user
may still be AnonymousUser at the time the middleware calls
get_extra_data(). Using SimpleLazyObject ensures the actor is resolved
at model-save time, when request.user has been updated.
"""
request = self.factory.get("/")
request.user = AnonymousUser()
def get_response(req):
# Simulate deferred auth setting the real user after middleware ran
req.user = self.user
SimpleModel.objects.create(text="I am not difficult.")
return self.response_mock
self.get_response_mock.side_effect = get_response
self.middleware(request)
history = SimpleModel.objects.last().history.get(action=LogEntry.Action.CREATE)
self.assertEqual(
history.actor,
self.user,
msg="Actor should be resolved lazily to the authenticated user, "
"not eagerly to None (AnonymousUser)",
)
class SimpleIncludeModelTest(TestCase):
"""Log only changes in include_fields"""