Django REST Framework - Разрешить сотрудникам доступ ко всем конечным точкам - PullRequest
1 голос
/ 26 марта 2019

Я создаю API-интерфейс DRF и хотел бы разрешить сотрудникам (is_staff == True) получать доступ ко всем конечным точкам REST, в то же время предоставляя настраиваемую проверку разрешений для ViewSet.В идеале это будет глобальная настройка, но я не против ее настройки для ViewSet.

Вот что я пробовал:


Вариант 1: проверка накаждое пользовательское разрешение

from rest_framework import permissions

class SomeModelPermission(permissions.BasePermission):
    def has_permission(self, request, view):
        if request.user.is_staff:
            return True

        # other logic

    def has_object_permission(self, request, view, obj):
        if request.user.is_staff:
            return True

        # other logic

Это работает, но я бы предпочел не повторять столько кода.


Вариант 2: побитовые операторы

Я попытался удалитьis_staff логика из настраиваемого разрешения выше и добавление его в ViewSet:

from rest_framework import permissions, viewsets

class SomeModelViewSet(viewsets.ModelViewSet):
    permission_classes = (permissions.IsAdminUser|SomeModelPermission,)

Однако, на самом деле, это не обеспечивает разрешения, как хотелось бы, потому что IsAdminUser наследуется от BasePermission,который определяется как:

class BasePermission(object):
    def has_permission(self, request, view):
        return True

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

IsAdminUser не определяет свой собственный has_object_permission, поэтому он всегда будет возвращать True при проверке разрешений объекта, что приведет к непреднамеренному доступу к объекту.


Есть идеи?Я надеялся, что найдется способ установить глобальную проверку разрешений, которая будет возвращать True, когда пользователь является сотрудником, и в противном случае использовать пользовательские разрешения.Но, прочитав , как определяются разрешения , я не уверен, что это возможно.

1 Ответ

2 голосов
/ 26 марта 2019

Побитовое решение:

Как насчет создания собственного IsAdminUser, который также определяет has_object_permission?Вы можете просто унаследовать от существующего:

from rest_framework.permissions import IsAdminUser as BaseIsAdminUser

class IsAdminUser(BaseIsAdminUser):
    def has_object_permission(self, request, view, obj):
        # Just reuse the same logic as `has_permission`...
        return self.has_permission(request, view)

Затем вы можете сделать то, что вы пытались выше, с помощью побитового оператора:

from rest_framework import permissions, viewsets
from your_own_project.permissions import IsAdminUser

class SomeModelViewSet(viewsets.ModelViewSet):
    permission_classes = (IsAdminUser|SomeModelPermission,)


Другое решение:

В некотором смысле немного "хакерский", но вы можете попытаться создать свои собственные типы разрешений на лету.

Таким образом, конечный результат будет выглядеть примерно так:

class SomeModelViewSet(viewsets.ModelViewSet):
    permission_classes = skip_for_staff((SomeModelPermission, SomeOtherPermission, ...))

С реализацией, похожей на:

class StaffAllowedMixin:
    def has_permission(self, request, view):
        if request.user.is_staff:
            return True
        return super().has_permission(request, view)

    def has_object_permission(self, request, view, obj):
        if request.user.is_staff:
            return True
        return super().has_object_permission(request, view, obj)


def skip_for_staff(permission_classes):
    # You can probably also use a comprehension here, but for clarity:
    staff_allowed_classes = []
    for permission_class in permissions(
       staff_allowed_classes.append(
           # Create a new type (class) with name StaffAllowed<...>
           type(f"StaffAllowed{permission_class}",
                # Inherit from the mixin above, and from the original class
                (StaffAllowedMixin, permission_class),
                # empty dictionary means you don't want to override any attributes
                {})
           )
    return tuple(staff_allowed_classes)

По существу, для каждого класса разрешений вы создаете новый класс с дополнительным миксином, который имеет приоритет и проверяет, является ли пользователь персоналом.Но вы делаете это на лету, где используются ваши разрешения, вместо того, чтобы предопределять его для каждого разрешения.

...