From b8ebea1479ddb9d3de88e6a888aa7d4e0162b28d Mon Sep 17 00:00:00 2001 From: bruno Date: Wed, 28 Oct 2026 16:11:53 -0400 Subject: [PATCH 1/2] CORS recherche: autoriser les origines agent (console Spark) prod+dev --- app/middleware/recherche_cors.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/app/middleware/recherche_cors.py b/app/middleware/recherche_cors.py index 0c6d029..8f99954 100644 --- a/app/middleware/recherche_cors.py +++ b/app/middleware/recherche_cors.py @@ -9,7 +9,10 @@ Ce module n'autorise donc que : - le chemin ``/api/search`` (jamais les autres routes, jamais les écritures) ; - les origines sœurs : les origines officielles **prod ET dev, - listées explicitement** (``PROD_ORIGINS | DEV_ORIGINS``), **plus** + listées explicitement** (``PROD_ORIGINS | DEV_ORIGINS``), **plus + les origines de la console Spark** (``AGENT_ORIGINS`` : agent prod + ET dev — fédération de la recherche depuis la console, + 2026-10-28), **plus** celles résolues par ``app.services.draco_urls.toutes()`` (explicite > hôte > dérivation > défaut officiel) — jamais ``*``. Correctif 2026-10-28 (fusion #68/#69, matrice constatée par @@ -49,6 +52,12 @@ DEV_ORIGINS = frozenset({ "https://kanbante-dev.dracodev.net", }) +AGENT_ORIGINS = frozenset({ + "https://agent.dracodev.net", + "https://agent-dev.dracodev.net", +}) + + # Clés de draco_urls.toutes() qui sont des applications sœurs. _SOEUR_KEYS = ("obsigate", "flowdeck", "tiktak", "kanbante") @@ -113,7 +122,7 @@ def sister_origins(resolved: dict | None = None, request_host: str | None = None # l'environnement de la requête, désormais inclus d'office). # ``resolved`` n'ajoute que des origines de configuration sans # jamais retirer une origine officielle. - origins |= PROD_ORIGINS | DEV_ORIGINS + origins |= PROD_ORIGINS | DEV_ORIGINS | AGENT_ORIGINS return frozenset(origins) -- 2.54.0 From fb942bc87146461676bd43a5e42e08b0b97eb38e Mon Sep 17 00:00:00 2001 From: bruno Date: Wed, 28 Oct 2026 16:11:58 -0400 Subject: [PATCH 2/2] Tests CORS recherche: origines agent autorisees, tiers refuse --- tests/test_recherche.py | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/tests/test_recherche.py b/tests/test_recherche.py index bcebe93..bd5f8ed 100644 --- a/tests/test_recherche.py +++ b/tests/test_recherche.py @@ -38,7 +38,7 @@ def test_origin_of(): def test_sister_origins_dev(): - # Correctif 2026-10-28 : 8 origines officielles, prod ET dev, + # Correctif 2026-10-28 : 10 origines officielles (8 sœurs + 2 agent), prod ET dev, # quelle que soit la résolution. assert sister_origins(RESOLVED_DEV) == { "https://og.dracodev.net", @@ -49,9 +49,24 @@ def test_sister_origins_dev(): "https://flowdeck-dev.dracodev.net", "https://tiktak-dev.dracodev.net", "https://kanbante-dev.dracodev.net", + "https://agent.dracodev.net", + "https://agent-dev.dracodev.net", } + +def test_cors_headers_agent_console_autorise(): + # Fédération depuis la console (2026-10-28) : les origines agent + # prod ET dev passent, avec credentials ; un tiers reste refusé. + for origine in ("https://agent.dracodev.net", + "https://agent-dev.dracodev.net"): + headers = cors_headers_for(origine, RESOLVED_DEV) + assert headers is not None + assert headers["Access-Control-Allow-Origin"] == origine + assert headers["Access-Control-Allow-Credentials"] == "true" + assert cors_headers_for("https://agent.dracodev.net.evil.example", + RESOLVED_DEV) is None + def test_sister_origins_ajoute_env_de_la_requete(): # Correctif 2026-10-28 : configuration muette, quel que soit # l'hôte de la requête, prod ET dev passent — l'hôte ne -- 2.54.0