How to Integrate LDAP with Django [2025]
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
- 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
- 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:
- Ensure your os_users.ldif file is correctly formatted.
- Use the ldapadd command to create the organizational unit from the LDIF file.
- 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:
- Ensure your new_user.ldif is correctly formatted.
- Use the ldapadd command to add the user from the LDIF file.
- 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.