Search code examples
django-rest-frameworkdjango-viewsdjango-permissions

DRF custom permission: allow 'retrieve', deny 'list'


tldr: I want to make a permission, that allows 'retrieve' action but not 'list'.

I'm writing REST API using Django Rest Framework and met a problem creating custom permission (for View from viewsets.ModelViewSet)

Got stuck creating an endpoint that should return site users:

-Admin should have permission to access every method

-Regular user should only be able to 'retrieve' and 'patch' his own account

I can't find a way to differ GET request 'list' from 'retrieve'. Checking for request.method == GET allows both of them, but I don't want user to be able to list all other users.

    from rest_framework.permissions import BasePermission

    ```

    def is_superuser(request):
         return request.user.is_superuser
        
    ```

    class IsAdminOrReadOnlyOwner(BasePermission):
        def has_permission(self, request, view):
            if is_superuser(request):
                return True
            else:
                return request.method in ['GET', 'PATCH']
        
        def has_object_permission(self, request, view, obj):
            is_owner_and_safe = int(request.user.id) == obj.id and request.method in ['GET', 'PATCH']
            return is_owner_and_safe or is_superuser(request)

Is there a way to do this, still using ModelViewSet?


Solution

  • When checking permissions, Django Rest passes the view to the permission class methods, and from the view you can determine the action. You can see an example for has_object_permission and RetrieveAPIView in the source code here.

    You can use view.action to identify the action from your permission class methods, for example:

    def has_permission(self, request, view):
        if is_superuser(request):
            return True
        else:
            return view.action in ['retrieve', 'update']
    

    You can check for view.action in has_object_permission to achieve what you want.