fix(0.32.2): résolution par requête des URL dracodev, base agent dérivée et CSP du panneau
CI / test (push) Successful in 2m21s
CI / test (push) Successful in 2m21s
La dérivation 0.32.1 ne couvrait que les URL sœurs depuis AGENT_EVENEMENTS_URL, jamais agent_url : sur la dev, le panneau visait le défaut prod (agent injoignable, prod non installée), console morte. Désormais, pour tout rendu à une requête (barre, iframe du panneau, CSP frame-src) : explicite > hôte public au motif fermé dracodev > dérivation élargie (événements ou base du service) > défaut officiel. Contextes sans requête inchangés sinon ; aucun SSO ajouté.
This commit is contained in:
@@ -223,3 +223,200 @@ def test_pas_de_barre_sur_le_login(monkeypatch, fake_gitea):
|
||||
r = client.get("/login")
|
||||
assert r.status_code == 200
|
||||
assert "draco-bar" not in r.text
|
||||
|
||||
|
||||
# ── Résolution par requête et base de l'agent dérivée (0.32.2) ──
|
||||
# Couche fautive corrigée : la dérivation 0.32.1 ne couvrait que les
|
||||
# URL sœurs depuis AGENT_EVENEMENTS_URL — jamais agent_url, donc le
|
||||
# panneau visait le défaut prod (agent injoignable : la prod n'est
|
||||
# pas installée) et la console était une page morte sur la dev.
|
||||
# Aucune résolution ne tenait compte de l'hôte de la requête, et
|
||||
# aucune CSP n'énonçait l'origine de l'agent.
|
||||
_ENV_RESOLUTION = _ENV_URLS_SOEURS + ("AGENT_EVENEMENTS_URL",)
|
||||
|
||||
|
||||
def _nettoyer_env_resolution(monkeypatch):
|
||||
for nom in _ENV_RESOLUTION:
|
||||
monkeypatch.delenv(nom, raising=False)
|
||||
|
||||
|
||||
def test_config_base_agent_derivee_du_seul_agent_evenements(monkeypatch):
|
||||
# Le cas réel de la dev : seul le journal est déclaré en -dev.
|
||||
# La base du service (panneau/console) doit suivre, sinon
|
||||
# l'iframe de la console charge l'agent prod, injoignable.
|
||||
from app.config import _env_settings
|
||||
|
||||
_nettoyer_env_resolution(monkeypatch)
|
||||
monkeypatch.setenv(
|
||||
"AGENT_EVENEMENTS_URL",
|
||||
"https://agent-dev.dracodev.net/api/v1/evenements",
|
||||
)
|
||||
s = _env_settings()
|
||||
assert s.agent_url == "https://agent-dev.dracodev.net"
|
||||
assert _urls_soeurs(s) == _URLS_DEV
|
||||
|
||||
|
||||
def test_config_derivation_elargie_a_la_base_du_service(monkeypatch):
|
||||
# Élargie : si l'URL d'événements est hors motif mais que la
|
||||
# base du service suit le motif, les sœurs dérivent de la base.
|
||||
from app.config import _env_settings
|
||||
|
||||
_nettoyer_env_resolution(monkeypatch)
|
||||
monkeypatch.setenv("AGENT_URL", "https://agent-dev.dracodev.net")
|
||||
monkeypatch.setenv(
|
||||
"AGENT_EVENEMENTS_URL", "https://journal.example.test/evenements"
|
||||
)
|
||||
s = _env_settings()
|
||||
assert s.agent_url == "https://agent-dev.dracodev.net"
|
||||
assert s.agent_evenements_url == (
|
||||
"https://journal.example.test/evenements"
|
||||
)
|
||||
assert _urls_soeurs(s) == _URLS_DEV
|
||||
|
||||
|
||||
def test_suffixe_environnement_requete_motif_ferme():
|
||||
from app.config import suffixe_environnement_requete
|
||||
|
||||
assert suffixe_environnement_requete("kanbante-dev.dracodev.net") == "-dev"
|
||||
assert suffixe_environnement_requete("kanbante.dracodev.net") == ""
|
||||
assert suffixe_environnement_requete("og-dev.dracodev.net") == "-dev"
|
||||
assert suffixe_environnement_requete("agent-dev.dracodev.net:8790") == "-dev"
|
||||
assert suffixe_environnement_requete("Kanbante-Dev.Dracodev.Net") == "-dev"
|
||||
# Hors motif : jamais de suffixe inventé depuis un hôte arbitraire.
|
||||
assert suffixe_environnement_requete("192.168.30.101:8082") is None
|
||||
assert suffixe_environnement_requete("127.0.0.1") is None
|
||||
assert suffixe_environnement_requete("kanbante-dev.autre-domaine.net") is None
|
||||
assert suffixe_environnement_requete("dev.dracodev.net") is None
|
||||
assert suffixe_environnement_requete("testserver") is None
|
||||
assert suffixe_environnement_requete("") is None
|
||||
assert suffixe_environnement_requete(None) is None
|
||||
|
||||
|
||||
def test_requete_hote_dev_sans_env_tout_en_dev(monkeypatch, fake_gitea):
|
||||
# Aucune variable d'environnement : barre, panneau (source de
|
||||
# l'iframe de la console) et CSP entièrement en -dev, cohérents.
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
import app.main as main
|
||||
|
||||
_nettoyer_env_resolution(monkeypatch)
|
||||
with TestClient(main.app) as client:
|
||||
r = client.get("/", headers={"Host": "kanbante-dev.dracodev.net"})
|
||||
assert r.status_code == 200
|
||||
for url in _URLS_DEV:
|
||||
assert f"{url}/" in r.text
|
||||
# Source de l'iframe du panneau : l'origine injectée au JS.
|
||||
assert 'api: "https://agent-dev.dracodev.net"' in r.text
|
||||
# La CSP de la même réponse énonce la même origine résolue.
|
||||
assert r.headers["content-security-policy"] == (
|
||||
"frame-src 'self' https://agent-dev.dracodev.net"
|
||||
)
|
||||
# Aucun nom prod ne subsiste dans le rendu.
|
||||
for url in _DEFAUTS_OFFICIELS:
|
||||
assert f"{url}/" not in r.text
|
||||
assert 'api: "https://agent.dracodev.net"' not in r.text
|
||||
|
||||
|
||||
def test_requete_hote_prod_sans_env_noms_officiels(monkeypatch, fake_gitea):
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
import app.main as main
|
||||
|
||||
_nettoyer_env_resolution(monkeypatch)
|
||||
with TestClient(main.app) as client:
|
||||
r = client.get("/", headers={"Host": "kanbante.dracodev.net"})
|
||||
assert r.status_code == 200
|
||||
for url in _DEFAUTS_OFFICIELS:
|
||||
assert f"{url}/" in r.text
|
||||
assert 'api: "https://agent.dracodev.net"' in r.text
|
||||
assert r.headers["content-security-policy"] == (
|
||||
"frame-src 'self' https://agent.dracodev.net"
|
||||
)
|
||||
|
||||
|
||||
def test_requete_hote_lan_repli_configuration(monkeypatch, fake_gitea):
|
||||
# Hôte hors motif (LAN/IP) : repli sur la configuration, ici
|
||||
# sans variable — donc les défauts officiels, jamais devinés.
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
import app.main as main
|
||||
|
||||
_nettoyer_env_resolution(monkeypatch)
|
||||
with TestClient(main.app) as client:
|
||||
r = client.get("/", headers={"Host": "192.168.30.101:8082"})
|
||||
assert r.status_code == 200
|
||||
for url in _DEFAUTS_OFFICIELS:
|
||||
assert f"{url}/" in r.text
|
||||
assert 'api: "https://agent.dracodev.net"' in r.text
|
||||
assert r.headers["content-security-policy"] == (
|
||||
"frame-src 'self' https://agent.dracodev.net"
|
||||
)
|
||||
|
||||
|
||||
def test_requete_hote_lan_repli_derivation_configuration(
|
||||
monkeypatch, fake_gitea
|
||||
):
|
||||
# Hôte hors motif mais configuration en -dev (journal seul) :
|
||||
# le repli est la dérivation de configuration, panneau inclus.
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
import app.main as main
|
||||
|
||||
_nettoyer_env_resolution(monkeypatch)
|
||||
monkeypatch.setenv(
|
||||
"AGENT_EVENEMENTS_URL",
|
||||
"https://agent-dev.dracodev.net/api/v1/evenements",
|
||||
)
|
||||
with TestClient(main.app) as client:
|
||||
r = client.get("/", headers={"Host": "192.168.30.101:8082"})
|
||||
assert r.status_code == 200
|
||||
for url in _URLS_DEV:
|
||||
assert f"{url}/" in r.text
|
||||
assert 'api: "https://agent-dev.dracodev.net"' in r.text
|
||||
assert r.headers["content-security-policy"] == (
|
||||
"frame-src 'self' https://agent-dev.dracodev.net"
|
||||
)
|
||||
|
||||
|
||||
def test_requete_explicite_gagne_sur_hote(monkeypatch, fake_gitea):
|
||||
# L'explicite reste prioritaire, URL par URL : Flowdeck et
|
||||
# l'agent explicites gagnent sur l'hôte dev ; les autres
|
||||
# sœurs suivent l'hôte.
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
import app.main as main
|
||||
|
||||
_nettoyer_env_resolution(monkeypatch)
|
||||
monkeypatch.setenv("DRACO_FLOWDECK_URL", "https://flowdeck.example.test")
|
||||
monkeypatch.setenv("AGENT_URL", "https://agent.example.test")
|
||||
with TestClient(main.app) as client:
|
||||
r = client.get("/", headers={"Host": "kanbante-dev.dracodev.net"})
|
||||
assert r.status_code == 200
|
||||
assert "https://flowdeck.example.test/" in r.text
|
||||
assert "https://flowdeck-dev.dracodev.net/" not in r.text
|
||||
assert 'api: "https://agent.example.test"' in r.text
|
||||
# La CSP consomme l'origine explicite, comme l'iframe.
|
||||
assert r.headers["content-security-policy"] == (
|
||||
"frame-src 'self' https://agent.example.test"
|
||||
)
|
||||
for url in (
|
||||
"https://og-dev.dracodev.net/",
|
||||
"https://tiktak-dev.dracodev.net/",
|
||||
"https://kanbante-dev.dracodev.net/",
|
||||
):
|
||||
assert url in r.text
|
||||
|
||||
|
||||
def test_resolution_sans_requete_inchangee(monkeypatch):
|
||||
# Contextes sans requête : la résolution par configuration
|
||||
# seule, comme avant (défauts officiels sans environnement).
|
||||
from app.config import _env_settings, resoudre_urls_draco
|
||||
|
||||
_nettoyer_env_resolution(monkeypatch)
|
||||
urls = resoudre_urls_draco()
|
||||
assert urls.sso_url == "https://og.dracodev.net"
|
||||
assert urls.flowdeck_url == "https://flowdeck.dracodev.net"
|
||||
assert urls.tiktak_url == "https://tiktak.dracodev.net"
|
||||
assert urls.public_url == "https://kanbante.dracodev.net"
|
||||
assert urls.agent_url == "https://agent.dracodev.net"
|
||||
assert _env_settings().agent_url == "https://agent.dracodev.net"
|
||||
|
||||
Reference in New Issue
Block a user