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_*
DRF request.data is dict for JSON but QueryDict for form data.
Use hasattr check to call .dict() only when available, otherwise
use dict() constructor for plain dicts.
dict(QueryDict) wraps every value in a list: {'first_name': ['Вадимин']}
QueryDict.dict() returns single values: {'first_name': 'Вадимин'}
This caused LDAP fields to be saved as "['Вадимин']" instead of 'Вадимин'
because the list was stringified by ldapdb before writing to AD.
- Удалён LDAP_SERVER_TYPE и все OpenLDAP ветки кода
- user_service.py: только AD (sAMAccountName, UAC, microsoft.modify_password)
- auth_backends.py: AD фильтры и атрибуты (objectCategory, objectGUID)
- Удалён server_configs.py (больше не нужен)
- Удалён OpenLDAP Docker окружение (docker-compose.ldap.yml, ldap/, QUICKSTART_LDAP.md)
- Фронтенд: мульти-шаговая регистрация, AvatarCropper 3:4, verify-email страница
- Убран CSRF для JWT API, IP-ограничения работают независимо
- Полевые ошибки регистрации с красной подсветкой на фронте
ПРОБЛЕМА:
- RegisterAPIView знал о режиме LDAP (вызывал _is_ldap_enabled())
- if ldap_enabled: create_ldap_user() else: create_db_user()
- View имел LDAP-специфичную логику выбора режима
- Нарушение принципа инкапсуляции
РЕШЕНИЕ:
Создан единый метод в LdapUserCreationMixin:
create_user(first_name, last_name, email, phone, password, ...)
- Автоматически выбирает LDAP или БД режим
- Инкапсулирует вызов _is_ldap_enabled()
- View больше не знает о существовании LDAP
ИЗМЕНЕНИЯ:
mixins.py:
+ create_user() - фасад, выбирает режим автоматически
• create_ldap_user() - остался для внутреннего использования
• create_db_user() - остался для внутреннего использования
auth.py:
- Удалён import _is_ldap_enabled
- Удалён блок if ldap_enabled: ... else: ...
- Заменён на: emp, error = self.create_user(...)
РЕЗУЛЬТАТ:
- View НЕ знает про LDAP вообще
- Вся LDAP логика инкапсулирована в миксине
- Чистое разделение ответственности
- Упрощение: -18 строк в view
БЫЛО в RegisterAPIView:
ldap_enabled = _is_ldap_enabled()
if ldap_enabled:
emp, err = self.create_ldap_user(...)
else:
emp = self.create_db_user(...)
СТАЛО:
emp, err = self.create_user(...)
Django check: успешно