Пользовательские разрешения во фреймворке 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>. Использовал операторы & и |, но не добился успеха.
Я увидел эту часть и реализовал ее.
- неаутентифицированные пользователи могут только читать публикации и изображения
- авторизованные пользователи могут создавать публикации и добавлять изображения
- авторизованные пользователи могут редактировать и удалять публикации и изображения, для которых они являются авторами
- админ может выполнять все действия, кроме редактирования контента
Прежде всего, запрос 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 требует разрешения на уровне объекта...