From 31ef6fb6a68b9bcb11506cc02048bb923082ccfc Mon Sep 17 00:00:00 2001 From: Jens Langhammer Date: Mon, 23 Jan 2023 16:24:27 +0100 Subject: [PATCH] core: delete session when user is set to inactive Signed-off-by: Jens Langhammer --- authentik/core/api/users.py | 14 +++++++++++++ authentik/core/tests/test_users_api.py | 27 +++++++++++++++++++++++++- 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/authentik/core/api/users.py b/authentik/core/api/users.py index add79ca83..d1e47f2c6 100644 --- a/authentik/core/api/users.py +++ b/authentik/core/api/users.py @@ -4,6 +4,8 @@ from json import loads from typing import Any, Optional from django.contrib.auth import update_session_auth_hash +from django.contrib.sessions.backends.cache import KEY_PREFIX +from django.core.cache import cache from django.db.models.functions import ExtractHour from django.db.models.query import QuerySet from django.db.transaction import atomic @@ -57,6 +59,7 @@ from authentik.core.models import ( USER_ATTRIBUTE_SA, USER_ATTRIBUTE_TOKEN_EXPIRING, USER_PATH_SERVICE_ACCOUNT, + AuthenticatedSession, Group, Token, TokenIntents, @@ -561,3 +564,14 @@ class UserViewSet(UsedByMixin, ModelViewSet): ) } ) + + def partial_update(self, request: Request, *args, **kwargs) -> Response: + response = super().partial_update(request, *args, **kwargs) + instance: User = self.get_object() + if not instance.is_active: + sessions = AuthenticatedSession.objects.filter(user=instance) + session_ids = sessions.values_list("session_key", flat=True) + cache.delete_many(f"{KEY_PREFIX}{session}" for session in session_ids) + sessions.delete() + LOGGER.debug("Deleted user's sessions", user=instance.username) + return response diff --git a/authentik/core/tests/test_users_api.py b/authentik/core/tests/test_users_api.py index ab5db977c..df1d7edcc 100644 --- a/authentik/core/tests/test_users_api.py +++ b/authentik/core/tests/test_users_api.py @@ -1,10 +1,12 @@ """Test Users API""" from json import loads +from django.contrib.sessions.backends.cache import KEY_PREFIX +from django.core.cache import cache from django.urls.base import reverse from rest_framework.test import APITestCase -from authentik.core.models import User +from authentik.core.models import AuthenticatedSession, User from authentik.core.tests.utils import create_test_admin_user, create_test_flow, create_test_tenant from authentik.flows.models import FlowDesignation from authentik.lib.config import CONFIG @@ -257,3 +259,26 @@ class TestUsersAPI(APITestCase): self.assertEqual(response.status_code, 200) body = loads(response.content.decode()) self.assertEqual(body["user"]["avatar"], "bar") + + def test_session_delete(self): + """Ensure sessions are deleted when a user is deactivated""" + user = create_test_admin_user() + session_id = generate_id() + AuthenticatedSession.objects.create( + user=user, + session_key=session_id, + last_ip="", + ) + cache.set(KEY_PREFIX + session_id, "foo") + + self.client.force_login(self.admin) + response = self.client.patch( + reverse("authentik_api:user-detail", kwargs={"pk": user.pk}), + data={ + "is_active": False, + }, + ) + self.assertEqual(response.status_code, 200) + + self.assertIsNone(cache.get(KEY_PREFIX + session_id)) + self.assertFalse(AuthenticatedSession.objects.filter(session_key=session_id).exists())