Compare commits
4
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3c4bbbf2f1 | ||
|
|
7556cd0bee | ||
|
|
aa98eb6866 | ||
|
|
409e67d57b |
+8
-2
@@ -438,10 +438,16 @@ class DracoAlertesCorsMiddleware(BaseHTTPMiddleware):
|
||||
if request.url.path != "/api/events":
|
||||
return await call_next(request)
|
||||
from app.config import resoudre_urls_draco
|
||||
from app.recherche import DEV_ORIGINS, PROD_ORIGINS, origin_of
|
||||
|
||||
urls = resoudre_urls_draco(request.headers.get("host"))
|
||||
soeurs = {urls.sso_url, urls.flowdeck_url, urls.tiktak_url,
|
||||
urls.public_url}
|
||||
# Correctif CORS alertes (fusion #69, 2026-10-28) : comme
|
||||
# pour /api/search, les origines sœurs prod ET dev sont
|
||||
# listées explicitement — l'ancienne allowlist (environnement
|
||||
# résolu seul) refusait les pages de l'autre environnement.
|
||||
soeurs = ({origin_of(u) for u in (urls.sso_url, urls.flowdeck_url,
|
||||
urls.tiktak_url, urls.public_url)}
|
||||
| set(PROD_ORIGINS) | set(DEV_ORIGINS)) - {None}
|
||||
origine = request.headers.get("origin", "")
|
||||
if origine not in soeurs:
|
||||
return await call_next(request)
|
||||
|
||||
+12
-4
@@ -9,9 +9,14 @@ testables sans base ni serveur.
|
||||
recherche Gitea de la page ``/search`` (titres + corps, tous états,
|
||||
tous projets du workspace) et la regroupe par dépôt.
|
||||
- La CORS bornée n'autorise que ``GET /api/search``, et seulement
|
||||
pour les origines sœurs résolues par ``app.config`` (explicite >
|
||||
hôte de la requête > dérivation > défaut officiel — donc ``-dev``
|
||||
reste ``-dev``), jamais ``*``. Le middleware est un ASGI pur :
|
||||
pour les origines sœurs officielles **prod ET dev, listées
|
||||
explicitement** (``PROD_ORIGINS | DEV_ORIGINS``), **plus** les
|
||||
origines résolues par ``app.config`` — jamais ``*``. Correctif
|
||||
2026-10-28 (fusion #68/#69, matrice constatée par Bruno) :
|
||||
l'ancienne allowlist ne contenait que l'environnement résolu
|
||||
(prod OU dev), donc une page de l'autre environnement voyait sa
|
||||
section afficher « Injoignable depuis cet hôte (CORS ou réseau) ».
|
||||
Le middleware est un ASGI pur :
|
||||
il répond lui-même aux preflights OPTIONS (qui ne portent jamais
|
||||
de cookies, donc qu'``AuthMiddleware`` refuserait sinon) et ajoute
|
||||
les en-têtes aux réponses de recherche, sans toucher aux autres
|
||||
@@ -97,7 +102,10 @@ def sister_origins(
|
||||
origin = origin_of((resolved or {}).get(key))
|
||||
if origin:
|
||||
origins.add(origin)
|
||||
return frozenset(origins)
|
||||
# Correctif 2026-10-28 : prod ET dev toujours, quelle que soit
|
||||
# la résolution — ``resolved`` n'ajoute que des origines de
|
||||
# configuration sans jamais retirer une origine officielle.
|
||||
return frozenset(origins) | PROD_ORIGINS | DEV_ORIGINS
|
||||
|
||||
|
||||
def cors_headers_for(
|
||||
|
||||
@@ -44,3 +44,10 @@ def test_cors_events_soeur_preflight_get_et_tiers(client):
|
||||
headers={"Origin": "https://evil.example",
|
||||
"Access-Control-Request-Method": "GET"})
|
||||
assert "access-control-allow-origin" not in r3.headers
|
||||
# Correctif 2026-10-28 : une origine sœur dev passe aussi.
|
||||
r4 = client.options("/api/events",
|
||||
headers={"Origin": "https://tiktak-dev.dracodev.net",
|
||||
"Access-Control-Request-Method": "GET"})
|
||||
assert r4.status_code == 200
|
||||
assert r4.headers.get("access-control-allow-origin") == "https://tiktak-dev.dracodev.net"
|
||||
assert r4.headers.get("access-control-allow-credentials") == "true"
|
||||
|
||||
+19
-2
@@ -34,7 +34,13 @@ def test_origin_of():
|
||||
|
||||
|
||||
def test_sister_origins_dev():
|
||||
# Correctif 2026-10-28 : 8 origines officielles, prod ET dev,
|
||||
# quelle que soit la résolution.
|
||||
assert sister_origins(RESOLVED_DEV) == {
|
||||
"https://og.dracodev.net",
|
||||
"https://flowdeck.dracodev.net",
|
||||
"https://tiktak.dracodev.net",
|
||||
"https://kanbante.dracodev.net",
|
||||
"https://og-dev.dracodev.net",
|
||||
"https://flowdeck-dev.dracodev.net",
|
||||
"https://tiktak-dev.dracodev.net",
|
||||
@@ -47,7 +53,11 @@ def test_cors_headers():
|
||||
assert headers["Access-Control-Allow-Origin"] == "https://tiktak-dev.dracodev.net"
|
||||
assert headers["Access-Control-Allow-Credentials"] == "true"
|
||||
assert cors_headers_for("https://evil.example", RESOLVED_DEV) is None
|
||||
assert cors_headers_for("https://og.dracodev.net", RESOLVED_DEV) is None
|
||||
assert cors_headers_for("https://og-dev.dracodev.net.evil.example",
|
||||
RESOLVED_DEV) is None
|
||||
# Autre environnement autorisé (correctif 2026-10-28).
|
||||
assert cors_headers_for("https://og.dracodev.net",
|
||||
RESOLVED_DEV) is not None
|
||||
|
||||
|
||||
def test_preflight_headers():
|
||||
@@ -86,7 +96,14 @@ def test_groupes_vers_json_vide():
|
||||
|
||||
|
||||
def test_sister_origins_hote_requete():
|
||||
# Configuration muette + hôte dev : le repli inclut les -dev.
|
||||
# Correctif 2026-10-28 : configuration muette, quel que soit
|
||||
# l'hôte de la requête, prod ET dev passent.
|
||||
assert "https://og-dev.dracodev.net" in sister_origins(
|
||||
{}, request_host="kanbante-dev.dracodev.net"
|
||||
)
|
||||
assert "https://og.dracodev.net" in sister_origins(
|
||||
{}, request_host="kanbante-dev.dracodev.net"
|
||||
)
|
||||
assert "https://og-dev.dracodev.net" in sister_origins(
|
||||
{}, request_host="kanbante.dracodev.net"
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user