feat: Multi-User-Unterstützung mit JWT-Authentifizierung

- User-Modell (username, password_hash, role admin/user, is_active)
- Standard-Admin-Benutzer wird beim ersten Start automatisch angelegt
- JWT-Tokens (HS256) für Benutzer-Sessions, konfigurierbare Ablaufzeit
- API-Key bleibt für service-to-service-Calls (backward-compatible)
- POST /api/v1/auth/login → JWT-Token
- GET  /api/v1/auth/me   → aktueller Benutzer
- CRUD /api/v1/users/    → Benutzerverwaltung (nur Admin)
- TUI zeigt Login-Screen beim Start; nach Erfolg → MainScreen
- Passwort-Hashing mit bcrypt (python-jose für JWT)
This commit is contained in:
2026-03-04 20:55:13 +01:00
parent 0e2a8a6bc0
commit 6eb27a62b1
14 changed files with 421 additions and 10 deletions

View File

@@ -22,7 +22,38 @@ class MCMApiClient:
def __init__(self) -> None:
self._base = f"http://127.0.0.1:{settings.port}/api/v1"
self._headers = {"Authorization": f"Bearer {settings.api_key}"}
self._token: str | None = None
@property
def _headers(self) -> dict[str, str]:
if self._token:
return {"Authorization": f"Bearer {self._token}"}
return {}
# ── Authentifizierung ──────────────────────────────────────────────────────
async def login(self, username: str, password: str) -> bool:
"""Meldet den Benutzer an und speichert den JWT-Token. Gibt True bei Erfolg zurück."""
try:
async with httpx.AsyncClient(timeout=5.0) as client:
resp = await client.post(
self._base + "/auth/login",
json={"username": username, "password": password},
)
if resp.status_code == 200:
self._token = resp.json()["access_token"]
return True
return False
except Exception as exc:
logger.warning("Login fehlgeschlagen: %s", exc)
return False
def logout(self) -> None:
self._token = None
@property
def is_authenticated(self) -> bool:
return self._token is not None
# ── Konversationen ─────────────────────────────────────────────────────────