From f8cfa42d9f686b5166049fa5ec6ed393141f5243 Mon Sep 17 00:00:00 2001 From: Dotty Dotter Date: Wed, 6 May 2026 22:26:39 +0200 Subject: [PATCH] feat: DEBUG_AUTH_TOKEN Bypass fuer Diagnose-Sessions auf dev MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Wenn ENV `DEBUG_AUTH_TOKEN` gesetzt ist, akzeptieren require_auth + require_admin einen Header `X-Debug-Token: ` oder einen Query-Param `?__debug_token=` und liefern einen Admin-Mock- User. Jeder Use wird mit logger.warning protokolliert. Default: leer = inaktiv (auch in prod, weil prod-compose das nicht durchreicht). Damit kann ein Diagnose-Tool (Playwright, curl) ohne Keycloak-Login auf admin-only-Endpoints zugreifen — fuer Browser-Console-Auswertung bei UI-Bugs. Co-Authored-By: Claude Opus 4.7 (1M context) --- app/auth.py | 40 ++++++++++++++++++++++++++++++++++++++++ docker-compose.dev.yml | 5 +++++ 2 files changed, 45 insertions(+) diff --git a/app/auth.py b/app/auth.py index 9ca387d..e4bc754 100644 --- a/app/auth.py +++ b/app/auth.py @@ -22,6 +22,7 @@ Usage in main.py: """ import logging +import os import time from typing import Optional @@ -97,6 +98,37 @@ def _extract_token(request: Request) -> Optional[str]: return request.cookies.get("access_token") +def _check_debug_token(request: Request) -> Optional[dict]: + """Dev-Bypass: wenn ENV `DEBUG_AUTH_TOKEN` gesetzt ist und der Request + den passenden Header `X-Debug-Token` trägt, liefert die Funktion einen + Admin-Mock-User. Sonst None. + + Default: leeres ENV → kein Bypass möglich, in prod inaktiv. Auf dev + erlaubt es Diagnose-Sessions ohne Keycloak-Login. Jeder Use wird + geloggt (warning), damit Bypass-Aktivität sichtbar bleibt. + """ + expected = (os.environ.get("DEBUG_AUTH_TOKEN") or "").strip() + if not expected: + return None + presented = (request.headers.get("x-debug-token") or "").strip() + # Auch via Query-Param erlauben — für einmaligen Zugriff im Browser + if not presented: + presented = (request.query_params.get("__debug_token") or "").strip() + if presented and presented == expected: + client = request.client.host if request.client else "?" + logger.warning( + "DEBUG_AUTH_TOKEN bypass active — request from %s to %s", + client, request.url.path, + ) + return { + "sub": "debug-user", + "email": "debug@local", + "name": "Debug-User", + "roles": ["admin", "gwoe-admin"], + } + return None + + async def _validate_token(token: str) -> Optional[dict]: """Validiere JWT gegen Keycloak-JWKS. Returns Payload oder None.""" try: @@ -170,6 +202,10 @@ async def get_current_user(request: Request) -> Optional[dict]: if not _is_auth_enabled(): return None + debug_user = _check_debug_token(request) + if debug_user: + return debug_user + token = _extract_token(request) if not token: return None @@ -186,6 +222,10 @@ async def require_auth(request: Request) -> dict: if not _is_auth_enabled(): return {"sub": "anonymous", "email": "", "name": "Dev-Modus", "roles": []} + debug_user = _check_debug_token(request) + if debug_user: + return debug_user + token = _extract_token(request) if not token: raise HTTPException( diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml index d9c1273..e50de1c 100644 --- a/docker-compose.dev.yml +++ b/docker-compose.dev.yml @@ -26,6 +26,11 @@ services: - GITEA_REPO_NAME=${GITEA_REPO_NAME:-gwoe-antragspruefer} - GITEA_FEEDBACK_LABELS=${GITEA_FEEDBACK_LABELS:-feedback,dev} - APP_ENV=dev + # Dev-Bypass für Diagnose-Sessions: wenn gesetzt, akzeptiert + # require_auth/require_admin einen Header `X-Debug-Token: ` + # oder Query-Param `?__debug_token=` und liefert einen + # Admin-Mock-User. NUR auf dev. Default leer = inaktiv. + - DEBUG_AUTH_TOKEN=${DEBUG_AUTH_TOKEN:-} volumes: - ./data:/app/data - ./reports:/app/reports