New

Experience Smart HR with Horilla Mobile App

Google Play Store
Home / Blogs

How to Integrate LDAP with Django [2025]

Django
·

December 5, 2024

how-to-integrate-ldap-with-django

In today’s interconnected systems, managing user data across multiple platforms can be challenging. LDAP (Lightweight Directory Access Protocol) offers a centralized directory system to streamline authentication and user management. Integrating LDAP with a Django project not only enhances security but also ensures a unified and scalable user management system.

This blog walks you through the step-by-step process of setting up an LDAP server using slapd, configuring it with Django, and enabling seamless data transfer between the Django database and the LDAP directory.

Whether you’re importing existing users into LDAP or syncing LDAP users back into Django, this guide will provide you with practical implementation strategies and tested code examples.

LDAP: Lightweight Directory Access Protocol

Required Modules

  1. django-auth-ldap: This package provides LDAP authentication for Django.

python-ldap: A Python library to interact with LDAP servers.

pip install django-auth-ldap 
pip install python-ldap

Step-by-Step Guide to Setting Up slapd

When you set up slapd (OpenLDAP server daemon), you create a directory structure and define an admin user who will manage the LDAP directory. Here’s how you go about it.

Step 1: Install slapd and ldap-utils

To install the LDAP server and necessary tools, open your terminal and run:

sudo apt update
sudo apt install slapd ldap-utils
  • slapd: This is the core LDAP server that runs and manages the LDAP directory.
  • ldap-utils: A set of utilities (e.g., ldapadd, ldapsearch) for managing and querying the LDAP directory.

Step 2: Configure slapd

Once installed, configure the LDAP server by running:

sudo dpkg-reconfigure slapd

During this configuration, you’ll be prompted for various details that will set up the LDAP directory structure and the admin user credentials.

Specify the Domain Name:
You’ll be asked to provide a domain name. For example, if you enter horilla.com, this sets the base DN of your directory to dc=horilla,dc=com.

Organization Name:
Enter your organization’s name, which will be stored as part of the LDAP directory information.

Admin DN and Password:
You’ll be prompted to set up the Admin Distinguished Name (DN) and password.
The admin DN often defaults to cn=admin,dc=yourdomain,dc=com, based on the domain you entered. For example:
cn=admin,dc=horilla,dc=com
Set a secure password for this admin user, which will allow you to bind to the LDAP server with admin privileges.

Database Type:
The setup will likely ask you to confirm the database backend. OpenLDAP typically uses the MDB (Memory-Mapped Database) by default, which is a reliable choice for production use.

Database Settings:
If prompted to remove the database when the package is purged, confirm as per your preference.
Allow the LDAP database to be readable by non-root users if you need broader access permissions on your server.

LDAP and TLS Configuration:
You can enable or disable TLS (encryption) for LDAP connections. For a basic setup, you can skip TLS configuration.

Step 3: Start and Enable slapd

Once configured, start the LDAP server and ensure it runs automatically on system boot:

sudo systemctl start slapd
sudo systemctl enable slapd

You can check that the service is active and running with:

sudo systemctl status slapd

Step 4: Test the Admin DN and Password

With slapd running, you can verify the setup by attempting to authenticate using the admin DN and password you created. Run the following command to confirm:

ldapwhoami -x -D "cn=admin,dc=horilla,dc=com" -w your_password

Replace your_password with the actual password you set. If the connection is successful, this command should return:

dn: cn=admin,dc=horilla,dc=com

This output confirms that the LDAP server is configured, running, and accessible with the admin DN.

Summary:

  • Install slapd and ldap-utils to set up and manage your LDAP directory.
  • Configure the LDAP directory structure, including the domain, organization name, and admin DN/password.
  • Start and enable slapd to ensure the LDAP server is running.
  • Verify the admin DN by binding with the credentials you set, confirming access to the LDAP directory.

LDAP Configuration in settings.py

  1. Import Required Modules:
import ldap
from django_auth_ldap.config import LDAPSearch, GroupOfNamesType
  • ldap: This is the Python LDAP library that allows you to interact with an LDAP directory.
  • LDAPSearch: This class is used to define how to search for users in the LDAP directory.
  • GroupOfNamesType: This is typically used for handling group memberships (not used in your current config, but available for future needs).

LDAP Server URI:

AUTH_LDAP_SERVER_URI = "ldap://127.0.0.1:389"
  • This specifies the URI of the LDAP server you’re connecting to. Here, it’s set to your local ApacheDS instance running on port 389.

