Flowdeck 7.85.5 — inscription locale fermée par défaut (fusion #146) + échec de connexion nommant la voie ObsiGate SSO (fusion #144)
FlowDeck CI / docker (push) Blocked by required conditions
FlowDeck CI / test (push) In progress
FlowDeck CI / lint (push) Failing after 1s
FlowDeck CI / lint (pull_request) Failing after 3s
FlowDeck CI / test (pull_request) Failing after 35m42s
FlowDeck CI / docker (pull_request) Skipped

This commit is contained in:
Spark
2026-11-07 04:29:23 +00:00
parent 8915c7ddd1
commit 931e4d740b
10 changed files with 451 additions and 9 deletions
+8
View File
@@ -127,6 +127,14 @@ def client():
from app.main import app
init_db()
# Fusion #146 : l'inscription locale est fermée par défaut en
# production (les comptes naissent au Hub). L'environnement de
# test l'ouvre explicitement pour les suites historiques qui
# créent leurs comptes par /auth/register ; les tests du réglage
# (tests/test_reglages_acces.py) la ferment eux-mêmes.
from app.services import reglages_acces
reglages_acces.set_reglages_acces(inscription_locale_ouverte=True)
yield login_test_client(TestClient(app))
# Cleanup
+177
View File
@@ -0,0 +1,177 @@
"""FlowDeck — réglages d'accès : inscription locale (fusion #146, #144).
Contrat de la phase 1 du programme « compte partout » :
- inscription locale **fermée par défaut** (décision de Bruno) :
le parcours public d'inscription est refusé proprement et nomme
le Hub Dracodev, là où les comptes se créent ;
- réglage **réversible par un administrateur** (API
``/api/admin/reglages-acces``) : ouverte, l'inscription redevient
exactement celle d'aujourd'hui ;
- la connexion des comptes existants (locaux et draco) n'est
**jamais** affectée par le réglage ;
- #144 : l'échec du formulaire de connexion locale suggère la voie
ObsiGate (SSO).
Le harnais de test ouvre l'inscription pour les suites historiques
(voir conftest) ; chaque test pose donc explicitement l'état qu'il
vérifie via le service des réglages.
"""
from __future__ import annotations
from conftest import anon_csrf
from app.services import reglages_acces
def _fermer():
reglages_acces.set_reglages_acces(inscription_locale_ouverte=False)
def _ouvrir():
reglages_acces.set_reglages_acces(inscription_locale_ouverte=True)
def _creer_compte_local(login: str, mot_de_passe: str, admin: int = 0) -> None:
from app.db import get_conn
from app.password_utils import hash_password
with get_conn() as conn:
conn.execute(
"INSERT OR REPLACE INTO users (login, full_name, email,"
" password_hash, is_admin) VALUES (?, ?, ?, ?, ?)",
(login, login, f"{login}@test.dev",
hash_password(mot_de_passe), admin),
)
conn.commit()
# ── Réglage fermé : inscription impossible + message ────────────────
def test_inscription_fermee_par_defaut_dans_le_code():
"""Le défaut du code (hors toute ligne en base) est « fermée »."""
from app.config import settings
assert settings.inscription_locale_ouverte is False
def test_inscription_fermee_post_refuse_avec_message_hub(client):
_fermer()
r = client.post("/auth/register", json={
"email": "[email protected]", "password": "secret123",
"name": "Visiteur"})
assert r.status_code == 403
erreur = r.json()["error"]
assert "Hub Dracodev" in erreur
# Lien dérivé vers le Hub fourni à l'écran comme aux API.
assert r.json()["hub_url"]
# Aucun compte n'a été créé.
from app.db import get_conn
with get_conn() as conn:
assert conn.execute(
"SELECT COUNT(*) FROM users WHERE login='[email protected]'"
).fetchone()[0] == 0
def test_inscription_fermee_page_refus_propre(client):
_fermer()
r = anon_csrf(client).get("/auth/register", follow_redirects=False)
assert r.status_code == 403
assert "Hub Dracodev" in r.text
assert "les comptes se créent" in r.text
def test_inscription_fermee_page_connexion_sans_onglet_register(client):
_fermer()
r = anon_csrf(client).get("/auth/login?provider=local")
assert r.status_code == 200
assert 'id="tab-register"' not in r.text
# L'avis nomme le Hub et la voie SSO reste visible.
assert "Hub Dracodev" in r.text
assert "Login with ObsiGate (SSO)" in r.text
# ── Réglage ouvert : inscription comme aujourd'hui ──────────────────
def test_inscription_ouverte_post_comme_avant(client):
_ouvrir()
r = client.post("/auth/register", json={
"email": "[email protected]", "password": "secret123",
"name": "Nouveau"})
assert r.status_code == 200
assert r.json()["status"] == "ok"
def test_inscription_ouverte_page_register_affichee(client):
_ouvrir()
r = anon_csrf(client).get("/auth/register", follow_redirects=False)
assert r.status_code == 200
assert 'id="tab-register"' in r.text
# ── Connexion des comptes existants : jamais affectée ───────────────
def test_connexion_compte_local_existant_inscription_fermee(client):
_creer_compte_local("ancien", "secret123")
_fermer()
r = anon_csrf(client).post("/auth/local-login", json={
"email": "ancien", "password": "secret123"})
assert r.status_code == 200
assert r.json()["status"] == "ok"
def test_connexion_compte_draco_sans_mot_de_passe_inchangee(client):
"""Un compte draco (sans mot de passe local) reste refusé en local
de la même façon, réglage ouvert ou fermé : le réglage ne change
rien à l'authentification."""
from app.db import get_conn
with get_conn() as conn:
conn.execute(
"INSERT OR REPLACE INTO users (login, full_name, email,"
" password_hash, auth_method) VALUES"
" ('draco-42', 'Dan Test', '[email protected]', NULL, 'draco')")
conn.commit()
for ouvrir in (True, False):
reglages_acces.set_reglages_acces(
inscription_locale_ouverte=ouvrir)
r = anon_csrf(client).post("/auth/local-login", json={
"email": "draco-42", "password": "nimporte"})
assert r.status_code == 401
# ── #144 : l'échec local suggère la voie ObsiGate (SSO) ─────────────
def test_echec_connexion_locale_suggere_sso(client):
_creer_compte_local("quelquun", "secret123")
r = anon_csrf(client).post("/auth/local-login", json={
"email": "quelquun", "password": "mauvais-mot-de-passe"})
assert r.status_code == 401
assert "ObsiGate (SSO)" in r.json()["error"]
assert "Hub Dracodev" in r.json()["error"]
def test_echec_connexion_compte_inconnu_suggere_sso(client):
r = anon_csrf(client).post("/auth/local-login", json={
"email": "[email protected]", "password": "nimporte"})
assert r.status_code == 401
assert "ObsiGate (SSO)" in r.json()["error"]
# ── Administration du réglage ────────────────────────────────────────
def test_api_admin_reglages_acces_aller_retour(client):
"""Le client de test est administrateur : lecture + écriture."""
_fermer()
r = client.get("/api/admin/reglages-acces")
assert r.status_code == 200
assert r.json()["inscription_locale_ouverte"] is False
r = client.put("/api/admin/reglages-acces",
json={"inscription_locale_ouverte": True})
assert r.status_code == 200
assert r.json()["inscription_locale_ouverte"] is True
assert reglages_acces.inscription_locale_ouverte() is True
def test_api_admin_reglages_acces_refusee_aux_anonymes(client):
r = anon_csrf(client).get("/api/admin/reglages-acces")
assert r.status_code in (401, 403)