authentik.sources.ldap.sync.groups
Sync LDAP Users and groups into authentik
1"""Sync LDAP Users and groups into authentik""" 2 3from collections.abc import Generator 4 5from django.core.exceptions import FieldError 6from django.db.utils import IntegrityError 7from ldap3 import ALL_ATTRIBUTES, ALL_OPERATIONAL_ATTRIBUTES, SUBTREE 8 9from authentik.core.expression.exceptions import ( 10 PropertyMappingExpressionException, 11 SkipObjectException, 12) 13from authentik.core.models import Group 14from authentik.core.sources.mapper import SourceMapper 15from authentik.events.models import Event, EventAction 16from authentik.lib.sync.outgoing.exceptions import StopSync 17from authentik.sources.ldap.models import ( 18 LDAP_UNIQUENESS, 19 GroupLDAPSourceConnection, 20 LDAPSource, 21 flatten, 22) 23from authentik.sources.ldap.sync.base import BaseLDAPSynchronizer 24from authentik.tasks.models import Task 25 26 27class GroupLDAPSynchronizer(BaseLDAPSynchronizer): 28 """Sync LDAP Users and groups into authentik""" 29 30 def __init__(self, source: LDAPSource, task: Task): 31 super().__init__(source, task) 32 self._source = source 33 self.mapper = SourceMapper(source) 34 self.manager = self.mapper.get_manager(Group, ["ldap", "dn"]) 35 36 @staticmethod 37 def name() -> str: 38 return "groups" 39 40 def get_objects(self, **kwargs) -> Generator: 41 if not self._source.sync_groups: 42 self._task.info("Group syncing is disabled for this Source") 43 return iter(()) 44 return self.search_paginator( 45 search_base=self.base_dn_groups, 46 search_filter=self._source.group_object_filter, 47 search_scope=SUBTREE, 48 attributes=[ 49 ALL_ATTRIBUTES, 50 ALL_OPERATIONAL_ATTRIBUTES, 51 self._source.object_uniqueness_field, 52 ], 53 **kwargs, 54 ) 55 56 def sync(self, page_data: list) -> int: 57 """Iterate over all LDAP Groups and create authentik_core.Group instances""" 58 if not self._source.sync_groups: 59 self._task.info("Group syncing is disabled for this Source") 60 return -1 61 group_count = 0 62 for group_data in page_data: 63 if (attributes := self.get_attributes(group_data)) is None: 64 continue 65 group_dn = flatten(flatten(group_data.get("entryDN", group_data.get("dn")))) 66 if not (uniq := self.get_identifier(attributes)): 67 self._task.info( 68 f"Uniqueness field not found/not set in attributes: '{group_dn}'", 69 attributes=list(attributes.keys()), 70 dn=group_dn, 71 ) 72 continue 73 try: 74 defaults = { 75 k: flatten(v) 76 for k, v in self.mapper.build_object_properties( 77 object_type=Group, 78 manager=self.manager, 79 user=None, 80 request=None, 81 dn=group_dn, 82 ldap=attributes, 83 ).items() 84 } 85 if "name" not in defaults: 86 raise IntegrityError("Name was not set by propertymappings") 87 # Special check for `users` field, as this is an M2M relation, and cannot be sync'd 88 if "users" in defaults: 89 del defaults["users"] 90 parent = defaults.pop("parent", None) 91 group, created = Group.update_or_create_attributes( 92 { 93 f"attributes__{LDAP_UNIQUENESS}": uniq, 94 }, 95 defaults, 96 ) 97 if parent: 98 group.parents.add(parent) 99 self._logger.debug("Created group with attributes", **defaults) 100 if not GroupLDAPSourceConnection.objects.filter( 101 source=self._source, identifier=uniq 102 ): 103 GroupLDAPSourceConnection.objects.create( 104 source=self._source, group=group, identifier=uniq 105 ) 106 except SkipObjectException: 107 continue 108 except PropertyMappingExpressionException as exc: 109 raise StopSync(exc, None, exc.mapping) from exc 110 except (IntegrityError, FieldError, TypeError, AttributeError) as exc: 111 Event.new( 112 EventAction.CONFIGURATION_ERROR, 113 message=( 114 f"Failed to create group: {str(exc)} " 115 "To merge new group with existing group, set the groups's " 116 f"Attribute '{LDAP_UNIQUENESS}' to '{uniq}'" 117 ), 118 source=self._source, 119 dn=group_dn, 120 ).save() 121 else: 122 self._logger.debug("Synced group", group=group.name, created=created) 123 group_count += 1 124 return group_count
28class GroupLDAPSynchronizer(BaseLDAPSynchronizer): 29 """Sync LDAP Users and groups into authentik""" 30 31 def __init__(self, source: LDAPSource, task: Task): 32 super().__init__(source, task) 33 self._source = source 34 self.mapper = SourceMapper(source) 35 self.manager = self.mapper.get_manager(Group, ["ldap", "dn"]) 36 37 @staticmethod 38 def name() -> str: 39 return "groups" 40 41 def get_objects(self, **kwargs) -> Generator: 42 if not self._source.sync_groups: 43 self._task.info("Group syncing is disabled for this Source") 44 return iter(()) 45 return self.search_paginator( 46 search_base=self.base_dn_groups, 47 search_filter=self._source.group_object_filter, 48 search_scope=SUBTREE, 49 attributes=[ 50 ALL_ATTRIBUTES, 51 ALL_OPERATIONAL_ATTRIBUTES, 52 self._source.object_uniqueness_field, 53 ], 54 **kwargs, 55 ) 56 57 def sync(self, page_data: list) -> int: 58 """Iterate over all LDAP Groups and create authentik_core.Group instances""" 59 if not self._source.sync_groups: 60 self._task.info("Group syncing is disabled for this Source") 61 return -1 62 group_count = 0 63 for group_data in page_data: 64 if (attributes := self.get_attributes(group_data)) is None: 65 continue 66 group_dn = flatten(flatten(group_data.get("entryDN", group_data.get("dn")))) 67 if not (uniq := self.get_identifier(attributes)): 68 self._task.info( 69 f"Uniqueness field not found/not set in attributes: '{group_dn}'", 70 attributes=list(attributes.keys()), 71 dn=group_dn, 72 ) 73 continue 74 try: 75 defaults = { 76 k: flatten(v) 77 for k, v in self.mapper.build_object_properties( 78 object_type=Group, 79 manager=self.manager, 80 user=None, 81 request=None, 82 dn=group_dn, 83 ldap=attributes, 84 ).items() 85 } 86 if "name" not in defaults: 87 raise IntegrityError("Name was not set by propertymappings") 88 # Special check for `users` field, as this is an M2M relation, and cannot be sync'd 89 if "users" in defaults: 90 del defaults["users"] 91 parent = defaults.pop("parent", None) 92 group, created = Group.update_or_create_attributes( 93 { 94 f"attributes__{LDAP_UNIQUENESS}": uniq, 95 }, 96 defaults, 97 ) 98 if parent: 99 group.parents.add(parent) 100 self._logger.debug("Created group with attributes", **defaults) 101 if not GroupLDAPSourceConnection.objects.filter( 102 source=self._source, identifier=uniq 103 ): 104 GroupLDAPSourceConnection.objects.create( 105 source=self._source, group=group, identifier=uniq 106 ) 107 except SkipObjectException: 108 continue 109 except PropertyMappingExpressionException as exc: 110 raise StopSync(exc, None, exc.mapping) from exc 111 except (IntegrityError, FieldError, TypeError, AttributeError) as exc: 112 Event.new( 113 EventAction.CONFIGURATION_ERROR, 114 message=( 115 f"Failed to create group: {str(exc)} " 116 "To merge new group with existing group, set the groups's " 117 f"Attribute '{LDAP_UNIQUENESS}' to '{uniq}'" 118 ), 119 source=self._source, 120 dn=group_dn, 121 ).save() 122 else: 123 self._logger.debug("Synced group", group=group.name, created=created) 124 group_count += 1 125 return group_count
Sync LDAP Users and groups into authentik
GroupLDAPSynchronizer( source: authentik.sources.ldap.models.LDAPSource, task: authentik.tasks.models.Task)
def
get_objects(self, **kwargs) -> Generator:
41 def get_objects(self, **kwargs) -> Generator: 42 if not self._source.sync_groups: 43 self._task.info("Group syncing is disabled for this Source") 44 return iter(()) 45 return self.search_paginator( 46 search_base=self.base_dn_groups, 47 search_filter=self._source.group_object_filter, 48 search_scope=SUBTREE, 49 attributes=[ 50 ALL_ATTRIBUTES, 51 ALL_OPERATIONAL_ATTRIBUTES, 52 self._source.object_uniqueness_field, 53 ], 54 **kwargs, 55 )
Get objects from LDAP, implemented in subclass
def
sync(self, page_data: list) -> int:
57 def sync(self, page_data: list) -> int: 58 """Iterate over all LDAP Groups and create authentik_core.Group instances""" 59 if not self._source.sync_groups: 60 self._task.info("Group syncing is disabled for this Source") 61 return -1 62 group_count = 0 63 for group_data in page_data: 64 if (attributes := self.get_attributes(group_data)) is None: 65 continue 66 group_dn = flatten(flatten(group_data.get("entryDN", group_data.get("dn")))) 67 if not (uniq := self.get_identifier(attributes)): 68 self._task.info( 69 f"Uniqueness field not found/not set in attributes: '{group_dn}'", 70 attributes=list(attributes.keys()), 71 dn=group_dn, 72 ) 73 continue 74 try: 75 defaults = { 76 k: flatten(v) 77 for k, v in self.mapper.build_object_properties( 78 object_type=Group, 79 manager=self.manager, 80 user=None, 81 request=None, 82 dn=group_dn, 83 ldap=attributes, 84 ).items() 85 } 86 if "name" not in defaults: 87 raise IntegrityError("Name was not set by propertymappings") 88 # Special check for `users` field, as this is an M2M relation, and cannot be sync'd 89 if "users" in defaults: 90 del defaults["users"] 91 parent = defaults.pop("parent", None) 92 group, created = Group.update_or_create_attributes( 93 { 94 f"attributes__{LDAP_UNIQUENESS}": uniq, 95 }, 96 defaults, 97 ) 98 if parent: 99 group.parents.add(parent) 100 self._logger.debug("Created group with attributes", **defaults) 101 if not GroupLDAPSourceConnection.objects.filter( 102 source=self._source, identifier=uniq 103 ): 104 GroupLDAPSourceConnection.objects.create( 105 source=self._source, group=group, identifier=uniq 106 ) 107 except SkipObjectException: 108 continue 109 except PropertyMappingExpressionException as exc: 110 raise StopSync(exc, None, exc.mapping) from exc 111 except (IntegrityError, FieldError, TypeError, AttributeError) as exc: 112 Event.new( 113 EventAction.CONFIGURATION_ERROR, 114 message=( 115 f"Failed to create group: {str(exc)} " 116 "To merge new group with existing group, set the groups's " 117 f"Attribute '{LDAP_UNIQUENESS}' to '{uniq}'" 118 ), 119 source=self._source, 120 dn=group_dn, 121 ).save() 122 else: 123 self._logger.debug("Synced group", group=group.name, created=created) 124 group_count += 1 125 return group_count
Iterate over all LDAP Groups and create authentik_core.Group instances