Kanbante 0.46.5 — pont draco : adoption de l'amorçage conditionnée (fusion #145) et échec de connexion suggérant le SSO (fusion #144)
CI / test (push) Failing after 2m26s
CI / test (pull_request) Failing after 2m35s

This commit is contained in:
Spark
2026-11-07 04:16:19 +00:00
parent d89b35061b
commit f8f8af63e1
8 changed files with 449 additions and 38 deletions
+53 -11
View File
@@ -432,8 +432,8 @@ class AuthMiddleware(BaseHTTPMiddleware):
# Recherche partout (#68) : /api/search accepte aussi le jeton
# SSO draco_session — depuis la 0.46.0 (HUB-16), par
# utilisateur : le sub doit désigner un compte Kanbante
# (correspondance existante ou compte d'amorçage lié au
# passage, voir app.accounts.resolve_draco_user) ; un jeton
# (correspondance existante, voir
# app.accounts.resolve_draco_user) ; un jeton
# valide sans correspondance ne désigne plus l'opérateur et
# ne suffit plus. Aucun JIT ici : la création du compte a
# lieu à la première connexion par le pont /auth/draco.
@@ -576,9 +576,33 @@ async def login_submit(
if ok and user is not None and user["active"]:
models.log_admin(user["id"], "login.env_fallback", username)
if not ok:
# Fusion #144 (part Kanbante) : l'échec ne doit plus être
# une impasse muette pour un compte de la suite. Un compte
# créé au Hub n'a pas de mot de passe local ici (il vit
# chez ObsiGate) : le message suggère la voie centrale —
# le bouton « Se connecter avec ObsiGate (SSO) » présent
# sur ce même écran — au lieu du seul refus sec. Quand
# l'identifiant ne correspond à aucun compte local (et
# n'est pas l'opérateur d'environnement), le message le
# dit explicitement : c'est le cas typique du compte
# centralisé essayé dans le formulaire local.
if user is None and username != settings.auth_user:
erreur = (
"Identifiants incorrects — aucun compte local ne "
"correspond à cet identifiant. Si ton compte a été "
"créé dans la suite Dracodev (au Hub), il n'a pas "
"de mot de passe local ici : utilise « Se connecter "
"avec ObsiGate (SSO) » ci-dessous."
)
else:
erreur = (
"Identifiants incorrects. Si tu te connectes avec "
"un compte de la suite Dracodev, utilise plutôt "
"« Se connecter avec ObsiGate (SSO) » ci-dessous."
)
return render(
request, "login.html", status_code=401,
next=_safe_next(next), error="Identifiants incorrects.",
next=_safe_next(next), error=erreur,
)
if user is not None:
models.update_user(
@@ -587,8 +611,10 @@ async def login_submit(
"%Y-%m-%d %H:%M:%S", time.gmtime()
),
)
# First doubly-connected visit: link the draco identity
# to this account (same mechanics as the other apps).
# Première visite doublement connectée : la session
# native vient d'être authentifiée par mot de passe —
# lier l'identité draco à ce compte est la voie (b) de
# liaison explicite de l'amorçage (fusion #145).
from app import draco
claims = draco.claims_for(request)
@@ -624,11 +650,15 @@ def draco_bridge(request: Request, next: str = "/"):
résolution centralisée dans app.accounts.resolve_draco_user :
- session native déjà ouverte : le ``sub`` libre est lié à
ce compte (première visite doublement connectée, inchangé) ;
ce compte (première visite doublement connectée, inchangé) —
c'est la voie (b) de la liaison explicite de l'amorçage ;
- ``sub`` lié à un compte : sa session est ouverte ;
- compte d'amorçage (unique compte, admin, sans liaison) :
liaison au premier passage, puis sa session — le compte
existant de l'opérateur garde son accès ;
adoption CONDITIONNÉE (fusion #145, 0.46.5) — seulement si
le registre du Hub donne « administrateur » pour kanbante
à ce ``sub`` (rôle consulté ci-dessous et transmis à la
résolution) ; dans le doute (Hub muet, rôle moindre), PAS
d'adoption : le parcours ordinaire suit, ci-dessous ;
- sinon : création JIT d'un compte propre à cette identité
(rôle membre, mot de passe local aléatoire non divulgué)
et ouverture de sa session.
@@ -682,6 +712,12 @@ def draco_bridge(request: Request, next: str = "/"):
),
)
# Déjà connecté nativement : lier le sub à ce compte.
# C'est la voie (b) de l'adoption de l'amorçage (fusion
# #145) : la liaison est initiée par l'utilisateur déjà
# authentifié en session native, qui connaît donc les
# identifiants du compte qu'il lie — aucun rôle du registre
# n'est exigé pour CE geste explicite (le refus « aucun »
# ci-dessus continue de le précéder, patron HUB-19).
current = _current_user(request)
if current is not None and current.get("id") is not None:
if not current.get("draco_sub"):
@@ -689,12 +725,18 @@ def draco_bridge(request: Request, next: str = "/"):
models.update_user(current["id"], draco_sub=sub)
models.log_admin(current["id"], "draco.link", sub)
return RedirectResponse(_safe_next(next), status_code=303)
# Correspondance, amorçage ou création JIT : ouvrir la
# session du compte que ce sub désigne — et seulement lui.
# Correspondance, amorçage conditionné ou création JIT :
# ouvrir la session du compte que ce sub désigne — et
# seulement lui. Le rôle du registre consulté ci-dessus est
# transmis à la résolution : c'est lui (et lui seul, hors
# session native déjà traitée plus haut) qui peut autoriser
# l'adoption du compte d'amorçage (fusion #145).
# La session est marquée « établie par draco » (segment signé,
# voir _make_session) : c'est elle, et elle seule, que le
# garde de /admin soumettra au registre du Hub.
user = accounts_service.resolve_draco_user(claims, provisionner=True)
user = accounts_service.resolve_draco_user(
claims, provisionner=True, role_hub=role_hub
)
if user is not None:
response = RedirectResponse(_safe_next(next), status_code=303)
response.set_cookie(