fix(ldap): исправлен primary_key в ORM моделях
ПРОБЛЕМА: - cn/ou были установлены как primary_key в LdapUser/LdapGroup/LdapOrganizationalUnit - CN/OU не уникальны глобально (могут быть в разных OU) - .objects.get(cn=...) возвращал случайную группу при дубликатах - Нарушался дизайн django-ldapdb, где dn - базовый primary_key РЕШЕНИЕ: - Убран primary_key=True из cn/ou полей - Используется dn (Distinguished Name) как primary_key из базового класса - DN - уникальный идентификатор объекта в LDAP, требуется для всех операций - Удалено избыточное поле distinguished_name ИЗМЕНЕНИЯ: - orm_models.py: cn/ou теперь обычные CharField, dn наследуется из ldapdb.models.Model - group_utils_orm.py: .get(cn=...) → .filter(cn=...).first() с предупреждением - group_service.py: find_dn() и find_dn_wrapped() используют .filter().first() ОБОСНОВАНИЕ ОТКАЗА ОТ objectGUID: - objectGUID неизменяем и уникален, НО: - LDAP протокол требует DN для всех операций (add/modify/delete/rename) - django-ldapdb жёстко завязан на self.dn для save()/delete() - GUID можно использовать только в search filter, результат всё равно возвращает DN - DN - естественный primary key для LDAP объектов
This commit is contained in:
parent
1b03ed8a7b
commit
df93afbcb1
@ -175,8 +175,8 @@ class LdapUser(LdapModel):
|
||||
object_classes = ['top', 'person', 'organizationalPerson', 'user']
|
||||
|
||||
# Основные атрибуты
|
||||
cn = CharField(db_column='cn', primary_key=True)
|
||||
distinguished_name = CharField(db_column='distinguishedName')
|
||||
# dn наследуется от ldapdb.models.Model как primary_key (Distinguished Name)
|
||||
cn = CharField(db_column='cn')
|
||||
|
||||
# Идентификация
|
||||
object_guid = CharField(db_column='objectGUID')
|
||||
@ -247,8 +247,8 @@ class LdapGroup(LdapModel):
|
||||
object_classes = ['top', 'group']
|
||||
|
||||
# Основные атрибуты
|
||||
cn = CharField(db_column='cn', primary_key=True)
|
||||
distinguished_name = CharField(db_column='distinguishedName')
|
||||
# dn наследуется от ldapdb.models.Model как primary_key (Distinguished Name)
|
||||
cn = CharField(db_column='cn')
|
||||
|
||||
# Идентификация
|
||||
object_guid = CharField(db_column='objectGUID')
|
||||
@ -301,8 +301,8 @@ class LdapOrganizationalUnit(LdapModel):
|
||||
object_classes = ['top', 'organizationalUnit']
|
||||
|
||||
# Основные атрибуты
|
||||
ou = CharField(db_column='ou', primary_key=True)
|
||||
distinguished_name = CharField(db_column='distinguishedName')
|
||||
# dn наследуется от ldapdb.models.Model как primary_key (Distinguished Name)
|
||||
ou = CharField(db_column='ou')
|
||||
|
||||
# Идентификация
|
||||
object_guid = CharField(db_column='objectGUID')
|
||||
|
||||
@ -218,12 +218,16 @@ class GroupService:
|
||||
def find_dn(
|
||||
self, conn: Connection, cn: str, bases: Optional[List[str]] = None
|
||||
) -> Optional[str]:
|
||||
"""Ищет DN группы по CN (ORM)."""
|
||||
try:
|
||||
group = LdapGroup.objects.get(cn=cn)
|
||||
"""Ищет DN группы по CN (ORM).
|
||||
|
||||
Warning: Если есть несколько групп с одинаковым CN,
|
||||
вернёт первую найденную.
|
||||
"""
|
||||
# .filter().first() вместо .get() т.к. cn больше не primary_key
|
||||
group = LdapGroup.objects.filter(cn=cn).first()
|
||||
if group:
|
||||
return group.dn
|
||||
except LdapGroup.DoesNotExist:
|
||||
return None
|
||||
return None
|
||||
|
||||
def groups_with_member(self, conn: Connection, member_dn: str) -> Set[str]:
|
||||
"""Находит все группы, в которых состоит DN (ORM через memberOf)."""
|
||||
@ -329,12 +333,16 @@ class GroupService:
|
||||
def find_dn_wrapped(
|
||||
self, cn: str, bases: Optional[list[str]] = None
|
||||
) -> Optional[str]:
|
||||
"""Ищет DN группы по CN (ORM)."""
|
||||
try:
|
||||
group = LdapGroup.objects.get(cn=cn)
|
||||
"""Ищет DN группы по CN (ORM).
|
||||
|
||||
Warning: Если есть несколько групп с одинаковым CN,
|
||||
вернёт первую найденную.
|
||||
"""
|
||||
# .filter().first() вместо .get() т.к. cn больше не primary_key
|
||||
group = LdapGroup.objects.filter(cn=cn).first()
|
||||
if group:
|
||||
return group.dn
|
||||
except LdapGroup.DoesNotExist:
|
||||
return None
|
||||
return None
|
||||
|
||||
def create_wrapped(
|
||||
self,
|
||||
|
||||
@ -55,16 +55,20 @@ def group_remove_member_orm(group_dn: str, member_dn: str) -> None:
|
||||
|
||||
|
||||
def resolve_group_dns_by_cn_orm(cns: set[str]) -> dict[str, str]:
|
||||
"""Ищет DN групп по их CN через ORM."""
|
||||
"""Ищет DN групп по их CN через ORM.
|
||||
|
||||
Warning: Если есть несколько групп с одинаковым CN в разных OU,
|
||||
вернёт первую найденную (может быть неправильной).
|
||||
Рекомендуется использовать DN напрямую вместо CN.
|
||||
"""
|
||||
if not cns:
|
||||
return {}
|
||||
found: dict[str, str] = {}
|
||||
for cn in sorted(cns):
|
||||
try:
|
||||
group = LdapGroup.objects.get(cn=cn)
|
||||
# .filter().first() вместо .get() т.к. cn больше не primary_key
|
||||
group = LdapGroup.objects.filter(cn=cn).first()
|
||||
if group:
|
||||
found[cn] = group.dn
|
||||
except LdapGroup.DoesNotExist:
|
||||
continue
|
||||
return found
|
||||
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user