Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to implement ldap authentication(without admin credentials) in django

My setup: Django-3.0, Python-3.8, django_auth_ldap
I have LDAP Server (Active Directory Server) in my organization. I am building a Django Application which serves some operations for all the users.
I know Django has built-in User Authentication mechanism, But it authenticates if users are present in User Model Database.

But my requirement is.
All user entries are in LDAP Server(Active Directory). Using proper user credentials LDAP server authenticates me.
I Created a Login Page in Django 'accounts' app,
1. whenever I enter username and password from Login Page, it should Authenticate using My Organization LDAP Server.
2. After Login I have to hold the session for the logged in user for 5 active minutes. (Django auth session)

I saw django_auth_ldap package gives some insight for my purpose.
I have these contents in settings.py.

import ldap

##Ldap settings
AUTH_LDAP_SERVER_URI = "ldap://myldapserver.com"
AUTH_LDAP_CONNECTION_OPTIONS = {ldap.OPT_REFERRALS : 0}
AUTH_LDAP_USER_DN_TEMPLATE = "uid=%(user)s, OU=USERS,dc=myldapserver, dc=com"
AUTH_LDAP_START_TLS = True

#Register authentication backend
AUTHENTICATION_BACKENDS = [
            "django_auth_ldap.backend.LDAPBackend",

            ]

Calling authenticate in views.py.

from django_auth_ldap.backend import LDAPBackend
def accounts_login(request):
    username = ""
    password = ""
    if request.method == "POST":
        username = request.POST.get('username')
        password = request.POST.get('password')
        auth = LDAPBackend()
        user = auth.authenticate(request, username=username, password=password)
        if user is not None:
            login(request, user)
            return redirect("/")
        else:
            error = "Authentication Failed"
            return render(request, "accounts/login.html", 'error':error)

    return render(request, "accounts/login.html")

But using above method always authenticate fails with the LDAP Server.

If I call using normal python simple_bind_s(), authentication is working fine to same LDAP server.

import ldap
def ldap_auth(username, password):
    conn = ldap.initialize(myproj.settings.LDAP_AUTH_URI)
    try:
        ldap.set_option(ldap.OPT_REFERRALS, 0)
        #ldap.set_option(ldap.OPT_PROTOCOL_VERSION, 3)
        conn.simple_bind_s(username, password)
    except ldap.LDAPError as e:
        return f'failed to authenticate'

    conn.unbind_s()
    return "Success"

Can anybody suggest me to make LDAPBackend Authentication work as per my requirement ?

Note: I do not have admin permission of LDAP Server.

like image 423
shivappa Avatar asked Jun 07 '20 17:06

shivappa


People also ask

What are three ways to LDAP authenticate?

In LDAP, authentication is supplied in the "bind" operation. LDAP v3 supports three types of authentication: anonymous, simple and SASL authentication.

How do I authenticate users using LDAP?

In order to authenticate a user with an LDAP directory you first need to obtain their DN as well as their password. With a login form, people typically enter a simple identifier such as their username or email address. You don't expect them to memorise the DN of their directory entry.

Does LDAP require authentication?

A user cannot access information stored within an LDAP database or directory without first authenticating (proving they are who they say they are). The database typically contains user, group, and permission information and delivers requested information to connected applications.


1 Answers

This is how I would do it with ldap3 and without django_auth_ldap packages.

1 - Create a custom AuthenticationBackend in your_app/backends.py :

import logging

from ldap3 import Server, Connection
from ldap3.core.exceptions import LDAPBindError

from django.conf import settings
from django.contrib.auth import get_user_model



logger = logging.getLogger(__name__)
UserModel = get_user_model()


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(settings.LDAP_HOST, get_info=ALL)
        try:
            conn = Connection(server, f"{username}@{settings.LDAP_DOMAIN}", password=password, auto_bind=True)
        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

2 - Declare LDAPBackend as your authentication backend in settings.py


AUTHENTICATION_BACKENDS = [
    'your_app.backends.LDAPBackend', 
    'django.contrib.auth.backends.ModelBackend'
]

This allows you to use django's native function for authentication, and it work with the admin.

To have session only work for 5 minutes, add this setting to your settings.py file :

SESSION_COOKIE_AGE = 5 * 60

Let me know if it works for your.

like image 117
Charlesthk Avatar answered Sep 22 '22 11:09

Charlesthk