Files
authentik/authentik/sources/ldap/auth.py
Jens L 9c69f67778 sources/ldap: log full exception when user password set fails (#5678)
* sources/ldap: log full exception when user password set fails

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* Update authentik/sources/ldap/auth.py

Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
Signed-off-by: Jens L. <jens@beryju.org>
Signed-off-by: Jens Langhammer <jens@goauthentik.io>

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Signed-off-by: Jens L. <jens@beryju.org>
Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
2023-05-18 19:00:17 +02:00

73 lines
3.0 KiB
Python

"""authentik LDAP Authentication Backend"""
from typing import Optional
from django.http import HttpRequest
from ldap3.core.exceptions import LDAPException, LDAPInvalidCredentialsResult
from structlog.stdlib import get_logger
from authentik.core.auth import InbuiltBackend
from authentik.core.models import User
from authentik.sources.ldap.models import LDAPSource
LOGGER = get_logger()
LDAP_DISTINGUISHED_NAME = "distinguishedName"
class LDAPBackend(InbuiltBackend):
"""Authenticate users against LDAP Server"""
def authenticate(self, request: HttpRequest, **kwargs):
"""Try to authenticate a user via ldap"""
if "password" not in kwargs:
return None
for source in LDAPSource.objects.filter(enabled=True):
LOGGER.debug("LDAP Auth attempt", source=source)
user = self.auth_user(source, **kwargs)
if user:
self.set_method("ldap", request, source=source)
return user
return None
def auth_user(self, source: LDAPSource, password: str, **filters: str) -> Optional[User]:
"""Try to bind as either user_dn or mail with password.
Returns True on success, otherwise False"""
users = User.objects.filter(**filters)
if not users.exists():
return None
user: User = users.first()
if LDAP_DISTINGUISHED_NAME not in user.attributes:
LOGGER.debug("User doesn't have DN set, assuming not LDAP imported.", user=user)
return None
# Either has unusable password,
# or has a password, but couldn't be authenticated by ModelBackend.
# This means we check with a bind to see if the LDAP password has changed
if self.auth_user_by_bind(source, user, password):
# Password given successfully binds to LDAP, so we save it in our Database
LOGGER.debug("Updating user's password in DB", user=user)
user.set_password(password, signal=False)
user.save()
return user
# Password doesn't match
LOGGER.debug("Failed to bind, password invalid")
return None
def auth_user_by_bind(self, source: LDAPSource, user: User, password: str) -> Optional[User]:
"""Attempt authentication by binding to the LDAP server as `user`. This
method should be avoided as its slow to do the bind."""
# Try to bind as new user
LOGGER.debug("Attempting to bind as user", user=user)
try:
temp_connection = source.connection(
connection_kwargs={
"user": user.attributes.get(LDAP_DISTINGUISHED_NAME),
"password": password,
}
)
temp_connection.bind()
return user
except LDAPInvalidCredentialsResult as exc:
LOGGER.debug("invalid LDAP credentials", user=user, exc=exc)
except LDAPException as exc:
LOGGER.warning("failed to bind to LDAP", exc=exc)
return None