- 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>
71 lines
2.0 KiB
Python
71 lines
2.0 KiB
Python
"""
|
||
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."
|
||
)
|