diff --git a/app/api/admin.py b/app/api/admin.py index bcafae4..7dac54e 100644 --- a/app/api/admin.py +++ b/app/api/admin.py @@ -1,25 +1,44 @@ -from fastapi import APIRouter, Depends, Request +from fastapi import APIRouter, Depends, HTTPException, Request -from app.auth import require_admin, require_admin_or_user +from app.auth import is_admin_user, require_admin, require_admin_or_user +from app.config import settings from app.dependencies import get_store from app.schemas import UserCreate, UserCreated router = APIRouter() -@router.get("/admin/request-headers", dependencies=[Depends(require_admin_or_user)]) -async def request_headers(request: Request): +@router.get("/admin/request-headers") +async def request_headers(request: Request, key: str | None = None): """Discovery: zeigt die eingehenden HTTP-Header + Quell-IP. Hilft, hinter dem Reverse-Proxy/SSO den richtigen Identitaets-Header - (TRUSTED_AUTH_HEADER) festzustellen. Nur fuer Admins. + (TRUSTED_AUTH_HEADER) festzustellen. + + Henne-Ei: Beim Einrichten kennt das Gateway den SSO-Admin noch nicht. Daher + ist der Endpoint auch per Admin-Key aufrufbar - als Query (`?key=...`, im Browser + durchs SSO bequem) oder X-Admin-Key-Header. Nach dem Setup wieder meiden bzw. + den Key rotieren (er landet sonst in Proxy-Logs). """ + expected = settings.admin_api_key.strip() + provided = key or request.headers.get("x-admin-key") + ok = bool(expected) and provided is not None and provided.strip() == expected + if not ok and not is_admin_user(_current_user_or_none(request)): + raise HTTPException(status_code=403, detail="Admin privileges required") return { "client": request.client.host if request.client else None, "headers": dict(request.headers), } +def _current_user_or_none(request: Request): + from app.auth import authenticate, _bearer_token + + client_host = request.client.host if request.client else "" + token = _bearer_token(request.headers.get("authorization")) + return authenticate(request.headers, client_host, token) + + @router.post("/admin/users", response_model=UserCreated, dependencies=[Depends(require_admin)]) async def create_user(payload: UserCreate): """Legt einen Nutzer an und gibt das Bearer-Token EINMALIG zurueck.""" diff --git a/deploy/README.md b/deploy/README.md index ea45337..f874b08 100644 --- a/deploy/README.md +++ b/deploy/README.md @@ -39,13 +39,17 @@ eintragen, die `map $http_upgrade …` einmalig im http{}-Kontext anlegen, dann anlegen (Let's Encrypt) und per SSO schützen (nur erlaubte Tester/Gruppe). ## 4. Discovery: richtigen Identitäts-Header bestimmen -Als Admin **hinter dem SSO** aufrufen: +**Hinter dem SSO** aufrufen (durchs SSO-Portal einloggen, dann diese URL). Da das +Gateway den SSO-Admin zu diesem Zeitpunkt noch nicht kennt (Henne-Ei), den Admin-Key +als Query mitgeben: ``` -https://assistent.linix.de/api/admin/request-headers +https://assistent.linix.de/api/admin/request-headers?key= ``` -Dort den Header finden, der den eingeloggten SSO-Usernamen trägt (z. B. `X-Remote-User`, -`Remote-User`, `Auth-User` …). Diesen Namen in `TRUSTED_AUTH_HEADER` **und** in der -nginx-`proxy_set_header …`-Zeile eintragen, Gateway + nginx neu laden. +In der Ausgabe den Header finden, der den eingeloggten SSO-Usernamen trägt (z. B. +`X-Remote-User`, `Remote-User`, `Auth-User`, oder `Authorization: Basic …` mit dem +Usernamen). Diesen Namen in `TRUSTED_AUTH_HEADER` **und** in der nginx-`proxy_set_header +…`-Zeile eintragen, Gateway + nginx neu laden. Danach `?key=` nicht mehr nutzen +(landet in Proxy-Logs) bzw. den Key rotieren. ## 5. Test - `https://assistent.linix.de/` lädt die UI, links „Angemeldet als ".