This repository has been archived on 2024-05-31. You can view files and clone it, but cannot push or open issues or pull requests.
authentik/passbook/sources/ldap/auth.py

77 lines
2.9 KiB
Python
Raw Normal View History

2018-11-11 12:41:48 +00:00
"""passbook LDAP Authentication Backend"""
from typing import Optional
import ldap3
2018-11-11 12:41:48 +00:00
from django.contrib.auth.backends import ModelBackend
from django.http import HttpRequest
2019-10-01 08:24:10 +00:00
from structlog import get_logger
2018-11-11 12:41:48 +00:00
from passbook.core.models import User
2019-10-07 14:33:48 +00:00
from passbook.sources.ldap.models import LDAPSource
2018-11-11 12:41:48 +00:00
LOGGER = get_logger()
2018-11-11 12:41:48 +00:00
class LDAPBackend(ModelBackend):
"""Authenticate users against LDAP Server"""
def authenticate(self, request: HttpRequest, **kwargs):
2018-11-11 12:41:48 +00:00
"""Try to authenticate a user via ldap"""
2019-12-31 11:51:16 +00:00
if "password" not in kwargs:
2018-11-11 12:41:48 +00:00
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:
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 "distinguishedName" 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 Binding as user", user=user)
try:
temp_connection = ldap3.Connection(
source.connection.server,
user=user.attributes.get("distinguishedName"),
password=password,
raise_exceptions=True,
)
temp_connection.bind()
return user
except ldap3.core.exceptions.LDAPInvalidCredentialsResult as exception:
LOGGER.debug("LDAPInvalidCredentialsResult", user=user, error=exception)
except ldap3.core.exceptions.LDAPException as exception:
LOGGER.warning(exception)
return None