Написал небольшой модуль для аутентификации в 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 не требуется, то стандартный модуль можно отключить.
Если у кого-то есть замечания или предложения по доработке модуля - готов вас выслушать.