Official original text link this series of articles github address reprint please indicate the source

permissions

Along with Authentication and Throttling, Permission determines whether access requests should be accepted or denied.

Permission checking is always run at the very beginning of the view, before any other code is allowed to proceed. Permission checking typically uses authentication information in the Request. user and Request. auth attributes to determine whether incoming requests are allowed.

Permissions are used to grant or deny different categories of users access to different parts of the API.

The simplest permission is to allow access to authenticated users and deny access to unauthenticated users. This corresponds to the IsAuthenticated class in the REST Framework.

Slightly looser permissions allow full access for authenticated users and read-only access for unauthenticated users. This corresponds to REST IsAuthenticatedOrReadOnly class in the framework.

How to determine permissions

Permissions in the REST Framework are always defined as a list of permission classes.

Before running the body of the view, check each permission in the list. If any permission check fails, will cause exceptions. PermissionDenied or exceptions. NotAuthenticated abnormalities, and the main body of view will not run again.

When the permission check fails, an “403 Forbidden” or “401 Unauthorized” response is returned based on the following rules:

  • The request was successfully authenticated, but permissions were denied.– The 403 Forbidden response is returned.
  • The request was not authenticated successfully and the highest priority authentication class was not addedWWW-AuthenticateThe header.– The 403 Forbidden response is returned.
  • The request was not authenticated successfully, but the highest priority authentication class was addedWWW-AuthenticateThe header.– An HTTP 401 Unauthorized response is returned with an appropriateWWW-AuthenticateThe header.

Object level permissions

REST Framework permissions also support object-level permissions. Object-level permissions are used to determine whether a user is allowed to operate on a particular object, which is typically a model instance.

When.get_object() is called, object-level permissions are exercised by the REST Framework’s generic view. With the view level authority, if the user is not allowed for a given object, will trigger exceptions. PermissionDenied anomalies.

If you are writing your own view and want to enforce object-level permissions, or if you override the get_object method on a generic view, you will need to explicitly call the.check_object_permissions(request, obj) method when you retrieve the object.

This will raise a PermissionDenied or NotAuthenticated exception, or return only if the view has the appropriate permissions.

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

Restrictions on object-level permissions

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

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

Setting permission Policies

The default permission policy can be set globally using DEFAULT_PERMISSION_CLASSES setting. For example.

REST_FRAMEWORK = {
    'DEFAULT_PERMISSION_CLASSES': (
        'rest_framework.permissions.IsAuthenticated')},Copy the code

If not specified, this setting defaults to allow unrestricted access:

'DEFAULT_PERMISSION_CLASSES': (
   'rest_framework.permissions.AllowAny'.)Copy the code

You can also set authentication policies on views based on the APIView class.

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 on a function view based on the @API_view decorator.

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: Default Settings in settings.py are ignored when you set new permission classes via class properties or decorators.


API reference

AllowAny

The AllowAny permission class will allow unrestricted access regardless of whether the request is authenticated or unauthenticated.

You can achieve the same result by setting an empty list or tuple for permissions, but you’ll find that using this permission makes the intent clearer.

IsAuthenticated

The IsAuthenticated permission class denies access to any unauthenticated user.

Use this permission if you want the API to be accessible only by registered users.

IsAdminUser

IsAdminUser permission allows access only for user.is_staff True, any other users will be denied.

Use this permission if you want the API to be accessible only by partially trusted administrators.

IsAuthenticatedOrReadOnly

IsAuthenticatedOrReadOnly allows authenticated users to perform any request. Unauthenticated users can only request “safe” methods: GET, HEAD, or OPTIONS.

You can use this if you want the API to allow anonymous users to have read permission and only to perform write permission on authenticated users.

DjangoModelPermissions

This permission class is bound to Django’s standard Django.contrib. auth model permissions. This permission applies only to views that have a set of.querySet attributes. Access is granted only if the user is authenticated and assigned the relevant model permissions.

  • POSTThe request requires that the user have theaddPermissions.
  • PUTPATCHThe request requires that the user have thechangePermissions.
  • DELETEThe request requires that the user have thedeletePermissions.

The default behavior can also be rewritten to support custom model permissions. For example, you might want to include view model permissions for GET requests.

To customize model permissions, inherit DjangoModelPermissions and set the.perms_map property. See the source code for more information.

Use not includequerysetProperty view.

If you use this permission with a view that overrides the get_querySet () method, there may be no QuerySet attribute on the view. In this case, we recommend using the Sentinel query set to tag the view so that the class can determine the required permissions. Such as:

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 tied to Djangos standard object permission framework, which allows permission validation for each model object. 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 can only be applied to views that have a.querySet attribute or a.get_queryset() method. Access is granted only after the user is authenticated and has the relevant permissions for each object and the relevant model permissions.

  • POSTThe request requires that the user have theaddPermissions.
  • PUTPATCHThe request requires that the user have thechangePermissions.
  • DELETEThe request requires that the user have thedeletePermissions.

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

As with DjangoModelPermissions, you can customize model permissions by inherits DjangoObjectPermissions and setting the.perms_map attribute. See the source code for more information.


Note: if you need to GET a GET, HEAD, and the OPTIONS of the request object level view permissions, also need to consider to add DjangoObjectPermissionsFilter class, to ensure that the list of endpoint returns only contains the object that the user has to check the permissions.



Custom permissions

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

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

The method should return True if the request is granted access, False otherwise.

If you need to test whether a request is a read or write operation, you should check the request method against the constant SAFE_METHODS, which is a tuple containing ‘GET’, ‘OPTIONS’, and ‘HEAD’. Such as:

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. Also note that in order to run instance-level checks, the 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 PermissionDenied. To change error messages associated with exceptions, implement the Message property directly on your custom permissions. Otherwise, the default_detail property of PermissionDenied will be used.

from rest_framework import permissions

class CustomerAccessPermission(permissions.BasePermission):
    message = 'Adding customers not allowed.'

    def has_permission(self, request, view):.Copy the code

Take a chestnut

Here is an example of a permission class that matches the IP address of an incoming request against a blacklist and rejects the request if the IP is blacklisted.

from rest_framework import permissions

class BlacklistPermission(permissions.BasePermission):
    """ Global permission check for blacklisted IPs. """

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

In addition to global permissions that run on all incoming requests, you can create object-level permissions that are only executed for operations that affect a particular object instance. 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 for appropriate object-level permissions, but if you are writing your own custom views, you need to be sure to check your own object-level permissions. You can do this by calling self.check_object_permissions(request, obj) from the view after you have an object instance. If any object-level permission checks fail, this call raises the appropriate APIException, otherwise it simply returns.

Also note that generic views will only check object level permissions for views of a single model instance. If you need object-level filtering for list views, you need to filter query sets separately.