fix: A3–A8 — bloc « fallback admin » de l'audit sécurité (401 sans session, ACL, CSRF)
- A3 : helper `_require_user_id()` (401 sans session) sur profile/password/token/forge ; `PUT /api/user/password` exige `current_password` vérifié ; `/api/user` sorti de la liste CSRF exemptée - A4 : `POST /api/v1/token` → 401 sans session, chemin legacy `user_id=0` supprimé - A5 : CRUD membres d'espace (POST/PUT/DELETE) : session + rôle admin de l'espace (ou admin global), placeholder user créé en `is_admin=0`, GET membres sans session → 401 - A6 : `_require_view` → 404 et `_require_edit` → 401 quand il n'y a pas de session (fin du legacy single-user sur les collections) - A7 : création ET lecture de page → 401 sans session (PermissionManager conservé) ; `/board/api/pages` sorti de `EXCLUDED_PATHS` ; header CSRF manquant ajouté sur setItemIcon (local_workspace) - A8 : seed admin sans mot de passe codé en dur — aléatoire au premier boot loggé une fois, ou `FLOWDECK_ADMIN_PASSWORD` ; re-seed seulement si absent - tests : client connecté par défaut via `_TestSessionAuth` (session + CSRF injectés à la volée, jamais dans le cookie jar → plus de CookieConflict), helper `anon()` sur les 40 tests d'anonymat ; 1016/1016 verts, `ruff check app tests` OK
This commit is contained in:
@@ -13,6 +13,7 @@ import secrets
|
||||
import tempfile
|
||||
|
||||
import pytest
|
||||
from conftest import login_test_client
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
|
||||
@@ -36,7 +37,7 @@ def client():
|
||||
from app.main import app
|
||||
init_db()
|
||||
|
||||
yield TestClient(app)
|
||||
yield login_test_client(TestClient(app))
|
||||
|
||||
try:
|
||||
os.unlink(db_path)
|
||||
@@ -58,7 +59,11 @@ def _login(client):
|
||||
uid = conn.execute("SELECT id FROM users WHERE login=?", (login,)).fetchone()["id"]
|
||||
conn.commit()
|
||||
session = SessionManager.create_session({"id": uid, "login": login, "is_admin": 0})
|
||||
csrf = client.get("/api/csrf-token", cookies={"flowdeck_session": session}).json()["csrf_token"]
|
||||
# Réutilise le CSRF du jar : /api/csrf-token fait tourner le token à chaque
|
||||
# appel, un token rendu par un login précédent deviendrait invalide (403).
|
||||
csrf = client.cookies.get("csrf_token")
|
||||
if not csrf:
|
||||
csrf = client.get("/api/csrf-token", cookies={"flowdeck_session": session}).json()["csrf_token"]
|
||||
return session, csrf, uid
|
||||
|
||||
|
||||
@@ -76,13 +81,17 @@ def _login_admin(client):
|
||||
uid = conn.execute("SELECT id FROM users WHERE login=?", (login,)).fetchone()["id"]
|
||||
conn.commit()
|
||||
session = SessionManager.create_session({"id": uid, "login": login, "is_admin": 1})
|
||||
csrf = client.get("/api/csrf-token", cookies={"flowdeck_session": session}).json()["csrf_token"]
|
||||
# Réutilise le CSRF du jar : /api/csrf-token fait tourner le token à chaque
|
||||
# appel, un token rendu par un login précédent deviendrait invalide (403).
|
||||
csrf = client.cookies.get("csrf_token")
|
||||
if not csrf:
|
||||
csrf = client.get("/api/csrf-token", cookies={"flowdeck_session": session}).json()["csrf_token"]
|
||||
return session, csrf, uid
|
||||
|
||||
|
||||
def _mk_page(client, session, title, blocks=None):
|
||||
r = client.post("/board/api/pages", params={"title": title, "section": "Private"},
|
||||
cookies={"flowdeck_session": session}, headers={"X-CSRF-Token": "x"})
|
||||
cookies={"flowdeck_session": session})
|
||||
assert r.status_code == 200
|
||||
pid = r.json()["id"]
|
||||
if blocks is not None:
|
||||
|
||||
Reference in New Issue
Block a user