feat: add Windows DNS integration and LDAP/AD authentication

Windows DNS (WinRM):
- New dns_service.py: create/delete A-records via PowerShell over WinRM (NTLM)
- Idempotent create (removes existing record first), graceful delete
- DNS failures are non-fatal — deployment continues, error logged
- test-dns endpoint: GET /api/settings/test-dns
- Integrated into deploy_customer() and undeploy_customer()

LDAP / Active Directory auth:
- New ldap_service.py: service-account bind + user search + user bind (ldap3)
- Optional AD group restriction via ldap_group_dn
- Login flow: LDAP first → local fallback (prevents admin lockout)
- LDAP users auto-created with auth_provider="ldap" and role="viewer"
- test-ldap endpoint: GET /api/settings/test-ldap
- reset-password/reset-mfa guards extended to block LDAP users

All credentials (dns_password, ldap_bind_password) encrypted with Fernet.
New DB columns added via backwards-compatible migrations.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-21 21:06:51 +01:00
parent bc9aa6624f
commit 7793ca3666
11 changed files with 623 additions and 15 deletions

View File

@@ -101,6 +101,23 @@ def _run_migrations() -> None:
("users", "totp_enabled", "BOOLEAN DEFAULT 0"),
("system_config", "ssl_mode", "TEXT DEFAULT 'letsencrypt'"),
("system_config", "wildcard_cert_id", "INTEGER"),
# Windows DNS
("system_config", "dns_enabled", "BOOLEAN DEFAULT 0"),
("system_config", "dns_server", "TEXT"),
("system_config", "dns_username", "TEXT"),
("system_config", "dns_password_encrypted", "TEXT"),
("system_config", "dns_zone", "TEXT"),
("system_config", "dns_record_ip", "TEXT"),
# LDAP
("system_config", "ldap_enabled", "BOOLEAN DEFAULT 0"),
("system_config", "ldap_server", "TEXT"),
("system_config", "ldap_port", "INTEGER DEFAULT 389"),
("system_config", "ldap_use_ssl", "BOOLEAN DEFAULT 0"),
("system_config", "ldap_bind_dn", "TEXT"),
("system_config", "ldap_bind_password_encrypted", "TEXT"),
("system_config", "ldap_base_dn", "TEXT"),
("system_config", "ldap_user_filter", "TEXT DEFAULT '(sAMAccountName={username})'"),
("system_config", "ldap_group_dn", "TEXT"),
]
for table, column, col_type in migrations:
if not _has_column(table, column):