# #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.