Corp/backend/common/ldap_password_mixin.py
lizerk e2a15f57a3 refactor(ldap): упростить архитектуру сигналов + добавить LdapPasswordMixin
- Убрана логика создания LDAP пользователей из сигналов
- Сигналы ТОЛЬКО синхронизируют изменения существующих LDAP пользователей
- Создание LDAP пользователей - ответственность RegisterAPIView
- Убран _ldap_password из perform_create (создание через API не создает LDAP пользователей)
- Создан LdapPasswordMixin для endpoint'ов работы с паролями
- perform_create теперь только создает Employee в БД (административное создание)

Архитектурное улучшение: разделение ответственности
- Auth views (RegisterAPIView) - создают LDAP пользователей напрямую
- Сигналы - только синхронизация изменений
- LdapPasswordMixin - для endpoint'ов смены пароля
2026-03-18 16:47:04 +03:00

107 lines
3.9 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""Миксин для API endpoint'ов, работающих с паролями в LDAP."""
import logging
from django.conf import settings
from employees.ldap.directory_service import DirectoryService
from employees.ldap.errors import DirectoryDbError, DirectoryLdapError, DirectoryServiceError
from employees.models import LdapSyncState
logger = logging.getLogger(__name__)
def _is_ldap_enabled():
"""Проверяет, включена ли интеграция с LDAP."""
return getattr(settings, "LDAP_ENABLED", False)
class LdapPasswordMixin:
"""Миксин для endpoint'ов, которые работают с паролями.
Автоматически синхронизирует изменения паролей с LDAP.
Использование:
class ChangePasswordAPIView(LdapPasswordMixin, APIView):
def post(self, request):
user = request.user
new_password = request.data['new_password']
# Меняем пароль в БД
user.set_password(new_password)
user.save()
# Синхронизируем с LDAP (автоматически через миксин)
self.sync_password_to_ldap(user, new_password)
return Response({'ok': True})
"""
def sync_password_to_ldap(self, employee_instance, new_password):
"""Синхронизирует пароль с LDAP для данного Employee.
Args:
employee_instance: инстанс Employee
new_password: новый пароль (plaintext)
Returns:
tuple: (success: bool, error: str or None)
"""
if not _is_ldap_enabled():
return True, None
if not employee_instance.is_ldap_managed:
return True, None
# Проверяем наличие LDAP пользователя
sync_state = LdapSyncState.objects.filter(
model='employee',
object_pk=str(employee_instance.pk)
).first()
if not sync_state or not (sync_state.ldap_dn or sync_state.ldap_guid):
logger.warning(
f"Employee {employee_instance.id} has no LDAP sync state, "
"password not synced to LDAP"
)
return False, "no_ldap_user"
try:
svc = DirectoryService()
svc.update_user(
emp=employee_instance,
changes={'password': new_password},
group_cns=None,
move_to_department_dn=None,
)
logger.info(f"Password synced to LDAP for Employee {employee_instance.id}")
return True, None
except DirectoryLdapError as e:
logger.error(
f"LDAP error during password sync for Employee {employee_instance.id}: {e}",
exc_info=True
)
return False, f"ldap_error: {str(e)}"
except DirectoryServiceError as e:
logger.error(
f"Service error during password sync for Employee {employee_instance.id}: {e}",
exc_info=True
)
return False, f"service_error: {str(e)}"
except DirectoryDbError as e:
logger.error(
f"DB error during password sync for Employee {employee_instance.id}: {e}",
exc_info=True
)
return False, f"db_error: {str(e)}"
except Exception as e:
logger.error(
f"Unexpected error during password sync for Employee {employee_instance.id}: {e}",
exc_info=True
)
return False, f"unexpected_error: {str(e)}"