Click to jump to the REST-Framework column directory

Authentication and permissions are used in combination to determine whether a request returns data or rejects requested data.

Determine the permissions

Before running the view should check each request permissions, if the check fails, will trigger exceptions. PermissionDenied abnormalities, and will not run view theme code.

When the check fails, return the 401 or 403 status code according to the following rules:

  • The request was successfully authenticated but permission denied and will be returnedHTTP403Forbidden response
  • The request was not authenticated successfully. The highest priority authentication class is not usedWWW-AuthenticateThe header will returnHTTP403Forbidden response
  • The request was not authenticated successfully, the highest priority authentication class did useWWW-AuthenticateThe header will return onehttp401Unauthorized response with appropriateWWW-AuthenticateThe header

Such as:

def get_object(self) :
    obj = get_object_or_404(self.get_queryset(), pk=self.kwargs["pk"])
    self.check_object_permissions(self.request, obj)
    return obj
Copy the code

Note that, with the exception of DjangoObjectPermissions, the permission classes provided in rest_framework.Permission do not implement the methods required to check object permissions.

If you want to use the provided permission class to check object permissions, you must subclass it and implement the has_object_Permission () method described in the Custom permissions section.

For performance reasons, when a list of objects is returned, the generic view does not automatically apply object-level permissions to each instance in the query set.

In general, when you use object-level permissions, you also need to filter the query set appropriately to ensure that users can only see the instances they are allowed to see.

Setting permission Policies

In settings.py in a Django project, you can use global Settings to set permission access policies:

REST_FRAMEWORK = {
    'DEFAULT_PERMISSION_CLASSES': [
        'rest_framework.permissions.IsAuthenticated']},Copy the code

Of course, if you do not specify this, the default is unlimited access:

'DEFAULT_PERMISSION_CLASSES': [
   'rest_framework.permissions.AllowAny',]Copy the code

You can also set policies for individual ApiViews:

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

class ExampleView(APIView) :
    permission_classes = [IsAuthenticated]

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

Or use a decorator to accessorize the policy:

from rest_framework.decorators import api_view, permission_classes
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response

@api_view(['GET'])
@permission_classes([IsAuthenticated])
def example_view(request, format=None) :
    content = {
        'status': 'request was permitted'
    }
    return Response(content)
Copy the code

Note: When you set a new permission class through the class property or decorator, you are telling the view to ignore the default list set on the settings.py file.

If they inherit from rest_framework. Permissions. BasePermission, you can use the standard Python bitwise operators to combine permissions, such as IsAuthenticatedOrReadOnly can write like this:

from rest_framework.permissions import BasePermission, IsAuthenticated, SAFE_METHODS
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)
Copy the code

Note: it supports & (and), | (or) and ~ (not).

IsAuthenticated

Permission will be granted if the user has been authenticated and has passed authentication.

IsAdminUser

The license class is authenticated after the user instance user.is_staff is True in case the license will be allowed.

IsAuthenticatedOrReadOnly

Requests to unauthorized users are allowed only if the request method is one of the security methods: GET, HEAD, and OPTIONS.

DjangoModelPermissions

This permission class is associated with Django’s standard Django.contrib. auth model permissions, which can only be applied to views of the.queryset property and are granted only if the user is authenticated and assigned the associated model permissions.

  • POSTRequests require user pairsaddModel has permissions
  • PUT,PATCHThe request asks the user tochangeModel has permissions
  • DELETERequests require user pairsdeleteModel has permissions

Default behavior can also be overridden to support custom model permissions, such as view model permissions that you might want to include for GET requests.

To use custom model permissions, override DjangoModelPermissions and set the.perms_map attribute. Refer to the source code for more information.

If you use this permission with a view that overrides the get_querySet () method, there may be no QuerySet attribute on the view, in which case we recommend that you also use sentinel QuerySet to mark the view so that the class can determine the required permissions, for example:

queryset = User.objects.none()  # Required for DjangoModelPermissions
Copy the code

