Подтвердить что ты не робот

Как получить разрешения OR вместо AND в REST framework

Кажется, что классы разрешений являются ANDed, когда REST framework проверяет разрешения. Это означает, что для каждого разрешения необходимо вернуть True для разрешения. Это делает такие вещи, как "если вы суперпользователь, вы можете получить доступ к чему угодно, но если вы обычный пользователь, вам нужны явные разрешения", которые трудно реализовать, вы не можете просто вернуть False, это приведет к сбою всего стека. Есть ли способ разрешить короткое замыкание? Что-то вроде "если это разрешение предоставлено, прекратите проверку?" или какой-то другой способ справиться с такими случаями?

4b9b3361

Ответ 1

Думаю, вы могли бы использовать библиотеку django-rules здесь. Ссылка

Это механизм, основанный на правилах, очень похожий на деревья решений, и его можно легко интегрировать с frameworksclass framework DRF.

Лучшая часть - вы можете выполнять операции над простыми разрешениями и создавать из них сложные разрешения.

Пример

>>> @rules.predicate
>>> def is_admin(user):
...     return user.is_staff 
...


>>> @rules.predicate
>>> def is_object_owner(user, object):
        return object.owner == user

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

is_object_editable = is_object_owner | is_admin

Вы можете использовать это новое правило предикатов is_object_editable внутри вашего метода has_permissions класса разрешений.

Ответ 2

Теперь DRF позволяет составлять разрешения с помощью побитовых операторов: & -and- и | -или же-.

Из документов:

При условии, что они наследуются от rest_framework.permissions.BasePermission, разрешения могут быть составлены с использованием стандартных битовых операторов Python. Например, IsAuthenticatedOrReadOnly может быть написано:

from rest_framework.permissions import BasePermission, IsAuthenticated
from rest_framework.response import Response
from rest_framework.views import APIView

class ReadOnly(BasePermission):
    def has_permission(self, request, view):
        return request.method in SAFE_METHODS

class ExampleView(APIView):
    permission_classes = (IsAuthenticated|ReadOnly,)

    def get(self, request, format=None):
        content = {
            'status': 'request was permitted'
        }
        return Response(content)

Отредактировано: обратите внимание, что после IsAuthenticated|ReadOnly есть запятая.

Ответ 3

Вам нужно создать собственный пользовательский http://www.django-rest-framework.org/api-guide/permissions/#custom-permissions, как описано в документах.

Что-то вроде:

from rest_framework import permissions

class IsAdminOrStaff(permissions.BasePermission):
    message = 'None of permissions requirements fulfilled.'

    def has_permission(self, request, view):
        return request.user.is_admin() or request.user.is_staff()

Тогда, на ваш взгляд:

permission_classes = (IsAdminOrStaff,)

Ответ 4

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

По состоянию на февраль 2016, те, кто обрабатывает разрешение сложного условия, включают в себя:

Ответ 5

Одним из способов было бы добавить еще один класс который объединяет существующие классы так, как вы хотите, например:

class IsAdmin(BasePermission):
    """Allow access to admins"""
    def has_object_permission(self, request, view, obj):
        return request.user.is_admin()


class IsOwner(BasePermission):
    """Allow access to owners"""
    def has_object_permission(self, request, view, obj):
        request.user.is_owner(obj)


class IsAdminOrOwner(BasePermission):
    """Allow access to admins and owners"""  
    def has_object_permission(*args):
        return (IsAdmin.has_object_permission(*args) or
                IsOwner.has_object_permission(*args))

Ответ 6

Вот общее решение:

from functools import reduce
from rest_framework.decorators import permission_classes
from rest_framework.permissions import BasePermission


def any_of(*perm_classes):
    """Returns permission class that allows access for
       one of permission classes provided in perm_classes"""
    class Or(BasePermission):
        def has_permission(*args):
            allowed = [p.has_permission(*args) for p in perm_classes]
            return reduce(lambda x, y: x or y, allowed)

    return Or


class IsAdmin(BasePermission):
    """Allow access to admins"""

    def has_object_permission(self, request, view, obj):
        return request.user.is_admin()


class IsOwner(BasePermission):
    """Allow access to owners"""

    def has_object_permission(self, request, view, obj):
        request.user.is_owner(obj)


"""Allow access to admins and owners"""


@permission_classes((any_of(IsAdmin, IsOwner),))
def you_function(request):
    # Your logic
    ...