Flowdeck 7.85.6 — porte centrale unique (phase 2) : écran réduit à l'entrée ObsiGate SSO + porte de secours administrateur par variables d'environnement, désactivée par défaut
This commit is contained in:
+14
-6
@@ -158,15 +158,23 @@ def get_reglages_acces(_admin=Depends(admin_required)):
|
||||
def put_reglages_acces(_admin=Depends(admin_required), body: dict = Body(default={})):
|
||||
"""Modifie les réglages d'accès — réversible à tout moment.
|
||||
|
||||
Corps accepté : ``{"inscription_locale_ouverte": true|false}``.
|
||||
Fermée (défaut) : aucun compte local ne se crée sur l'instance,
|
||||
les comptes naissent dans le Hub Dracodev. La connexion des
|
||||
comptes existants n'est jamais affectée.
|
||||
Corps accepté : ``{"inscription_locale_ouverte": true|false,
|
||||
"porte_centrale_active": true|false}`` (chaque clé optionnelle).
|
||||
Inscription fermée (défaut) : aucun compte local ne se crée sur
|
||||
l'instance, les comptes naissent dans le Hub Dracodev.
|
||||
Porte centrale (défaut : inactive) : activée, seuls la voie
|
||||
ObsiGate (SSO) et la porte de secours administrateur ouvrent
|
||||
une session.
|
||||
"""
|
||||
from app.services import reglages_acces
|
||||
kwargs = {}
|
||||
if "inscription_locale_ouverte" in body:
|
||||
return reglages_acces.set_reglages_acces(
|
||||
inscription_locale_ouverte=bool(body["inscription_locale_ouverte"]))
|
||||
kwargs["inscription_locale_ouverte"] = bool(
|
||||
body["inscription_locale_ouverte"])
|
||||
if "porte_centrale_active" in body:
|
||||
kwargs["porte_centrale_active"] = bool(body["porte_centrale_active"])
|
||||
if kwargs:
|
||||
return reglages_acces.set_reglages_acces(**kwargs)
|
||||
return reglages_acces.get_reglages_acces()
|
||||
|
||||
|
||||
|
||||
+99
-6
@@ -94,15 +94,64 @@ body{{background:#191919;color:#fff;font-family:-apple-system,BlinkMacSystemFont
|
||||
</html>"""
|
||||
|
||||
|
||||
def _rendre_page_connexion(html: str) -> str:
|
||||
"""Rend la page de connexion en tenant compte du réglage #146.
|
||||
def _page_porte_centrale() -> str:
|
||||
"""Écran de connexion en mode porte centrale (phase 2).
|
||||
|
||||
Inscription fermée : l'onglet Register disparaît et un avis nomme
|
||||
le Hub Dracodev (lien dérivé de l'hôte, comme les autres liens de
|
||||
la suite). Inscription ouverte : page intégralement inchangée.
|
||||
Ne présente **que** l'entrée centrale : le bouton ObsiGate (SSO),
|
||||
bien visible. Aucun formulaire local, aucun onglet Register —
|
||||
la porte de secours administrateur passe par le chemin discret
|
||||
``?secours=1``, jamais proposé ici.
|
||||
"""
|
||||
from app.services import draco_urls
|
||||
|
||||
hub = draco_urls.hub_url()
|
||||
return """<!DOCTYPE html>
|
||||
<html lang="fr">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>FlowDeck — Connexion</title>
|
||||
<style>
|
||||
body{background:#191919;color:#fff;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;display:flex;align-items:center;justify-content:center;min-height:100vh;margin:0;}
|
||||
.box{background:#222;border:1px solid rgba(255,255,255,.08);border-radius:12px;padding:40px;width:100%;max-width:400px;}
|
||||
.box h1{font-size:24px;margin:0 0 8px;}
|
||||
.box p{color:rgba(255,255,255,.55);font-size:14px;line-height:1.6;margin:0 0 20px;}
|
||||
.box a{color:#2383E2;}
|
||||
.sso{display:flex;align-items:center;justify-content:center;gap:8px;width:100%;padding:14px;border-radius:8px;font-size:15px;font-weight:500;cursor:pointer;border:1px solid rgba(35,131,226,.6);background:#2383E2;color:#fff;text-decoration:none;box-sizing:border-box;}
|
||||
.sso:hover{background:#2C8CEB;}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="box">
|
||||
<h1>FlowDeck</h1>
|
||||
<p>Cette instance n'accepte que la connexion centrale de la suite
|
||||
Dracodev. Les comptes se créent dans le
|
||||
<a href="__HUB_URL__">Hub Dracodev</a>.</p>
|
||||
<a class="sso" href="__SSO_LOGIN_URL__">🧭 Login with ObsiGate (SSO)</a>
|
||||
</div>
|
||||
</body>
|
||||
</html>""".replace("__HUB_URL__", hub)
|
||||
|
||||
|
||||
def _rendre_page_connexion(html: str, secours: bool = False) -> str:
|
||||
"""Rend la page de connexion en tenant compte des réglages d'accès.
|
||||
|
||||
Porte centrale active (phase 2) : seul l'écran central est rendu
|
||||
— sauf par le chemin discret de secours (``secours=True``), qui
|
||||
rend le formulaire local en marquant ses envois ``secours``.
|
||||
Inscription fermée (#146) : l'onglet Register disparaît et un
|
||||
avis nomme le Hub Dracodev (lien dérivé de l'hôte, comme les
|
||||
autres liens de la suite). Sinon : page intégralement inchangée.
|
||||
"""
|
||||
from app.services import draco_urls, reglages_acces
|
||||
|
||||
if reglages_acces.porte_centrale_active():
|
||||
if not secours:
|
||||
return _with_nonce(_page_porte_centrale())
|
||||
html = html.replace(
|
||||
"const body={email,password};",
|
||||
"const body={email,password,secours:true};")
|
||||
return _with_nonce(html)
|
||||
if reglages_acces.inscription_locale_ouverte():
|
||||
return _with_nonce(html)
|
||||
html = html.replace(
|
||||
@@ -257,6 +306,9 @@ def register_page(request: Request):
|
||||
"""
|
||||
from app.services import reglages_acces
|
||||
|
||||
if reglages_acces.porte_centrale_active():
|
||||
return HTMLResponse(_with_nonce(_page_porte_centrale()),
|
||||
status_code=403)
|
||||
if not reglages_acces.inscription_locale_ouverte():
|
||||
return HTMLResponse(_page_inscription_fermee(), status_code=403)
|
||||
return HTMLResponse(_with_nonce(LOCAL_LOGIN_HTML.replace(
|
||||
@@ -283,7 +335,13 @@ def login(request: Request, provider: str = Query("gitea")):
|
||||
# Local login page (POST handled by /auth/local-login)
|
||||
from fastapi.responses import HTMLResponse
|
||||
if provider == "local":
|
||||
return HTMLResponse(_rendre_page_connexion(LOCAL_LOGIN_HTML), status_code=200)
|
||||
# Chemin discret de la porte de secours (phase 2) :
|
||||
# ?secours=1 rend le formulaire local même en mode porte
|
||||
# centrale — jamais lié depuis l'écran principal.
|
||||
secours = request.query_params.get("secours") == "1"
|
||||
return HTMLResponse(
|
||||
_rendre_page_connexion(LOCAL_LOGIN_HTML, secours=secours),
|
||||
status_code=200)
|
||||
|
||||
# OAuth flow — check if provider is configured
|
||||
from app.auth.providers import get_provider
|
||||
@@ -341,6 +399,13 @@ def register(request: Request, body: dict = Body(default={})):
|
||||
# naissent dans le Hub Dracodev — aucun compte local ne se crée
|
||||
# ici. La connexion des comptes existants n'est pas touchée.
|
||||
from app.services import draco_urls, reglages_acces
|
||||
if reglages_acces.porte_centrale_active():
|
||||
from fastapi.responses import JSONResponse
|
||||
return JSONResponse(
|
||||
{"error": reglages_acces.message_porte_centrale(),
|
||||
"hub_url": draco_urls.hub_url()},
|
||||
status_code=403,
|
||||
)
|
||||
if not reglages_acces.inscription_locale_ouverte():
|
||||
from fastapi.responses import JSONResponse
|
||||
return JSONResponse(
|
||||
@@ -400,6 +465,34 @@ def local_login(request: Request, body: dict = Body(default={})):
|
||||
if not email or not password:
|
||||
return JSONResponse({"error": "Email and password required"}, status_code=400)
|
||||
|
||||
# Porte centrale unique (phase 2) : les identifiants locaux
|
||||
# ordinaires ne sont plus acceptés — comptes historiques inclus.
|
||||
# Seule exception : la porte de secours administrateur, par le
|
||||
# drapeau explicite `secours` (posé par le chemin discret) et les
|
||||
# identifiants des variables d'environnement. Les sessions déjà
|
||||
# établies ne passent pas par ici et ne sont pas touchées.
|
||||
from app.services import reglages_acces as _reglages_acces
|
||||
if _reglages_acces.porte_centrale_active():
|
||||
if not body.get("secours"):
|
||||
return JSONResponse(
|
||||
{"error": _reglages_acces.message_porte_centrale()},
|
||||
status_code=403,
|
||||
)
|
||||
compte_secours = _reglages_acces.authentifier_secours(email, password)
|
||||
if compte_secours is None:
|
||||
return JSONResponse(
|
||||
{"error": "Porte de secours : identifiants invalides."},
|
||||
status_code=401,
|
||||
)
|
||||
session = SessionManager.create_session(compte_secours, request)
|
||||
_log_login(compte_secours["id"], request)
|
||||
response = JSONResponse({"status": "ok", "user": {
|
||||
"login": compte_secours["login"],
|
||||
"name": compte_secours["full_name"]}})
|
||||
response.set_cookie("flowdeck_session", session, httponly=True,
|
||||
max_age=86400 * 7, samesite="lax", path="/")
|
||||
return response
|
||||
|
||||
with get_conn() as conn:
|
||||
user = conn.execute("SELECT * FROM users WHERE login=?", (email,)).fetchone()
|
||||
if not user:
|
||||
|
||||
Reference in New Issue
Block a user