djangodjango-rest-frameworkdjango-permissions

Django (drf) dynamic permissions from BasePermission


I want to have easy way to check if somebody is owner or admin of post, proposal and so on he's trying to edit \ delete.

So, every time I use IsAuthenticated permission and in the method of ModelViewSet I get instance and check if instance.author or sometimes instance.owner is the user who requested it (request.user == instance.owner on some objects it's request.user == instance.author).

Question

The main question is: how can I create permission class that can check this kind of ownership with dynamic user attribute name on instance?

One of mine solutions (not the best, i think)

I've created function that take user attribute instance name returns permission class:

def is_owner_or_admin_permission_factory(owner_prop_name):
    class IsOwnerOrAdmin(BasePermission):
        def has_permission(self, request, view, *args, **kwargs):
            instance = view.get_object()
            try:
                owner = getattr(instance, owner_prop_name)
            except AttributeError:
                return False
            return (
                request.user and request.user.id and (owner == request.user or request.user.is_staff)
            )

    return IsOwnerOrAdmin

Solution

  • I have also been frustrated over the same exact problem for days, and I've managed to find a suitable work-around (at least for me, of course), when dealing with multiple models with different lookup names for user attributes.

    The work-around was something like this, in the ModelViewSet defined a separate attribute user_lookup_kwarg in the view, which could be used for checking the appropriate permissions.

    Eg,

    class YourViewSet(viewsets.ModelViewSet):
        queryset = YourModel.objects.all()
        serializer_class = YourSerializer
        user_lookup_kwarg = 'user' #or 'account/created_by' whatever.
    

    Now, your permission_class would be somewhat like this,

    class CustomPermission(BasePermission):
    
        def has_object_permission(self, request, view, obj):
            try:
                return request.user.is_superuser or getattr(obj, view.user_lookup_kwarg) == request.user
            except:
                return False
            return request.user.is_superuser
    

    You only just need to override has_object_permission() method to check instance level permissions.