Пользовательские разрешения во фреймворке rests

Мне нужны разрешения для моего приложения в DRF.

Ввод:

  • неавторизованные пользователи могут читать только публикации и изображения

  • аутентифицированные пользователи могут создавать публикации и добавлять изображения

  • авторизованные пользователи могут редактировать и удалять публикации и изображения, для которых они являются авторами

  • админ может выполнять все действия, кроме редактирования контента

Модели:

class Publication(models.Model):
    pub_text = models.TextField(null=True, blank=True)
    pub_date = models.DateTimeField(auto_now_add=True)
    pub_author = models.ForeignKey(User, on_delete=models.CASCADE)

class Image(models.Model):
    image = models.ImageField(upload_to='images', null=True)
    image_to_pub = models.ForeignKey(Publication, on_delete=models.CASCADE, null=True, related_name='images')

Просмотров:

class PublicationViewSet(viewsets.ModelViewSet):
    queryset = Publication.objects.all()
    serializer_class = PublicationSerializer
    permission_classes = [PublicPermission]


class ImageViewSet(viewsets.ModelViewSet):
    queryset = Image.objects.all()
    serializer_class = ImageSerializer
    permission_classes = [ImagePermission]

Разрешения:

class ImagePermission(BasePermission):

    edit_methods = ['PUT', 'PATCH']

    def has_permission(self, request, view):
        if request.method in SAFE_METHODS:
            return True
        if request.user.is_authenticated:
            return True

    def has_object_permission(self, request, view, obj):

        if request.user.is_superuser:
            return True

        if request.method in SAFE_METHODS:
            return True

        if request.user.id == Publication.objects.get(id=obj.image_to_pub_id).pub_author_id:
            return True

        if request.user.is_staff and request.method not in self.edit_methods:
            return True

        return False


class ImageAuthorPermission(BasePermission):
    def has_object_permission(self, request, view, obj):
        if request.user.id == Publication.objects.get(id=obj.image_to_pub_id).pub_author_id:
            return True
        return False

Теперь все работает так, как я описал выше. Но я не уверен, что это хорошая практика.

Я имею в виду класс <ImagePermission>. В SAFE_METHODS.

есть два раза проверить метод if.

Если я удалю этот чек-аут из <has_permission>, то неаутентифицированные пользователи не будут иметь прав ReadOnly.

Если я удаляю чек-аут из <has_object_permission>, аутентифицированные пользователи не имеют прав на редактирование и удаление.

Я уверен, что есть лучший способ настроить эти разрешения. Разве нет?

Также я попытался проверить, имеет ли текущий пользователь объектные права на изображения, относящиеся к публикации, автором которой он является. Это работает, но есть ли стандартная практика проверки прав на связанные объекты? Я попытался удалить проверку

if request.user.id == Publication.objects.get(id=obj.image_to_pub_id).pub_author_id:

из <ImagePermission> и объединить оба класса <ImagePermission> и <ImageAuthorPermission> в список <permission_classes>. Использовал операторы & и |, но не добился успеха.

Я увидел эту часть и реализовал ее.

  1. неаутентифицированные пользователи могут только читать публикации и изображения
  2. авторизованные пользователи могут создавать публикации и добавлять изображения
  3. авторизованные пользователи могут редактировать и удалять публикации и изображения, для которых они являются авторами
  4. админ может выполнять все действия, кроме редактирования контента

Прежде всего, запрос SAFE_METHOD должен быть только для чтения, а такие действия, как создание, могут выполняться только аутентифицированными пользователями.

Для этой части используйте класс IsAuthenticatedOrReadOnly, который DRF Permission предоставляет по умолчанию.

rest_framework.permission

class IsAuthenticatedOrReadOnly(BasePermission):
    """
    The request is authenticated as a user, or is a read-only request.
    """

    def has_permission(self, request, view):
        return bool(
            request.method in SAFE_METHODS or
            request.user and
            request.user.is_authenticated
        )

Для реализации № 3 и 4 только владелец может изменять и удалять его, а администратор - только модифицировать ("PUT", "PATCH").

from rest_framework.permissions import IsAuthenticatedOrReadOnly, BasePermission


class AdminObjectPermission(BasePermission):
  
  def has_object_permission(self, request, view, obj):
    if bool(request.user and request.user.is_staff) and request.method in ["PUT", "PATCH"]:
      return True


class PublicPermission(IsAuthenticatedOrReadOnly):
  
  def has_object_permission(self, request, view, obj):
    
    if request.user.id == obj.pub_author.id:
      return True
    
    return False


class ImagePermission(IsAuthenticatedOrReadOnly, AdminObjectPermission):
  
  def has_object_permission(self, request, view, obj):

    if request.user.id == obj.image_to_pub.pub_author.id:
      return True
    
    return False

Для объекта Publication он связан с пользователем с полем pub_author.

if request.user.id == obj.pub_author.id

Объекты изображений сравнивались через pub_author на связанных объектах Publication.

if request.user.id == obj.image_to_pub.pub_author.id

views.py

class PublicationViewSet(viewsets.ModelViewSet):
    queryset = Publication.objects.all()
    serializer_class = PublicationSerializer
    authentication_classes = [YourAuthenticationClass]
    permission_classes = [PublicPermission|AdminObjectPermission]


class ImageViewSet(viewsets.ModelViewSet):
    queryset = Image.objects.all()
    serializer_class = ImageSerializer
    authentication_classes = [YourAuthenticationClass]
    permission_classes = [ImagePermission|AdminObjectPermission]

Этот класс работает:

class PublicationPermission(BasePermission):

edit_methods = ['PUT', 'PATCH']

def has_permission(self, request, view):
    if request.method in SAFE_METHODS:
        return True
    if request.user.is_authenticated:
        return True

def has_object_permission(self, request, view, obj):
    if request.method in SAFE_METHODS:
        return True
    if request.user.id == obj.pub_author.id:
        return True
    if request.user.is_staff and request.method not in self.edit_methods:
        return True

has_permission проверяет, разрешен ли запрос или нет, основываясь на авторизации пользователя и методах запроса. Если запрос разрешен, то объект получается из базы данных, а has_object_permission проверяет права пользователя на объект. Моя проблема заключалась в том, что без этих строк в has_object_permission:

        if request.method in SAFE_METHODS:
        return True
Метод

retrive не был разрешен, только метод списка без аутентификации. Понятия не имею, почему. Возможно, метод retrive требует разрешения на уровне объекта...

Вернуться на верх