DjangoModelPermissionsOrAnonReadOnly

Similar to DjangoModelPermissions, but also allows unauthenticated users to have read-only access to the API.

DjangoObjectPermissions

This permission class is associated with Djangos standard object permission framework, which allows object-specific permissions on models. To use this permission class, you also need to add a permission back end that supports object-level permissions, such as Django-Guardian.

As with DjangoModelPermissions, this permission must only apply to views that have the.querySet attribute or the.get_queryset() method, and authorization is granted only after the user has authenticated and assigned the associated permissions for each object and the associated model permissions.

  • POSTThe request asks the user toaddModel instances have permissions
  • PUT,PATCHThe request asks the user tochangeModel instances have permissions
  • DELETEThe request asks the user todeleteModel instances have permissions

Note that DjangoObjectPermissions does not require the Django-Guardian package and should support other object-level backends as well.

As with DjangoModelPermissions, you can use custom model permissions by overriding DjangoObjectPermissions and setting the.perms_map attribute, refer to the source code for details.

Note: If you need object-level view permissions for GET, HEAD, and OPTIONS requests and are using Django-Guardian as an object-level permission back end, Need to consider to use djangorestframework – the guardian package provides DjangoObjectPermissionsFilter, it ensures that a list of the endpoint returns only results, including the user object with the proper view permissions.

Custom permissions

To implement custom permissions, override BasePermission and implement one or both of the following methods:

  • .has_permission(self, request, view)
  • .has_object_permission(self, request, view, obj)

Request access should be granted if the method should return True, otherwise return False.

If you need to test, if a request is a read or write operation, you should check SAFE_METHODS on constants, which is a tuple containing ‘GET’, ‘OPTIONS’, and ‘HEAD’, for example:

if request.method in permissions.SAFE_METHODS:
    # Check permissions for read-only request
else:
    # Check permissions for write request
Copy the code

Note: The instance-level has_object_Permission method is called only if the view-level has_permission check has passed. Note also that in order to run instance-level checks, View code should explicitly call.check_object_permissions(request, Obj), if you are using a generic view, this will be handled for you by default (function-based views will need to explicitly check object permissions, raising PermissionDenied on failure).


If the test fails, the custom permission will raise the PermissionDenied exception. To change the error message associated with the exception, implement the message property directly on the custom permission. Otherwise, the default_detail property of PermissionDenied will be used. Also, to change the code identifier associated with the exception, implement the code attribute directly on your custom permission. Otherwise, the default_codeFrom property of PermissionDenied is used.

example

Here is an example of a permission class that checks the IP address of an incoming request against a block list and rejects the request if the IP is blocked:

from rest_framework import permissions

class BlocklistPermission(permissions.BasePermission) :
    """ Global permission check for blocked IPs. """

    def has_permission(self, request, view) :
        ip_addr = request.META['REMOTE_ADDR']
        blocked = Blocklist.objects.filter(ip_addr=ip_addr).exists()
        return not blocked
Copy the code

In addition to global permissions that run on all incoming requests, you can create object-level permissions that run only on operations that affect specific object instances, such as:

class IsOwnerOrReadOnly(permissions.BasePermission) :
    """ Object-level permission to only allow owners of an object to edit it. Assumes the model instance has an `owner` attribute. """

    def has_object_permission(self, request, view, obj) :
        # Read permissions are allowed to any request,
        # so we'll always allow GET, HEAD or OPTIONS requests.
        if request.method in permissions.SAFE_METHODS:
            return True

        # Instance must have an attribute named `owner`.
        return obj.owner == request.user
Copy the code

Note that generic views will check the appropriate object-level permissions, but if you are writing your own custom views, you need to make sure you check the object-level permissions yourself, by calling self.check_object_permissions(request, Obj), if any object-level permission checks fail, the call will throw an APIException, otherwise it will simply return.

Also note that generic views will only check object level permissions for views that retrieve a single model instance, and if object level filtering for list views is required, query sets will need to be filtered separately. See the Filtering documentation for more details.