v4.1.0: DMS email documents, category-specific Nachweis linking, version system
- Save cover email body as DMS document with new 'email' context type - Show email body separately from attachments in email detail view - Add per-category DMS document assignment in quarterly confirmation (Studiennachweis, Einkommenssituation, Vermögenssituation) - Add VERSION file and context processor for automatic version display - Add MCP server, agent system, import/export, and new migrations - Update compose files and production environment template Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
70
app/mcp_server/auth.py
Normal file
70
app/mcp_server/auth.py
Normal file
@@ -0,0 +1,70 @@
|
||||
"""
|
||||
MCP-Authentifizierung – Token-basierte Authentifizierung mit 3 Rollen.
|
||||
|
||||
Tokens werden über Umgebungsvariablen konfiguriert:
|
||||
MCP_TOKEN_READONLY – Nur-Lese-Zugriff (alle Daten, PII maskiert)
|
||||
MCP_TOKEN_EDITOR – Lesen + Schreiben (PII maskiert)
|
||||
MCP_TOKEN_ADMIN – Voll-Zugriff (keine PII-Maskierung, alle Schreib-Ops)
|
||||
|
||||
Das aktive Token wird per MCP_AUTH_TOKEN übergeben (wird vom MCP-Client gesetzt).
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
ROLE_READONLY = "readonly"
|
||||
ROLE_EDITOR = "editor"
|
||||
ROLE_ADMIN = "admin"
|
||||
|
||||
# Rollenrangfolge (höher = mehr Rechte)
|
||||
ROLE_RANK = {ROLE_READONLY: 1, ROLE_EDITOR: 2, ROLE_ADMIN: 3}
|
||||
|
||||
|
||||
def _token_map() -> dict[str, str]:
|
||||
"""Erstellt Mapping token → Rolle aus Umgebungsvariablen."""
|
||||
mapping: dict[str, str] = {}
|
||||
for role, env_var in [
|
||||
(ROLE_READONLY, "MCP_TOKEN_READONLY"),
|
||||
(ROLE_EDITOR, "MCP_TOKEN_EDITOR"),
|
||||
(ROLE_ADMIN, "MCP_TOKEN_ADMIN"),
|
||||
]:
|
||||
token = os.environ.get(env_var, "").strip()
|
||||
if token:
|
||||
mapping[token] = role
|
||||
return mapping
|
||||
|
||||
|
||||
def get_role_for_token(token: str) -> str | None:
|
||||
"""
|
||||
Gibt die Rolle für einen Token zurück oder None bei ungültigem Token.
|
||||
"""
|
||||
if not token:
|
||||
return None
|
||||
return _token_map().get(token)
|
||||
|
||||
|
||||
def get_current_role() -> str | None:
|
||||
"""
|
||||
Gibt die Rolle des aktuell gesetzten MCP_AUTH_TOKEN zurück.
|
||||
Wird vom Server beim Start einmalig ausgewertet.
|
||||
"""
|
||||
token = os.environ.get("MCP_AUTH_TOKEN", "").strip()
|
||||
return get_role_for_token(token)
|
||||
|
||||
|
||||
def can_write(role: str | None) -> bool:
|
||||
"""Darf die Rolle Schreiboperationen ausführen?"""
|
||||
return role in (ROLE_EDITOR, ROLE_ADMIN)
|
||||
|
||||
|
||||
def can_read_unmasked(role: str | None) -> bool:
|
||||
"""Darf die Rolle ungemaskierte PII-Daten lesen?"""
|
||||
return role == ROLE_ADMIN
|
||||
|
||||
|
||||
def require_role(role: str | None) -> None:
|
||||
"""Wirft ValueError wenn keine gültige Rolle vorhanden."""
|
||||
if not role:
|
||||
raise ValueError(
|
||||
"Ungültiger oder fehlender MCP_AUTH_TOKEN. "
|
||||
"Bitte MCP_AUTH_TOKEN-Umgebungsvariable setzen."
|
||||
)
|
||||
Reference in New Issue
Block a user