5.2 KiB
#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êteX-Draco-Machine-Key(même famille et même en-tête queGET /api/sso/revocations). Distincte deDRACO_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éfautservice-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
jtidraco émis pour le compte sont révoqués via le mécanisme existant (draco_session.revoke, liste publiée parGET /api/sso/revocations). Pour les connaître,draco_sessiontient désormais un registre des émissions (data/draco_issued.json,jti → expparsub, purgé à l'expiration) etrevoke_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.