authentik.sources.ldap.sync.users

Sync LDAP Users into authentik

  1"""Sync LDAP Users 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 User
 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    LDAPSource,
 20    UserLDAPSourceConnection,
 21    flatten,
 22)
 23from authentik.sources.ldap.sync.base import BaseLDAPSynchronizer
 24from authentik.sources.ldap.sync.vendor.freeipa import FreeIPA
 25from authentik.sources.ldap.sync.vendor.ms_ad import MicrosoftActiveDirectory
 26from authentik.tasks.models import Task
 27
 28
 29class UserLDAPSynchronizer(BaseLDAPSynchronizer):
 30    """Sync LDAP Users into authentik"""
 31
 32    def __init__(self, source: LDAPSource, task: Task):
 33        super().__init__(source, task)
 34        self.mapper = SourceMapper(source)
 35        self.manager = self.mapper.get_manager(User, ["ldap", "dn"])
 36
 37    @staticmethod
 38    def name() -> str:
 39        return "users"
 40
 41    def get_objects(self, **kwargs) -> Generator:
 42        if not self._source.sync_users:
 43            self._task.info("User syncing is disabled for this Source")
 44            return iter(())
 45        return self.search_paginator(
 46            search_base=self.base_dn_users,
 47            search_filter=self._source.user_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 Users and create authentik_core.User instances"""
 59        if not self._source.sync_users:
 60            self._task.info("User syncing is disabled for this Source")
 61            return -1
 62        user_count = 0
 63        for user in page_data:
 64            if (attributes := self.get_attributes(user)) is None:
 65                continue
 66            user_dn = flatten(user.get("entryDN", user.get("dn")))
 67            if not (uniq := self.get_identifier(attributes)):
 68                self._task.info(
 69                    f"Uniqueness field not found/not set in attributes: '{user_dn}'",
 70                    attributes=list(attributes.keys()),
 71                    dn=user_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=User,
 79                        manager=self.manager,
 80                        user=None,
 81                        request=None,
 82                        dn=user_dn,
 83                        ldap=attributes,
 84                    ).items()
 85                }
 86                self._logger.debug("Writing user with attributes", **defaults)
 87                if "username" not in defaults:
 88                    raise IntegrityError("Username was not set by propertymappings")
 89                ak_user, created = User.update_or_create_attributes(
 90                    {f"attributes__{LDAP_UNIQUENESS}": uniq}, defaults
 91                )
 92                if not UserLDAPSourceConnection.objects.filter(
 93                    source=self._source, identifier=uniq
 94                ):
 95                    UserLDAPSourceConnection.objects.create(
 96                        source=self._source, user=ak_user, identifier=uniq
 97                    )
 98            except PropertyMappingExpressionException as exc:
 99                raise StopSync(exc, None, exc.mapping) from exc
100            except SkipObjectException:
101                continue
102            except (IntegrityError, FieldError, TypeError, AttributeError) as exc:
103                Event.new(
104                    EventAction.CONFIGURATION_ERROR,
105                    message=(
106                        f"Failed to create user: {str(exc)} "
107                        "To merge new user with existing user, set the user's "
108                        f"Attribute '{LDAP_UNIQUENESS}' to '{uniq}'"
109                    ),
110                    source=self._source,
111                    dn=user_dn,
112                ).save()
113            else:
114                self._logger.debug("Synced User", user=ak_user.username, created=created)
115                user_count += 1
116                MicrosoftActiveDirectory(self._source, self._task).sync(
117                    attributes, ak_user, created
118                )
119                FreeIPA(self._source, self._task).sync(attributes, ak_user, created)
120        return user_count
class UserLDAPSynchronizer(authentik.sources.ldap.sync.base.BaseLDAPSynchronizer):
 30class UserLDAPSynchronizer(BaseLDAPSynchronizer):
 31    """Sync LDAP Users into authentik"""
 32
 33    def __init__(self, source: LDAPSource, task: Task):
 34        super().__init__(source, task)
 35        self.mapper = SourceMapper(source)
 36        self.manager = self.mapper.get_manager(User, ["ldap", "dn"])
 37
 38    @staticmethod
 39    def name() -> str:
 40        return "users"
 41
 42    def get_objects(self, **kwargs) -> Generator:
 43        if not self._source.sync_users:
 44            self._task.info("User syncing is disabled for this Source")
 45            return iter(())
 46        return self.search_paginator(
 47            search_base=self.base_dn_users,
 48            search_filter=self._source.user_object_filter,
 49            search_scope=SUBTREE,
 50            attributes=[
 51                ALL_ATTRIBUTES,
 52                ALL_OPERATIONAL_ATTRIBUTES,
 53                self._source.object_uniqueness_field,
 54            ],
 55            **kwargs,
 56        )
 57
 58    def sync(self, page_data: list) -> int:
 59        """Iterate over all LDAP Users and create authentik_core.User instances"""
 60        if not self._source.sync_users:
 61            self._task.info("User syncing is disabled for this Source")
 62            return -1
 63        user_count = 0
 64        for user in page_data:
 65            if (attributes := self.get_attributes(user)) is None:
 66                continue
 67            user_dn = flatten(user.get("entryDN", user.get("dn")))
 68            if not (uniq := self.get_identifier(attributes)):
 69                self._task.info(
 70                    f"Uniqueness field not found/not set in attributes: '{user_dn}'",
 71                    attributes=list(attributes.keys()),
 72                    dn=user_dn,
 73                )
 74                continue
 75            try:
 76                defaults = {
 77                    k: flatten(v)
 78                    for k, v in self.mapper.build_object_properties(
 79                        object_type=User,
 80                        manager=self.manager,
 81                        user=None,
 82                        request=None,
 83                        dn=user_dn,
 84                        ldap=attributes,
 85                    ).items()
 86                }
 87                self._logger.debug("Writing user with attributes", **defaults)
 88                if "username" not in defaults:
 89                    raise IntegrityError("Username was not set by propertymappings")
 90                ak_user, created = User.update_or_create_attributes(
 91                    {f"attributes__{LDAP_UNIQUENESS}": uniq}, defaults
 92                )
 93                if not UserLDAPSourceConnection.objects.filter(
 94                    source=self._source, identifier=uniq
 95                ):
 96                    UserLDAPSourceConnection.objects.create(
 97                        source=self._source, user=ak_user, identifier=uniq
 98                    )
 99            except PropertyMappingExpressionException as exc:
