A previous `git pull --rebase origin main` dropped 177 local commits,
losing 3400+ files across admin-v2, backend, studio-v2, website,
klausur-service, and many other services. The partial restore attempt
(660295e2) only recovered some files.
This commit restores all missing files from pre-rebase ref 98933f5e
while preserving post-rebase additions (night-scheduler, night-mode UI,
NightModeWidget dashboard integration).
Restored features include:
- AI Module Sidebar (FAB), OCR Labeling, OCR Compare
- GPU Dashboard, RAG Pipeline, Magic Help
- Klausur-Korrektur (8 files), Abitur-Archiv (5+ files)
- Companion, Zeugnisse-Crawler, Screen Flow
- Full backend, studio-v2, website, klausur-service
- All compliance SDKs, agent-core, voice-service
- CI/CD configs, documentation, scripts
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
97 lines
2.7 KiB
Python
97 lines
2.7 KiB
Python
"""
|
|
Auth Middleware für LLM Gateway.
|
|
|
|
Unterstützt:
|
|
- API Key Auth (X-API-Key Header oder Authorization Bearer)
|
|
- JWT Token Auth (vom Consent Service)
|
|
"""
|
|
|
|
import logging
|
|
from typing import Optional
|
|
from fastapi import HTTPException, Header, Depends
|
|
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
|
|
import jwt
|
|
|
|
from ..config import get_config
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
security = HTTPBearer(auto_error=False)
|
|
|
|
|
|
async def verify_api_key(
|
|
x_api_key: Optional[str] = Header(None, alias="X-API-Key"),
|
|
authorization: Optional[HTTPAuthorizationCredentials] = Depends(security),
|
|
) -> str:
|
|
"""
|
|
Verifiziert den API Key oder JWT Token.
|
|
|
|
Akzeptiert:
|
|
- X-API-Key Header
|
|
- Authorization: Bearer <token>
|
|
|
|
Returns:
|
|
str: User ID oder "api_key" bei API Key Auth
|
|
"""
|
|
config = get_config()
|
|
|
|
# 1. Prüfe X-API-Key Header
|
|
if x_api_key:
|
|
if x_api_key in config.api_keys:
|
|
return "api_key"
|
|
logger.warning(f"Invalid API key attempted")
|
|
raise HTTPException(
|
|
status_code=401,
|
|
detail={"error": "unauthorized", "message": "Invalid API key"},
|
|
)
|
|
|
|
# 2. Prüfe Authorization Header
|
|
if authorization:
|
|
token = authorization.credentials
|
|
|
|
# Prüfe ob es ein API Key ist
|
|
if token in config.api_keys:
|
|
return "api_key"
|
|
|
|
# Versuche JWT zu dekodieren
|
|
if config.jwt_secret:
|
|
try:
|
|
payload = jwt.decode(
|
|
token,
|
|
config.jwt_secret,
|
|
algorithms=["HS256"],
|
|
)
|
|
user_id = payload.get("user_id") or payload.get("sub")
|
|
if user_id:
|
|
return str(user_id)
|
|
except jwt.ExpiredSignatureError:
|
|
raise HTTPException(
|
|
status_code=401,
|
|
detail={"error": "token_expired", "message": "Token has expired"},
|
|
)
|
|
except jwt.InvalidTokenError as e:
|
|
logger.warning(f"Invalid JWT token: {e}")
|
|
raise HTTPException(
|
|
status_code=401,
|
|
detail={"error": "invalid_token", "message": "Invalid token"},
|
|
)
|
|
|
|
# 3. In Development Mode ohne Auth erlauben
|
|
if config.debug:
|
|
logger.warning("Auth bypassed in debug mode")
|
|
return "debug_user"
|
|
|
|
# 4. Keine gültige Auth gefunden
|
|
raise HTTPException(
|
|
status_code=401,
|
|
detail={
|
|
"error": "unauthorized",
|
|
"message": "API key or valid token required",
|
|
},
|
|
)
|
|
|
|
|
|
def get_current_user_id(user_id: str = Depends(verify_api_key)) -> str:
|
|
"""Dependency um die aktuelle User ID zu bekommen."""
|
|
return user_id
|