Files
ObsiGate/docs/features/hub-sessions-machine-227.md

3.8 KiB

#227 — HUB-22 : API machine des sessions draco (liste et révocation)

Carte : vague C du plan Hub v2.1 (HUB-22) — le Hub consomme ces routes en 0.17.0 pour l'écran des sessions d'un utilisateur. Version : 2.85.0. Prérequis : HUB-13 (#226) — registre des émissions jti et garde machine.

Objet

Le Hub doit lister les sessions actives d'un utilisateur et les révoquer une à une ou toutes, via le canal jti livré en HUB-13. Aucune route machine « sessions » n'existait : cette livraison la crée, sous la même garde (require_machine_admin, clé DRACO_ADMIN_MACHINE_KEY, en-têtes X-Draco-Machine-Key / X-Draco-Actor) et avec la même journalisation d'audit que les routes comptes existantes. Sans la clé configurée, l'API reste inerte (404) ; sans clé valide, 401.

Routes — /api/admin/machine/comptes/{username}/sessions

Méthode Chemin Effet
GET /api/admin/machine/comptes/{username}/sessions Sessions actives du compte
POST /api/admin/machine/comptes/{username}/sessions/{jti}/revoquer Révocation d'une session
POST /api/admin/machine/comptes/{username}/sessions/revoquer-toutes Révocation de toutes les sessions

Codes : 401 sans clé machine valide, 404 compte inconnu (ou API non configurée), 404 jti absent de l'ensemble actif du compte.

Liste — GET …/sessions

200 {"username": …, "sessions": [{"jti", "exp", "expire_le", "revoquee"}]} — une entrée par jti émis et non expiré, triée par exp croissant ; expire_le est l'échéance en ISO 8601 UTC et revoquee signale un jti déjà présent dans la liste des révocations.

Révocation unitaire — POST …/sessions/{jti}/revoquer

200 {"jti", "revoquee": true}. Le jti n'est révoqué que s'il appartient à l'ensemble émis du compte visé et n'est pas expiré : 404 sinon, jamais de révocation cross-utilisateur.

Tout révoquer — POST …/sessions/revoquer-toutes

200 {"revoquees": n} — réutilise revoke_all_for_user de HUB-13. C'est la « déconnexion globale », distincte de la désactivation : le compte reste actif, une nouvelle connexion émet une nouvelle session.

Backend — backend/draco_session.py

  • issued_sessions_for_user(sub) : sessions actives (exp > maintenant) du sub, [{jti, exp, revoquee}] triées par exp croissant ; les entrées expirées du sub sont purgées du registre au passage.
  • revoke_one_for_user(sub, jti) : révoque via le revoke() existant uniquement si le jti appartient à l'ensemble émis de ce sub et n'est pas expiré ; retourne un booléen.

La clé du registre est le même sub que celui de HUB-13 (_identifiant_sessions dans comptes_machine.py : id du compte, à défaut username) — factorisée depuis _revoquer_sessions, qui l'utilise aussi désormais.

Journalisation

Les deux écritures sont consignées dans data/audit.log via log_compte_admin : actions session_revoquee (détail : le jti) et sessions_revoquees (détail : nombre de jti révoqués), acteur = identité de service X-Draco-Actor. La lecture suit le patron des lectures existantes (non journalisée).

Tests

  • tests/test_draco_session.py : liste triée et marquage revoquee, purge des expirés, révocation unitaire, refus cross-utilisateur, refus d'un jti expiré ; le registre des émissions est isolé en fichier temporaire dans la fixture.
  • tests/test_comptes_machine.py (TestSessions) : liste (contenu, expire_le ISO 8601 UTC), liste vide, compte inconnu 404, révocation unitaire (succès + effet sur verify_token), cross-utilisateur 404 sans effet, tout-révoquer (compte toujours actif, autres comptes intacts, seconde passe à 0), garde (401 sans clé, 404 API non configurée), journalisation.