Corp/docs/guides/ldap-password-change-admin.md
lizerk 540e295ef4 feat: Fix LDAP DN bugs + add password management in admin
BREAKING CHANGES:
- Department.head автоматически очищается при увольнении руководителя
- LDAP managedBy атрибут очищается при увольнении

Fixes:
- DN не перемещался при восстановлении сотрудника (Dismissed → Users)
- DN не перемещался при увольнении сотрудника без отделов
- Ошибка при увольнении из множественных отделов (двойной modify_dn)
- Неправильные URL patterns для LDAP моделей в админке
- Двойное кодирование DN в редиректе смены пароля

Features:
- Добавлен метод _ensure_ldap_dn_location для гарантированного перемещения DN
- Добавлена проверка already_there в sync_department_member_to_ldap
- Управление паролями LDAP пользователей через админку:
  * Кнопка смены пароля в карточке пользователя
  * Массовое действие смены пароля
  * Автосинхронизация с Django password hash
- Автоочистка Department.head и LDAP managedBy при увольнении руководителя
- Добавлены методы set_password() и change_password() в LdapUser

Changed:
- Переписан сигнал sync_department_member_to_ldap с реальными методами
- Исправлены URL patterns: ldap_* → employees_*
2026-03-23 20:32:39 +03:00

9.2 KiB
Raw Blame History

Смена паролей LDAP через админку

Возможности

Теперь можно менять пароли пользователей LDAP прямо через Django админ-панель!

Добавлено в LdapUser модель:

  1. Поле unicode_pwd (write-only) - для записи паролей в AD
  2. Метод set_password(new_password) - установка пароля администратором
  3. Метод change_password(old_password, new_password) - смена пароля пользователем

Добавлено в админку:

  1. Кнопка "🔑 Изменить пароль" в карточке пользователя
  2. Action "🔑 Изменить пароль выбранных пользователей" в списке

Как использовать

Вариант 1: Через карточку пользователя (РЕКОМЕНДУЕТСЯ ⭐)

  1. Откройте /admin/employees/ldapuser/
  2. Кликните на нужном пользователе (откроется карточка)
  3. Прокрутите до секции "⚙️ Управление учетной записью"
  4. Нажмите кнопку "🔑 Изменить пароль"
  5. Введите новый пароль (минимум 7 символов)
  6. Подтвердите пароль
  7. Нажмите "Изменить пароль"

Результат:

  • Пароль изменён в LDAP (Active Directory)
  • Пароль автоматически синхронизирован в Django БД (если есть связь через employee_number)
  • Вы вернётесь на страницу редактирования пользователя

Преимущества:

  • ✅ Удобнее - не нужно искать пользователя в списке и выбирать action
  • ✅ Быстрее - сразу видно, какого пользователя редактируем
  • ✅ Безопаснее - меньше шансов ошибиться с выбором пользователя

Вариант 2: Через action в списке (массовая операция)

  1. Откройте /admin/employees/ldapuser/
  2. Выберите одного или нескольких пользователей (чекбоксы)
  3. В dropdown "Действие" выберите "🔑 Изменить пароль выбранных пользователей"
  4. Нажмите "Выполнить"
  5. Введите новый пароль (минимум 7 символов)
  6. Подтвердите пароль
  7. Нажмите "Изменить пароль"

Результат:

  • Пароль изменён в LDAP (Active Directory)
  • Пароль автоматически синхронизирован в Django БД (если есть связь через employee_number)
  • Вы получите сообщение об успехе или ошибке

Когда использовать:

  • При необходимости сменить пароль сразу нескольким пользователям
  • Для массовых операций (например, сброс паролей всем новым сотрудникам)

Вариант 3: Программно через ORM

from employees.ldap.orm_models import LdapUser

# Найти пользователя
user = LdapUser.objects.get(sam_account_name='ivanov_i')

# Установить новый пароль (админ)
user.set_password('NewSecurePass123!')

# Или сменить пароль (требует старый пароль)
user.change_password('OldPass123', 'NewSecurePass123!')
4
# НЕ нужно вызывать .save() — пароль уже изменён в AD!

