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

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