"""Session user and the only way to end sessions (AD-3, AD-6).""" from __future__ import annotations import time from dataclasses import dataclass from .models import UserSession from .session_store import CREATED_AT, USER_KEY, USER_SAM @dataclass(frozen=True) class SessionUser: key: str # lower-case, used for every database key sam: str # exact sAMAccountName from AD @classmethod def from_sam(cls, sam: str) -> SessionUser: return cls(key=sam.lower(), sam=sam) def start_session(request, sam: str) -> SessionUser: """Sign a user in on this request; the session key is rotated and old data dropped.""" user = SessionUser.from_sam(sam) request.session.flush() request.session[USER_KEY] = user.key request.session[USER_SAM] = user.sam request.session[CREATED_AT] = time.time() return user def current_user(request) -> SessionUser | None: session = getattr(request, "session", None) if session is None: return None key = session.get(USER_KEY) sam = session.get(USER_SAM) if not key or not sam: return None return SessionUser(key=key, sam=sam) def end_sessions(user: SessionUser | str, except_session_key: str | None = None) -> int: """Delete every session of one user, optionally keeping one. Returns how many were ended.""" key = (user.key if isinstance(user, SessionUser) else str(user)).lower() if not key: return 0 qs = UserSession.objects.filter(username=key) if except_session_key: qs = qs.exclude(session_key=except_session_key) deleted, _ = qs.delete() return deleted