99 lines
3.3 KiB
Python
99 lines
3.3 KiB
Python
# 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
|