events: fix race condition (#10602)
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
This commit is contained in:
@ -35,6 +35,7 @@ IGNORED_MODELS = tuple(
|
|||||||
|
|
||||||
_CTX_OVERWRITE_USER = ContextVar[User | None]("authentik_events_log_overwrite_user", default=None)
|
_CTX_OVERWRITE_USER = ContextVar[User | None]("authentik_events_log_overwrite_user", default=None)
|
||||||
_CTX_IGNORE = ContextVar[bool]("authentik_events_log_ignore", default=False)
|
_CTX_IGNORE = ContextVar[bool]("authentik_events_log_ignore", default=False)
|
||||||
|
_CTX_REQUEST = ContextVar[HttpRequest | None]("authentik_events_log_request", default=None)
|
||||||
|
|
||||||
|
|
||||||
def should_log_model(model: Model) -> bool:
|
def should_log_model(model: Model) -> bool:
|
||||||
@ -149,11 +150,13 @@ class AuditMiddleware:
|
|||||||
m2m_changed.disconnect(dispatch_uid=request.request_id)
|
m2m_changed.disconnect(dispatch_uid=request.request_id)
|
||||||
|
|
||||||
def __call__(self, request: HttpRequest) -> HttpResponse:
|
def __call__(self, request: HttpRequest) -> HttpResponse:
|
||||||
|
_CTX_REQUEST.set(request)
|
||||||
self.connect(request)
|
self.connect(request)
|
||||||
|
|
||||||
response = self.get_response(request)
|
response = self.get_response(request)
|
||||||
|
|
||||||
self.disconnect(request)
|
self.disconnect(request)
|
||||||
|
_CTX_REQUEST.set(None)
|
||||||
return response
|
return response
|
||||||
|
|
||||||
def process_exception(self, request: HttpRequest, exception: Exception):
|
def process_exception(self, request: HttpRequest, exception: Exception):
|
||||||
@ -167,7 +170,7 @@ class AuditMiddleware:
|
|||||||
thread = EventNewThread(
|
thread = EventNewThread(
|
||||||
EventAction.SUSPICIOUS_REQUEST,
|
EventAction.SUSPICIOUS_REQUEST,
|
||||||
request,
|
request,
|
||||||
message=str(exception),
|
message=exception_to_string(exception),
|
||||||
)
|
)
|
||||||
thread.run()
|
thread.run()
|
||||||
elif before_send({}, {"exc_info": (None, exception, None)}) is not None:
|
elif before_send({}, {"exc_info": (None, exception, None)}) is not None:
|
||||||
@ -192,6 +195,8 @@ class AuditMiddleware:
|
|||||||
return
|
return
|
||||||
if _CTX_IGNORE.get():
|
if _CTX_IGNORE.get():
|
||||||
return
|
return
|
||||||
|
if request.request_id != _CTX_REQUEST.get().request_id:
|
||||||
|
return
|
||||||
user = self.get_user(request)
|
user = self.get_user(request)
|
||||||
|
|
||||||
action = EventAction.MODEL_CREATED if created else EventAction.MODEL_UPDATED
|
action = EventAction.MODEL_CREATED if created else EventAction.MODEL_UPDATED
|
||||||
@ -205,6 +210,8 @@ class AuditMiddleware:
|
|||||||
return
|
return
|
||||||
if _CTX_IGNORE.get():
|
if _CTX_IGNORE.get():
|
||||||
return
|
return
|
||||||
|
if request.request_id != _CTX_REQUEST.get().request_id:
|
||||||
|
return
|
||||||
user = self.get_user(request)
|
user = self.get_user(request)
|
||||||
|
|
||||||
EventNewThread(
|
EventNewThread(
|
||||||
@ -230,6 +237,8 @@ class AuditMiddleware:
|
|||||||
return
|
return
|
||||||
if _CTX_IGNORE.get():
|
if _CTX_IGNORE.get():
|
||||||
return
|
return
|
||||||
|
if request.request_id != _CTX_REQUEST.get().request_id:
|
||||||
|
return
|
||||||
user = self.get_user(request)
|
user = self.get_user(request)
|
||||||
|
|
||||||
EventNewThread(
|
EventNewThread(
|
||||||
|
|||||||
@ -238,6 +238,8 @@ class Event(SerializerModel, ExpiringModel):
|
|||||||
"args": cleanse_dict(QueryDict(request.META.get("QUERY_STRING", ""))),
|
"args": cleanse_dict(QueryDict(request.META.get("QUERY_STRING", ""))),
|
||||||
"user_agent": request.META.get("HTTP_USER_AGENT", ""),
|
"user_agent": request.META.get("HTTP_USER_AGENT", ""),
|
||||||
}
|
}
|
||||||
|
if hasattr(request, "request_id"):
|
||||||
|
self.context["http_request"]["request_id"] = request.request_id
|
||||||
# Special case for events created during flow execution
|
# Special case for events created during flow execution
|
||||||
# since they keep the http query within a wrapped query
|
# since they keep the http query within a wrapped query
|
||||||
if QS_QUERY in self.context["http_request"]["args"]:
|
if QS_QUERY in self.context["http_request"]["args"]:
|
||||||
|
|||||||
@ -8,6 +8,7 @@ from django.urls import reverse
|
|||||||
from rest_framework.exceptions import ValidationError
|
from rest_framework.exceptions import ValidationError
|
||||||
|
|
||||||
from authentik.brands.utils import get_brand_for_request
|
from authentik.brands.utils import get_brand_for_request
|
||||||
|
from authentik.core.middleware import RESPONSE_HEADER_ID
|
||||||
from authentik.core.tests.utils import create_test_admin_user, create_test_flow
|
from authentik.core.tests.utils import create_test_admin_user, create_test_flow
|
||||||
from authentik.events.models import Event, EventAction
|
from authentik.events.models import Event, EventAction
|
||||||
from authentik.flows.models import FlowDesignation, FlowStageBinding
|
from authentik.flows.models import FlowDesignation, FlowStageBinding
|
||||||
@ -186,6 +187,7 @@ class AuthenticatorValidateStageDuoTests(FlowTestCase):
|
|||||||
"method": "GET",
|
"method": "GET",
|
||||||
"path": f"/api/v3/flows/executor/{flow.slug}/",
|
"path": f"/api/v3/flows/executor/{flow.slug}/",
|
||||||
"user_agent": "",
|
"user_agent": "",
|
||||||
|
"request_id": response[RESPONSE_HEADER_ID],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user