Django Rest Framework: Permissions based on Object Attributes/Ownership

django, django-rest-framework, python

Solution

It depends on the rest of your code.

Normally DRF does object-level checks in a method called `check_object_permissions` in your `ViewSet` or permission backend.

This method gets called by (the default implementation) of `get_object` to check the permissions when any of the generics tries to get an object to work on.

If you use only Generic ViewSets and `/notes` is an `@action` this would be the easiest way.

If the object in these ViewSets is the `note`, I would suggest to build something looking similar (for example in a mixin you add to every ViewSet sitting under `/user/`). There are many different approaches to build nested resources and the routing for them.

Problem

I'd like to allow users to create and view resources, but only if: - They are staff OR - They are the 'owner' of the object they're trying to create/view I've got read-only permissions worked out fine, since users only have permission to get lists of objects when their primary key is used to generate the viewset. For example: GET /api/users/1/notes returns just the notes for the user with pk=1. However, in testing, I discovered that users can create an object 'owned' by another user by posting it to their own list endpoint. For example user 1 can send a POST to /api/users/1/notes, but specify the note data as {user: "http://host.tld/users/2/", text: "Look! I created a note in another person's account!"} I've got a fix below that seems to work fine, though I get the feeling I'm swimming against the current. Right now, within a custom permission, I create an instance of the object that would be created and check that its owner is the user making the request. Is there a cleaner way of doing this? Current Fix: ``` def has_permission(self, request, view): if request.user.is_staff: return True elif request.method in permissions.SAFE_METHODS: # check that the user is looking for their own list return request.user == User.objects.get(pk=view.kwargs['user_pk']) elif request.method not in permissions.SAFE_METHODS: # the user can create/modify the object if the new object's user == the request user # roundabout way of figuring this out... probably a better way user_path = request.POST['user'].split(request.get_host())[1] func = resolve(user_path).func kwargs = resolve(user_path).kwargs user_for_object = func.cls.model.objects.get(pk=kwargs['pk']) return user_for_object == request.user else: return False ```

Original source