events: fix incorrect user logged when using API token authentication (#9302)
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
This commit is contained in:
@ -11,7 +11,6 @@ from django.db.models.expressions import BaseExpression, Combinable
|
|||||||
from django.db.models.signals import post_init
|
from django.db.models.signals import post_init
|
||||||
from django.http import HttpRequest
|
from django.http import HttpRequest
|
||||||
|
|
||||||
from authentik.core.models import User
|
|
||||||
from authentik.events.middleware import AuditMiddleware, should_log_model
|
from authentik.events.middleware import AuditMiddleware, should_log_model
|
||||||
from authentik.events.utils import cleanse_dict, sanitize_item
|
from authentik.events.utils import cleanse_dict, sanitize_item
|
||||||
|
|
||||||
@ -28,13 +27,10 @@ class EnterpriseAuditMiddleware(AuditMiddleware):
|
|||||||
super().connect(request)
|
super().connect(request)
|
||||||
if not self.enabled:
|
if not self.enabled:
|
||||||
return
|
return
|
||||||
user = getattr(request, "user", self.anonymous_user)
|
|
||||||
if not user.is_authenticated:
|
|
||||||
user = self.anonymous_user
|
|
||||||
if not hasattr(request, "request_id"):
|
if not hasattr(request, "request_id"):
|
||||||
return
|
return
|
||||||
post_init.connect(
|
post_init.connect(
|
||||||
partial(self.post_init_handler, user=user, request=request),
|
partial(self.post_init_handler, request=request),
|
||||||
dispatch_uid=request.request_id,
|
dispatch_uid=request.request_id,
|
||||||
weak=False,
|
weak=False,
|
||||||
)
|
)
|
||||||
@ -76,7 +72,7 @@ class EnterpriseAuditMiddleware(AuditMiddleware):
|
|||||||
diff[key] = {"previous_value": value, "new_value": after.get(key)}
|
diff[key] = {"previous_value": value, "new_value": after.get(key)}
|
||||||
return sanitize_item(diff)
|
return sanitize_item(diff)
|
||||||
|
|
||||||
def post_init_handler(self, user: User, request: HttpRequest, sender, instance: Model, **_):
|
def post_init_handler(self, request: HttpRequest, sender, instance: Model, **_):
|
||||||
"""post_init django model handler"""
|
"""post_init django model handler"""
|
||||||
if not should_log_model(instance):
|
if not should_log_model(instance):
|
||||||
return
|
return
|
||||||
@ -90,7 +86,6 @@ class EnterpriseAuditMiddleware(AuditMiddleware):
|
|||||||
|
|
||||||
def post_save_handler(
|
def post_save_handler(
|
||||||
self,
|
self,
|
||||||
user: User,
|
|
||||||
request: HttpRequest,
|
request: HttpRequest,
|
||||||
sender,
|
sender,
|
||||||
instance: Model,
|
instance: Model,
|
||||||
@ -112,6 +107,4 @@ class EnterpriseAuditMiddleware(AuditMiddleware):
|
|||||||
for field_set in ignored_field_sets:
|
for field_set in ignored_field_sets:
|
||||||
if set(diff.keys()) == set(field_set):
|
if set(diff.keys()) == set(field_set):
|
||||||
return None
|
return None
|
||||||
return super().post_save_handler(
|
return super().post_save_handler(request, sender, instance, created, thread_kwargs, **_)
|
||||||
user, request, sender, instance, created, thread_kwargs, **_
|
|
||||||
)
|
|
||||||
|
|||||||
@ -110,26 +110,32 @@ class AuditMiddleware:
|
|||||||
|
|
||||||
self.anonymous_user = get_anonymous_user()
|
self.anonymous_user = get_anonymous_user()
|
||||||
|
|
||||||
|
def get_user(self, request: HttpRequest) -> User:
|
||||||
|
user = _CTX_OVERWRITE_USER.get()
|
||||||
|
if user:
|
||||||
|
return user
|
||||||
|
user = getattr(request, "user", self.anonymous_user)
|
||||||
|
if not user.is_authenticated:
|
||||||
|
return self.anonymous_user
|
||||||
|
return user
|
||||||
|
|
||||||
def connect(self, request: HttpRequest):
|
def connect(self, request: HttpRequest):
|
||||||
"""Connect signal for automatic logging"""
|
"""Connect signal for automatic logging"""
|
||||||
self._ensure_fallback_user()
|
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"):
|
if not hasattr(request, "request_id"):
|
||||||
return
|
return
|
||||||
post_save.connect(
|
post_save.connect(
|
||||||
partial(self.post_save_handler, user=user, request=request),
|
partial(self.post_save_handler, request=request),
|
||||||
dispatch_uid=request.request_id,
|
dispatch_uid=request.request_id,
|
||||||
weak=False,
|
weak=False,
|
||||||
)
|
)
|
||||||
pre_delete.connect(
|
pre_delete.connect(
|
||||||
partial(self.pre_delete_handler, user=user, request=request),
|
partial(self.pre_delete_handler, request=request),
|
||||||
dispatch_uid=request.request_id,
|
dispatch_uid=request.request_id,
|
||||||
weak=False,
|
weak=False,
|
||||||
)
|
)
|
||||||
m2m_changed.connect(
|
m2m_changed.connect(
|
||||||
partial(self.m2m_changed_handler, user=user, request=request),
|
partial(self.m2m_changed_handler, request=request),
|
||||||
dispatch_uid=request.request_id,
|
dispatch_uid=request.request_id,
|
||||||
weak=False,
|
weak=False,
|
||||||
)
|
)
|
||||||
@ -174,7 +180,6 @@ class AuditMiddleware:
|
|||||||
|
|
||||||
def post_save_handler(
|
def post_save_handler(
|
||||||
self,
|
self,
|
||||||
user: User,
|
|
||||||
request: HttpRequest,
|
request: HttpRequest,
|
||||||
sender,
|
sender,
|
||||||
instance: Model,
|
instance: Model,
|
||||||
@ -187,22 +192,20 @@ class AuditMiddleware:
|
|||||||
return
|
return
|
||||||
if _CTX_IGNORE.get():
|
if _CTX_IGNORE.get():
|
||||||
return
|
return
|
||||||
if _new_user := _CTX_OVERWRITE_USER.get():
|
user = self.get_user(request)
|
||||||
user = _new_user
|
|
||||||
|
|
||||||
action = EventAction.MODEL_CREATED if created else EventAction.MODEL_UPDATED
|
action = EventAction.MODEL_CREATED if created else EventAction.MODEL_UPDATED
|
||||||
thread = EventNewThread(action, request, user=user, model=model_to_dict(instance))
|
thread = EventNewThread(action, request, user=user, model=model_to_dict(instance))
|
||||||
thread.kwargs.update(thread_kwargs or {})
|
thread.kwargs.update(thread_kwargs or {})
|
||||||
thread.run()
|
thread.run()
|
||||||
|
|
||||||
def pre_delete_handler(self, user: User, request: HttpRequest, sender, instance: Model, **_):
|
def pre_delete_handler(self, request: HttpRequest, sender, instance: Model, **_):
|
||||||
"""Signal handler for all object's pre_delete"""
|
"""Signal handler for all object's pre_delete"""
|
||||||
if not should_log_model(instance): # pragma: no cover
|
if not should_log_model(instance): # pragma: no cover
|
||||||
return
|
return
|
||||||
if _CTX_IGNORE.get():
|
if _CTX_IGNORE.get():
|
||||||
return
|
return
|
||||||
if _new_user := _CTX_OVERWRITE_USER.get():
|
user = self.get_user(request)
|
||||||
user = _new_user
|
|
||||||
|
|
||||||
EventNewThread(
|
EventNewThread(
|
||||||
EventAction.MODEL_DELETED,
|
EventAction.MODEL_DELETED,
|
||||||
@ -211,9 +214,7 @@ class AuditMiddleware:
|
|||||||
model=model_to_dict(instance),
|
model=model_to_dict(instance),
|
||||||
).run()
|
).run()
|
||||||
|
|
||||||
def m2m_changed_handler(
|
def m2m_changed_handler(self, request: HttpRequest, sender, instance: Model, action: str, **_):
|
||||||
self, user: User, request: HttpRequest, sender, instance: Model, action: str, **_
|
|
||||||
):
|
|
||||||
"""Signal handler for all object's m2m_changed"""
|
"""Signal handler for all object's m2m_changed"""
|
||||||
if action not in ["pre_add", "pre_remove", "post_clear"]:
|
if action not in ["pre_add", "pre_remove", "post_clear"]:
|
||||||
return
|
return
|
||||||
@ -221,8 +222,7 @@ class AuditMiddleware:
|
|||||||
return
|
return
|
||||||
if _CTX_IGNORE.get():
|
if _CTX_IGNORE.get():
|
||||||
return
|
return
|
||||||
if _new_user := _CTX_OVERWRITE_USER.get():
|
user = self.get_user(request)
|
||||||
user = _new_user
|
|
||||||
|
|
||||||
EventNewThread(
|
EventNewThread(
|
||||||
EventAction.MODEL_UPDATED,
|
EventAction.MODEL_UPDATED,
|
||||||
|
|||||||
@ -3,7 +3,7 @@
|
|||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from rest_framework.test import APITestCase
|
from rest_framework.test import APITestCase
|
||||||
|
|
||||||
from authentik.core.models import Application
|
from authentik.core.models import Application, Token, TokenIntents
|
||||||
from authentik.core.tests.utils import create_test_admin_user
|
from authentik.core.tests.utils import create_test_admin_user
|
||||||
from authentik.events.middleware import audit_ignore, audit_overwrite_user
|
from authentik.events.middleware import audit_ignore, audit_overwrite_user
|
||||||
from authentik.events.models import Event, EventAction
|
from authentik.events.models import Event, EventAction
|
||||||
@ -27,14 +27,13 @@ class TestEventsMiddleware(APITestCase):
|
|||||||
data={"name": uid, "slug": uid},
|
data={"name": uid, "slug": uid},
|
||||||
)
|
)
|
||||||
self.assertTrue(Application.objects.filter(name=uid).exists())
|
self.assertTrue(Application.objects.filter(name=uid).exists())
|
||||||
self.assertTrue(
|
event = Event.objects.filter(
|
||||||
Event.objects.filter(
|
action=EventAction.MODEL_CREATED,
|
||||||
action=EventAction.MODEL_CREATED,
|
context__model__model_name="application",
|
||||||
context__model__model_name="application",
|
context__model__app="authentik_core",
|
||||||
context__model__app="authentik_core",
|
context__model__name=uid,
|
||||||
context__model__name=uid,
|
).first()
|
||||||
).exists()
|
self.assertIsNotNone(event)
|
||||||
)
|
|
||||||
|
|
||||||
def test_delete(self):
|
def test_delete(self):
|
||||||
"""Test model creation event"""
|
"""Test model creation event"""
|
||||||
@ -88,3 +87,30 @@ class TestEventsMiddleware(APITestCase):
|
|||||||
user__username=new_user.username,
|
user__username=new_user.username,
|
||||||
).exists()
|
).exists()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def test_create_with_api(self):
|
||||||
|
"""Test model creation event (with API token auth)"""
|
||||||
|
self.client.logout()
|
||||||
|
token = Token.objects.create(user=self.user, intent=TokenIntents.INTENT_API, expiring=False)
|
||||||
|
uid = generate_id()
|
||||||
|
self.client.post(
|
||||||
|
reverse("authentik_api:application-list"),
|
||||||
|
data={"name": uid, "slug": uid},
|
||||||
|
HTTP_AUTHORIZATION=f"Bearer {token.key}",
|
||||||
|
)
|
||||||
|
self.assertTrue(Application.objects.filter(name=uid).exists())
|
||||||
|
event = Event.objects.filter(
|
||||||
|
action=EventAction.MODEL_CREATED,
|
||||||
|
context__model__model_name="application",
|
||||||
|
context__model__app="authentik_core",
|
||||||
|
context__model__name=uid,
|
||||||
|
).first()
|
||||||
|
self.assertIsNotNone(event)
|
||||||
|
self.assertEqual(
|
||||||
|
event.user,
|
||||||
|
{
|
||||||
|
"pk": self.user.pk,
|
||||||
|
"email": self.user.email,
|
||||||
|
"username": self.user.username,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user