Implement native LDAP authentication support

- Create archivebox/config/ldap.py with LDAPConfig class
- Create archivebox/ldap/ Django app with custom auth backend
- Update core/settings.py to conditionally load LDAP when enabled
- Add LDAP_CREATE_SUPERUSER support to auto-grant superuser privileges
- Add comprehensive tests in test_auth_ldap.py (no mocks, no skips)
- LDAP only activates if django-auth-ldap is installed and LDAP_ENABLED=True
- Helpful error messages when LDAP libraries are missing or config is incomplete

Fixes #1664

Co-authored-by: Nick Sweeting <pirate@users.noreply.github.com>
This commit is contained in:
claude[bot]
2026-01-05 21:30:26 +00:00
parent 28b980a84a
commit c2bb4b25cb
7 changed files with 413 additions and 8 deletions

View File

@@ -92,6 +92,7 @@ def get_CONFIG():
ARCHIVING_CONFIG,
SEARCH_BACKEND_CONFIG,
)
from .ldap import LDAP_CONFIG
return {
'SHELL_CONFIG': SHELL_CONFIG,
'STORAGE_CONFIG': STORAGE_CONFIG,
@@ -99,4 +100,5 @@ def get_CONFIG():
'SERVER_CONFIG': SERVER_CONFIG,
'ARCHIVING_CONFIG': ARCHIVING_CONFIG,
'SEARCHBACKEND_CONFIG': SEARCH_BACKEND_CONFIG,
'LDAP_CONFIG': LDAP_CONFIG,
}

56
archivebox/config/ldap.py Normal file
View File

@@ -0,0 +1,56 @@
__package__ = "archivebox.config"
from typing import Optional
from pydantic import Field
from archivebox.config.configset import BaseConfigSet
class LDAPConfig(BaseConfigSet):
"""
LDAP authentication configuration.
Only loads and validates if django-auth-ldap is installed.
These settings integrate with Django's LDAP authentication backend.
"""
toml_section_header: str = "LDAP_CONFIG"
LDAP_ENABLED: bool = Field(default=False)
LDAP_SERVER_URI: Optional[str] = Field(default=None)
LDAP_BIND_DN: Optional[str] = Field(default=None)
LDAP_BIND_PASSWORD: Optional[str] = Field(default=None)
LDAP_USER_BASE: Optional[str] = Field(default=None)
LDAP_USER_FILTER: str = Field(default="(uid=%(user)s)")
LDAP_USERNAME_ATTR: str = Field(default="username")
LDAP_FIRSTNAME_ATTR: str = Field(default="givenName")
LDAP_LASTNAME_ATTR: str = Field(default="sn")
LDAP_EMAIL_ATTR: str = Field(default="mail")
LDAP_CREATE_SUPERUSER: bool = Field(default=False)
def validate_ldap_config(self) -> tuple[bool, str]:
"""
Validate that all required LDAP settings are configured.
Returns:
Tuple of (is_valid, error_message)
"""
if not self.LDAP_ENABLED:
return True, ""
required_fields = [
"LDAP_SERVER_URI",
"LDAP_BIND_DN",
"LDAP_BIND_PASSWORD",
"LDAP_USER_BASE",
]
missing = [field for field in required_fields if not getattr(self, field)]
if missing:
return False, f"LDAP_* config options must all be set if LDAP_ENABLED=True\nMissing: {', '.join(missing)}"
return True, ""
# Singleton instance
LDAP_CONFIG = LDAPConfig()