Разрешение Django для каждого действия

Я новичок в разработке с Django + Django Rest-framework, и я работаю над проектом, который обеспечивает доступ REST Api. Мне было интересно, что лучше всего назначить другое разрешение для каждого действия данного ApiView или Viewset.

Предположим, что я определил некоторые классы разрешений, такие как "IsAdmin", "IsRole1", "IsRole2",... и я хочу предоставить разные разрешения для отдельных действий (например, пользователь с Role1 может создавать или извлекать, пользователь с Role2 может обновлять, и только администратор может удалить).

Как я могу структурировать представление на основе класса, чтобы назначить класс разрешений для действий "create", "list", "retrieve", "update", "delete"? Я пытаюсь сделать это, чтобы иметь класс, который можно использовать повторно для разных таблиц с одинаковым шаблоном разрешения.

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

Ответы

Ответ 1

Вы можете создать собственный класс разрешений, расширяющий DRF BasePermission.

Вы реализуете has_permission, где у вас есть доступ к объектам request и view. Вы можете проверить request.user для соответствующей роли и вернуть True/False в зависимости от ситуации.

Посмотрите на предоставленный класс IsAuthenticatedOrReadOnly (и другие) для хорошего примера того, насколько это просто.

Я надеюсь, что это поможет.

Ответ 2

В документации DRF,

Примечание. Метод has_object_permission на уровне экземпляра будет вызываться только в том случае, если проверки уровня_изличения уже прошли

Предположим следующее разрешение о user object

  • Список: только для персонала
  • Создать: любой
  • Получить: самостоятельно или персонал
  • Обновление, частичное обновление: собственная личность или персонал.
  • Уничтожьте: только персонал

<я > permissons.py

from rest_framework import permissions                                                              


class UserPermission(permissions.BasePermission):

    def has_permission(self, request, view):                                                        
        if view.action == 'list':                                                                   
            return request.user.is_authenticated() and request.user.is_admin                        
        elif view.action == 'create':                                                               
            return True                                                                             
        elif view.action in ['retrieve', 'update', 'partial_update', 'destroy']:                    
            return True                                                                             
        else:                                                                                       
            return False                                                                            

    def has_object_permission(self, request, view, obj):                                            
        if view.action == 'retrieve':                                                               
            return request.user.is_authenticated() and (obj == request.user or request.user.is_admin)    
        elif view.action in ['update', 'partial_update']:                                           
            return request.user.is_authenticated() and (obj == request.user or request.user.is_admin)    
        elif view.action == 'destroy':
            return request.user.is_authenticated() and request.user.is_admin                        
        else:
            return False

<я > views.py

from .models import User
from .permissions import UserPermission
from .serializers import UserSerializer
from rest_framework import viewsets


class UserViewSet(viewsets.ModelViewSet):
    queryset = User.objects.all()
    serializer_class = UserSerializer
    permission_classes = (UserPermission,)

Ответ 3

Django имеет класс persmissions, называемый DjangoObjectPermissions, который использует Django Guardian в качестве сервера аутентификации.

Когда у вас есть хранитель Django, активный в ваших настройках, вы просто добавляете permission_classes = [DjandoObjectPermissions] к своему представлению и автоматически выполняете авторизацию прав, поэтому вы можете "CRUD" на основе разрешения, установленного для определенной группы или пользователя django.contrib.auth.

См. gist с примером.

Вы можете установить Django Guardian в качестве вашей аутентификации http://django-guardian.readthedocs.org/en/latest/installation.html

Ответ 4

Представления класса на основе RestFramework имеют методы для каждого HTTP-глагола (т.е. HTTP GET = > view.get() и т.д.). Вам просто нужно использовать разрешения django.contrib.auth, пользователей, группы и декораторы, как описано.

Ответ 5

Я лично ненавижу такие специфические разрешения frankenmonster, на мой взгляд, это не очень идиоматично, когда дело доходит до рамки Django; Поэтому я придумал следующее решение - он очень похож на то, как работают декораторы @list_route и @detail_route. Мы полагаемся на то, что методы/функции являются объектами первого класса

Прежде всего, я создаю такой dectorator:

decorators.py

def route_action_arguments(**kwargs):
"""
Add arguments to the action method
"""
def decorator(func):
    func.route_action_kwargs = kwargs
    return func
return decorator

Как вы можете видеть, он добавляет словарь к функции, которую он украшает параметрами, переданными как список arg

Теперь я создал такой mixin: mixins.py

class RouteActionArgumentsMixin (object):
    """
    Use action specific parameters to 
    provide:
    - serializer
    - permissions
    """

def _get_kwargs(self):
    action = getattr(self, 'action')
    if not action:
        raise AttributeError
    print('getting route kwargs for action:' + action)
    action_method = getattr(self, action)
    kwargs = getattr(action_method, 'route_action_kwargs')
    print(dir(kwargs))
    return kwargs

def get_serializer_class(self):
    try:
        kwargs = self._get_kwargs()
        return kwargs['serializer']
    except (KeyError, AttributeError):
        return super(RouteActionArgumentsMixin, self).get_serializer_class()

def get_permissions(self):
    try:
        kwargs = self._get_kwargs()
        return kwargs['permission_classes']
    except (KeyError, AttributeError):
        return super(RouteActionArgumentsMixin, self).get_permissions()

Mixin делает две вещи; когда get_permissions вызывается, он проверяет, который выполняется "действие", и looksup коллекции permission_classes от "route_action_kwargs", связанный с viewset.action_method.route_action_kwargs

когда вызывается get_serializer_class, он делает то же самое и выбирает "сериализатор" из "route_action_kwargs"

Теперь мы можем использовать его:

@method_decorator(route_action_arguments(serializer=LoginSerializer), name='create')
class UserViewSet (RouteActionArgumentsMixin, RequestContextMixin, viewsets.ModelViewSet):
    """
    User and profile managment viewset
    """

    queryset = User.objects.all()
    serializer_class = UserSerializer

    @list_route(methods=['post'])
    @route_action_arguments(permission_classes=(AllowAny,), serializer=LoginSerializer)
    def login(self, request):
        serializer = self.get_serializer_class()(data=request.data)

Для пользовательского маршрута мы явно определяем, что мы можем просто задать @route_action_arguments явно для метода.

В терминах общих представлений и методов мы все равно можем их добавить, используя @method_decorator

@method_decorator(route_action_arguments(serializer=LoginSerializer), name='create')
class UserViewSet (RouteActionArgumentsMixin, RequestContextMixin, viewsets.ModelViewSet):