0

I have used Django in python3 to develop website. Now I want to implement LDAP login.

In our LDAP server, a fixed authentication string should be send in the request to LDAP server. That is, in headers of the request, authentication item should be: "example_auth"

However, in connection class offered by ldap3 package, authentication could only be set to SIMPLE, ANONYMOUS, SASL, NTLM.

Then how could I set authentication to my authentication code for LDAP login?

class LDAPBackend:

def authenticate(self, request, username=None, password=None, **kwargs):  
    # set username to lowercase for consistency
    username = username.lower()
    # get the bind client to resolve DN
    logger.info('authenticating %s' % username)
    # set your server
    server = Server("example.com/REST/json/service/loginStaff", port=389)
    try:   
        conn = Connection(server, user=username, password=password, auto_bind=True,)
        conn.open()
        conn.bind()   
    except LDAPBindError as e:
        logger.info('LDAP authentication failed')
        logger.info(e)
        return None
    user = UserModel.objects.update_or_create(username=username)
    return user
def get_user(self, user_id):
    try:
        return UserModel._default_manager.get(pk=user_id)
    except UserModel.DoesNotExist:
        return None
Django
  • 99
  • 1
  • 1
  • 11

2 Answers2

1

For anyone else who finds this page, this worked for me but I had to combine the above with elements from the source code that the author mentions. To make someone else's life easier, here's the complete config I needed to replace the authenticate method with LDAP:

# Add to settings.py:
... <YOUR OTHER SETTINGS ABOVE>


LDAP_HOST = '<YOUR LDAP HOST>'
LDAP_DOMAIN = '<YOUR DOMAIN'
LDAP_BASE_DN = 'OU=<YOU OU>,dc=< YOUR DC...>'

SESSION_EXPIRE_AT_BROWSER_CLOSE = False
# Age of cookie, in seconds (default: 2 weeks, here set to 26 weeks).
SESSION_COOKIE_AGE = 60 * 60 * 24 * 7 * 26

AUTHENTICATION_BACKENDS = [
    'django_react.backends.LDAPBackend',
    'django.contrib.auth.backends.ModelBackend'
]
---- end config file

And this:

# add to django_react/backends.py:
---- start config file
import logging

from ldap3 import Server, Connection, SAFE_SYNC, SUBTREE
from ldap3.core.exceptions import *
logger = logging.getLogger(__name__)
from django.conf import settings
from django.contrib.auth import get_user_model
UserModel = get_user_model()

class LDAPBackend:

    def get_user(self, user_id):
        try:
            return UserModel._default_manager.get(pk=user_id)
        except UserModel.DoesNotExist:
            return None

    def authenticate(self, request, username=None, password=None, **kwargs):
        # set username to lowercase for consistency
        username = username.lower()

        # get the bind client to resolve DN
        logger.info('authenticating %s' % username)
        # set your server
        server = Server(settings.LDAP_HOST, get_info='ALL')
        try:
            conn = Connection(server, f"{username}@{settings.LDAP_DOMAIN}", password=password, client_strategy=SAFE_SYNC,
                              auto_bind=True)
            status, result, response, _ = conn.search(
                search_base=settings.LDAP_BASE_DN,
                search_filter='(&(objectClass=person)(samaccountname=' + username + '))',
                search_scope=SUBTREE,
                attributes=['samaccountname', 'givenName', 'mail', 'sn']
            )
        except LDAPBindError as e:
            logger.info('LDAP authentication failed')
            logger.info(e)
            return None
        user, created = UserModel.objects.update_or_create(username=username,
                                                           defaults={
                                                               'first_name': response[0]['attributes']['givenName'],
                                                               'last_name': response[0]['attributes']['sn'],
                                                               'email': response[0]['attributes']['mail']
                                                           }
                                                           )
        return user

---- end config file
Derek Darves
  • 192
  • 1
  • 5
0

You need to change the "Server". LDAP does not support URLs and as far as I know, it does not support headers as it is a different protocol than http/s

Set:

  • settings.LDAP_HOST to your LDAP server IP or FQDN (e.g. 192.168.x.x or pdc.ad.example.com)
  • settings.LDAP_DOMAIN to you LDAP Domain (e.g. ad.example.com)
  • settings.LDAP_BASE_DN to the Organization Unit(OU) under which your users exist. (e.g. OU=Users,dc=ad,dc=example,dc=com)

Below code will authenticate users against LDAP and Create or Update user in Django on successful authentication. Tested on MS Active Directory via LDAP. Original code taken from https://stackoverflow.com/a/63510132

from ldap3 import Server, Connection, SAFE_SYNC, SUBTREE
from ldap3.core.exceptions import *

def authenticate(self, request, username=None, password=None, **kwargs):     
    # set username to lowercase for consistency
    username = username.lower()
        
    # get the bind client to resolve DN
    logger.info('authenticating %s' % username)
    # set your server
    server = Server(settings.LDAP_HOST, get_info='ALL')
    try:
        conn = Connection(server, f"{username}@{settings.LDAP_DOMAIN}", password=password, client_strategy=SAFE_SYNC, auto_bind=True)
            status, result, response, _ = conn.search(
                    search_base = settings.LDAP_BASE_DN,
                    search_filter = '(&(objectClass=person)(samaccountname='+username+'))',
                    search_scope = SUBTREE,
                    attributes = ['samaccountname', 'givenName', 'mail', 'sn']
                )

    except LDAPBindError as e:
        logger.info('LDAP authentication failed')
        logger.info(e)
        return None
    user, created = UserModel.objects.update_or_create(username=username,
                defaults={
                    'first_name': response[0]['attributes']['givenName'],
                    'last_name':response[0]['attributes']['sn'],
                    'email':response[0]['attributes']['mail']
                }
            ) 
    return user
Dharman
  • 30,962
  • 25
  • 85
  • 135
Erevodifosin
  • 31
  • 1
  • 4