168 lines
7.8 KiB
Markdown
168 lines
7.8 KiB
Markdown
# Correspondance draco par utilisateur (HUB-16, 0.46.0)
|
|
|
|
Kanbante participe au SSO de la suite dracodev : ObsiGate émet le
|
|
cookie `draco_session` (JWS HS256, claims `sub`, `name`, `iat`,
|
|
`exp`, `jti`), Kanbante le valide localement (`app/draco.py`) et le
|
|
pont `GET /auth/draco` le convertit en session Kanbante native.
|
|
|
|
Depuis la 0.46.0, un jeton draco valide désigne **le compte
|
|
Kanbante de cette identité** — et aucun autre. L'ancienne
|
|
hypothèse mono-utilisateur (« un jeton valide = l'opérateur ») a
|
|
disparu. C'est ce qui fait tenir le critère de la suite : « un
|
|
compte créé fonctionne dans toutes les applications ».
|
|
|
|
## Où vit la correspondance
|
|
|
|
Dans le modèle existant des comptes : la colonne
|
|
**`users.draco_sub`** (texte, `UNIQUE`, présente depuis la 0.24.0)
|
|
porte le `sub` draco lié à chaque compte Kanbante.
|
|
`models.get_user_by_draco(sub)` est la résolution canonique ; la
|
|
règle de décision complète vit dans
|
|
`app.accounts.resolve_draco_user()`, seul point d'entrée du pont
|
|
et du contrôle draco de `/api/search`. La correspondance est donc
|
|
biunivoque : un `sub` ↔ un compte, garanti par la contrainte
|
|
d'unicité.
|
|
|
|
## Résolution d'une connexion draco (`/auth/draco`)
|
|
|
|
Dans l'ordre :
|
|
|
|
1. **Session native déjà ouverte** : si le navigateur porte à la
|
|
fois une session Kanbante et un cookie draco dont le `sub`
|
|
n'est lié à aucun compte, le `sub` est lié au compte de la
|
|
session (première visite doublement connectée — mécanisme
|
|
historique, inchangé, aussi appliqué à la connexion native
|
|
dans `login_submit`).
|
|
2. **Correspondance existante** : la session du compte lié est
|
|
ouverte. Un compte **désactivé** n'ouvre aucune session et
|
|
n'est jamais contourné (voir § Désactivation).
|
|
3. **Compte d'amorçage** : s'il n'existe qu'**un seul compte** et
|
|
que c'est un **admin actif sans liaison**, le premier `sub`
|
|
valide s'y lie et sa session est ouverte. C'est le cas du
|
|
compte créé à l'installation depuis l'environnement
|
|
(`KANBANTE_AUTH_USER` / `KANBANTE_AUTH_PASSWORD`, Bruno) : sa
|
|
correspondance s'établit au premier passage, sans le verrouiller
|
|
dehors. Ses autres accès restent inchangés dans tous les cas :
|
|
connexion native avec le mot de passe du compte, et repli
|
|
d'urgence par les identifiants d'environnement (formulaire et
|
|
Basic), qui ne dépendent pas de draco.
|
|
4. **Création JIT** : sinon, un compte Kanbante est créé pour
|
|
cette identité (détail ci-dessous) et sa session est ouverte.
|
|
|
|
Un `sub` inconnu n'ouvre **jamais** la session de l'opérateur
|
|
d'environnement ni celle d'un compte existant non lié — y compris
|
|
si le nom affiché draco ressemble au nom d'un compte local : le
|
|
JIT crée toujours un compte **nouveau** (nom suffixé `-2`, `-3`…
|
|
en cas de collision), jamais une adoption.
|
|
|
|
## Création JIT (juste-à-temps)
|
|
|
|
À la première connexion draco d'une identité sans correspondance :
|
|
|
|
- **nom d'utilisateur** : nom affiché des claims slugifié
|
|
(`Alice Exemple` → `alice-exemple`), libre ou suffixé ;
|
|
repli `draco-<sub>` si le nom est inexploitable ;
|
|
- **nom d'affichage** : le `name` des claims ;
|
|
- **rôle** : `member` (utilisateur standard) — jamais admin ;
|
|
- **mot de passe local** : secret aléatoire, haché (PBKDF2 comme
|
|
tous les comptes), **jamais divulgué** — l'accès normal passe
|
|
par draco. Un administrateur peut ensuite poser un mot de
|
|
passe local depuis `/admin` (bouton « Réinitialiser ») si un
|
|
accès local est voulu ;
|
|
- **espace** : le compte rejoint l'espace Principal comme membre,
|
|
comme tout utilisateur créé par l'administration ;
|
|
- le geste est journalisé (`draco.jit` dans le journal
|
|
d'administration).
|
|
|
|
## Liaison par l'administrateur (comptes préexistants)
|
|
|
|
Pour associer un compte Kanbante existant à une identité draco
|
|
sans attendre un premier passage :
|
|
|
|
1. Se connecter à Kanbante avec un compte **admin**.
|
|
2. Ouvrir **Administration** (`/admin`), section **Utilisateurs**.
|
|
3. Dans la colonne **Draco (sub)** du compte visé, saisir le
|
|
`sub` draco de l'identité (l'identifiant du compte de suite
|
|
chez l'émettrice, ObsiGate) et cliquer **Lier**.
|
|
4. La liaison est immédiate et journalisée (`draco.link.admin`).
|
|
Le bouton **Délier** de la même colonne la retire
|
|
(`draco.unlink.admin`).
|
|
|
|
Gardes : un `sub` déjà lié à un autre compte est refusé avec un
|
|
message d'erreur (délier l'autre compte d'abord). L'administrateur
|
|
peut lier **son propre** compte — c'est la voie manuelle pour le
|
|
compte d'amorçage si le premier passage ne l'a pas lié.
|
|
|
|
Chaque utilisateur peut aussi voir sa propre liaison dans
|
|
**Mon profil → Liaison draco (SSO)** et la délier lui-même.
|
|
|
|
> Le registre matriciel des rôles par application côté Hub
|
|
> (carte HUB-19) lit cette correspondance ; la 0.46.0 ne livrait
|
|
> que le côté Kanbante, qui reste la source de vérité locale.
|
|
> La consommation du registre par Kanbante est décrite à la
|
|
> section suivante (0.46.2).
|
|
|
|
## Rôles du Hub (HUB-19, 0.46.2)
|
|
|
|
Le registre des rôles du Hub (matrice par application, contrat
|
|
D3(a)) est consulté par `app/hub_roles.py` — portage du pilote
|
|
ShaarIt 0.7.0 : `GET {hub}/api/roles/kanbante`, en-tête Cookie
|
|
complet de la requête relayé, délai 2 s, cache mémoire par
|
|
`sub` de 60 s avec horodatage posé avant l'appel (un échec
|
|
conserve l'ancien cache et ne déclenche pas de rafale). L'URL
|
|
du Hub est celle de la barre commune (`resoudre_urls_draco`).
|
|
Toute erreur rend un rôle **inconnu**, qui vaut toujours
|
|
« comportement actuel » — jamais un verrouillage nouveau.
|
|
|
|
Effets, pour les seules sessions **établies par le pont
|
|
draco** :
|
|
|
|
1. **Pont `/auth/draco`** : la consultation précède la
|
|
liaison, la résolution de la correspondance et la création
|
|
JIT. Rôle connu `aucun` → refus explicite (403, en
|
|
français) : aucune session ouverte par draco pour ce
|
|
`sub`, aucun compte créé. La session que le pont ouvre
|
|
porte un marqueur « draco » couvert par la signature du
|
|
cookie (`user.expiry.draco.hmac`).
|
|
2. **`/admin`** (et ses actions, même garde
|
|
`_require_admin`) : session établie par draco + rôle du
|
|
registre connu ⇒ il faut `administrateur` dans le
|
|
registre, **en plus** du rôle local. Rôle inconnu ou
|
|
session native ⇒ règles locales, inchangées.
|
|
|
|
Les autres accès ne sont pas draco et ne sont pas touchés :
|
|
session native préexistante, connexion native (mot de
|
|
passe), Basic d'environnement, jetons `kbt_`. Ni les droits
|
|
internes ni la réconciliation par étiquettes ne changent.
|
|
|
|
## Surfaces voisines
|
|
|
|
- **`GET /api/search`** (« Rechercher partout ») : le cookie draco
|
|
n'y est plus accepté « en tant qu'opérateur ». Il faut que le
|
|
`sub` désigne un compte (correspondance existante ou compte
|
|
d'amorçage lié au passage). Aucune création JIT sur cette
|
|
surface : la création a lieu à la première vraie connexion, par
|
|
le pont. Les autres modes d'accès à `/api/search` (session,
|
|
Basic, jeton `kbt_`) sont inchangés.
|
|
- **Désactivation** : désactiver un compte dans `/admin` coupe
|
|
tous ses accès — session native, pont draco (la correspondance
|
|
résout à « refusé ») et jetons `kbt_`. Réactiver le compte
|
|
rétablit la correspondance telle quelle.
|
|
|
|
## Non-régressions garanties
|
|
|
|
- **Jetons personnels `kbt_`** : création, usage sur `/mcp` et
|
|
`/api/`, révocation — inchangés ; un jeton n'ouvre toujours pas
|
|
l'interface web.
|
|
- **Sessions propres Kanbante** et **connexion native**
|
|
(formulaire, mot de passe de la base) : inchangées, y compris
|
|
la liaison automatique à la première visite doublement
|
|
connectée.
|
|
- **Authentification Basic** (identifiants d'environnement, pour
|
|
les scripts) : inchangée.
|
|
- **Pont inerte sans `DRACO_SESSION_SECRET`** : sans secret
|
|
configuré, `/auth/draco` renvoie au login, comme avant.
|
|
- La réconciliation Kanbante/Gitea par étiquettes (adoption d'une
|
|
colonne seulement si exactement une étiquette `kanban:*` est
|
|
reconnue) n'est pas touchée par cette carte.
|