feat: URLs publiques de l'écosystème par configuration — dev/prod sans noms en dur #223
This commit is contained in:
+39
-3
@@ -186,15 +186,21 @@ class SecurityHeadersMiddleware(BaseHTTPMiddleware):
|
||||
# avec un nonce frais (`backend.csp`) ou servis par 'self'/CDN
|
||||
# listés s'exécutent. `style-src` garde 'unsafe-inline' (attributs
|
||||
# `style=` et `el.style` omniprésents — chantier séparé).
|
||||
# #223 : l'origine du service d'agents n'est pas figée — elle
|
||||
# est dérivée de la configuration (DRACO_AGENT_URL, sinon
|
||||
# l'origine d'AGENT_EVENEMENTS_URL, sinon le défaut officiel).
|
||||
from backend.draco_config import agent_origine
|
||||
|
||||
agent = agent_origine()
|
||||
response.headers["Content-Security-Policy"] = (
|
||||
"default-src 'self'; "
|
||||
f"script-src 'self' 'nonce-{nonce}' blob: https://cdnjs.cloudflare.com https://unpkg.com https://esm.sh https://cdn.jsdelivr.net https://static.cloudflareinsights.com; "
|
||||
"style-src 'self' 'unsafe-inline' https://cdnjs.cloudflare.com https://fonts.googleapis.com https://cdn.jsdelivr.net https://esm.sh; "
|
||||
"img-src 'self' data: blob:; "
|
||||
"connect-src 'self' blob: https://esm.sh https://unpkg.com https://cdnjs.cloudflare.com https://fonts.googleapis.com https://fonts.gstatic.com https://cdn.jsdelivr.net https://agent.dracodev.net; "
|
||||
f"connect-src 'self' blob: https://esm.sh https://unpkg.com https://cdnjs.cloudflare.com https://fonts.googleapis.com https://fonts.gstatic.com https://cdn.jsdelivr.net {agent}; "
|
||||
"font-src 'self' data: https://fonts.gstatic.com https://esm.sh; "
|
||||
"worker-src 'self' blob:; "
|
||||
"frame-src 'self' blob: https://agent.dracodev.net; "
|
||||
f"frame-src 'self' blob: {agent}; "
|
||||
"object-src 'none'; "
|
||||
"base-uri 'self'; "
|
||||
"form-action 'self'; "
|
||||
@@ -829,6 +835,36 @@ def _html_with_nonce(request: Request, name: str) -> str:
|
||||
request.state.csp_nonce,
|
||||
)
|
||||
|
||||
|
||||
def _index_html(request: Request) -> str:
|
||||
"""``index.html`` + injection des URL publiques de l'écosystème (#223).
|
||||
|
||||
Contrat d'injection de dracodev-commons : l'hôte (ObsiGate, qui
|
||||
sert la page) fournit les URL aux assets partagés — panneau Spark,
|
||||
barre d'apps, liens inter-apps lisent ``window.DRACO_URLS`` et ne
|
||||
portent que les défauts officiels en repli. Les valeurs viennent
|
||||
de :mod:`backend.draco_config` (environnement ; défauts officiels,
|
||||
noms en ``-dev`` déclarés par le seul ``.env`` du poste). Le script
|
||||
injecté passe par le même mécanisme de nonce CSP que les autres
|
||||
scripts inline de la page.
|
||||
"""
|
||||
import json as _json
|
||||
|
||||
from backend.csp import inject_csp_nonce
|
||||
from backend.draco_config import urls_publiques
|
||||
|
||||
html = (FRONTEND_DIR / "index.html").read_text(encoding="utf-8")
|
||||
script = (
|
||||
"<script>window.DRACO_URLS = "
|
||||
+ _json.dumps(urls_publiques()).replace("<", "\\u003c")
|
||||
+ ";</script>"
|
||||
)
|
||||
if "</head>" in html:
|
||||
html = html.replace("</head>", script + "</head>", 1)
|
||||
else: # repli défensif : tout en tête de document
|
||||
html = script + html
|
||||
return inject_csp_nonce(html, request.state.csp_nonce)
|
||||
|
||||
if FRONTEND_DIR.exists():
|
||||
# ``Cache-Control`` for /static is set by SecurityHeadersMiddleware (no-cache).
|
||||
app.mount("/static", StaticFiles(directory=str(FRONTEND_DIR)), name="static")
|
||||
@@ -907,5 +943,5 @@ if FRONTEND_DIR.exists():
|
||||
"""Serve the SPA index.html for all non-API routes."""
|
||||
index_file = FRONTEND_DIR / "index.html"
|
||||
if index_file.exists():
|
||||
return HTMLResponse(content=_html_with_nonce(request, "index.html"), headers={"Cache-Control": "no-cache"})
|
||||
return HTMLResponse(content=_index_html(request), headers={"Cache-Control": "no-cache"})
|
||||
raise HTTPException(status_code=404, detail="Frontend not found")
|
||||
|
||||
Reference in New Issue
Block a user