feat(admin): Header-Discovery per ?key= durchs SSO aufrufbar
Beim SSO-Setup kennt das Gateway den Admin-Nutzer noch nicht (Henne-Ei). Der Discovery-Endpoint /api/admin/request-headers akzeptiert daher zusaetzlich den ADMIN_API_KEY als Query (?key=) oder X-Admin-Key-Header, damit man den von SSOwat injizierten Identitaets-Header im Browser bestimmen kann. Doku entsprechend. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
434a154af2
commit
887faa315e
2 changed files with 33 additions and 10 deletions
|
|
@ -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.dependencies import get_store
|
||||||
from app.schemas import UserCreate, UserCreated
|
from app.schemas import UserCreate, UserCreated
|
||||||
|
|
||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
|
|
||||||
|
|
||||||
@router.get("/admin/request-headers", dependencies=[Depends(require_admin_or_user)])
|
@router.get("/admin/request-headers")
|
||||||
async def request_headers(request: Request):
|
async def request_headers(request: Request, key: str | None = None):
|
||||||
"""Discovery: zeigt die eingehenden HTTP-Header + Quell-IP.
|
"""Discovery: zeigt die eingehenden HTTP-Header + Quell-IP.
|
||||||
|
|
||||||
Hilft, hinter dem Reverse-Proxy/SSO den richtigen Identitaets-Header
|
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 {
|
return {
|
||||||
"client": request.client.host if request.client else None,
|
"client": request.client.host if request.client else None,
|
||||||
"headers": dict(request.headers),
|
"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)])
|
@router.post("/admin/users", response_model=UserCreated, dependencies=[Depends(require_admin)])
|
||||||
async def create_user(payload: UserCreate):
|
async def create_user(payload: UserCreate):
|
||||||
"""Legt einen Nutzer an und gibt das Bearer-Token EINMALIG zurueck."""
|
"""Legt einen Nutzer an und gibt das Bearer-Token EINMALIG zurueck."""
|
||||||
|
|
|
||||||
|
|
@ -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).
|
anlegen (Let's Encrypt) und per SSO schützen (nur erlaubte Tester/Gruppe).
|
||||||
|
|
||||||
## 4. Discovery: richtigen Identitäts-Header bestimmen
|
## 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=<ADMIN_API_KEY>
|
||||||
```
|
```
|
||||||
Dort den Header finden, der den eingeloggten SSO-Usernamen trägt (z. B. `X-Remote-User`,
|
In der Ausgabe den Header finden, der den eingeloggten SSO-Usernamen trägt (z. B.
|
||||||
`Remote-User`, `Auth-User` …). Diesen Namen in `TRUSTED_AUTH_HEADER` **und** in der
|
`X-Remote-User`, `Remote-User`, `Auth-User`, oder `Authorization: Basic …` mit dem
|
||||||
nginx-`proxy_set_header …`-Zeile eintragen, Gateway + nginx neu laden.
|
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
|
## 5. Test
|
||||||
- `https://assistent.linix.de/` lädt die UI, links „Angemeldet als <SSO-User>".
|
- `https://assistent.linix.de/` lädt die UI, links „Angemeldet als <SSO-User>".
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue