198 lines
6.7 KiB
Python
Raw Permalink 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.

"""Django signals для синхронизации Position с LDAP.
Файл: employees/signals/ldap/position.py
Отвечает за синхронизацию POS-групп должностей с Active Directory
при изменении Position.groups.
"""
import logging
from django.conf import settings
from django.db.models.signals import m2m_changed, post_delete, post_save
from django.dispatch import receiver
from employees.ldap import PositionService
from employees.ldap.errors import (
DirectoryDbError,
DirectoryLdapError,
DirectoryServiceError,
)
from employees.models import Position
from employees.signals.ldap._queue import _enqueue
logger = logging.getLogger(__name__)
def _is_ldap_enabled():
"""Проверяет, включена ли интеграция с LDAP."""
return getattr(settings, "LDAP_ENABLED", False)
@receiver(post_save, sender=Position)
def sync_position_to_ldap_on_save(sender, instance, created, **kwargs):
"""Создает/обновляет POS-группу должности в LDAP при сохранении Position.
При создании: создает POS-группу (CN=POS_<name>)
При обновлении: переименовывает POS-группу при изменении имени
Использует временный атрибут _skip_ldap_sync для отключения синхронизации.
"""
if not _is_ldap_enabled():
return
if getattr(instance, "_skip_ldap_sync", False):
return
try:
svc = PositionService()
# reconcile_position гарантирует наличие POS-группы
# и обновляет её при необходимости
# Также синхронизирует вложенность в целевые группы и участников
svc.reconcile_position(instance)
logger.info(
"LDAP position synchronized",
extra={
"event": "ldap.position.synced",
"position_id": instance.pk,
"object_created": created,
},
)
except (DirectoryLdapError, DirectoryServiceError, DirectoryDbError) as e:
logger.exception(
"LDAP position sync failed",
extra={
"event": "ldap.position.sync_failed",
"position_id": instance.pk,
"object_created": created,
"error_type": type(e).__name__,
},
)
_enqueue(
"position_save",
"position",
instance.pk,
{
"object_pk": str(instance.pk),
},
)
except Exception as e:
logger.exception(
"Unexpected LDAP position sync failure",
extra={
"event": "ldap.position.sync_unexpected_failure",
"position_id": instance.pk,
"object_created": created,
"error_type": type(e).__name__,
},
)
@receiver(post_delete, sender=Position)
def sync_position_to_ldap_on_delete(sender, instance, **kwargs):
"""Удаляет POS-группу должности из LDAP при удалении Position."""
if not _is_ldap_enabled():
return
try:
svc = PositionService()
svc.delete_position_group(instance)
logger.info(
"LDAP position group deleted",
extra={"event": "ldap.position.deleted", "position_id": instance.pk},
)
except (DirectoryLdapError, DirectoryServiceError, DirectoryDbError) as e:
logger.exception(
"LDAP position deletion failed",
extra={
"event": "ldap.position.delete_failed",
"position_id": instance.pk,
"error_type": type(e).__name__,
},
)
_enqueue(
"position_delete",
"position",
instance.pk,
{
"object_pk": str(instance.pk),
"name": instance.name,
},
)
except Exception as e:
logger.exception(
"Unexpected LDAP position deletion failure",
extra={
"event": "ldap.position.delete_unexpected_failure",
"position_id": instance.pk,
"error_type": type(e).__name__,
},
)
@receiver(m2m_changed, sender=Position.groups.through)
def sync_position_groups_to_ldap(sender, instance, action, **kwargs):
"""Синхронизирует изменения Position.groups с LDAP.
При изменении групп должности (add/remove/clear/set):
- Обновляет вложенность POS-группы в целевые AD-группы
- POS-группа добавляется в AD-группы, соответствующие Position.groups
- POS-группа удаляется из AD-групп, не указанных в Position.groups
Реагирует на post_add, post_remove, post_clear
для синхронизации вложенности.
"""
if not _is_ldap_enabled():
return
if action not in ["post_add", "post_remove", "post_clear"]:
return
if getattr(instance, "_skip_ldap_sync", False):
return
try:
svc = PositionService()
# reconcile_position обновит вложенность POS-группы в целевые AD-группы
# на основе текущего состояния Position.groups
svc.reconcile_position(instance)
logger.info(
"LDAP position groups synchronized",
extra={
"event": "ldap.position.groups_synced",
"position_id": instance.pk,
"action": action,
},
)
except (DirectoryLdapError, DirectoryServiceError, DirectoryDbError) as e:
logger.exception(
"LDAP position groups sync failed",
extra={
"event": "ldap.position.groups_sync_failed",
"position_id": instance.pk,
"action": action,
"error_type": type(e).__name__,
},
)
_enqueue(
"position_save",
"position",
instance.pk,
{
"object_pk": str(instance.pk),
},
)
except Exception as e:
logger.exception(
"Unexpected LDAP position groups sync failure",
extra={
"event": "ldap.position.groups_sync_unexpected_failure",
"position_id": instance.pk,
"action": action,
"error_type": type(e).__name__,
},
)