authentik.sources.ldap.auth

authentik LDAP Authentication Backend

 1"""authentik LDAP Authentication Backend"""
 2
 3from django.http import HttpRequest
 4from ldap3.core.exceptions import LDAPException, LDAPInvalidCredentialsResult
 5from structlog.stdlib import get_logger
 6
 7from authentik.core.auth import InbuiltBackend
 8from authentik.core.models import User
 9from authentik.sources.ldap.models import LDAP_DISTINGUISHED_NAME, LDAPSource
10
11LOGGER = get_logger()
12
13
14class LDAPBackend(InbuiltBackend):
15    """Authenticate users against LDAP Server"""
16
17    def authenticate(self, request: HttpRequest, **kwargs):
18        """Try to authenticate a user via ldap"""
19        if "password" not in kwargs:
20            return None
21        for source in LDAPSource.objects.filter(enabled=True):
22            LOGGER.debug("LDAP Auth attempt", source=source)
23            user = self.auth_user(request, source, **kwargs)
24            if user:
25                self.set_method("ldap", request, source=source)
26                return user
27        return None
28
29    def auth_user(
30        self, request: HttpRequest, source: LDAPSource, password: str, **filters: str
31    ) -> User | None:
32        """Try to bind as either user_dn or mail with password.
33        Returns True on success, otherwise False"""
34        users = User.objects.filter(**filters)
35        if not users.exists():
36            return None
37        user: User = users.first()
38        if LDAP_DISTINGUISHED_NAME not in user.attributes:
39            LOGGER.debug("User doesn't have DN set, assuming not LDAP imported.", user=user)
40            return None
41        # Either has unusable password,
42        # or has a password, but couldn't be authenticated by ModelBackend.
43        # This means we check with a bind to see if the LDAP password has changed
44        if self.auth_user_by_bind(source, user, password):
45            if source.password_login_update_internal_password:
46                # Password given successfully binds to LDAP, so we save it in our Database
47                LOGGER.debug("Updating user's password in DB", user=user)
48                user.set_password(password, sender=source, request=request)
49                user.save()
50            return user
51        # Password doesn't match
52        LOGGER.debug("Failed to bind, password invalid")
53        return None
54
55    def auth_user_by_bind(self, source: LDAPSource, user: User, password: str) -> User | None:
56        """Attempt authentication by binding to the LDAP server as `user`. This
57        method should be avoided as its slow to do the bind."""
58        # Try to bind as new user
59        LOGGER.debug("Attempting to bind as user", user=user)
60        try:
61            # source.connection also attempts to bind
62            source.connection(
63                connection_kwargs={
64                    "user": user.attributes.get(LDAP_DISTINGUISHED_NAME),
65                    "password": password,
66                }
67            )
68            return user
69        except LDAPInvalidCredentialsResult as exc:
70            LOGGER.debug("invalid LDAP credentials", user=user, exc=exc)
71        except LDAPException as exc:
72            LOGGER.warning("failed to bind to LDAP", exc=exc)
73        return None
LOGGER = <BoundLoggerLazyProxy(logger=None, wrapper_class=None, processors=None, context_class=None, initial_values={}, logger_factory_args=())>
class LDAPBackend(authentik.core.auth.InbuiltBackend):
15class LDAPBackend(InbuiltBackend):
16    """Authenticate users against LDAP Server"""
17
18    def authenticate(self, request: HttpRequest, **kwargs):
19        """Try to authenticate a user via ldap"""
20        if "password" not in kwargs:
21            return None
22        for source in LDAPSource.objects.filter(enabled=True):
23            LOGGER.debug("LDAP Auth attempt", source=source)
24            user = self.auth_user(request, source, **kwargs)
25            if user:
26                self.set_method("ldap", request, source=source)
27                return user
28        return None
29
30    def auth_user(
31        self, request: HttpRequest, source: LDAPSource, password: str, **filters: str
32    ) -> User | None:
33        """Try to bind as either user_dn or mail with password.
34        Returns True on success, otherwise False"""
35        users = User.objects.filter(**filters)
36        if not users.exists():
37            return None
38        user: User = users.first()
39        if LDAP_DISTINGUISHED_NAME not in user.attributes:
40            LOGGER.debug("User doesn't have DN set, assuming not LDAP imported.", user=user)
41            return None
42        # Either has unusable password,
43        # or has a password, but couldn't be authenticated by ModelBackend.
44        # This means we check with a bind to see if the LDAP password has changed
45        if self.auth_user_by_bind(source, user, password):
46            if source.password_login_update_internal_password:
47                # Password given successfully binds to LDAP, so we save it in our Database
48                LOGGER.debug("Updating user's password in DB", user=user)
49                user.set_password(password, sender=source, request=request)
50                user.save()
51            return user
52        # Password doesn't match
53        LOGGER.debug("Failed to bind, password invalid")
54        return None
55
56    def auth_user_by_bind(self, source: LDAPSource, user: User, password: str) -> User | None:
57        """Attempt authentication by binding to the LDAP server as `user`. This
58        method should be avoided as its slow to do the bind."""
59        # Try to bind as new user
60        LOGGER.debug("Attempting to bind as user", user=user)
61        try:
62            # source.connection also attempts to bind
63            source.connection(
64                connection_kwargs={
65                    "user": user.attributes.get(LDAP_DISTINGUISHED_NAME),
66                    "password": password,
67                }
68            )
69            return user
70        except LDAPInvalidCredentialsResult as exc:
71            LOGGER.debug("invalid LDAP credentials", user=user, exc=exc)
72        except LDAPException as exc:
73            LOGGER.warning("failed to bind to LDAP", exc=exc)
74        return None