100                raise StopSync(exc, None, exc.mapping) from exc
101            except SkipObjectException:
102                continue
103            except (IntegrityError, FieldError, TypeError, AttributeError) as exc:
104                Event.new(
105                    EventAction.CONFIGURATION_ERROR,
106                    message=(
107                        f"Failed to create user: {str(exc)} "
108                        "To merge new user with existing user, set the user's "
109                        f"Attribute '{LDAP_UNIQUENESS}' to '{uniq}'"
110                    ),
111                    source=self._source,
112                    dn=user_dn,
113                ).save()
114            else:
115                self._logger.debug("Synced User", user=ak_user.username, created=created)
116                user_count += 1
117                MicrosoftActiveDirectory(self._source, self._task).sync(
118                    attributes, ak_user, created
119                )
120                FreeIPA(self._source, self._task).sync(attributes, ak_user, created)
121        return user_count

Sync LDAP Users into authentik

UserLDAPSynchronizer( source: authentik.sources.ldap.models.LDAPSource, task: authentik.tasks.models.Task)
33    def __init__(self, source: LDAPSource, task: Task):
34        super().__init__(source, task)
35        self.mapper = SourceMapper(source)
36        self.manager = self.mapper.get_manager(User, ["ldap", "dn"])
mapper
manager
@staticmethod
def name() -> str:
38    @staticmethod
39    def name() -> str:
40        return "users"

UI name for the type of object this class synchronizes

def get_objects(self, **kwargs) -> Generator:
42    def get_objects(self, **kwargs) -> Generator:
43        if not self._source.sync_users:
44            self._task.info("User syncing is disabled for this Source")
45            return iter(())
46        return self.search_paginator(
47            search_base=self.base_dn_users,
48            search_filter=self._source.user_object_filter,
49            search_scope=SUBTREE,
50            attributes=[
51                ALL_ATTRIBUTES,
52                ALL_OPERATIONAL_ATTRIBUTES,
53                self._source.object_uniqueness_field,
54            ],
55            **kwargs,
56        )

Get objects from LDAP, implemented in subclass

def sync(self, page_data: list) -> int:
 58    def sync(self, page_data: list) -> int:
 59        """Iterate over all LDAP Users and create authentik_core.User instances"""
 60        if not self._source.sync_users:
 61            self._task.info("User syncing is disabled for this Source")
 62            return -1
 63        user_count = 0
 64        for user in page_data:
 65            if (attributes := self.get_attributes(user)) is None:
 66                continue
 67            user_dn = flatten(user.get("entryDN", user.get("dn")))
 68            if not (uniq := self.get_identifier(attributes)):
 69                self._task.info(
 70                    f"Uniqueness field not found/not set in attributes: '{user_dn}'",
 71                    attributes=list(attributes.keys()),
 72                    dn=user_dn,
 73                )
 74                continue
 75            try:
 76                defaults = {
 77                    k: flatten(v)
 78                    for k, v in self.mapper.build_object_properties(
 79                        object_type=User,
 80                        manager=self.manager,
 81                        user=None,
 82                        request=None,
 83                        dn=user_dn,
 84                        ldap=attributes,
 85                    ).items()
 86                }
 87                self._logger.debug("Writing user with attributes", **defaults)
 88                if "username" not in defaults:
 89                    raise IntegrityError("Username was not set by propertymappings")
 90                ak_user, created = User.update_or_create_attributes(
 91                    {f"attributes__{LDAP_UNIQUENESS}": uniq}, defaults
 92                )
 93                if not UserLDAPSourceConnection.objects.filter(
 94                    source=self._source, identifier=uniq
 95                ):
 96                    UserLDAPSourceConnection.objects.create(
 97                        source=self._source, user=ak_user, identifier=uniq
 98                    )
 99            except PropertyMappingExpressionException as exc:
100                raise StopSync(exc, None, exc.mapping) from exc
101            except SkipObjectException:
102                continue
103            except (IntegrityError, FieldError, TypeError, AttributeError) as exc:
104                Event.new(
105                    EventAction.CONFIGURATION_ERROR,
106                    message=(
107                        f"Failed to create user: {str(exc)} "
108                        "To merge new user with existing user, set the user's "
109                        f"Attribute '{LDAP_UNIQUENESS}' to '{uniq}'"
110                    ),
111                    source=self._source,
112                    dn=user_dn,
113                ).save()
114            else:
115                self._logger.debug("Synced User", user=ak_user.username, created=created)
116                user_count += 1
117                MicrosoftActiveDirectory(self._source, self._task).sync(
118                    attributes, ak_user, created
119                )
120                FreeIPA(self._source, self._task).sync(attributes, ak_user, created)
121        return user_count

Iterate over all LDAP Users and create authentik_core.User instances