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:
@@ -172,6 +172,28 @@ class SystemConfig(Base):
|
||||
String(255), nullable=True,
|
||||
comment="If set, only Azure AD users in this group (object ID) are allowed to log in."
|
||||
)
|
||||
|
||||
# Windows DNS integration
|
||||
dns_enabled: Mapped[bool] = mapped_column(Boolean, default=False)
|
||||
dns_server: Mapped[Optional[str]] = mapped_column(String(255), nullable=True)
|
||||
dns_username: Mapped[Optional[str]] = mapped_column(String(255), nullable=True)
|
||||
dns_password_encrypted: Mapped[Optional[str]] = mapped_column(Text, nullable=True)
|
||||
dns_zone: Mapped[Optional[str]] = mapped_column(String(255), nullable=True)
|
||||
dns_record_ip: Mapped[Optional[str]] = mapped_column(String(45), nullable=True)
|
||||
|
||||
# LDAP / Active Directory authentication
|
||||
ldap_enabled: Mapped[bool] = mapped_column(Boolean, default=False)
|
||||
ldap_server: Mapped[Optional[str]] = mapped_column(String(255), nullable=True)
|
||||
ldap_port: Mapped[int] = mapped_column(Integer, default=389)
|
||||
ldap_use_ssl: Mapped[bool] = mapped_column(Boolean, default=False)
|
||||
ldap_bind_dn: Mapped[Optional[str]] = mapped_column(String(500), nullable=True)
|
||||
ldap_bind_password_encrypted: Mapped[Optional[str]] = mapped_column(Text, nullable=True)
|
||||
ldap_base_dn: Mapped[Optional[str]] = mapped_column(String(500), nullable=True)
|
||||
ldap_user_filter: Mapped[Optional[str]] = mapped_column(
|
||||
String(255), default="(sAMAccountName={username})"
|
||||
)
|
||||
ldap_group_dn: Mapped[Optional[str]] = mapped_column(String(500), nullable=True)
|
||||
|
||||
created_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow)
|
||||
updated_at: Mapped[datetime] = mapped_column(
|
||||
DateTime, default=datetime.utcnow, onupdate=datetime.utcnow
|
||||
@@ -208,6 +230,21 @@ class SystemConfig(Base):
|
||||
"azure_client_id": self.azure_client_id or "",
|
||||
"azure_client_secret_set": bool(self.azure_client_secret_encrypted),
|
||||
"azure_allowed_group_id": self.azure_allowed_group_id or "",
|
||||
"dns_enabled": bool(self.dns_enabled),
|
||||
"dns_server": self.dns_server or "",
|
||||
"dns_username": self.dns_username or "",
|
||||
"dns_password_set": bool(self.dns_password_encrypted),
|
||||
"dns_zone": self.dns_zone or "",
|
||||
"dns_record_ip": self.dns_record_ip or "",
|
||||
"ldap_enabled": bool(self.ldap_enabled),
|
||||
"ldap_server": self.ldap_server or "",
|
||||
"ldap_port": self.ldap_port or 389,
|
||||
"ldap_use_ssl": bool(self.ldap_use_ssl),
|
||||
"ldap_bind_dn": self.ldap_bind_dn or "",
|
||||
"ldap_bind_password_set": bool(self.ldap_bind_password_encrypted),
|
||||
"ldap_base_dn": self.ldap_base_dn or "",
|
||||
"ldap_user_filter": self.ldap_user_filter or "(sAMAccountName={username})",
|
||||
"ldap_group_dn": self.ldap_group_dn or "",
|
||||
"created_at": self.created_at.isoformat() if self.created_at else None,
|
||||
"updated_at": self.updated_at.isoformat() if self.updated_at else None,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user