events: save login event in session after login
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> #4070
This commit is contained in:
parent
f22f1ebcde
commit
5019346ab6
|
@ -196,9 +196,9 @@ class ApplicationViewSet(UsedByMixin, ModelViewSet):
|
||||||
if not should_cache:
|
if not should_cache:
|
||||||
allowed_applications = self._get_allowed_applications(queryset)
|
allowed_applications = self._get_allowed_applications(queryset)
|
||||||
if should_cache:
|
if should_cache:
|
||||||
LOGGER.debug("Caching allowed application list")
|
|
||||||
allowed_applications = cache.get(user_app_cache_key(self.request.user.pk))
|
allowed_applications = cache.get(user_app_cache_key(self.request.user.pk))
|
||||||
if not allowed_applications:
|
if not allowed_applications:
|
||||||
|
LOGGER.debug("Caching allowed application list")
|
||||||
allowed_applications = self._get_allowed_applications(queryset)
|
allowed_applications = self._get_allowed_applications(queryset)
|
||||||
cache.set(
|
cache.set(
|
||||||
user_app_cache_key(self.request.user.pk),
|
user_app_cache_key(self.request.user.pk),
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
"""Events middleware"""
|
"""Events middleware"""
|
||||||
from functools import partial
|
from functools import partial
|
||||||
from typing import Callable
|
from threading import Thread
|
||||||
|
from typing import Any, Callable, Optional
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib.sessions.models import Session
|
from django.contrib.sessions.models import Session
|
||||||
|
@ -13,7 +14,6 @@ from guardian.models import UserObjectPermission
|
||||||
|
|
||||||
from authentik.core.models import AuthenticatedSession, User
|
from authentik.core.models import AuthenticatedSession, User
|
||||||
from authentik.events.models import Event, EventAction, Notification
|
from authentik.events.models import Event, EventAction, Notification
|
||||||
from authentik.events.signals import EventNewThread
|
|
||||||
from authentik.events.utils import model_to_dict
|
from authentik.events.utils import model_to_dict
|
||||||
from authentik.flows.models import FlowToken
|
from authentik.flows.models import FlowToken
|
||||||
from authentik.lib.sentry import before_send
|
from authentik.lib.sentry import before_send
|
||||||
|
@ -37,6 +37,25 @@ def should_log_model(model: Model) -> bool:
|
||||||
return not isinstance(model, IGNORED_MODELS)
|
return not isinstance(model, IGNORED_MODELS)
|
||||||
|
|
||||||
|
|
||||||
|
class EventNewThread(Thread):
|
||||||
|
"""Create Event in background thread"""
|
||||||
|
|
||||||
|
action: str
|
||||||
|
request: HttpRequest
|
||||||
|
kwargs: dict[str, Any]
|
||||||
|
user: Optional[User] = None
|
||||||
|
|
||||||
|
def __init__(self, action: str, request: HttpRequest, user: Optional[User] = None, **kwargs):
|
||||||
|
super().__init__()
|
||||||
|
self.action = action
|
||||||
|
self.request = request
|
||||||
|
self.user = user
|
||||||
|
self.kwargs = kwargs
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
Event.new(self.action, **self.kwargs).from_http(self.request, user=self.user)
|
||||||
|
|
||||||
|
|
||||||
class AuditMiddleware:
|
class AuditMiddleware:
|
||||||
"""Register handlers for duration of request-response that log creation/update/deletion
|
"""Register handlers for duration of request-response that log creation/update/deletion
|
||||||
of models"""
|
of models"""
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
"""authentik events signal listener"""
|
"""authentik events signal listener"""
|
||||||
from threading import Thread
|
|
||||||
from typing import Any, Optional
|
from typing import Any, Optional
|
||||||
|
|
||||||
from django.contrib.auth.signals import user_logged_in, user_logged_out
|
from django.contrib.auth.signals import user_logged_in, user_logged_out
|
||||||
|
@ -19,63 +18,40 @@ from authentik.stages.invitation.signals import invitation_used
|
||||||
from authentik.stages.password.stage import PLAN_CONTEXT_METHOD, PLAN_CONTEXT_METHOD_ARGS
|
from authentik.stages.password.stage import PLAN_CONTEXT_METHOD, PLAN_CONTEXT_METHOD_ARGS
|
||||||
from authentik.stages.user_write.signals import user_write
|
from authentik.stages.user_write.signals import user_write
|
||||||
|
|
||||||
|
SESSION_LOGIN_EVENT = "login_event"
|
||||||
class EventNewThread(Thread):
|
|
||||||
"""Create Event in background thread"""
|
|
||||||
|
|
||||||
action: str
|
|
||||||
request: HttpRequest
|
|
||||||
kwargs: dict[str, Any]
|
|
||||||
user: Optional[User] = None
|
|
||||||
|
|
||||||
def __init__(self, action: str, request: HttpRequest, user: Optional[User] = None, **kwargs):
|
|
||||||
super().__init__()
|
|
||||||
self.action = action
|
|
||||||
self.request = request
|
|
||||||
self.user = user
|
|
||||||
self.kwargs = kwargs
|
|
||||||
|
|
||||||
def run(self):
|
|
||||||
Event.new(self.action, **self.kwargs).from_http(self.request, user=self.user)
|
|
||||||
|
|
||||||
|
|
||||||
@receiver(user_logged_in)
|
@receiver(user_logged_in)
|
||||||
# pylint: disable=unused-argument
|
# pylint: disable=unused-argument
|
||||||
def on_user_logged_in(sender, request: HttpRequest, user: User, **_):
|
def on_user_logged_in(sender, request: HttpRequest, user: User, **_):
|
||||||
"""Log successful login"""
|
"""Log successful login"""
|
||||||
thread = EventNewThread(EventAction.LOGIN, request)
|
kwargs = {}
|
||||||
if SESSION_KEY_PLAN in request.session:
|
if SESSION_KEY_PLAN in request.session:
|
||||||
flow_plan: FlowPlan = request.session[SESSION_KEY_PLAN]
|
flow_plan: FlowPlan = request.session[SESSION_KEY_PLAN]
|
||||||
if PLAN_CONTEXT_SOURCE in flow_plan.context:
|
if PLAN_CONTEXT_SOURCE in flow_plan.context:
|
||||||
# Login request came from an external source, save it in the context
|
# Login request came from an external source, save it in the context
|
||||||
thread.kwargs[PLAN_CONTEXT_SOURCE] = flow_plan.context[PLAN_CONTEXT_SOURCE]
|
kwargs[PLAN_CONTEXT_SOURCE] = flow_plan.context[PLAN_CONTEXT_SOURCE]
|
||||||
if PLAN_CONTEXT_METHOD in flow_plan.context:
|
if PLAN_CONTEXT_METHOD in flow_plan.context:
|
||||||
thread.kwargs[PLAN_CONTEXT_METHOD] = flow_plan.context[PLAN_CONTEXT_METHOD]
|
|
||||||
# Save the login method used
|
# Save the login method used
|
||||||
thread.kwargs[PLAN_CONTEXT_METHOD_ARGS] = flow_plan.context.get(
|
kwargs[PLAN_CONTEXT_METHOD] = flow_plan.context[PLAN_CONTEXT_METHOD]
|
||||||
PLAN_CONTEXT_METHOD_ARGS, {}
|
kwargs[PLAN_CONTEXT_METHOD_ARGS] = flow_plan.context.get(PLAN_CONTEXT_METHOD_ARGS, {})
|
||||||
)
|
event = Event.new(EventAction.LOGIN, **kwargs).from_http(request, user=user)
|
||||||
thread.user = user
|
request.session[SESSION_LOGIN_EVENT] = event
|
||||||
thread.run()
|
|
||||||
|
|
||||||
|
|
||||||
@receiver(user_logged_out)
|
@receiver(user_logged_out)
|
||||||
# pylint: disable=unused-argument
|
# pylint: disable=unused-argument
|
||||||
def on_user_logged_out(sender, request: HttpRequest, user: User, **_):
|
def on_user_logged_out(sender, request: HttpRequest, user: User, **_):
|
||||||
"""Log successfully logout"""
|
"""Log successfully logout"""
|
||||||
thread = EventNewThread(EventAction.LOGOUT, request)
|
Event.new(EventAction.LOGOUT).from_http(request, user=user)
|
||||||
thread.user = user
|
|
||||||
thread.run()
|
|
||||||
|
|
||||||
|
|
||||||
@receiver(user_write)
|
@receiver(user_write)
|
||||||
# pylint: disable=unused-argument
|
# pylint: disable=unused-argument
|
||||||
def on_user_write(sender, request: HttpRequest, user: User, data: dict[str, Any], **kwargs):
|
def on_user_write(sender, request: HttpRequest, user: User, data: dict[str, Any], **kwargs):
|
||||||
"""Log User write"""
|
"""Log User write"""
|
||||||
thread = EventNewThread(EventAction.USER_WRITE, request, **data)
|
data["created"] = kwargs.get("created", False)
|
||||||
thread.kwargs["created"] = kwargs.get("created", False)
|
Event.new(EventAction.USER_WRITE, **data).from_http(request, user=user)
|
||||||
thread.user = user
|
|
||||||
thread.run()
|
|
||||||
|
|
||||||
|
|
||||||
@receiver(login_failed)
|
@receiver(login_failed)
|
||||||
|
@ -89,26 +65,23 @@ def on_login_failed(
|
||||||
**kwargs,
|
**kwargs,
|
||||||
):
|
):
|
||||||
"""Failed Login, authentik custom event"""
|
"""Failed Login, authentik custom event"""
|
||||||
thread = EventNewThread(EventAction.LOGIN_FAILED, request, **credentials, stage=stage, **kwargs)
|
Event.new(EventAction.USER_WRITE, **credentials, stage=stage, **kwargs).from_http(request)
|
||||||
thread.run()
|
|
||||||
|
|
||||||
|
|
||||||
@receiver(invitation_used)
|
@receiver(invitation_used)
|
||||||
# pylint: disable=unused-argument
|
# pylint: disable=unused-argument
|
||||||
def on_invitation_used(sender, request: HttpRequest, invitation: Invitation, **_):
|
def on_invitation_used(sender, request: HttpRequest, invitation: Invitation, **_):
|
||||||
"""Log Invitation usage"""
|
"""Log Invitation usage"""
|
||||||
thread = EventNewThread(
|
Event.new(EventAction.INVITE_USED, invitation_uuid=invitation.invite_uuid.hex).from_http(
|
||||||
EventAction.INVITE_USED, request, invitation_uuid=invitation.invite_uuid.hex
|
request
|
||||||
)
|
)
|
||||||
thread.run()
|
|
||||||
|
|
||||||
|
|
||||||
@receiver(password_changed)
|
@receiver(password_changed)
|
||||||
# pylint: disable=unused-argument
|
# pylint: disable=unused-argument
|
||||||
def on_password_changed(sender, user: User, password: str, **_):
|
def on_password_changed(sender, user: User, password: str, **_):
|
||||||
"""Log password change"""
|
"""Log password change"""
|
||||||
thread = EventNewThread(EventAction.PASSWORD_SET, None, user=user)
|
Event.new(EventAction.PASSWORD_SET).from_http(None, user=user)
|
||||||
thread.run()
|
|
||||||
|
|
||||||
|
|
||||||
@receiver(post_save, sender=Event)
|
@receiver(post_save, sender=Event)
|
||||||
|
|
Reference in New Issue