from fastapi import APIRouter, Depends, HTTPException, Request 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 MemoryCreate, MemoryOut, UserCreate, UserCreated, UserUpdate router = APIRouter() @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. 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.""" user, token = get_store().create_user(payload.display_name) return UserCreated(user_id=user.id, display_name=user.display_name, token=token) @router.delete("/admin/users/{user_id}", dependencies=[Depends(require_admin)]) async def delete_user(user_id: str): """Loescht einen Nutzer und alle seine Daten (Sessions, Nachrichten, Erinnerungen, Nutzungsdaten). Der anonyme Nutzer kann nicht geloescht werden.""" try: deleted = get_store().delete_user(user_id) except ValueError as exc: raise HTTPException(status_code=400, detail=str(exc)) if not deleted: raise HTTPException(status_code=404, detail=f"Nutzer {user_id!r} nicht gefunden.") return {"deleted": user_id} @router.post("/admin/users/{user_id}/token", response_model=UserCreated, dependencies=[Depends(require_admin)]) async def reset_token(user_id: str): """Stellt einen neuen Bearer-Token aus; der alte wird sofort ungueltig. Der neue Token wird EINMALIG zurueckgegeben und danach nicht mehr angezeigt.""" result = get_store().reset_token(user_id) if result is None: raise HTTPException(status_code=404, detail=f"Nutzer {user_id!r} nicht gefunden.") user, token = result return UserCreated(user_id=user.id, display_name=user.display_name, token=token) @router.put("/admin/users/{user_id}", dependencies=[Depends(require_admin)]) async def update_user(user_id: str, payload: UserUpdate): """Aktualisiert den Anzeigenamen eines Nutzers (z. B. nach erstem SSO-Login).""" user = get_store().update_display_name(user_id, payload.display_name) if user is None: raise HTTPException(status_code=404, detail=f"Nutzer {user_id!r} nicht gefunden.") return {"user_id": user.id, "display_name": user.display_name} @router.post("/admin/users/{user_id}/memories", response_model=MemoryOut, dependencies=[Depends(require_admin)]) async def add_user_memory(user_id: str, payload: MemoryCreate): """Legt eine Erinnerung fuer einen Nutzer an (Admin kann Kontext vorbelegen).""" store = get_store() if store.get_user(user_id) is None: raise HTTPException(status_code=404, detail=f"Nutzer {user_id!r} nicht gefunden.") memory = store.add_memory(user_id, payload.content) return MemoryOut(id=memory.id, content=memory.content, created_at=memory.created_at) @router.get("/admin/users", dependencies=[Depends(require_admin_or_user)]) async def list_users(): """Listet die Nutzer (ohne Secrets). Fuer Admins (SSO/ADMIN_USERS) oder ADMIN_API_KEY.""" return [ { "user_id": u.id, "display_name": u.display_name, "external_id": u.external_id, "created_at": u.created_at, } for u in get_store().list_users() ]