# backend\api\v1\permissions.py from __future__ import annotations from typing import Iterable, Tuple from rest_framework.permissions import BasePermission from rest_framework.request import Request class IsSelfOrStaff(BasePermission): """Доступ для staff/superuser или владельца объекта. ВАЖНО: • SAFE-методы НЕ дают автоматического допуска. • Добавлена поддержка кейса "объект = сам пользователь" (self-update). """ DEFAULT_OWNER_ID_ATTRS: Tuple[str, ...] = ( "employee_id", "author_id", "user_id", "owner_id", ) DEFAULT_OWNER_OBJ_ATTRS: Tuple[str, ...] = ( "employee", "author", "user", "owner", ) def has_permission(self, request: Request, view) -> bool: """Пускаем только аутентифицированных. Args: request (Request): DRF-запрос. view: DRF ViewSet. Returns: bool: True если пользователь аутентифицирован. """ user = getattr(request, "user", None) return bool(user and user.is_authenticated) def has_object_permission(self, request: Request, view, obj) -> bool: """Разрешает доступ staff/superuser или владельцу объекта. Логика владельца: 1) Совпадение *_id полей (employee_id/user_id/…) 2) Совпадение связанных owner-объектов (employee/user/…) 3) Фоллбек "self-object": obj.__class__ == user.__class__ и obj.pk == user.pk Args: request (Request): DRF-запрос. view: DRF ViewSet. obj: Проверяемый объект. Returns: bool: True если доступ разрешён. Raises: Ничего не выбрасывает. """ user = getattr(request, "user", None) if not user or not user.is_authenticated: return False # Админский доступ if getattr(user, "is_staff", False) or getattr(user, "is_superuser", False): return True # 1) *_id поля владельца owner_id_attrs: Iterable[str] = getattr( view, "owner_id_attrs", self.DEFAULT_OWNER_ID_ATTRS ) for attr in owner_id_attrs: if getattr(obj, attr, None) == user.id: return True # 2) связанные owner-объекты owner_obj_attrs: Iterable[str] = getattr( view, "owner_obj_attrs", self.DEFAULT_OWNER_OBJ_ATTRS ) for attr in owner_obj_attrs: owner = getattr(obj, attr, None) if owner is not None and getattr(owner, "id", None) == user.id: return True # 3) self-object: редактирование собственной учётки (Employee ↔ # Employee) if obj.__class__ is user.__class__ and getattr(obj, "pk", None) == getattr( user, "pk", None, ): return True return False