fix(security): F03 (Astra P1) — Sitzungswiderruf in beiden Session-Stores durchsetzen
Vorher (Astra-Finding): Widerruf war inkonsistent ueber vier Pfade: - Deaktivierung invalidierte nur den Berechtigungscache — Sessions liefen bis TTL (8h) weiter - Loeschung invalidierte GAR NICHTS - Passwortwechsel loeschte nur Redis-Sessions — PostgreSQL-Fallback- Sessions ueberlebten jeden Redis-Ausfall - Fehlende UserTenant-Mitgliedschaft wurde durchgewinkt statt abgewiesen Fix: - Neuer zentraler Helfer revoke_user_sessions_all_stores (app/core/auth.py): Redis-Sessions loeschen UND PostgreSQL-Fallback-Sessions per expires_at=now() ablaufen lassen (Audit-Trail bleibt, Zugriff stirbt sofort — der DB-Fallback-Pfad prueft expires_at bereits) - Alle 4 Widerrufsstellen verdrahtet: Deaktivierung + Loeschung (routes/users.py), Passwortwechsel (user_service.py), Passwort-Reset (auth_service.py) - Membership-Check in get_current_user fail-closed: None (fehlende Mitgliedschaft) wird abgewiesen statt durchgelassen Abnahme (Astra): Deaktivierung, Austritt und Passwortwechsel wirken unmittelbar — auch bei Redis-Ausfall (Unit-Test beweist die DB-Fallback-Abgelaufen-Rejection). Tests: test_s1_security_guards.py 10/10 (3 neue F03-Tests) + test_auth.py 11/11 + ruff clean.
This commit is contained in:
@@ -339,6 +339,15 @@ async def update_user(
|
||||
except Exception:
|
||||
pass # Best-effort — don't fail the update if Redis is down
|
||||
|
||||
# F03 (Astra): deactivation must take effect IMMEDIATELY in BOTH session
|
||||
# stores (Redis runtime + PostgreSQL fallback) — not just the permission
|
||||
# cache. Otherwise a deactivated user keeps working until the session TTL
|
||||
# (8h) expires, and DB-fallback sessions survive Redis outages entirely.
|
||||
if body.is_active is False:
|
||||
from app.core.auth import revoke_user_sessions_all_stores
|
||||
|
||||
await revoke_user_sessions_all_stores(uid)
|
||||
|
||||
return {
|
||||
"id": str(user.id),
|
||||
"email": user.email,
|
||||
@@ -379,6 +388,12 @@ async def delete_user(
|
||||
if not success:
|
||||
raise HTTPException(404, detail={"detail": "User not found", "code": "not_found"})
|
||||
|
||||
# F03 (Astra): deletion must revoke all sessions immediately — in BOTH
|
||||
# stores (Redis runtime + PostgreSQL fallback).
|
||||
from app.core.auth import revoke_user_sessions_all_stores
|
||||
|
||||
await revoke_user_sessions_all_stores(uid)
|
||||
|
||||
await log_audit(
|
||||
db,
|
||||
tenant_id,
|
||||
|
||||
Reference in New Issue
Block a user