# Mail-RBAC Architektur mit Mitarbeiter-Anonymisierung **Version:** 1.0.0 **Datum:** 2026-01-10 **Status:** Architekturplanung --- ## Executive Summary Dieses Dokument beschreibt eine neuartige Architektur, die E-Mail, Kalender und Videokonferenzen mit rollenbasierter Zugriffskontrolle (RBAC) verbindet. Das Kernkonzept ermöglicht die **vollständige Anonymisierung von Mitarbeiterdaten** bei Verlassen des Unternehmens, während geschäftliche Kommunikationshistorie erhalten bleibt. **Wichtig:** Dieses Konzept existiert in dieser Form noch nicht als fertige Lösung auf dem Markt. Es handelt sich um eine innovative Architektur, die entwickelt werden muss. --- ## 1. Das Problem ### Traditionelle E-Mail-Systeme ``` max.mustermann@firma.de → Person gebunden → DSGVO: Daten müssen gelöscht werden → Geschäftshistorie geht verloren ``` ### BreakPilot-Lösung: Rollenbasierte E-Mail ``` klassenlehrer.5a@schule.breakpilot.app → Rolle gebunden → Person kann anonymisiert werden → Kommunikationshistorie bleibt erhalten ``` --- ## 2. Architektur-Übersicht ``` ┌─────────────────────────────────────────────────────────────────┐ │ BreakPilot Groupware │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │ │ Webmail │ │ Kalender │ │ Jitsi │ │ │ │ (SOGo) │ │ (SOGo) │ │ Meeting │ │ │ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ │ │ │ │ │ │ │ └────────────────┼────────────────┘ │ │ │ │ │ ┌───────────┴───────────┐ │ │ │ RBAC-Mail-Bridge │ ◄─── Neue Komponente │ │ │ (Python/Go) │ │ │ └───────────┬───────────┘ │ │ │ │ │ ┌─────────────────────┼─────────────────────┐ │ │ │ │ │ │ │ ▼ ▼ ▼ │ │ ┌──────────┐ ┌──────────────┐ ┌────────────┐ │ │ │PostgreSQL│ │ Mail Server │ │ MinIO │ │ │ │(RBAC DB) │ │ (Stalwart) │ │ (Backups) │ │ │ └──────────┘ └──────────────┘ └────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────┘ ``` --- ## 3. Komponenten-Auswahl ### 3.1 E-Mail Server: Stalwart Mail Server **Empfehlung:** [Stalwart Mail Server](https://stalw.art/) | Kriterium | Bewertung | |-----------|-----------| | Lizenz | AGPL-3.0 (Open Source) | | Sprache | Rust (performant, sicher) | | Features | IMAP, SMTP, JMAP, WebSocket | | Kalender | CalDAV integriert | | Kontakte | CardDAV integriert | | Spam/Virus | Integriert | | API | REST API für Administration | **Lizenz-Implikation für kommerzielle Nutzung:** - AGPL-3.0 erfordert Veröffentlichung von Änderungen am Stalwart-Code selbst - Nutzung als Service ohne Code-Änderungen: keine Veröffentlichungspflicht - Unsere RBAC-Bridge ist separater Code: kann proprietär bleiben ### 3.2 Webmail-Client: SOGo oder Roundcube **Option A: SOGo** (empfohlen) - Lizenz: GPL-2.0 / LGPL-2.1 - Kalender, Kontakte, Mail in einem - ActiveSync Support - Outlook-ähnliche Oberfläche **Option B: Roundcube** - Lizenz: GPL-3.0 - Nur Webmail - Benötigt separaten Kalender ### 3.3 Kalender-Integration Stalwart bietet CalDAV, kann mit: - SOGo Webinterface - Thunderbird - iOS/Android Kalender - Outlook (via CalDAV Plugin) ### 3.4 Jitsi-Integration Bereits vorhanden in BreakPilot: - `backend/jitsi_api.py` - Meeting-Erstellung - `email_service.py` - Jitsi-Einladungen per E-Mail - Kalender-Events können Jitsi-Links enthalten --- ## 4. Datenmodell für Rollen-E-Mail ### 4.1 Neue Datenbank-Tabellen ```sql -- Funktionale E-Mail-Adressen (rollengebunden) CREATE TABLE functional_mailboxes ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), -- Rolle und Mailbox role_key VARCHAR(100) NOT NULL, -- z.B. "klassenlehrer_5a" email_address VARCHAR(255) UNIQUE NOT NULL, -- z.B. "klassenlehrer.5a@schule.bp.app" display_name VARCHAR(255) NOT NULL, -- z.B. "Klassenlehrer 5a" -- Zuordnung tenant_id UUID NOT NULL REFERENCES tenants(id), resource_type VARCHAR(50) DEFAULT 'class', -- class, department, function resource_id VARCHAR(100), -- z.B. "5a" oder "mathematik" -- Status is_active BOOLEAN DEFAULT true, created_at TIMESTAMP DEFAULT NOW(), CONSTRAINT fk_role FOREIGN KEY (role_key) REFERENCES roles(role_key) ON DELETE RESTRICT ); -- Zuordnung: Welche Person hat welche funktionale Mailbox CREATE TABLE mailbox_assignments ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), mailbox_id UUID NOT NULL REFERENCES functional_mailboxes(id), user_id UUID NOT NULL REFERENCES users(id), -- Zeitraum valid_from TIMESTAMP DEFAULT NOW(), valid_to TIMESTAMP, -- Audit assigned_by UUID REFERENCES users(id), assigned_at TIMESTAMP DEFAULT NOW(), revoked_by UUID, revoked_at TIMESTAMP, -- Constraints CONSTRAINT unique_active_assignment UNIQUE (mailbox_id, user_id) WHERE revoked_at IS NULL ); -- Persönliche E-Mail-Adressen (für Anonymisierung) CREATE TABLE personal_email_accounts ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), user_id UUID NOT NULL REFERENCES users(id), -- E-Mail email_address VARCHAR(255) UNIQUE NOT NULL, -- Anonymisierungsstatus is_anonymized BOOLEAN DEFAULT false, anonymized_at TIMESTAMP, anonymized_by UUID, -- Original-Daten (verschlüsselt, für DSGVO-Auskunft) original_name_encrypted BYTEA, original_email_encrypted BYTEA, encryption_key_id VARCHAR(100), -- Audit created_at TIMESTAMP DEFAULT NOW() ); -- Audit-Trail für E-Mail-Kommunikation (ohne personenbezogene Daten) CREATE TABLE email_audit_trail ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), -- Kommunikation mailbox_id UUID REFERENCES functional_mailboxes(id), direction VARCHAR(10) NOT NULL, -- 'inbound', 'outbound' -- Metadaten (keine Inhalte!) subject_hash VARCHAR(64), -- SHA-256 für Deduplizierung timestamp TIMESTAMP NOT NULL, external_party_domain VARCHAR(255), -- z.B. "eltern.de" (nicht volle Adresse) -- Rolle zum Zeitpunkt role_key VARCHAR(100) NOT NULL, -- Person NICHT gespeichert - nur über Assignment nachvollziehbar -- Bei Anonymisierung: Assignment-User wird anonymisiert created_at TIMESTAMP DEFAULT NOW() ); -- Anonymisierungsprotokoll (DSGVO-Nachweis) CREATE TABLE anonymization_log ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), -- Was wurde anonymisiert entity_type VARCHAR(50) NOT NULL, -- 'user', 'email_account' entity_id UUID NOT NULL, -- Wie anonymization_type VARCHAR(50) NOT NULL, -- 'pseudonymization', 'deletion' fields_affected JSONB NOT NULL, -- Warum reason VARCHAR(100) NOT NULL, -- 'employee_departure', 'dsgvo_request' -- Audit performed_by UUID NOT NULL, performed_at TIMESTAMP DEFAULT NOW(), -- Bestätigung legal_basis VARCHAR(255), -- z.B. "Art. 17 DSGVO" retention_period_days INTEGER ); ``` ### 4.2 Anonymisierungs-Workflow ``` Mitarbeiter kündigt │ ▼ ┌───────────────────────────┐ │ 1. Functional Mailboxes │ │ → Neu zuweisen oder │ │ → Deaktivieren │ └───────────┬───────────────┘ │ ▼ ┌───────────────────────────┐ │ 2. Personal Email Account │ │ → Anonymisieren: │ │ max.mustermann@... │ │ → mitarbeiter_a7x2@... │ └───────────────────────────┘ │ ▼ ┌───────────────────────────┐ │ 3. Users-Tabelle │ │ → Pseudonymisieren: │ │ name: "Max Mustermann" │ │ → "Ehem. Mitarbeiter" │ └───────────────────────────┘ │ ▼ ┌───────────────────────────┐ │ 4. Mailbox Assignments │ │ → Bleiben für Audit │ │ → User-Referenz zeigt │ │ auf anonymisierte │ │ Daten │ └───────────────────────────┘ │ ▼ ┌───────────────────────────┐ │ 5. E-Mail-Archiv │ │ → Header anonymisieren │ │ → Inhalte optional │ │ löschen │ └───────────────────────────┘ ``` --- ## 5. RBAC-Mail-Bridge Implementierung ### 5.1 Python-Komponente ```python # rbac_mail_bridge/anonymizer.py class EmployeeAnonymizer: """ Anonymisiert Mitarbeiterdaten bei Ausscheiden. Erhält Audit-Trail und funktionale Zuordnungen. """ async def anonymize_employee( self, user_id: str, reason: str, performed_by: str ) -> AnonymizationResult: """ Vollständige Anonymisierung eines Mitarbeiters. Schritte: 1. Functional Mailboxes neu zuweisen 2. Personal Email anonymisieren 3. User-Daten pseudonymisieren 4. E-Mail-Archive bereinigen 5. Audit-Log erstellen """ async with self.db.transaction(): # 1. Functional Mailboxes assignments = await self.get_active_assignments(user_id) for assignment in assignments: await self.revoke_assignment( assignment.id, revoked_by=performed_by, reason="employee_departure" ) # 2. Personal Email personal_email = await self.get_personal_email(user_id) if personal_email: # Verschlüssele Original für DSGVO-Auskunft encrypted = await self.encrypt_for_retention( personal_email.email_address, personal_email.display_name ) # Anonymisiere anon_email = f"user_{generate_random_id()}@anon.local" await self.db.execute(""" UPDATE personal_email_accounts SET email_address = $1, is_anonymized = true, anonymized_at = NOW(), original_email_encrypted = $2 WHERE user_id = $3 """, anon_email, encrypted, user_id) # 3. User-Daten await self.db.execute(""" UPDATE users SET name = 'Ehemaliger Mitarbeiter', email = $1, is_active = false, anonymized_at = NOW() WHERE id = $2 """, f"anon_{user_id[:8]}@deleted.local", user_id) # 4. E-Mail-Archive await self.mail_server.anonymize_mailbox(user_id) # 5. Audit-Log await self.create_anonymization_log( entity_type="user", entity_id=user_id, reason=reason, performed_by=performed_by ) return AnonymizationResult(success=True) ``` ### 5.2 API-Endpunkte ```python # rbac_mail_bridge/api.py router = APIRouter(prefix="/api/v1/mail-rbac", tags=["mail-rbac"]) @router.get("/mailboxes") async def list_functional_mailboxes( user: Dict = Depends(get_current_admin) ) -> List[FunctionalMailbox]: """Liste aller funktionalen Mailboxen""" pass @router.post("/mailboxes") async def create_functional_mailbox( mailbox: FunctionalMailboxCreate, user: Dict = Depends(get_current_admin) ) -> FunctionalMailbox: """Erstellt eine rollengebundene Mailbox""" pass @router.post("/mailboxes/{mailbox_id}/assign") async def assign_mailbox_to_user( mailbox_id: str, assignment: MailboxAssignment, user: Dict = Depends(get_current_admin) ) -> Assignment: """Weist eine Mailbox einem Benutzer zu""" pass @router.post("/users/{user_id}/anonymize") async def anonymize_user( user_id: str, request: AnonymizationRequest, user: Dict = Depends(get_current_admin) ) -> AnonymizationResult: """Anonymisiert einen ausgeschiedenen Mitarbeiter""" pass @router.get("/audit/anonymizations") async def list_anonymizations( user: Dict = Depends(get_current_admin) ) -> List[AnonymizationLog]: """Liste aller Anonymisierungen (DSGVO-Nachweis)""" pass ``` --- ## 6. Docker-Compose Integration ```yaml # docker-compose.yml Erweiterung services: # ... bestehende Services ... # Stalwart Mail Server stalwart: image: stalwartlabs/mail-server:latest container_name: breakpilot-mail hostname: mail.breakpilot.local ports: - "25:25" # SMTP - "143:143" # IMAP - "465:465" # SMTPS - "993:993" # IMAPS - "4190:4190" # ManageSieve - "8080:8080" # Web Admin volumes: - stalwart-data:/opt/stalwart-mail/data - ./config/stalwart:/opt/stalwart-mail/etc environment: - STALWART_HOSTNAME=mail.breakpilot.local networks: - breakpilot-pwa-network depends_on: - postgres profiles: - mail # Nur mit --profile mail starten # SOGo Groupware (Webmail + Kalender) sogo: image: sogo/sogo:latest container_name: breakpilot-sogo ports: - "20000:20000" volumes: - ./config/sogo:/etc/sogo environment: - SOGO_HOSTNAME=groupware.breakpilot.local depends_on: - stalwart - postgres networks: - breakpilot-pwa-network profiles: - mail # RBAC-Mail-Bridge rbac-mail-bridge: build: context: ./rbac-mail-bridge dockerfile: Dockerfile container_name: breakpilot-rbac-mail environment: - DATABASE_URL=${DATABASE_URL} - STALWART_API_URL=http://stalwart:8080 - STALWART_API_KEY=${STALWART_API_KEY} depends_on: - postgres - stalwart networks: - breakpilot-pwa-network profiles: - mail volumes: stalwart-data: ``` --- ## 7. Admin-UI im Frontend ### 7.1 Neue Admin-Seite: `/admin/mail-management` ```tsx // website/app/admin/mail-management/page.tsx // Tabs: // 1. Funktionale Mailboxen // - Liste aller rollengebundenen Adressen // - Zuweisungen verwalten // // 2. Mitarbeiter E-Mail // - Persönliche Accounts // - Anonymisierungs-Status // // 3. Anonymisierung // - Mitarbeiter-Offboarding // - Anonymisierungs-Wizard // // 4. Audit-Log // - Alle Anonymisierungen // - DSGVO-Export ``` --- ## 8. Lizenz-Übersicht | Komponente | Lizenz | Kommerzielle Nutzung | Veröffentlichungspflicht | |------------|--------|---------------------|-------------------------| | Stalwart Mail | AGPL-3.0 | Ja | Nur bei Code-Änderungen | | SOGo | GPL-2.0/LGPL | Ja | Nur bei Code-Änderungen | | Roundcube | GPL-3.0 | Ja | Nur bei Code-Änderungen | | RBAC-Mail-Bridge | Eigene | N/A | Kann proprietär bleiben | | BreakPilot Backend | Eigene | N/A | Proprietär | **Empfehlung:** Bei reiner Nutzung der Open-Source-Komponenten ohne Code-Änderungen besteht keine Veröffentlichungspflicht. Die RBAC-Mail-Bridge ist unser eigener Code. --- ## 9. Implementierungsreihenfolge ### Phase 1: Grundlagen (2-3 Wochen Entwicklung) 1. Datenbank-Schema erstellen 2. RBAC-Mail-Bridge Backend 3. Basic API-Endpunkte ### Phase 2: Mail-Server Integration 4. Stalwart konfigurieren 5. SOGo als Webclient 6. LDAP/OAuth Bridge ### Phase 3: Anonymisierung 7. Anonymisierungs-Service 8. E-Mail-Archive-Bereinigung 9. DSGVO-Export-Funktion ### Phase 4: Frontend 10. Admin-UI erstellen 11. Offboarding-Wizard 12. Audit-Dashboard ### Phase 5: Kalender & Jitsi 13. CalDAV Integration 14. Jitsi-Meeting-Einladungen aus Kalender 15. Mobile Sync (ActiveSync/CardDAV) --- ## 10. Existiert das schon? **Kurze Antwort: Nein, nicht in dieser Form.** ### Ähnliche Konzepte: 1. **Funktionale Mailboxen** (existiert) - Shared Mailboxes in Exchange/Microsoft 365 - Group Addresses in Google Workspace - → Aber: Keine RBAC-Integration, keine Anonymisierung 2. **DSGVO-Löschung** (existiert) - Standard: Konto komplett löschen - → Aber: Verlust der Geschäftshistorie 3. **Pseudonymisierung** (existiert als Konzept) - In Forschungsdatenbanken üblich - → Aber: Nicht für E-Mail-Systeme implementiert ### Was BreakPilot anders macht: ``` Traditionell: max.mustermann@firma.de → Kündigung → Löschen → Historie weg BreakPilot: klassenlehrer.5a@schule.bp.app ← Max Mustermann zugeordnet ← Maria Müller übernimmt ← Historie bleibt, Person anonymisiert ``` **Fazit:** Das Konzept ist innovativ und müsste entwickelt werden. Es gibt keine fertige Open-Source-Lösung, die all diese Features kombiniert. --- ## 11. Alternative: Minimale Implementation Falls das vollständige System zu komplex ist, gibt es eine minimale Variante: ### 11.1 Nur Functional Mailboxes ```sql -- Minimales Schema CREATE TABLE role_email_aliases ( id UUID PRIMARY KEY, role_key VARCHAR(100) UNIQUE NOT NULL, email_alias VARCHAR(255) UNIQUE NOT NULL, -- klassenlehrer.5a@... forward_to UUID REFERENCES users(id), -- Aktuelle Person is_active BOOLEAN DEFAULT true ); ``` - Keine eigene Mail-Infrastruktur - Aliases werden an persönliche Adressen weitergeleitet - Bei Kündigung: Alias umleiten - Limitation: Keine echte Anonymisierung der Historie ### 11.2 Mailpit als Development-Only Bestehendes Mailpit bleibt für Entwicklung: - Kein Produktions-Mailserver - Externe Mails über Gmail/Microsoft - Functional Mailboxes als Konzept in DB --- ## 12. Nächste Schritte 1. **Entscheidung:** Vollständiges System oder minimale Variante? 2. **Proof of Concept:** Functional Mailboxes mit bestehendem RBAC 3. **Evaluierung:** Stalwart Mail Server in Testumgebung 4. **Architektur-Review:** Mit Datenschutzbeauftragtem abstimmen --- ## Anhang A: Referenzen - [Stalwart Mail Server](https://stalw.art/) - [SOGo Groupware](https://www.sogo.nu/) - [Roundcube Webmail](https://roundcube.net/) - [CalDAV Standard](https://tools.ietf.org/html/rfc4791) - [DSGVO Art. 17 - Recht auf Löschung](https://dsgvo-gesetz.de/art-17-dsgvo/) ## Anhang B: Bestehende BreakPilot-Integration - RBAC-System: `/backend/rbac_api.py` - E-Mail-Service: `/backend/email_service.py` - Jitsi-Integration: `/backend/jitsi_api.py` - Mailpit-Config: `docker-compose.yml:193-202` --- ## Anhang C: Unified Inbox Implementation (2026-01) ### Implementierte Komponenten Die Unified Inbox wurde als Teil des klausur-service implementiert: | Komponente | Pfad | Beschreibung | |------------|------|--------------| | **Models** | `klausur-service/backend/mail/models.py` | Pydantic Models für Accounts, E-Mails, Tasks | | **Database** | `klausur-service/backend/mail/mail_db.py` | PostgreSQL-Operationen mit asyncpg | | **Credentials** | `klausur-service/backend/mail/credentials.py` | Vault-Integration für IMAP/SMTP-Passwörter | | **Aggregator** | `klausur-service/backend/mail/aggregator.py` | Multi-Account IMAP Sync | | **AI Service** | `klausur-service/backend/mail/ai_service.py` | KI-Analyse (Absender, Fristen, Kategorien) | | **Task Service** | `klausur-service/backend/mail/task_service.py` | Arbeitsvorrat-Management | | **API** | `klausur-service/backend/mail/api.py` | FastAPI Router mit 30+ Endpoints | ### Frontend-Komponenten | Komponente | Pfad | Beschreibung | |------------|------|--------------| | **Admin UI** | `website/app/admin/mail/page.tsx` | Kontenverwaltung, KI-Einstellungen | | **User Inbox** | `website/app/mail/page.tsx` | Unified Inbox mit KI-Panel | | **Arbeitsvorrat** | `website/app/mail/tasks/page.tsx` | Task-Management Dashboard | ### API-Endpoints (Port 8086) ``` # Account Management POST /api/v1/mail/accounts - Neues Konto hinzufügen GET /api/v1/mail/accounts - Alle Konten auflisten DELETE /api/v1/mail/accounts/{id} - Konto entfernen POST /api/v1/mail/accounts/{id}/test - Verbindung testen # Unified Inbox GET /api/v1/mail/inbox - Aggregierte Inbox GET /api/v1/mail/inbox/{id} - Einzelne E-Mail POST /api/v1/mail/send - E-Mail senden # KI-Features POST /api/v1/mail/analyze/{id} - E-Mail analysieren GET /api/v1/mail/suggestions/{id} - Antwortvorschläge # Arbeitsvorrat GET /api/v1/mail/tasks - Alle Tasks POST /api/v1/mail/tasks - Manuelle Task erstellen PATCH /api/v1/mail/tasks/{id} - Task aktualisieren GET /api/v1/mail/tasks/dashboard - Dashboard-Statistiken ``` ### Niedersachsen-spezifische Absendererkennung ```python KNOWN_AUTHORITIES_NI = { "@mk.niedersachsen.de": "Kultusministerium Niedersachsen", "@rlsb.de": "Regionales Landesamt für Schule und Bildung", "@landesschulbehoerde-nds.de": "Landesschulbehörde", "@nibis.de": "NiBiS", } ``` ### LLM-Playbook für E-Mail-Analyse Das Playbook `mail_analysis` in `/backend/llm_gateway/services/playbook_service.py` enthält: - Absender-Klassifikation (12 Typen) - Fristenerkennung mit Datumsextraktion - Kategorisierung (11 Kategorien) - Prioritätsvorschlag