feat: HUB-19 — rôles du Hub consommés par ObsiGate (registre D3(a)) #228
CI / lint (pull_request) Successful in 1m17s
CI / security (pull_request) Successful in 21s
CI / test (pull_request) Failing after 3m39s
CI / build (pull_request) Skipped
CI / e2e (pull_request) Skipped

This commit is contained in:
Spark
2026-11-06 16:30:30 +00:00
parent a3f76e9b0c
commit dcf847e2fd
15 changed files with 618 additions and 20 deletions
+24 -2
View File
@@ -1,6 +1,6 @@
# ObsiGate — Roadmap
> **Version :** 2.85.0 | **Dernière mise à jour :** 2026-11-06
> **Version :** 2.85.1 | **Dernière mise à jour :** 2026-11-06
> **Ce fichier ne contient que le travail à venir** (🔵 En cours + ⚪ Backlog) et un index compact
> vers les fonctionnalités livrées.
> - **Méthode de livraison à appliquer pour toute tâche : [DELIVERY_WORKFLOW.md](./DELIVERY_WORKFLOW.md)**
@@ -32,7 +32,7 @@
### 227. Hub HUB-22 — API machine des sessions draco (liste et révocation)
- **Effort :** ~0,5 jour | **Impact :** 🟡 | **Ouvert :** 2026-11-06 | **Statut :** ✅ livré en 2.85.0 (PR en attente de fusion — voir CHANGELOG ; redéploiement Docker requis)
- **Effort :** ~0,5 jour | **Impact :** 🟡 | **Ouvert :** 2026-11-06 | **Statut :** ✅ livré en 2.85.0 (PR #68 fusionnée et release publiée — voir CHANGELOG ; redéploiement Docker requis)
- **Fiche :** [features/hub-sessions-machine-227.md](./features/hub-sessions-machine-227.md)
- **Description :** vague C du plan Hub v2.1 (HUB-22) : le Hub doit lister
les sessions actives d'un utilisateur et les révoquer une à une ou
@@ -47,6 +47,28 @@
- [x] `issued_sessions_for_user` + `revoke_one_for_user` (`backend/draco_session.py`)
- [x] Trois routes sessions (`backend/routers/comptes_machine.py`)
- [x] Tests étendus (`tests/test_comptes_machine.py`, `tests/test_draco_session.py`)
### 228. Hub HUB-19 — propagation des rôles du Hub à ObsiGate (consommatrice)
- **Effort :** ~0,5 jour | **Impact :** 🟡 | **Ouvert :** 2026-11-06 | **Statut :** ✅ livré en 2.85.1 (voir CHANGELOG ; branche rebasée sur la 2.85.0 avant fusion ; redéploiement Docker requis)
- **Fiche :** [features/hub-roles-obsigate-228.md](./features/hub-roles-obsigate-228.md)
- **Description :** suite de HUB-19 (plan Hub v2 §2.2, décision D3(a)) : le
pilote ShaarIt (0.7.0, en production) a posé le contrat ; ObsiGate —
à la fois émettrice et consommatrice du SSO — le consomme à son tour.
À l'ouverture de session draco, consultation de
`GET {hub}/api/roles/obsigate` (Cookie relayé, délai 2 s), cache
mémoire 60 s par `sub` (horodatage posé avant l'appel). Niveau connu
`aucun` → session draco refusée (réponse explicite en français) ;
routes `/api/auth/admin/*` authentifiées par le canal draco :
niveau `administrateur` exigé quand le rôle est connu. Toute erreur
du Hub → rôle inconnu → comportement actuel. L'authentification
native d'ObsiGate (émettrice, accès de secours) n'est jamais touchée ;
validation locale du jeton, émission et révocations inchangées,
aucun rôle dans le jeton.
- **Sous-tâches :**
- [x] Client `backend/hub_roles.py` (contrat D3(a), cache/repli, URL du Hub résolue comme la barre — `draco_config.app_url("hub")`)
- [x] Refus « aucun » dans `draco_session.user_for_draco` + réponse explicite de `require_auth`
- [x] Garde `require_admin_hub` sur les routes `/api/auth/admin/*`
- [x] Tests `tests/test_hub_roles.py` (client + intégration du chemin draco)
### 226. Hub HUB-13 — API d'administration des comptes (machine-à-machine)
+77
View File
@@ -0,0 +1,77 @@
# #228 — HUB-19 : propagation des rôles du Hub à ObsiGate
**Carte :** suite de HUB-19 (plan Hub v2 §2.2, décision D3(a)) — le pilote
ShaarIt (0.7.0, en production) a posé le contrat ; ObsiGate le consomme à
son tour, comme hôte à la fois **émettrice** et **consommatrice** du SSO.
**Version cible :** 2.85.1 (branche depuis le main 2.84.1 ; hypothèse : à
rebaser après la PR « API sessions HUB-22 » — 2.85.0 — si elle fusionne
d'abord).
## Contrat consommé (D3(a))
À l'ouverture de session draco — chez ObsiGate : la validation locale du
cookie partagé, `draco_session.user_for_draco()` (ACC-3.2, #217), appelée
par le repli draco de `get_current_user` — consultation du registre :
- `GET {hub}/api/roles/obsigate`, en-tête **Cookie complet** de la requête
relayé tel quel, `Accept: application/json`, délai **2 s** ;
- réponse 200 : `{"app", "sub", "role", "explicite", "ttl_secondes": 60}`,
`role` ∈ {`aucun`, `utilisateur`, `administrateur`} ;
- **URL du Hub** : `draco_config.app_url("hub")`, la résolution de la
barre commune (#223) — `DRACO_HUB_URL` explicite d'abord, dérivation
par l'hôte sœur (suffixe d'environnement de l'hôte de l'agent)
ensuite, défaut officiel enfin. Jamais d'URL dev figée.
## Client — `backend/hub_roles.py`
Sur le patron du pilote (`src/hub_roles.rs` de ShaarIt) :
- cache mémoire par `sub`, TTL **60 s** ;
- horodatage posé **avant** l'appel : un échec conserve l'ancien cache
et ne déclenche pas de rafale vers un Hub en panne ;
- toute erreur (réseau, délai, statut ≠ 200, JSON illisible, rôle hors
contrat) → rôle **inconnu** (`None`) → l'appelant garde son
comportement actuel. Le registre ne crée jamais un verrouillage
nouveau.
## Effets (sessions établies par draco seulement)
1. **Niveau connu `aucun`** → `user_for_draco` refuse : pas de session
établie par draco. `require_auth` répond alors **403** avec un
message explicite en français (niveau « aucun », matrice des rôles
du Hub, accès natif toujours possible), consigné via
`request.state.role_hub_refuse`. L'**authentification native**
d'ObsiGate (son propre login — c'est l'émettrice, son accès propre
est le secours) n'est **jamais** touchée.
2. **Routes d'administration `/api/auth/admin/*`** (CRUD des comptes,
`backend/auth/router.py`) : nouvelle garde `require_admin_hub`
(`backend/auth/middleware.py`) = `require_admin` + , quand la
requête est authentifiée par le **canal draco** (`_draco_sub`
présent, `_hub_role` joint par `user_for_draco`) et que le rôle est
**connu**, exigence du niveau `administrateur` du registre.
Sessions natives → règles actuelles inchangées ; rôle inconnu →
règles actuelles inchangées. Le registre n'élève aucun privilège :
un compte local non admin reste refusé par la règle locale, quel
que soit son niveau au Hub.
3. **Inchangés** : validation locale du jeton, émission du cookie,
révocations (`jti`), renouvellement glissant. **Aucun rôle dans le
jeton** (claims minimaux de #216 préservés).
Périmètre assumé : la garde du registre vise les routes nommées par la
carte, `/api/auth/admin/*`. Les autres surfaces gardées par
`require_admin` (configuration, vaults, coffres…) gardent leurs règles
actuelles ; les y soumettre serait un changement d'une ligne par
surface (`require_admin_hub` est réutilisable tel quel) si Bruno le
décide.
## Tests
`tests/test_hub_roles.py` : parsing du contrat (URL, Cookie relayé,
`Accept`, délai 2 s), rôles reconnus / hors contrat, JSON illisible,
erreurs HTTP et réseau → inconnu ; résolution de l'URL du Hub
(explicite, dérivée `-dev`, défaut officiel) ; cache par `sub`, TTL,
échec sans rafale avec ancien cache conservé ; intégration sur le
chemin draco (refus « aucun » explicite, login natif intact sous
« aucun », rôle inconnu = comportement actuel, `utilisateur` Hub
refusé sur `/api/auth/admin/*`, `administrateur` Hub accepté, compte
local non admin non élevé, session native jamais bloquée par le Hub).