Flowdeck 7.85.2 — HUB-19 : propagation des rôles du Hub (registre D3(a))
FlowDeck CI / test (push) Failing after 1s
FlowDeck CI / docker (push) Skipped
FlowDeck CI / lint (push) Failing after 12s
FlowDeck CI / test (pull_request) Failing after 2s
FlowDeck CI / docker (pull_request) Skipped
FlowDeck CI / lint (pull_request) Failing after 14s

This commit is contained in:
Spark
2026-11-06 15:36:56 +00:00
parent 5203b6288e
commit bd5357f589
12 changed files with 579 additions and 4 deletions
+10
View File
@@ -11,6 +11,16 @@ async def admin_required(request: Request):
user = get_current_user(request)
if not user:
raise HTTPException(status_code=403, detail="Admin access required")
# HUB-19 (registre des rôles du Hub) : session draco + rôle connu
# pour Flowdeck → exiger « administrateur » dans le registre,
# en plus du drapeau local. Pas de session draco ou rôle inconnu
# (Hub injoignable) : comportement actuel.
from app.services import hub_roles
if not hub_roles.admin_hub_ok(request):
raise HTTPException(
status_code=403,
detail="Niveau « administrateur » requis dans le registre "
"des rôles du Hub pour FlowDeck")
# Also check DB directly (session cookie may be stale)
if not user.get("is_admin"):
from app.db import get_conn
+8
View File
@@ -127,6 +127,14 @@ def _current_admin(request: Request) -> dict:
row = conn.execute("SELECT is_admin FROM users WHERE id=?", (user.get("id"),)).fetchone()
if not row or not row["is_admin"]:
raise HTTPException(status_code=403, detail="Accès administrateur requis")
# HUB-19 : session draco + rôle Hub connu → exiger « administrateur »
# dans le registre du Hub (voir app.services.hub_roles.admin_hub_ok).
from app.services import hub_roles
if not hub_roles.admin_hub_ok(request):
raise HTTPException(
status_code=403,
detail="Niveau « administrateur » requis dans le registre "
"des rôles du Hub pour FlowDeck")
return user
+8
View File
@@ -27,6 +27,14 @@ def _admin_user(request: Request) -> dict:
row = conn.execute("SELECT is_admin FROM users WHERE id=?",
(sess.get("id"),)).fetchone()
if row and row["is_admin"]:
# HUB-19 : session draco + rôle Hub connu → exiger
# « administrateur » dans le registre du Hub (la voie
# Bearer ci-dessous, contrat d'API propre, est exemptée).
from app.services import hub_roles
if not hub_roles.admin_hub_ok(request):
raise HTTPException(
403, "Niveau « administrateur » requis dans le "
"registre des rôles du Hub pour FlowDeck")
return sess
raise HTTPException(403, "Admin required")
auth = request.headers.get("authorization") or request.headers.get("Authorization") or ""
+30 -1
View File
@@ -483,10 +483,39 @@ def draco_bridge(request: Request, next: str = "/workspaces"):
Point de retour de ``GET /sso/authorize`` de l'émettrice : si le
cookie est valide et résolu vers un compte local, une session
Flowdeck native est créée ; sinon, retour à la connexion locale.
HUB-19 (registre des rôles du Hub, contrat D3(a)) : rôle connu
``aucun`` pour Flowdeck → refus 403 en français nommant le
registre du Hub, **avant** la création de la session locale et
avant tout provisionnement JIT (aucun compte créé pour un
``sub`` refusé). Exemption, patron du pilote ShaarIt : une
session locale préexistante (accès de secours) n'est pas
refusée — le pont la laisse simplement se poursuivre. Rôle
inconnu (Hub injoignable/erreur) : comportement actuel.
"""
from fastapi.responses import HTMLResponse
from app.services import draco
user = draco.user_for_draco(request)
_cookie_session = request.cookies.get("flowdeck_session", "")
session_existante = (SessionManager.decode_session(_cookie_session)
if _cookie_session else None)
try:
user = draco.user_for_draco(
request, refuser_aucun=not session_existante)
except draco.RoleAucunRefuse:
return HTMLResponse(
"""<!DOCTYPE html><html lang="fr"><head><meta charset="utf-8">
<title>Accès refusé — FlowDeck</title></head><body><main>
<h1>Accès refusé</h1>
<p>Le registre des rôles de la suite (Hub Dracodev) attribue le niveau
« aucun » à votre compte pour FlowDeck : l'accès par la session draco
est refusé. L'administrateur de suite peut modifier ce niveau dans la
matrice des rôles du Hub. L'accès de secours par le mot de passe local
de l'instance reste possible par la page de connexion.</p>
</main></body></html>""",
status_code=403,
)
if user is None:
return RedirectResponse(url="/auth/login?provider=local", status_code=302)
session = SessionManager.create_session(user, request)
+8
View File
@@ -46,6 +46,14 @@ def _admin_session(request: Request) -> dict:
row = conn.execute("SELECT is_admin FROM users WHERE id=?", (user["id"],)).fetchone()
if not row or not row["is_admin"]:
raise HTTPException(403, "Admin required")
# HUB-19 : session draco + rôle Hub connu → exiger « administrateur »
# dans le registre du Hub (les jetons SCIM Bearer, contrat propre,
# passent par _scim_guard et sont exemptés).
from app.services import hub_roles
if not hub_roles.admin_hub_ok(request):
raise HTTPException(
403, "Niveau « administrateur » requis dans le registre "
"des rôles du Hub pour FlowDeck")
return user
+9
View File
@@ -527,6 +527,15 @@ def _require_admin(request: Request, *, write: bool) -> dict:
).fetchone()
if not row or not row["is_admin"]:
raise HTTPException(status_code=403, detail="Admin access required")
# HUB-19 : session draco + rôle Hub connu → exiger « administrateur »
# dans le registre du Hub (la voie Bearer ci-dessus, contrat d'API
# propre, est exemptée).
from app.services import hub_roles
if not hub_roles.admin_hub_ok(request):
raise HTTPException(
status_code=403,
detail="Niveau « administrateur » requis dans le registre "
"des rôles du Hub pour FlowDeck")
if write and request.method in ("POST", "PUT", "PATCH", "DELETE"):
cookie = request.cookies.get("csrf_token", "")
header = request.headers.get("X-CSRF-Token", "")