Bind DN and Password

AUTH_LDAP_BIND_DN = "cn=admin,dc=horilla,dc=com"
AUTH_LDAP_BIND_PASSWORD = "your_password"
  • These settings define the distinguished name (DN) and password used to bind (authenticate) to the LDAP server. This user needs permission to search for users.

User Search Configuration:

AUTH_LDAP_USER_SEARCH = LDAPSearch(
    "ou=users,dc=horilla,dc=com",
    ldap.SCOPE_SUBTREE,
    "(uid=%(user)s)"
)
  • This configuration defines where to look for users in the LDAP directory.
  • The search will be performed under the ou=users,dc=horilla,dc=com organizational unit.
  • ldap.SCOPE_SUBTREE: This means the search will include all entries under the specified base DN.
  • The filter “(uid=%(user)s)” allows you to find the user by their username.

Map LDAP Attributes to Django User Fields:

AUTH_LDAP_USER_ATTR_MAP = {
    "first_name": "givenName",
    "last_name": "sn",
    "email": "mail",
}
  • This maps attributes from the LDAP user entries to the corresponding fields in Django’s user model.
  • Here, givenName is mapped to first_name, sn to last_name, and mail to email.

Authentication Backends:

AUTHENTICATION_BACKENDS = (
    'django_auth_ldap.backend.LDAPBackend',
    'django.contrib.auth.backends.ModelBackend',  # Keep Django's default user model
)
  • This configures Django to use the LDAP backend for authentication.
  • The second entry (ModelBackend) ensures that Django can still use its default user model, allowing local users to authenticate alongside LDAP users.

Auto-create or Update User:

