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>
10 KiB
10 KiB
BreakPilot Authentifizierung & Autorisierung
Uebersicht
BreakPilot verwendet einen Hybrid-Ansatz fuer Authentifizierung und Autorisierung:
┌─────────────────────────────────────────────────────────────────────────┐
│ AUTHENTIFIZIERUNG │
│ "Wer bist du?" │
│ ┌────────────────────────────────────────────────────────────────────┐ │
│ │ HybridAuthenticator │ │
│ │ ┌─────────────────────┐ ┌─────────────────────────────────┐ │ │
│ │ │ Keycloak │ │ Lokales JWT │ │ │
│ │ │ (Produktion) │ OR │ (Entwicklung) │ │ │
│ │ │ RS256 + JWKS │ │ HS256 + Secret │ │ │
│ │ └─────────────────────┘ └─────────────────────────────────┘ │ │
│ └────────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────┐
│ AUTORISIERUNG │
│ "Was darfst du?" │
│ ┌────────────────────────────────────────────────────────────────────┐ │
│ │ rbac.py (Eigenentwicklung) │ │
│ │ ┌─────────────────┐ ┌─────────────────┐ ┌───────────────────┐ │ │
│ │ │ Rollen-Hierarchie│ │ PolicySet │ │ DEFAULT_PERMISSIONS│ │ │
│ │ │ 15+ Rollen │ │ Bundesland- │ │ Matrix │ │ │
│ │ │ - Erstkorrektor │ │ spezifisch │ │ Rolle→Ressource→ │ │ │
│ │ │ - Klassenlehrer │ │ - Niedersachsen │ │ Aktion │ │ │
│ │ │ - Schulleitung │ │ - Bayern │ │ │ │ │
│ │ └─────────────────┘ └─────────────────┘ └───────────────────┘ │ │
│ └────────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────┘
Warum dieser Ansatz?
Alternative Loesungen (verworfen)
| Tool | Problem fuer BreakPilot |
|---|---|
| Casbin | Zu generisch fuer Bundesland-spezifische Policies |
| Cerbos | Overhead: Externer PDP-Service fuer ~15 Rollen ueberdimensioniert |
| OpenFGA | Zanzibar-Modell optimiert fuer Graph-Beziehungen, nicht Hierarchien |
| Keycloak RBAC | Kann keine ressourcen-spezifischen Zuweisungen (User X ist Erstkorrektor fuer Package Y) |
Vorteile des Hybrid-Ansatzes
-
Keycloak fuer Authentifizierung:
- Bewährtes IAM-System
- SSO, Federation, MFA
- Apache-2.0 Lizenz
-
Eigenes rbac.py fuer Autorisierung:
- Domaenenspezifische Logik (Korrekturkette, Zeugnis-Workflow)
- Bundesland-spezifische Regeln
- Zeitlich begrenzte Zuweisungen
- Key-Sharing fuer verschluesselte Klausuren
Authentifizierung (auth/keycloak_auth.py)
Konfiguration
# Entwicklung: Lokales JWT (Standard)
JWT_SECRET=your-secret-key
# Produktion: Keycloak
KEYCLOAK_SERVER_URL=https://keycloak.breakpilot.app
KEYCLOAK_REALM=breakpilot
KEYCLOAK_CLIENT_ID=breakpilot-backend
KEYCLOAK_CLIENT_SECRET=your-client-secret
Token-Erkennung
Der HybridAuthenticator erkennt automatisch den Token-Typ:
# Keycloak-Token (RS256)
{
"iss": "https://keycloak.breakpilot.app/realms/breakpilot",
"sub": "user-uuid",
"realm_access": {"roles": ["teacher", "admin"]},
...
}
# Lokales JWT (HS256)
{
"iss": "breakpilot",
"user_id": "user-uuid",
"role": "admin",
...
}
FastAPI Integration
from auth import get_current_user
@app.get("/api/protected")
async def protected_endpoint(user: dict = Depends(get_current_user)):
# user enthält: user_id, email, role, realm_roles, tenant_id
return {"user_id": user["user_id"]}
Autorisierung (klausur-service/backend/rbac.py)
Rollen (15+)
| Rolle | Beschreibung | Bereich |
|---|---|---|
erstkorrektor |
Erster Prüfer | Klausur |
zweitkorrektor |
Zweiter Prüfer | Klausur |
drittkorrektor |
Dritter Prüfer | Klausur |
klassenlehrer |
Klassenleitung | Zeugnis |
fachlehrer |
Fachlehrkraft | Noten |
fachvorsitz |
Fachkonferenz-Leitung | Fachschaft |
schulleitung |
Schulleiter/in | Schule |
zeugnisbeauftragter |
Zeugnis-Koordination | Zeugnis |
sekretariat |
Verwaltung | Schule |
data_protection_officer |
DSB | DSGVO |
| ... |
Ressourcentypen (25+)
class ResourceType(str, Enum):
EXAM_PACKAGE = "exam_package" # Klausurpaket
STUDENT_SUBMISSION = "student_submission"
CORRECTION = "correction"
ZEUGNIS = "zeugnis"
FACHNOTE = "fachnote"
KOPFNOTE = "kopfnote"
BEMERKUNG = "bemerkung"
...
Aktionen (17)
class Action(str, Enum):
CREATE = "create"
READ = "read"
UPDATE = "update"
DELETE = "delete"
SIGN_OFF = "sign_off" # Freigabe
BREAK_GLASS = "break_glass" # Notfall-Zugriff
SHARE_KEY = "share_key" # Schlüssel teilen
...
Permission-Pruefung
from klausur_service.backend.rbac import PolicyEngine
engine = PolicyEngine()
# Pruefe ob User X Klausur Y korrigieren darf
allowed = engine.check_permission(
user_id="user-uuid",
action=Action.UPDATE,
resource_type=ResourceType.CORRECTION,
resource_id="klausur-uuid"
)
Bundesland-spezifische Policies
@dataclass
class PolicySet:
bundesland: str
abitur_type: str # "landesabitur" | "zentralabitur"
# Korrekturkette
korrektoren_anzahl: int # 2 oder 3
anonyme_erstkorrektur: bool
# Sichtbarkeit
zk_visibility_mode: ZKVisibilityMode # BLIND | SEMI | FULL
eh_visibility_mode: EHVisibilityMode
# Zeugnis
kopfnoten_enabled: bool
...
Beispiel: Niedersachsen
NIEDERSACHSEN_POLICY = PolicySet(
bundesland="niedersachsen",
abitur_type="landesabitur",
korrektoren_anzahl=2,
anonyme_erstkorrektur=True,
zk_visibility_mode=ZKVisibilityMode.BLIND,
eh_visibility_mode=EHVisibilityMode.SUMMARY_ONLY,
kopfnoten_enabled=True,
)
Workflow-Beispiele
Klausurkorrektur-Workflow
1. Lehrer laedt Klausuren hoch
└── Rolle: "lehrer" + Action.CREATE auf EXAM_PACKAGE
2. Erstkorrektor korrigiert
└── Rolle: "erstkorrektor" (ressourcen-spezifisch) + Action.UPDATE auf CORRECTION
3. Zweitkorrektor ueberprueft
└── Rolle: "zweitkorrektor" + Action.READ auf CORRECTION
└── Policy: zk_visibility_mode bestimmt Sichtbarkeit
4. Drittkorrektor (bei Abweichung)
└── Rolle: "drittkorrektor" + Action.SIGN_OFF
Zeugnis-Workflow
1. Fachlehrer traegt Noten ein
└── Rolle: "fachlehrer" + Action.CREATE auf FACHNOTE
2. Klassenlehrer prueft
└── Rolle: "klassenlehrer" + Action.READ auf ZEUGNIS
└── Action.SIGN_OFF freigeben
3. Zeugnisbeauftragter final
└── Rolle: "zeugnisbeauftragter" + Action.SIGN_OFF
4. Schulleitung unterzeichnet
└── Rolle: "schulleitung" + Action.SIGN_OFF
Dateien
| Datei | Beschreibung |
|---|---|
backend/auth/__init__.py |
Auth-Modul Exports |
backend/auth/keycloak_auth.py |
Hybrid-Authentifizierung |
klausur-service/backend/rbac.py |
Autorisierungs-Engine |
backend/rbac_api.py |
REST API fuer Rollenverwaltung |
Konfiguration
Entwicklung (ohne Keycloak)
# .env
ENVIRONMENT=development
JWT_SECRET=dev-secret-32-chars-minimum-here
Produktion (mit Keycloak)
# .env
ENVIRONMENT=production
JWT_SECRET=<openssl rand -hex 32>
KEYCLOAK_SERVER_URL=https://keycloak.breakpilot.app
KEYCLOAK_REALM=breakpilot
KEYCLOAK_CLIENT_ID=breakpilot-backend
KEYCLOAK_CLIENT_SECRET=<from keycloak admin console>
Sicherheitshinweise
- Secrets niemals im Code - Immer Umgebungsvariablen verwenden
- JWT_SECRET in Produktion - Mindestens 32 Bytes, generiert mit
openssl rand -hex 32 - Keycloak HTTPS - KEYCLOAK_VERIFY_SSL=true in Produktion
- Token-Expiration - Keycloak-Tokens kurz halten (5-15 Minuten)
- Audit-Trail - Alle Berechtigungspruefungen werden geloggt