Вариант 3: Через Django Shell

cd /home/lizerk/Dev/EUSRR/backend
.venv/bin/python manage.py shell
from employees.ldap.orm_models import LdapUser
from employees.models import Employee

# Найти LDAP пользователя
ldap_user = LdapUser.objects.get(sam_account_name='ivanov_i')

# Установить пароль
ldap_user.set_password('NewPass123!')

# Синхронизировать с Django (опционально)
if ldap_user.employee_number:
    emp = Employee.objects.get(pk=int(ldap_user.employee_number))
    emp.set_password('NewPass123!')
    emp.save(update_fields=['password'])
    print(f"✅ Пароль синхронизирован для {emp.email}")

Требования к паролю

Active Directory политика по умолчанию:

  • ✅ Минимум 7 символов (обычно 8+)
  • ✅ Буквы верхнего и нижнего регистра
  • ✅ Цифры
  • ✅ Специальные символы (рекомендуется)
  • ❌ НЕ должен содержать имя пользователя
  • ❌ НЕ должен быть в истории паролей

Обработка ошибок

ValueError

Пароль не соответствует политике сложности Active Directory

Решение: Используйте более сложный пароль (комбинация букв, цифр, спецсимволов)

ValueError

Пароль слишком короткий

Решение: Увеличьте длину пароля (минимум 7-8 символов)

DirectoryLdapError

Не удалось установить пароль: {...}

Причины:

  • Отсутствует SSL/TLS соединение с AD
  • Недостаточно прав у сервисной учётки
  • Проблемы с подключением к LDAP

Безопасность

⚠️ Важно:

  • Пароли передаются через защищённое LDAP соединение (SSL/TLS)
  • В БД Django хранится только хэш (не plaintext)
  • В LDAP пароль хранится в защищённом виде (невозможно прочитать)
  • Логируются только попытки смены пароля (без самого пароля)

Интеграция с существующим функционалом

Эта функция дополняет, а не заменяет:

  • UserService.update_user(emp, changes={'password': '...'}) - работает как и раньше
  • Django admin кнопка "Изменить пароль" для Employee - работает, но НЕ синхронизирует с LDAP автоматически

Для автоматической синхронизации при смене пароля через стандартную админку Employee нужно добавить переопределение метода (см. предыдущее обсуждение).

Техническая информация

Как это реализовано

  1. LdapUser.set_password() вызывает UserPasswordService.set_password()
  2. Сервис использует conn.extend.microsoft.modify_password() (AD extended operation)
  3. Это более надёжно, чем прямая запись в атрибут unicodePwd
  4. Политика паролей AD проверяется автоматически
  5. При ошибках возвращаются понятные сообщения

Поле unicode_pwd

  • Тип: CharField (но пустое по умолчанию)
  • Write-only: можно записать, нельзя прочитать
  • Кодировка: UTF-16-LE с кавычками (обрабатывается автоматически)
  • Использование: Не рекомендуется напрямую, используйте .set_password()

Тестирование

# В Django shell
from employees.ldap.orm_models import LdapUser

# Создать тестового пользователя (если есть)
user = LdapUser.objects.get(sam_account_name='test_user')

# Попробовать сменить пароль
try:
    user.set_password('TestPass123!')
    print("✅ Пароль успешно изменён")
except ValueError as e:
    print(f"❌ Ошибка валидации: {e}")
except Exception as e:
    print(f"❌ Ошибка LDAP: {e}")

История изменений

23.03.2026:

  • ✅ Добавлено поле unicode_pwd в LdapUser
  • ✅ Добавлены методы set_password() и change_password()
  • ✅ Добавлен action "🔑 Изменить пароль" в список пользователей
  • ✅ Добавлена кнопка "🔑 Изменить пароль" в карточку пользователя ⭐
  • ✅ Создан шаблон admin/ldap_change_password.html для массовой смены
  • ✅ Создан шаблон admin/ldap_user_password_change.html для индивидуальной смены
  • ✅ Автоматическая синхронизация с Django Employee при смене пароля