Написал небольшой модуль для аутентификации в Django через LDAP-сервер. Для входа нужно ввести полное имя пользователя в формате user@domain.tld и пароль пользователя.
Модуль учитывает состояние пользователя - заблокирован он в каталоге LDAP или нет. Если пользователь найден в LDAP, но отсутствует в таблице auth_user, то пользователь создаётся и помечается активным. Если пользователь не найден в LDAP (может быть не найден из-за неверного пароля или из-за того, что пользователь заблокирован), но найден в таблице auth_user, то отметка активности пользователя в таблице снимается.
При каждом входе из каталога LDAP в таблицу auth_user копируются имя пользователя, фамилия и почтовый ящик.
При первом входе пользователя в поле пароля в таблице auth_user прописывается случайно сгенерированный пароль. Это делается для того, чтобы пользователь, получивший доступ к базе данных или интерфейсу администрирования Django, не смог скопировать хэш настоящего пароля пользователя и не смог бы с помощью специальных программ подобрать пароль, подходящий к этому хэшу. Можно было, конечно, поступить и проще - прописывать в поле хэша пароля всегда один и тот же текст, который не может получиться в результате хэширования пароля.
Модуль аутентификации LDAP может использоваться и совместно со стандартным модулем аутентификации Django. Именно поэтому в базу данных каждый раз вносится случайно сгенерированный пароль. Если бы в базу данных каждый раз вносился один и тот же пароль (пусть даже и в хэшированном виде), то всё ещё оставалась бы возможность войти под учётной записью этого пользователя, зная этот стандартный пароль, а не настоящий пароль пользователя в каталоге LDAP.
Сохраним бэкенд в файл application/ldap_backend.py внутри каталога проекта:
# -*- coding: UTF-8 -*-
import random, ldap
from django.contrib.auth.models import User
from django.contrib.auth.hashers import make_password
# Функция, генерирующая пароль или ключ указанной сложности
def genkey(upper=True, lower=True, digits=True, signs=False, length=64):
chars = ''
if upper:
chars += 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
if lower:
chars += 'abcdefghijklmnopqrstuvwxyz'
if digits:
chars += '0123456789'
if signs:
chars += '()[]{}<>|/~!@#$%^&,.;:*-+=_'
key = ''
num = len(chars)
for i in xrange(0, length):
j = int(random.random() * num)
key += chars[j]
return key
class LDAPBackend(object):
def authenticate(self, username=None, password=None):
# Ищем пользователя в LDAP
try:
login, domain = username.split('@')
except ValueError:
return None
base_dn = domain.split('.')
base_dn = map(lambda x: 'dc=' + x, base_dn)
base_dn = ','.join(base_dn)
l = ldap.initialize('ldap://' + domain)
try:
l.simple_bind_s(username, password)
except ldap.INVALID_CREDENTIALS:
return None
l.set_option(ldap.OPT_REFERRALS, 0)
result = l.search_s(base_dn,
ldap.SCOPE_SUBTREE,
'(&(sAMAccountName=%s)(objectClass=user)(!(UserAccountControl:1.2.840.113556.1.4.803:=2)))' % login,
None)
found, data = result[0]
l.unbind_s()
# Ищем пользователя в django или создаём его
try:
user = User.objects.get(username=username)
except User.DoesNotExist:
user = User(username=username)
user.password = make_password(genkey())
user.is_staff = True
user.is_superuser = False
# Обновляем пользователя в django
if found:
user.email = data.get('mail', [''])[0]
user.first_name = data.get('givenName', [''])[0].decode('UTF-8') # data.get('middleName', [''])[0].decode('UTF-8')
user.last_name = data.get('sn', [''])[0].decode('UTF-8')
user.is_active = True
else:
user.is_active = False
user.save()
return user
def get_user(self, user_id):
try:
return User.objects.get(pk=user_id)
except User.DoesNotExist:
return None
Для использования этого модуля аутентификации, нужно прописать его в настройках проекта, в файле settings.py:
AUTHENTICATION_BACKENDS = ( 'application.ldap_backend.LDAPBackend', 'django.contrib.auth.backends.ModelBackend' )
Если стандартная аутентификация Django не требуется, то стандартный модуль можно отключить.
Если у кого-то есть замечания или предложения по доработке модуля - готов вас выслушать.