Authenticate users against LDAP Server

def authenticate(self, request: django.http.request.HttpRequest, **kwargs):
18    def authenticate(self, request: HttpRequest, **kwargs):
19        """Try to authenticate a user via ldap"""
20        if "password" not in kwargs:
21            return None
22        for source in LDAPSource.objects.filter(enabled=True):
23            LOGGER.debug("LDAP Auth attempt", source=source)
24            user = self.auth_user(request, source, **kwargs)
25            if user:
26                self.set_method("ldap", request, source=source)
27                return user
28        return None

Try to authenticate a user via ldap

def auth_user( self, request: django.http.request.HttpRequest, source: authentik.sources.ldap.models.LDAPSource, password: str, **filters: str) -> authentik.core.models.User | None:
30    def auth_user(
31        self, request: HttpRequest, source: LDAPSource, password: str, **filters: str
32    ) -> User | None:
33        """Try to bind as either user_dn or mail with password.
34        Returns True on success, otherwise False"""
35        users = User.objects.filter(**filters)
36        if not users.exists():
37            return None
38        user: User = users.first()
39        if LDAP_DISTINGUISHED_NAME not in user.attributes:
40            LOGGER.debug("User doesn't have DN set, assuming not LDAP imported.", user=user)
41            return None
42        # Either has unusable password,
43        # or has a password, but couldn't be authenticated by ModelBackend.
44        # This means we check with a bind to see if the LDAP password has changed
45        if self.auth_user_by_bind(source, user, password):
46            if source.password_login_update_internal_password:
47                # Password given successfully binds to LDAP, so we save it in our Database
48                LOGGER.debug("Updating user's password in DB", user=user)
49                user.set_password(password, sender=source, request=request)
50                user.save()
51            return user
52        # Password doesn't match
53        LOGGER.debug("Failed to bind, password invalid")
54        return None

Try to bind as either user_dn or mail with password. Returns True on success, otherwise False

def auth_user_by_bind( self, source: authentik.sources.ldap.models.LDAPSource, user: authentik.core.models.User, password: str) -> authentik.core.models.User | None:
56    def auth_user_by_bind(self, source: LDAPSource, user: User, password: str) -> User | None:
57        """Attempt authentication by binding to the LDAP server as `user`. This
58        method should be avoided as its slow to do the bind."""
59        # Try to bind as new user
60        LOGGER.debug("Attempting to bind as user", user=user)
61        try:
62            # source.connection also attempts to bind
63            source.connection(
64                connection_kwargs={
65                    "user": user.attributes.get(LDAP_DISTINGUISHED_NAME),
66                    "password": password,
67                }
68            )
69            return user
70        except LDAPInvalidCredentialsResult as exc:
71            LOGGER.debug("invalid LDAP credentials", user=user, exc=exc)
72        except LDAPException as exc:
73            LOGGER.warning("failed to bind to LDAP", exc=exc)
74        return None

Attempt authentication by binding to the LDAP server as user. This method should be avoided as its slow to do the bind.