smilebomb
smilebomb

Reputation: 5483

django rest framework object permission

I don't seem to be triggering the has_object_permission function.

class MyPermission(DefaultPermission):
    def has_permission(self, request, view):
        return request.user.is_superuser

    def has_object_permission(self, request, view, obj):
        import pdb;pdb.set_trace()
        return request.user == obj.submitter

Going to mydomain.com/api/mymodel/100, this should be considered accessing the object, correct? I am viewing object 100 here. Why isn't my trace() being picked up?

View

class MyModelViewSet(viewsets.ModelViewSet):
    serializer_class = MyModelSerializer
    permission_classes = (MyPermission,)

    def get_queryset(self):
        queryset = MyModel.objects.all()
        return queryset

Upvotes: 1

Views: 1763

Answers (2)

theguru42
theguru42

Reputation: 3378

In a DRF generic views check_permissions(request) is always called in every request since it is inside the dispatch method.

check_permissions method collects all the permissions from the permission classes and checks each permission. If any permission return false the method raises an exception.

So if your has_permission method returns false has_object_permission is not called.

check_object_permissions(request, obj) calls has_object_permission method on each permission class. It is called inside get_object method.

So the rule is if has_permission for all permission classes returns true then and only then has_object_permission is checked.

class MyPermission(DefaultPermission):

    def has_permission(self, request, view):
        # check if the request is for a single object
        if view.lookup_url_kwarg in view.kwargs:
            return True
        return request.user.is_superuser

    def has_object_permission(self, request, view, obj):
        import pdb;pdb.set_trace()
        return request.user == obj.submitter

Upvotes: 4

zaidfazil
zaidfazil

Reputation: 9235

Permission won't work because check_object_permissions method is called only in get_object function. So you should call that function in your views:

def check(self, request, pk=None):
    obj = self.get_object()

Or you could add permissions directly in the decorator,

Example

    class MyViewSet(ModelViewSet):
        queryset = MyModel.objects.all()
        ....
        permission_classes = (MyPermission, )


        @detail_route(methods=['GET', ])
        def custom(self, request, pk=None):
            my_obj = self.get_object() # do this and your permissions shall be checked
            return Response('whatever')

From the docs(django-rest-framework)

Note: The instance-level has_object_permission method will only be called if the view-level has_permission checks have already passed. Also note that in order for the instance-level checks to run, the view code should explicitly call .check_object_permissions(request, obj). If you are using the generic views then this will be handled for you by default.

Upvotes: 1

Related Questions