22dfe1300b
- PROJ-16: Abhängigkeit zu PROJ-21 dokumentiert, Phase A (config.yml) / Phase B (pro-Mandant DB) getrennt, Rolle "admin" → "domain_admin" in group_mappings, UpsertLDAPUser erhält tenant_id in Phase B, neue /api/tenant/ldap Endpunkte - PROJ-21: tenant_ldap-Tabelle in Phase 1 ergänzt, Phasen-Abhängigkeit zu PROJ-16 explizit, LDAP in Zugriffsmatrix aufgenommen Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
7.1 KiB
7.1 KiB
id, title, status, priority, created
| id | title | status | priority | created |
|---|---|---|---|---|
| PROJ-16 | LDAP / Active Directory Anbindung | In Progress | P1 | 2026-03-13 |
PROJ-16 — LDAP / Active Directory Anbindung
Ziel
Authentifizierung gegen einen LDAP-Server (OpenLDAP, Microsoft Active Directory, Samba AD).
Lokale Accounts bleiben weiterhin nutzbar. LDAP-User werden beim ersten Login automatisch
in der Datenbank angelegt (source: ldap) und bei jedem Login synchronisiert.
Abhängigkeit zu PROJ-21 (Multi-Tenancy)
Wichtig: PROJ-16 muss mit PROJ-21 abgestimmt werden. Im Multi-Tenant-Betrieb hat jeder Mandant seinen eigenen LDAP-Server. Die Umsetzung berücksichtigt daher zwei Modi:
- Single-Tenant (Kompatibilitätsmodus): LDAP global in
config.yml— wie unten beschrieben- Multi-Tenant: LDAP-Konfiguration pro Mandant in der Datenbank (
tenant_ldap-Tabelle)PROJ-16 wird in zwei Phasen umgesetzt:
- Phase A (unabhängig): Single-Tenant-LDAP via
config.yml— implementierbar vor PROJ-21- Phase B (nach PROJ-21 Phase 2): Pro-Mandant-LDAP via DB,
UpsertLDAPUsermittenant_id
User Stories
- Als Admin möchte ich LDAP in
config.ymlkonfigurieren, damit Mitarbeiter ihre bestehenden Windows/AD-Zugangsdaten nutzen können. - Als Endnutzer möchte ich mich mit meinem AD-Passwort anmelden, ohne einen separaten archivmail-Account zu benötigen.
- Als Domain-Admin (Multi-Tenant) möchte ich den LDAP-Server meines Mandanten selbst
konfigurieren, ohne Zugriff auf
config.ymlzu benötigen. - Als Admin möchte ich LDAP-User einer Rolle (user/auditor/domain_admin) zuweisen können, entweder per fester Zuordnung oder über AD-Gruppen.
- Als Admin möchte ich LDAP deaktivieren können, ohne den restlichen Betrieb zu stören.
Akzeptanzkriterien
- Login mit LDAP-Credentials funktioniert wenn
ldap.enabled: true - Lokale Accounts funktionieren weiterhin (Fallback wenn LDAP fehlschlägt oder deaktiviert)
- LDAP-User werden beim Login automatisch via
UpsertLDAPUserangelegt/aktualisiert - Rollen-Mapping via AD-Gruppen konfigurierbar (optional, Fallback: default_role); Rollen:
user,auditor,domain_admin(nichtsuperadmin) - STARTTLS und LDAPS (Port 636) werden unterstützt
- Bind-User (Service Account) für AD-Suche konfigurierbar
- Fehlermeldung bei falschem Passwort ist identisch zu lokalem Login (kein Info-Leak)
- LDAP-Fehler landen im Audit-Log
- Konfigurierbar per
config.ymlAbschnittldap:
Konfigurationsformat (config.yml)
ldap:
enabled: true
url: "ldap://192.168.1.10:389" # oder ldaps://...
bind_dn: "CN=archivmail-svc,OU=ServiceAccounts,DC=corp,DC=local"
bind_password: "geheim"
base_dn: "OU=Users,DC=corp,DC=local"
user_filter: "(sAMAccountName=%s)" # %s wird durch eingegebenen Username ersetzt
tls: false # STARTTLS
tls_skip_verify: false
default_role: "user" # Rolle für neue LDAP-User
group_mappings: # optional: AD-Gruppe → archivmail-Rolle
- group_dn: "CN=archivmail-admins,OU=Groups,DC=corp,DC=local"
role: "domain_admin" # PROJ-21: "admin" → "domain_admin"
- group_dn: "CN=archivmail-auditors,OU=Groups,DC=corp,DC=local"
role: "auditor"
# Multi-Tenant (PROJ-21 Phase B): default_tenant_id weist LDAP-User einem Mandanten zu.
# Im Single-Tenant-Betrieb weglassen oder auf den Default-Mandanten setzen.
# default_tenant_id: 1
Pro-Mandant-LDAP (Multi-Tenant, Phase B)
Nach PROJ-21 Phase 2 wird LDAP zusätzlich pro Mandant in der DB konfigurierbar:
CREATE TABLE tenant_ldap (
tenant_id BIGINT PRIMARY KEY REFERENCES tenants(id) ON DELETE CASCADE,
enabled BOOLEAN NOT NULL DEFAULT false,
url TEXT NOT NULL,
bind_dn TEXT NOT NULL,
bind_password TEXT NOT NULL, -- verschlüsselt (AES-256-GCM wie IMAP-Passwörter)
base_dn TEXT NOT NULL,
user_filter TEXT NOT NULL DEFAULT '(sAMAccountName=%s)',
tls BOOLEAN NOT NULL DEFAULT false,
tls_skip_verify BOOLEAN NOT NULL DEFAULT false,
default_role VARCHAR(20) NOT NULL DEFAULT 'user',
group_mappings JSONB
);
API-Endpunkte für Domain-Admins (nach PROJ-21):
GET /api/tenant/ldap Aktuelle LDAP-Konfiguration des eigenen Mandanten
PUT /api/tenant/ldap LDAP konfigurieren / aktualisieren
DELETE /api/tenant/ldap LDAP deaktivieren
POST /api/tenant/ldap/test Verbindung testen
Technische Umsetzung
Neues Paket: internal/ldapauth
internal/ldapauth/
ldap.go — Client, Bind, Search, Authenticate
ldap_test.go — Tests mit Mock-LDAP
Abhängigkeit: github.com/go-ldap/ldap/v3
Ablauf Login mit LDAP (Phase A — Single-Tenant)
auth.Manager.Login(username, password)prüft zuerst lokale DB- Wenn lokaler User nicht gefunden UND LDAP aktiviert → LDAP-Auth versuchen
- LDAP-Bind mit Service Account → User-DN per
user_filtersuchen - User-Bind mit gefundener DN + eingegebenem Passwort
- Optional: Gruppen-Mitgliedschaft abfragen → Rolle bestimmen
userstore.UpsertLDAPUser(username, email, role)aufrufen- JWT-Token wie bei lokalem Login ausstellen
Ablauf Login mit LDAP (Phase B — Multi-Tenant, nach PROJ-21)
- Username enthält Domain-Hinweis (
user@mustermann.de) oder Mandant wird im Login-Formular gewählt - Domain →
tenant_domains-Tabelle →tenant_idermitteln tenant_ldap-Konfiguration für diesen Mandanten laden (Fallback: globaleconfig.yml)- LDAP-Auth wie Phase A, aber mit mandantenspezifischem Server
userstore.UpsertLDAPUser(username, email, role, tenantID)aufrufen —tenant_idwird mitgegeben- JWT enthält
tenant_id(PROJ-21)
Felder aus LDAP lesen
| LDAP-Attribut | archivmail-Feld |
|---|---|
sAMAccountName / uid |
username |
mail |
|
memberOf |
→ Gruppen-Mapping → role |
displayName |
(für spätere Anzeige) |
API-Endpunkte
# Phase A (Single-Tenant, superadmin/admin):
GET /api/admin/ldap/test Globale LDAP-Verbindung testen
# Phase B (Multi-Tenant, domain_admin — nach PROJ-21):
GET /api/tenant/ldap Eigene LDAP-Konfiguration abrufen
PUT /api/tenant/ldap Eigene LDAP-Konfiguration setzen
DELETE /api/tenant/ldap LDAP deaktivieren
POST /api/tenant/ldap/test Verbindung testen (ohne Speichern)
Antwort /test:
{"ok": true, "message": "LDAP-Verbindung erfolgreich", "users_found": 42}
Nicht in diesem Feature
- Automatische User-Synchronisation (Bulk-Import aller AD-User) — separates Feature
- LDAP-Gruppen als Postfach-Zuweisungen
- Kerberos / SAML / OAuth2 (separate Features)
- Pro-Mandant-LDAP-UI im Frontend — wird in PROJ-21 (Phase 7) integriert
Dateien
internal/ldapauth/ldap.go(neu)internal/auth/auth.go(erweitert: LDAP-Fallback)config/config.go(erweitert:LDAPConfig)cmd/archivmail/main.go(erweitert: LDAP-Client initialisieren)internal/api/server.go(erweitert:/api/admin/ldap/test)install.sh(erweitert: LDAP-Kommentar in config.yml)