Files
ObsiGate/docs/features/hub-api-admin-comptes-226.md
T

106 lines
5.2 KiB
Markdown

# #226 — HUB-13 : API d'administration des comptes (machine-à-machine)
**Carte :** fusion #127 (HUB-13, plan Hub v2 §2.2 et §4 vague A ; décisions
D1(a) — magasin des comptes chez ObsiGate — et D2(a) — ObsiGate émettrice).
**Version :** 2.84.0.
## Objet
Le Hub administre les comptes de la suite depuis son interface
d'administration ; ObsiGate reste le magasin des comptes. Cette API est
le bras armé de cette interface : administration complète des comptes,
en lecture et en écriture, réservée aux appels machine-à-machine.
## Routes — `/api/admin/machine/comptes`
Emplacement choisi : l'espace d'administration existant (`/api/admin/…`),
segment `machine` explicite. Il distingue ces routes de l'administration
par session (`/api/auth/admin/users`, garde `require_admin`) : aucune
session utilisateur, même administratrice, ne les ouvre.
| Méthode | Chemin | Effet |
|---|---|---|
| GET | `/api/admin/machine/comptes` | Liste des comptes (profils étendus) |
| GET | `/api/admin/machine/comptes/{username}` | Profil étendu d'un compte |
| POST | `/api/admin/machine/comptes` | Création (201 ; 409 si identifiant pris) |
| PATCH | `/api/admin/machine/comptes/{username}` | Modification (nom affiché, rôle global, langue, vaults) |
| POST | `/api/admin/machine/comptes/{username}/desactiver` | Désactivation + révocation des sessions |
| POST | `/api/admin/machine/comptes/{username}/reactiver` | Réactivation |
| DELETE | `/api/admin/machine/comptes/{username}` | Suppression + révocation des sessions |
| POST | `/api/admin/machine/comptes/{username}/reinitialiser-mot-de-passe` | Nouveau mot de passe temporaire, changement forcé |
| POST | `/api/admin/machine/comptes/{username}/reinitialiser-mfa` | Effacement de l'enrôlement TOTP |
Codes : 401 sans clé machine valide, 404 compte inconnu (ou API non
configurée), 409 conflit d'identifiant, 422 corps invalide.
## Protection
- Clé machine **dédiée** : `DRACO_ADMIN_MACHINE_KEY`, en-tête
`X-Draco-Machine-Key` (même famille et même en-tête que
`GET /api/sso/revocations`). Distincte de `DRACO_SSO_MACHINE_KEY` :
l'écriture de comptes est plus sensible que la lecture des
révocations (plan §3). Comparaison à temps constant.
- Sans la variable, l'API est inerte (404 « Non configuré »).
- Acteur consigné : en-tête facultatif `X-Draco-Actor` (défaut
`service-machine`).
## Profil étendu
Liste blanche stricte : `id`, `username`, `display_name`, `language`,
`avatar` (valeur du modèle — data-URL ou `null`, comme
`GET /api/auth/me`) et `avatar_present`, `role`, `active`, `vaults`,
`mfa_enabled` (oui/non), `mfa_method`, `must_change_password`,
`created_at`, `last_login`. **Jamais** le haché du mot de passe, le
secret TOTP, les codes de récupération ni les identifiants WebAuthn.
## Désactivation et sessions
- `active = false` → la connexion est refusée (401 uniforme, BUG-039)
et plus aucun jeton n'est émis.
- Les `jti` draco émis pour le compte sont révoqués via le mécanisme
existant (`draco_session.revoke`, liste publiée par
`GET /api/sso/revocations`). Pour les connaître, `draco_session`
tient désormais un registre des émissions (`data/draco_issued.json`,
`jti → exp` par `sub`, purgé à l'expiration) et
`revoke_all_for_user(sub)`.
- Le repère `password_changed_at` (BUG-028) est avancé : les jetons
natifs déjà émis sont invalidés et ne ressuscitent pas à la
réactivation. Les anciennes sessions draco restent révoquées après
réactivation : une nouvelle connexion est nécessaire.
## Changement forcé du mot de passe
Nouveau champ du modèle utilisateur : `must_change_password` (booléen,
défaut `false` — ajouté par `create_user`, aucun fichier à migrer :
les comptes existants sans le champ valent `false`). Posé à la
création machine et à la réinitialisation du mot de passe ; signalé
dans la réponse de connexion et `GET /api/auth/me` ; effacé par
`POST /api/auth/change-password`.
## Réinitialisation du MFA
Efface `mfa_secret`, `mfa_secret_pending` et `mfa_recovery_codes`.
Les clés WebAuthn éventuelles sont conservées et gardent le MFA
actif (méthode `webauthn`) ; sans elles, `mfa_enabled` passe à `false`.
## Journalisation
Chaque action est consignée dans le journal d'audit existant
(`data/audit.log`, JSON lignes) via `backend.audit.log_compte_admin` :
horodatage, `acteur` (identité de service), `action` (`compte_cree`,
`compte_modifie`, `compte_desactive`, `compte_reactive`,
`compte_supprime`, `mot_de_passe_reinitialise`, `mfa_reinitialise`),
`cible` (compte visé), IP et détails non sensibles (champs modifiés,
nombre de `jti` révoqués — jamais de mot de passe ni de secret).
## Tests
`tests/test_comptes_machine.py` : refus sans clé / mauvaise clé / clé
SSO / session admin, API inerte non configurée, liste et profil étendu
sans secret, création puis lecture et connexion, doublon 409,
modification, désactivation (connexion refusée, session native
refusée, `jti` draco révoqué et publié) puis réactivation,
réinitialisation du mot de passe (ancien refusé, nouveau accepté,
changement forcé signalé puis effacé au changement), réinitialisation
MFA (connexion directe après), suppression, journalisation.