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_*
196 lines
9.2 KiB
Markdown
196 lines
9.2 KiB
Markdown
# Смена паролей 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
|
||
|
||
```python
|
||
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
|
||
|
||
```bash
|
||
cd /home/lizerk/Dev/EUSRR/backend
|
||
.venv/bin/python manage.py shell
|
||
```
|
||
|
||
```python
|
||
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()`
|
||
|
||
## Тестирование
|
||
|
||
```python
|
||
# В 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 при смене пароля
|