AUTH_LDAP_ALWAYS_UPDATE_USER = True
  • This setting tells Django to automatically create or update the Django user whenever they authenticate through LDAP. If the user doesn’t exist in Django, they will be created, and any LDAP attribute mapped to Django fields will be updated.
        import ldap
        from django_auth_ldap.config import LDAPSearch, GroupOfNamesType
        
        AUTH_LDAP_SERVER_URI = "ldap://127.0.0.1:389"  # Replace with your LDAP server address
        
        # Bind credentials, if required
        AUTH_LDAP_BIND_DN = "cn=admin,dc=horilla,dc=com"  # Replace with your bind DN
        AUTH_LDAP_BIND_PASSWORD = "your_password"         # Replace with your LDAP bind password
        
        # Define the search base and user lookup
        AUTH_LDAP_USER_SEARCH = LDAPSearch("ou=users,dc=horilla,dc=com", ldap.SCOPE_SUBTREE, "(uid=%(user)s)")
        
        # Map LDAP fields to Django user fields
        AUTH_LDAP_USER_ATTR_MAP = {
            "first_name": "givenName",
            "last_name": "sn",
            "email": "mail",
        }
        
        AUTHENTICATION_BACKENDS = [
            "django_auth_ldap.backend.LDAPBackend",
            "django.contrib.auth.backends.ModelBackend",
        ]
        AUTH_LDAP_ALWAYS_UPDATE_USER = True

        Summary:

        This configuration sets up Django to authenticate users against an LDAP directory using your local ApacheDS instance. It defines how to connect, where to search for users, how to map user attributes, and how to manage user creation and updates.

        LDAP Organizational Unit Creation from Django

        The os_users.ldif file is used to create the organizational unit (OU) in your LDAP directory where user entries will be stored. Let’s go through the steps to create the ou=users organizational unit using the LDIF file.

        Step 1: Prepare the LDIF File

        Make sure your LDIF file for the organizational unit looks like this:os_users.ldif:

        dn: ou=users,dc=horilla,dc=com
        objectClass: organizationalUnit
        ou: users

        Step 2: Use Command Line to Import the LDIF

        You can create the organizational unit using the command line with the ldapadd command. Here’s how to do that:

        Run the Following Command:

        ldapadd -x -D "cn=admin,dc=horilla,dc=com" -w your_password -f os_users.ldif

        -x: Use simple authentication.
        -D: The bind DN for the administrator.
        -w: The password for the bind DN.
        -f: Specifies the LDIF file to read from.

        Step 3: Verify the Organizational Unit Creation

        After running the above command, you can verify that the organizational unit was created successfully by running:

        ldapsearch -x -b "dc=horilla,dc=com" "(ou=users)"

        This command will search for the ou=users entry in your LDAP directory.

        Summary:

        1. Ensure your os_users.ldif file is correctly formatted.
        2. Use the ldapadd command to create the organizational unit from the LDIF file.
        3. Verify the creation of the organizational unit using ldapsearch.

        This method keeps everything within the command line and makes it straightforward to manage your LDAP structure.                     

        LDAP User Creation from Django

        Step 1: Prepare the LDIF File

        Since you mentioned that you have the LDIF files ready, make sure your file looks like this for the user you want to create:

        new_user.ldif

        dn: uid=testuser,ou=users,dc=horilla,dc=com
        objectClass: inetOrgPerson
        cn: Test User
        sn: User
        uid: testuser
        mail: testuser@mycompany.com
        userPassword: testpassword

        Step 2: Use Command Line to Import LDIF

        If you want to create the user using the command line and the LDIF file directly, you can use the ldapadd command. Here’s how to do that from your command line, assuming you have LDAP command line tools installed:

        Run the Following Command

        ldapadd -x -D "cn=admin,dc=horilla,dc=com" -w your_password -f new_user.ldif
        • -x: Use simple authentication.
        • -D: Bind DN (the administrator’s DN).
        • -w: The password for the bind DN.
        • -f: Specifies the file to read the LDIF data from.

        Step 3: Verify User Creation

        You can verify that the user has been added by running:

        ldapsearch -x -b "ou=users,dc=horilla,dc=com" "(uid=testuser)"

        This will display the user details if the user was successfully added.

        Summary:

        1. Ensure your new_user.ldif is correctly formatted.
        2. Use the ldapadd command to add the user from the LDIF file.
        3. Verify the user using ldapsearch.

        Testing LDAP Configuration

        You can verify the entries created using ldapsearch, or, if using ApacheDS as an LDAP directory, you can also connect via the Apache Directory Studio UI to view and manage entries under dc=horilla,dc=com.

        Conclusion

        With the setup of LDAP using slapd, integration with Django, and optionally managing entries visually with ApacheDS, you’re set up to create and authenticate users, organize them within your LDAP directory, and manage directory structure. This setup lets you easily expand with additional groups, users, and other organizational units as needed.

        Now let’s check how all the employees can import to LDAP Database from our Django Database and vice-versa.

        LDAP – Importing users from the Django database to the LDAP database

        After connecting both the database write a function in any of the app in your django project.

        Eg: Employee → management →commands →import_users_to_ldap.py And the file looks like,

        from django.core.management.base import BaseCommand
        from django.contrib.auth import get_user_model
        from ldap3 import Server, Connection, ALL, ALL_ATTRIBUTES
        from employee.models import Employee
        
        
        User = get_user_model()
        
        
        class Command(BaseCommand):
            help = 'Import users from Django to LDAP'
        
        
            def handle(self, *args, **kwargs):
                # LDAP server details
                ldap_server = 'localhost'
                bind_dn = 'cn=admin,dc=horilla,dc=com' # Replace with your bind DN
                bind_password = 'your_ldap_password'  # Change to your LDAP admin password
        
                # Connect to the LDAP server
                server = Server(ldap_server, get_info=ALL)
        
        
                try:
                    conn = Connection(server, bind_dn, bind_password, auto_bind=True)
        
                    # Fetch all users from Django
                    users = Employee.objects.all()
        
                    for user in users:
        
                        # Prepare user data for LDAPclear
                        dn = f"uid={user.employee_user_id.username},ou=users,dc=horilla,dc=com"
                        attributes = {
                            'objectClass': ['inetOrgPerson'],
                            'givenName': user.employee_first_name,
                            'sn': user.employee_last_name,
                            'cn': f"{user.employee_first_name} {user.employee_last_name}",
                            'uid': user.email,
                            'mail': user.email,
                            "telephoneNumber": user.phone, 
                            'userPassword': user.phone,
                        }
        
        
                        #    Check if the user already exists in LDAP
                        conn.search('ou=users,dc=testing,dc=com', f'(uid={user.employee_user_id.username})', attributes=ALL_ATTRIBUTES)
        
        
                        if conn.entries:
                            self.stdout.write(self.style.WARNING(f'{user.employee_first_name} {user.employee_last_name} already exists in LDAP. Skipping...'))
                        else:
                            # Add user to LDAP
                            if not conn.add(dn, attributes=attributes):
                                self.stdout.write(self.style.ERROR(f'Failed to add {user.employee_first_name} {user.employee_last_name}: {conn.result}'))
                            else:
                                self.stdout.write(self.style.SUCCESS(f'Successfully added {user.employee_first_name} {user.employee_last_name} to LDAP.'))
        
        
                    conn.unbind()
                except Exception as e:
                    self.stdout.write(self.style.ERROR(f'An error occurred: {e}'))

        Here import_users_to_ldap is the name of the created file that’s why the command looks like this,

        Run the command,

        python manage.py import_users_to_ldap

        LDAP – Importing users from the LDAP database to the Django database

        After connecting both the database write a function in any of the app in your django project.

        Eg: Employee → management →commands →import_ldap_users.py

        And the file looks like,

        import ldap
        from django.conf import settings
        from django.core.management.base import BaseCommand
        from employee.models import Employee
        from django.contrib.auth.models import User
        from django.db.models import Q
        
        
        class Command(BaseCommand):
            help = "Imports employees from LDAP into the Django database"
        
            def handle(self, *args, **kwargs):
                try:
                    connection = ldap.initialize(settings.AUTH_LDAP_SERVER_URI)
                    connection.simple_bind_s(settings.AUTH_LDAP_BIND_DN, settings.AUTH_LDAP_BIND_PASSWORD)
                    
                    search_base = "ou=users,dc=horilla,dc=com"  # Replace with your actual search base
                    search_filter = "(objectClass=inetOrgPerson)"
                    
                    results = connection.search_s(search_base, ldap.SCOPE_SUBTREE, search_filter)
                    
                    for dn, entry in results:
        
                        user_id = entry.get("uid", [b""])[0].decode("utf-8")
                        email = entry.get("mail", [b""])[0].decode("utf-8")
                        first_name = entry.get("givenName", [b""])[0].decode("utf-8")
                        last_name = entry.get("sn", [b""])[0].decode("utf-8")
                        name = entry.get("cn", [b""])[0].decode("utf-8")
                        phone = entry.get("telephoneNumber", [b""])[0].decode("utf-8")
        
                        # Get the password from LDAP
                        ldap_password = entry.get("userPassword", [b""])[0].decode("utf-8")
        
        
                        # Create or update the Employee record, storing the LDAP password
                        employee, created = Employee.objects.update_or_create(
                            email = email,
                            defaults={
                                "employee_first_name": first_name,
                                "employee_last_name": last_name,
                                "email": email,
                                "phone": phone,
                            }
                        )
        
                        # Retrieve the associated User if it exists
                        try:
                            user = User.objects.get(Q(username=email) | Q(username=user_id) | Q(email=email))
                            user.username = user_id
                            user.set_password(ldap_password)  # Hash and set the password securely
                            user.save()  # Save the changes to the User instance
                            action = "Updated"
                        except User.DoesNotExist:
                            # If the user does not exist, handle it accordingly (e.g., log a message or create a new user)
                            self.stdout.write(self.style.WARNING(f"User for employee {name} does not exist."))
                            continue
        
        
                        action = "Created" if created else "Updated"
                        self.stdout.write(self.style.SUCCESS(f"{action} employee {name} with LDAP password"))
        
                    connection.unbind_s()
        
                except ldap.LDAPError as e:
                    self.stderr.write(self.style.ERROR(f"LDAP Error: {e}"))

        Here, import_ldap_users is the name of the created file that’s why the command looks like this,

        Run the command,

        python manage.py import_ldap_users

        Integrating LDAP with Django creates a robust and centralized user authentication system, which is essential for scalable applications. This guide covered every aspect of setting up slapd, configuring LDAP settings in Django, and implementing bidirectional user data synchronization between the Django database and the LDAP directory. With these tools and processes in place, you can efficiently manage users, ensure data consistency, and maintain a secure authentication pipeline. The outlined steps, coupled with the provided scripts, empower you to implement LDAP integration in your Django project with confidence.

        Horilla Editorial Team Author

        Horilla Editorial Team is a group of experienced writers and editors who are passionate about HR software. We have a deep understanding of the HR landscape and are committed to providing our readers with the most up-to-date and informative content. We have written extensively on a variety of HR software topics, including applicant tracking systems, performance management software, and payroll software etc. We are always looking for new ways to share our knowledge with the HR community. If you have a question about HR software, please don't hesitate to contact us.