| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218 |
- """
- LDAP authentication tests for ArchiveBox.
- Tests LDAP configuration, validation, and integration with Django.
- Per CLAUDE.md: NO MOCKS, NO SKIPS - all tests use real code paths.
- """
- import os
- import sys
- import tempfile
- import unittest
- from pathlib import Path
- class TestLDAPConfig(unittest.TestCase):
- """Test LDAP configuration loading and validation."""
- def test_ldap_config_defaults(self):
- """Test that LDAP config loads with correct defaults."""
- from archivebox.config.ldap import LDAP_CONFIG
- # Check default values
- self.assertFalse(LDAP_CONFIG.LDAP_ENABLED)
- self.assertIsNone(LDAP_CONFIG.LDAP_SERVER_URI)
- self.assertIsNone(LDAP_CONFIG.LDAP_BIND_DN)
- self.assertIsNone(LDAP_CONFIG.LDAP_BIND_PASSWORD)
- self.assertIsNone(LDAP_CONFIG.LDAP_USER_BASE)
- self.assertEqual(LDAP_CONFIG.LDAP_USER_FILTER, "(uid=%(user)s)")
- self.assertEqual(LDAP_CONFIG.LDAP_USERNAME_ATTR, "username")
- self.assertEqual(LDAP_CONFIG.LDAP_FIRSTNAME_ATTR, "givenName")
- self.assertEqual(LDAP_CONFIG.LDAP_LASTNAME_ATTR, "sn")
- self.assertEqual(LDAP_CONFIG.LDAP_EMAIL_ATTR, "mail")
- self.assertFalse(LDAP_CONFIG.LDAP_CREATE_SUPERUSER)
- def test_ldap_config_validation_disabled(self):
- """Test that validation passes when LDAP is disabled."""
- from archivebox.config.ldap import LDAPConfig
- config = LDAPConfig(LDAP_ENABLED=False)
- is_valid, error_msg = config.validate_ldap_config()
- self.assertTrue(is_valid)
- self.assertEqual(error_msg, "")
- def test_ldap_config_validation_missing_fields(self):
- """Test that validation fails when required fields are missing."""
- from archivebox.config.ldap import LDAPConfig
- # Enable LDAP but don't provide required fields
- config = LDAPConfig(LDAP_ENABLED=True)
- is_valid, error_msg = config.validate_ldap_config()
- self.assertFalse(is_valid)
- self.assertIn("LDAP_* config options must all be set", error_msg)
- self.assertIn("LDAP_SERVER_URI", error_msg)
- self.assertIn("LDAP_BIND_DN", error_msg)
- self.assertIn("LDAP_BIND_PASSWORD", error_msg)
- self.assertIn("LDAP_USER_BASE", error_msg)
- def test_ldap_config_validation_complete(self):
- """Test that validation passes when all required fields are provided."""
- from archivebox.config.ldap import LDAPConfig
- config = LDAPConfig(
- LDAP_ENABLED=True,
- LDAP_SERVER_URI="ldap://localhost:389",
- LDAP_BIND_DN="cn=admin,dc=example,dc=com",
- LDAP_BIND_PASSWORD="password",
- LDAP_USER_BASE="ou=users,dc=example,dc=com",
- )
- is_valid, error_msg = config.validate_ldap_config()
- self.assertTrue(is_valid)
- self.assertEqual(error_msg, "")
- def test_ldap_config_in_get_config(self):
- """Test that LDAP_CONFIG is included in get_CONFIG()."""
- from archivebox.config import get_CONFIG
- all_config = get_CONFIG()
- self.assertIn('LDAP_CONFIG', all_config)
- self.assertEqual(all_config['LDAP_CONFIG'].__class__.__name__, 'LDAPConfig')
- class TestLDAPIntegration(unittest.TestCase):
- """Test LDAP integration with Django settings."""
- def test_django_settings_without_ldap_enabled(self):
- """Test that Django settings work correctly when LDAP is disabled."""
- # Import Django settings (LDAP_ENABLED should be False by default)
- from django.conf import settings
- # Should have default authentication backends
- self.assertIn("django.contrib.auth.backends.RemoteUserBackend", settings.AUTHENTICATION_BACKENDS)
- self.assertIn("django.contrib.auth.backends.ModelBackend", settings.AUTHENTICATION_BACKENDS)
- # LDAP backend should not be present when disabled
- ldap_backends = [b for b in settings.AUTHENTICATION_BACKENDS if 'ldap' in b.lower()]
- self.assertEqual(len(ldap_backends), 0, "LDAP backend should not be present when LDAP_ENABLED=False")
- def test_django_settings_with_ldap_library_check(self):
- """Test that Django settings check for LDAP libraries when enabled."""
- # Try to import django-auth-ldap to see if it's available
- try:
- import django_auth_ldap
- import ldap
- ldap_available = True
- except ImportError:
- ldap_available = False
- # If LDAP libraries are not available, settings should handle gracefully
- if not ldap_available:
- # Settings should have loaded without LDAP backend
- from django.conf import settings
- ldap_backends = [b for b in settings.AUTHENTICATION_BACKENDS if 'ldap' in b.lower()]
- self.assertEqual(len(ldap_backends), 0, "LDAP backend should not be present when libraries unavailable")
- class TestLDAPAuthBackend(unittest.TestCase):
- """Test custom LDAP authentication backend."""
- def test_ldap_backend_class_exists(self):
- """Test that ArchiveBoxLDAPBackend class is defined."""
- from archivebox.ldap.auth import ArchiveBoxLDAPBackend
- self.assertTrue(hasattr(ArchiveBoxLDAPBackend, 'authenticate_ldap_user'))
- def test_ldap_backend_inherits_correctly(self):
- """Test that ArchiveBoxLDAPBackend has correct inheritance."""
- from archivebox.ldap.auth import ArchiveBoxLDAPBackend
- # Should have authenticate_ldap_user method (from base or overridden)
- self.assertTrue(callable(getattr(ArchiveBoxLDAPBackend, 'authenticate_ldap_user', None)))
- class TestArchiveBoxWithLDAP(unittest.TestCase):
- """Test ArchiveBox commands with LDAP configuration."""
- def setUp(self):
- """Set up test environment."""
- self.work_dir = tempfile.mkdtemp(prefix='archivebox-ldap-test-')
- def test_archivebox_init_without_ldap(self):
- """Test that archivebox init works without LDAP enabled."""
- import subprocess
- # Run archivebox init
- result = subprocess.run(
- [sys.executable, '-m', 'archivebox', 'init'],
- cwd=self.work_dir,
- capture_output=True,
- timeout=45,
- env={
- **os.environ,
- 'DATA_DIR': self.work_dir,
- 'LDAP_ENABLED': 'False',
- }
- )
- # Should succeed
- self.assertEqual(result.returncode, 0, f"archivebox init failed: {result.stderr.decode()}")
- def test_archivebox_version_with_ldap_config(self):
- """Test that archivebox version works with LDAP config set."""
- import subprocess
- # Run archivebox version with LDAP config env vars
- result = subprocess.run(
- [sys.executable, '-m', 'archivebox', 'version'],
- capture_output=True,
- timeout=10,
- env={
- **os.environ,
- 'LDAP_ENABLED': 'False',
- 'LDAP_SERVER_URI': 'ldap://localhost:389',
- }
- )
- # Should succeed
- self.assertEqual(result.returncode, 0, f"archivebox version failed: {result.stderr.decode()}")
- class TestLDAPConfigValidationInArchiveBox(unittest.TestCase):
- """Test LDAP config validation when running ArchiveBox commands."""
- def setUp(self):
- """Set up test environment."""
- self.work_dir = tempfile.mkdtemp(prefix='archivebox-ldap-validation-')
- def test_archivebox_init_with_incomplete_ldap_config(self):
- """Test that archivebox init fails with helpful error when LDAP config is incomplete."""
- import subprocess
- # Run archivebox init with LDAP enabled but missing required fields
- result = subprocess.run(
- [sys.executable, '-m', 'archivebox', 'init'],
- cwd=self.work_dir,
- capture_output=True,
- timeout=45,
- env={
- **os.environ,
- 'DATA_DIR': self.work_dir,
- 'LDAP_ENABLED': 'True',
- # Missing: LDAP_SERVER_URI, LDAP_BIND_DN, etc.
- }
- )
- # Should fail with validation error
- self.assertNotEqual(result.returncode, 0, "Should fail with incomplete LDAP config")
- # Check error message
- stderr = result.stderr.decode()
- self.assertIn("LDAP_* config options must all be set", stderr,
- f"Expected validation error message in: {stderr}")
- if __name__ == '__main__':
- unittest.main()
|