Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django REST Framework viewset per-action permissions

Is there a best practice to assign a different permission to each action of a given APIView or ViewSet?

Let's suppose I defined some permissions classes such as 'IsAdmin', 'IsRole1', 'IsRole2', ..., and I want to grant different permissions to the single actions (e.g. a user with Role1 can create or retrieve, a user with Role2 can update, and only an Admin can delete).

How can I structure a class based view in order to assign a permission class to the 'create', 'list', 'retrieve', 'update', 'delete' actions? I'm trying to do so to have a class that can be reused for different tables that have the same permission pattern.

like image 976
lWhitmore Avatar asked Oct 11 '13 08:10

lWhitmore


People also ask

What is ViewSet in Django REST framework?

A ViewSet class is simply a type of class-based View, that does not provide any method handlers such as . get() or . post() , and instead provides actions such as . list() and . create() .

Should I use APIView or ViewSet?

APIView allow us to define functions that match standard HTTP methods like GET, POST, PUT, PATCH, etc. Viewsets allow us to define functions that match to common API object actions like : LIST, CREATE, RETRIEVE, UPDATE, etc.

What is difference between View and ViewSet in Django?

While regular views act as handlers for HTTP methods, viewsets give you actions, like create or list . The great thing about viewsets is how they make your code consistent and save you from repetition. Every time you write views that should do more than one thing, a viewset is the thing that you want to go for.

How do I give permission in Django?

With Django, you can create groups to class users and assign permissions to each group so when creating users, you can just assign the user to a group and, in turn, the user has all the permissions from that group. To create a group, you need the Group model from django. contrib. auth.


2 Answers

In DRF documentation,

Note: The instance-level has_object_permission method will only be called if the view-level has_permission checks have already passed

Let's assume following permission about user object

  • List : staff only
  • Create : anyone
  • Retrieve : own self or staff
  • Update, Partial update : own self or staff
  • Destroy : staff only

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):         # Deny actions on objects if the user is not authenticated         if not request.user.is_authenticated():             return False          if view.action == 'retrieve':             return obj == request.user or request.user.is_admin         elif view.action in ['update', 'partial_update']:             return obj == request.user or request.user.is_admin         elif view.action == 'destroy':             return 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,) 

For Django 2.0 replace is_authenticated() with is_authenticated. The method has been turned into an attribute.

like image 87
Chemical Programmer Avatar answered Sep 21 '22 13:09

Chemical Programmer


You can create a custom permission class extending DRF's BasePermission.

You implement has_permission where you have access to the request and view objects. You can check request.user for the appropriate role and return True/False as appropriate.

Have a look at the provided IsAuthenticatedOrReadOnly class (and others) for a good example of how easy it is.

I hope that helps.

like image 33
Carlton Gibson Avatar answered Sep 20 '22 13:09

Carlton Gibson