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)
This commit is contained in:
+36
-6
@@ -79,13 +79,26 @@ def ensure_bootstrap_admin() -> dict | None:
|
||||
# (app.main.draco_bridge) et du contrôle draco de /api/search :
|
||||
#
|
||||
# 1. correspondance existante → son compte (None si désactivé) ;
|
||||
# 2. 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 —
|
||||
# le compte d'amorçage (celui de Bruno, créé depuis
|
||||
# l'environnement) garde ainsi son accès sans verrouillage ;
|
||||
# 2. amorçage CONDITIONNÉ (fusion #145, 0.46.5) : s'il n'existe
|
||||
# qu'UN seul compte et que c'est un admin actif sans liaison,
|
||||
# ce compte d'amorçage ne peut être adopté que si
|
||||
# a) le sub entrant est « administrateur » pour kanbante
|
||||
# au registre des rôles du Hub (app.hub_roles, rôle
|
||||
# transmis par l'appelant via ``role_hub`` — dans le
|
||||
# doute, registre muet ou rôle moindre, PAS d'adoption),
|
||||
# ou b) l'adoption est initiée depuis une session native
|
||||
# déjà authentifiée (``liaison_session_native=True`` :
|
||||
# l'utilisateur connecté lie explicitement son compte) ;
|
||||
# tout autre sub inconnu suit le parcours ordinaire (JIT ou
|
||||
# refus selon la surface), JAMAIS l'amorçage ;
|
||||
# 3. sinon, si provisionner=True : création JIT d'un compte
|
||||
# propre à cette identité (voir provision_draco_user).
|
||||
#
|
||||
# Avant la 0.46.5, l'étape 2 liait l'amorçage au PREMIER sub
|
||||
# valide de passage : sur une instance fraîche, un simple
|
||||
# utilisateur de la suite devenait l'administrateur local
|
||||
# (audit « compte partout » du 2026-11-06, carte fusion #145).
|
||||
#
|
||||
# Un sub inconnu ne désigne JAMAIS un compte existant non lié
|
||||
# (ni l'opérateur d'environnement, ni un compte dont le nom
|
||||
# ressemblerait au nom affiché draco) : la liaison d'un compte
|
||||
@@ -163,7 +176,12 @@ def provision_draco_user(sub: str, nom_affiche: str = "") -> dict | None:
|
||||
return models.get_user(user_id)
|
||||
|
||||
|
||||
def resolve_draco_user(claims: dict | None, provisionner: bool = False) -> dict | None:
|
||||
def resolve_draco_user(
|
||||
claims: dict | None,
|
||||
provisionner: bool = False,
|
||||
role_hub: str | None = None,
|
||||
liaison_session_native: bool = False,
|
||||
) -> dict | None:
|
||||
"""Compte Kanbante désigné par des claims draco valides.
|
||||
|
||||
provisionner=False : correspondance et amorçage seulement
|
||||
@@ -173,6 +191,15 @@ def resolve_draco_user(claims: dict | None, provisionner: bool = False) -> dict
|
||||
connexion draco » du critère « un compte fonctionne
|
||||
partout »). Un compte désactivé résout à None et n'est
|
||||
jamais contourné par un JIT (la correspondance existe).
|
||||
|
||||
Adoption de l'amorçage (fusion #145) : ``role_hub`` est le
|
||||
rôle du sub au registre du Hub pour kanbante, déjà consulté
|
||||
par l'appelant (app.hub_roles ; ``None`` = inconnu, Hub
|
||||
muet) et ``liaison_session_native`` signale une adoption
|
||||
initiée depuis une session native authentifiée. L'amorçage
|
||||
n'est adopté que si l'un des deux vaut respectivement
|
||||
``"administrateur"`` / ``True`` — sinon, aucun paramètre ne
|
||||
doit être deviné ici : dans le doute, pas d'adoption.
|
||||
"""
|
||||
sub = str((claims or {}).get("sub") or "")
|
||||
if not sub:
|
||||
@@ -186,8 +213,11 @@ def resolve_draco_user(claims: dict | None, provisionner: bool = False) -> dict
|
||||
seul["role"] == "admin"
|
||||
and seul["active"]
|
||||
and not seul.get("draco_sub")
|
||||
and (role_hub == "administrateur" or liaison_session_native)
|
||||
):
|
||||
# Compte d'amorçage : liaison au premier passage.
|
||||
# Compte d'amorçage : adoption conditionnée (#145) —
|
||||
# administrateur au registre du Hub, ou liaison
|
||||
# explicite depuis une session native authentifiée.
|
||||
models.update_user(seul["id"], draco_sub=sub)
|
||||
models.log_admin(seul["id"], "draco.link.amorcage", sub)
|
||||
return models.get_user(seul["id"])
|
||||
|
||||
+53
-11
@@ -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(
|
||||
|
||||
Reference in New Issue
Block a user