Django, LDAP and SSL

Objectives:

  • Users can log in using LDAP only
  • Admin can log in with or without LDAP
  • Only username and password are verified, nothing more
  • First, we need openldap and python-ldap packages.

I have created my own authenticate backend based on django.contrib.auth.backends.ModelBackend.

# coding=utf-8
import ldap
import logging
from django.contrib.auth.models import User
from django.contrib.auth.backends import ModelBackend
logger = logging.getLogger('helper')
class LdapBackend(ModelBackend):
def authenticate(self, username=None, password=None):
if username:
"""
All usernames in database are lowercase.
"""
username = username.lower()
try:
user = User.objects.get(username=username)
if user.has_usable_password():
"""
User can log in without LDAP.
"""
if user.check_password(password):
return user
else:
"""
LDAP only.
"""
ldap_address = 'IP:PORT'
domain = 'eshlox.net'
l = ldap.initialize('ldaps://%s' % ldap_address)
l.set_option(ldap.OPT_REFERRALS, 0)
l.set_option(ldap.OPT_TIMEOUT, 5)
l.set_option(ldap.OPT_NETWORK_TIMEOUT, 5)
l.set_option(ldap.OPT_PROTOCOL_VERSION, ldap.VERSION3)
l.set_option(ldap.OPT_X_TLS, ldap.OPT_X_TLS_DEMAND)
l.set_option(ldap.OPT_X_TLS_DEMAND, True)
l.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, ldap.OPT_X_TLS_NEVER)
try:
l.simple_bind_s('%s@%s' % (username.encode('utf-8'),
domain),
password.encode('utf-8'))
return user
except ldap.INVALID_CREDENTIALS as e:
"""
Wrong username or password.
"""
return None
except Exception as e:
"""
It has not happened yet. ;-)
"""
logger.error(u'LDAP Error. User %s. %s' % (username,
e.args[0]['desc']))
return None
except User.DoesNotExist:
return None

Set new authentication backend in settings.py.

AUTHENTICATION_BACKENDS = ('project.ldapbackend.LdapBackend', )

Last thing. I have set unusable password while user is creating. One admin (me). The rest have to use LDAP. Otherwise I can set a password for user manually.

from django.contrib.auth.models import User
from django.db.models.signals import post_save
from django.dispatch import receiver
@receiver(post_save, sender=User)
def createUserProfile(sender, instance, created, **kwargs):
if created:
profile, created = UserProfile.objects.get_or_create(user=instance)
if created:
instance.set_unusable_password()
instance.save()

Improvements? ;-)