feat: validation locale du jeton draco_session — repli SSO dans get_current_user (#217)
CI / lint (push) Successful in 3m34s
CI / security (push) Successful in 2m17s
CI / test (push) Successful in 5m38s
CI / build (push) Successful in 2m15s
CI / e2e (push) Successful in 18m31s

This commit is contained in:
Spark
2026-10-20 23:47:45 -04:00
parent cb80b08f77
commit 68d3dd85a9
13 changed files with 188 additions and 19 deletions
+40
View File
@@ -175,6 +175,46 @@ def clear_cookie(response) -> None:
response.delete_cookie(COOKIE_NAME, path="/", **extra)
# ── Validation locale (ACC-3.2, #9) ─────────────────────────────────────
def user_for_draco(request) -> dict | None:
"""Utilisateur ObsiGate prouvé par le cookie draco_session.
ObsiGate est à la fois émettrice et consommatrice : le `sub` du
jeton EST son identifiant d'utilisateur — la correspondance est
directe, par id. Le garde-fou « jeton antérieur au dernier
changement de mot de passe » (BUG-028 côté natif) est appliqué
à l'identique via le claim `iat`.
"""
claims = verify_token(request.cookies.get(COOKIE_NAME, ""))
if not claims:
return None
sub = str(claims.get("sub", ""))
if not sub:
return None
from backend.auth.user_store import get_all_users
for candidate in get_all_users():
if str(candidate.get("id")) != sub:
continue
if not candidate.get("active"):
return None
pca = candidate.get("password_changed_at")
iat = claims.get("iat")
if pca is not None and iat is not None:
try:
if int(iat) < int(float(pca)):
return None
except (TypeError, ValueError):
return None
user = dict(candidate)
user["_token_vaults"] = candidate.get("vaults", [])
user["_token_scope"] = "full"
user["_draco_sub"] = sub
return user
return None
# ── Renouvellement glissant (middleware) ────────────────────────────────
class DracoRenewalMiddleware: