Open In App

Adding Permission in API – Django REST Framework

Improve
Improve
Improve
Like Article
Like
Save Article
Save
Share
Report issue
Report

There are many different scenarios to consider when it comes to access control. Allowing unauthorized access to risky operations or restricted areas results in a massive vulnerability. This highlights the importance of adding permissions in APIs.  

Django REST framework allows us to leverage permissions to define what can be accessed and what actions can be performed in a meaningful or common way. The permission checks always run at the beginning of every view. It uses the authentication information in the ‘request.user’ and ‘request.auth’ properties for each incoming request. If the permission check fails, then the view code will not run. 

Note: Together with authentication, permissions determine whether to grant or deny access for an incoming request. In this section, we will combine Basic Authentication with Django REST framework permission to set access control. You can refer Browsable API in Django REST Framework for Models, Serializers, and Views

Let’s dig deep into the Django REST framework permissions.

  • AllowAny
  • IsAuthenticated
  • IsAdminUser
  • IsAuthenticatedOrReadOnly
  • DjangoModelPermissions
  • DjangoModelPermissionsOrAnonReadOnly
  • DjangoObjectPermissions

AllowAny

The AllowAny permission class will allow unrestricted access, irrespective of whether the request was authenticated or unauthenticated. Here the permission settings default to unrestricted access

'DEFAULT_PERMISSION_CLASSES': [
   'rest_framework.permissions.AllowAny',
]

You don’t need to set this permission, but it is advised to mention it to make the intention explicit. Apart from mentioning it globally, you can set the permission policy on a per-view basis. If you are using the APIView class-based views, the code is as follows

Python3




from rest_framework.permissions import AllowAny
from rest_framework.response import Response
from rest_framework.views import APIView
  
class ClassBasedView(APIView):
    permission_classes = [AllowAny]
  
    def get(self, request, format=None):
        content = {
            'status': 'request was permitted'
        }
        return Response(content)


While using the @api_view decorator with function-based views, the code as follows

Python3




from rest_framework.decorators import api_view, permission_classes
from rest_framework.permissions import AllowAny
from rest_framework.response import Response
  
@api_view(['GET'])
@permission_classes([AllowAny])
def function_view(request, format=None):
    content = {
        'status': 'request was permitted'
    }
    return Response(content)


IsAuthenticated

The IsAuthenticated permission class denies unauthenticated users to use the APIs for any operations. This ensures APIs accessibility only to registered users. Let’s use the IsAuthenticated class in our RESTful web service. Here, we can set the permission policy on a per-view basis. Let’s import and add the permission class in our RobotDetail and RobotList class. The code is as follows:

from rest_framework.permissions import IsAuthenticated

Python3




class RobotDetail(generics.RetrieveUpdateDestroyAPIView):
    permission_classes = [IsAuthenticated]
    queryset = Robot.objects.all()
    serializer_class = RobotSerializer
    name = 'robot-detail'
  
  
class RobotList(generics.ListCreateAPIView):
    permission_classes = [IsAuthenticated]
    queryset = Robot.objects.all()
    serializer_class = RobotSerializer
    name = 'robot-list'


Let’s try to retrieve robots without providing any credentials. The HTTPie command is

http :8000/robot/

Output

Since we haven’t provided any authentication details, the API has rejected the request that retrieves the robot details. Now we will create a new user using Django’s interactive shell and try the HTTPie command with credentials.

Note: You can refer to the article Create a User for Django Using Django’s Interactive Shell

The HTTPie command is as follows:

http -a “sonu”:”sn@pswrd” :8000/robot/

Output

Let’s try an HTTPie command that creates a new robot entry. 

http -a “sonu”:”sn@pswrd” POST :8000/robot/ name=”IRB 1100″ robot_category=”Articulated Robots” currency=”USD” price=25000 manufacturer=”ABB” manufacturing_date=”2020-05-10 00:00:00+00:00″

Output

IsAdminUser

The IsAdminUser permission class will allow permission to users whose user.is_staff is True. This permission class ensures that the API is accessible to trusted administrators. Let’s make use of IsAdminUser permission class. Let’s import the permission class

from rest_framework.permissions import IsAdminUser

 You can replace the RobotDetail and RobotList class with the below code.

Python3




class RobotDetail(generics.RetrieveUpdateDestroyAPIView):
    permission_classes = [IsAdminUser]
    queryset = Robot.objects.all()
    serializer_class = RobotSerializer
    name = 'robot-detail'
  
  
class RobotList(generics.ListCreateAPIView):
    permission_classes = [IsAdminUser]
    queryset = Robot.objects.all()
    serializer_class = RobotSerializer
    name = 'robot-list'


Now let’s try by providing normal user credentials. The HTTPie command is

http -a “sonu”:”sn@pswrd” :8000/robot/

Output

You can notice the message saying “You do not have permission to perform this action”. This is because the user is not an administrator. Let’s provide our super admin credentials. The HTTPie command is

http -a “admin”:”admin@123″ :8000/robot/

Output

IsAuthenticatedOrReadOnly

The IsAuthenticatedOrReadOnly permission class allows unauthorized users to perform safe methods, whereas authenticated users can perform any operations. This class is useful when we need to set read permissions for anonymous users and read/write permissions for authenticated users. Let’s import the permission class

from rest_framework.permissions import IsAuthenticatedOrReadOnly

Now, you can replace the RobotDetail and RobotList class with the below code.

Python3




class RobotList(generics.ListCreateAPIView):
    permission_classes = [IsAuthenticatedOrReadOnly]
    queryset = Robot.objects.all()
    serializer_class = RobotSerializer
    name = 'robot-list' 
  
class RobotDetail(generics.RetrieveUpdateDestroyAPIView):
    permission_classes = [IsAuthenticatedOrReadOnly]
    queryset = Robot.objects.all()
    serializer_class = RobotSerializer
    name = 'robot-detail'


Let’s try to retrieve robot details without providing any credentials. The HTTPie command is as follows:

http :8000/robot/

Output

Now let’s try to create a new robot without providing credentials. The HTTPie command is as follows:

http POST :8000/robot/ name=”IRB 120″ robot_category=”Articulated Robots” currency=”USD” price=35000 manufacturer=”ABB” manufacturing_date=”2020-08-10 00:00:00+00:00″

Output

The IsAuthenticatedOrReadOnly permission class permits only safe operations for unauthenticated users. Let’s create a new robot by providing user credentials.

http -a “sonu”:”sn@pswrd” POST :8000/robot/ name=”IRB 120″ robot_category=”Articulated Robots” currency=”USD” price=35000 manufacturer=”ABB” manufacturing_date=”2020-08-10 00:00:00+00:00″

Output

DjangoModelPermissions

In DjangoModelPermissions class, the authentication is granted only if the user is authenticated and has the relevant model permissions assigned.  The model permissions are as follows

  • The user must have the add permission on the model to make POST requests.
  • The user must have the change permission on the model to make PUT and PATCH requests.
  • The user must have the delete permission on the model to make DELETE requests.

By default,  the class permits GET requests for authenticated users. DjangoModelPermissions class ties into Django’s standard django.contrib.auth model permissions. It must only be applied to views that have a .queryset property or get_queryset() method. 

You can import the DjangoModelPermissions class. 

from rest_framework.permissions import DjangoModelPermissions

Next, replace the RobotDetail and RobotList class with the below code.

Python3




class RobotList(generics.ListCreateAPIView):
    permission_classes = [DjangoModelPermissions]
    queryset = Robot.objects.all()
    serializer_class = RobotSerializer
    name = 'robot-list'
  
class RobotDetail(generics.RetrieveUpdateDestroyAPIView):
    permission_classes = [DjangoModelPermissions]
    queryset = Robot.objects.all()
    serializer_class = RobotSerializer
    name = 'robot-detail'


Let’s try to create a robot by providing user credentials. The HTTPie command as follows:

http -a “sonu”:”sn@pswrd” POST :8000/robot/ name=”IRB 140″ robot_category=”Articulated Robots” currency=”USD” price=35000 manufacturer=”ABB” manufacturing_date=”2021-01-10 00:00:00+00:00″

Output

You can notice, permission to create a robot is denied. This is because we haven’t set model-level permission for the given user. To set permission to add a robot, you can log in through the admin panel as a superuser and add permission by selecting the user under the Users section. Finally, save the changes. Sharing the screenshot below:

Let’s try again the same HTTPie command. 

http -a “sonu”:”sn@pswrd” POST :8000/robot/ name=”IRB 140″ robot_category=”Articulated Robots” currency=”USD” price=35000 manufacturer=”ABB” manufacturing_date=”2021-01-10 00:00:00+00:00″

Output

DjangoModelPermissionsOrAnonReadOnly

DjangoModelPermissionsOrAnonReadOnly permission class is the same as that of the DjangoModelPermissions class except it allows unauthenticated users to have read-only access to the API. 

Let’s import the DjangoModelPermissionsOrAnonReadOnly class.

from rest_framework.permissions import DjangoModelPermissionsOrAnonReadOnly

Now you can add the permission class to  RobotDetail and RobotList class. The code is as follows:

Python3




class RobotList(generics.ListCreateAPIView):
    permission_classes = [DjangoModelPermissionsOrAnonReadOnly]
    queryset = Robot.objects.all()
    serializer_class = RobotSerializer
    name = 'robot-list'
  
class RobotDetail(generics.RetrieveUpdateDestroyAPIView):
    permission_classes = [DjangoModelPermissionsOrAnonReadOnly]
    queryset = Robot.objects.all()
    serializer_class = RobotSerializer
    name = 'robot-detail'


Let’s try to retrieve robots without providing any credentials. The HTTPie command is as follows:

http :8000/robot/

Output

DjangoObjectPermissions

The DjangoObjectPermissions allows per-object permissions on models, which can set permissions for individual rows in a table (model instances). The user must be authenticated to grant authorization and should be assigned with relevant per-object permissions and relevant model permissions. To set relevant per-object permissions, we need to subclass the DjangoObjectPermissions and implement the has_object_permission() method.  The relevant model permissions are as follows:

  • The user must have the add permission on the model to make POST requests.
  • The user must have the change permission on the model to make PUT and PATCH requests.
  • The user must have the delete permission on the model to make DELETE requests.

With DjangoModelPermissions you can create custom model permissions by overriding DjangoObjectPermissions. You can check Customizing Object Level Permission in Django REST Framework.



Last Updated : 14 Sep, 2021
Like Article
Save Article
Previous
Next
Share your thoughts in the comments
Similar Reads