config.py 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  1. __package__ = 'plugins_auth.ldap'
  2. import sys
  3. from typing import Dict, List, Optional
  4. from pydantic import Field, model_validator, computed_field
  5. from abx.archivebox.base_configset import BaseConfigSet
  6. LDAP_LIB = None
  7. LDAP_SEARCH = None
  8. def get_ldap_lib(extra_paths=()):
  9. global LDAP_LIB, LDAP_SEARCH
  10. if LDAP_LIB and LDAP_SEARCH:
  11. return LDAP_LIB, LDAP_SEARCH
  12. try:
  13. for path in extra_paths:
  14. if path not in sys.path:
  15. sys.path.append(path)
  16. import ldap
  17. from django_auth_ldap.config import LDAPSearch
  18. LDAP_LIB, LDAP_SEARCH = ldap, LDAPSearch
  19. except ImportError:
  20. pass
  21. return LDAP_LIB, LDAP_SEARCH
  22. ###################### Config ##########################
  23. class LdapConfig(BaseConfigSet):
  24. """
  25. LDAP Config gets imported by core/settings.py very early during startup.
  26. It needs to be in a separate file from apps.py so that it can be imported
  27. during settings.py initialization before the apps are loaded.
  28. """
  29. LDAP_ENABLED: bool = Field(default=False, alias='LDAP')
  30. LDAP_SERVER_URI: str = Field(default=None)
  31. LDAP_BIND_DN: str = Field(default=None)
  32. LDAP_BIND_PASSWORD: str = Field(default=None)
  33. LDAP_USER_BASE: str = Field(default=None)
  34. LDAP_USER_FILTER: str = Field(default=None)
  35. LDAP_CREATE_SUPERUSER: bool = Field(default=False)
  36. LDAP_USERNAME_ATTR: str = Field(default='username')
  37. LDAP_FIRSTNAME_ATTR: str = Field(default='first_name')
  38. LDAP_LASTNAME_ATTR: str = Field(default='last_name')
  39. LDAP_EMAIL_ATTR: str = Field(default='email')
  40. @model_validator(mode='after')
  41. def validate_ldap_config(self):
  42. if self.LDAP_ENABLED:
  43. LDAP_LIB, _LDAPSearch = get_ldap_lib()
  44. # Check that LDAP libraries are installed
  45. if LDAP_LIB is None:
  46. sys.stderr.write('[X] Error: LDAP Authentication is enabled but LDAP libraries are not installed. You may need to run: pip install archivebox[ldap]\n')
  47. # dont hard exit here. in case the user is just running "archivebox version" or "archivebox help", we still want those to work despite broken ldap
  48. # sys.exit(1)
  49. self.update_in_place(LDAP_ENABLED=False)
  50. # Check that all required LDAP config options are set
  51. if self.LDAP_CONFIG_IS_SET:
  52. missing_config_options = [
  53. key for key, value in self.model_dump().items()
  54. if value is None and key != 'LDAP_ENABLED'
  55. ]
  56. sys.stderr.write('[X] Error: LDAP_* config options must all be set if LDAP_ENABLED=True\n')
  57. sys.stderr.write(f' Missing: {", ".join(missing_config_options)}\n')
  58. self.update_in_place(LDAP_ENABLED=False)
  59. return self
  60. @computed_field
  61. @property
  62. def LDAP_CONFIG_IS_SET(self) -> bool:
  63. """Check that all required LDAP config options are set"""
  64. if self.LDAP_ENABLED:
  65. LDAP_LIB, _LDAPSearch = get_ldap_lib()
  66. return bool(LDAP_LIB) and self.LDAP_ENABLED and bool(
  67. self.LDAP_SERVER_URI
  68. and self.LDAP_BIND_DN
  69. and self.LDAP_BIND_PASSWORD
  70. and self.LDAP_USER_BASE
  71. and self.LDAP_USER_FILTER
  72. )
  73. return False
  74. @computed_field
  75. @property
  76. def LDAP_USER_ATTR_MAP(self) -> Dict[str, str]:
  77. return {
  78. 'username': self.LDAP_USERNAME_ATTR,
  79. 'first_name': self.LDAP_FIRSTNAME_ATTR,
  80. 'last_name': self.LDAP_LASTNAME_ATTR,
  81. 'email': self.LDAP_EMAIL_ATTR,
  82. }
  83. @computed_field
  84. @property
  85. def AUTHENTICATION_BACKENDS(self) -> List[str]:
  86. if self.LDAP_ENABLED:
  87. return [
  88. 'django.contrib.auth.backends.ModelBackend',
  89. 'django_auth_ldap.backend.LDAPBackend',
  90. ]
  91. return []
  92. @computed_field
  93. @property
  94. def AUTH_LDAP_USER_SEARCH(self) -> Optional[object]:
  95. if self.LDAP_ENABLED:
  96. LDAP_LIB, LDAPSearch = get_ldap_lib()
  97. return self.LDAP_USER_FILTER and LDAPSearch(
  98. self.LDAP_USER_BASE,
  99. LDAP_LIB.SCOPE_SUBTREE, # type: ignore
  100. '(&(' + self.LDAP_USERNAME_ATTR + '=%(user)s)' + self.LDAP_USER_FILTER + ')',
  101. )
  102. return None
  103. LDAP_CONFIG = LdapConfig()