Django - Custom Object Permission View

Viewed 586

I'm trying to give shop owner permissions for a view. So I made a file in which I created different permissions. In my permission I first of all check if the user was logged in with a has_permission function. I am now trying to determine if a user actually owns the shop with the has_object_permission function. Unfortunately, I don't feel that my function was performed correctly. I can always, despite my permission, make a request from any account, shop owner or not.

Here are the models I use:

models.py

class Shop(models.Model):
    name = models.CharField(max_length=255)
    category = models.ForeignKey(ShopCategory, on_delete=models.SET_NULL, null=True, blank=True)
    description = models.TextField(blank=True, null=True)
    path = models.CharField(max_length=255, unique=True, null=True, blank=True) # Set a null and blank = True for serializer
    mustBeLogged = models.BooleanField(default=False)
    deliveries = models.FloatField(default=7)

    def __str__(self):
        return self.name

class UserShop(models.Model):
    user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
    shop = models.ForeignKey(Shop, on_delete=models.CASCADE)

    def __str__(self):
        return f"{self.user.name} {self.user.surname} - {self.shop.name}"

Here are my permissions :

utils.py

class IsOwner(BasePermission):
    """
    Check if the user who made the request is owner.
    Use like that : permission_classes = [IsOwner]
    """
    def has_permission(self, request, view):
        return request.user and request.user.is_authenticated
    def has_object_permission(self, request, view, obj):
        try:
            user_shop = UserShop.objects.get(user=request.user, shop=obj)
            return True
        except:
            return False

class OwnerView(APIView):
    """
    Check if a user is owner
    """
    permission_classes = (IsOwner,)

Here is my view :

views.py

class ShopDetail(OwnerView):
    """Edit ou delete a shop"""
    def put(self, request, path):
        """For admin or shop owner to edit a shop"""
        shop = get_object_or_404(Shop, path=path)
        serializer = ShopSerializer(shop, data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data)
        return Response(serializer.errors)

And here is my serializer :

serializers.py

class ShopSerializer(serializers.ModelSerializer):    
    class Meta:
        model = Shop
        fields = '__all__'

Thank you in advance for your help

1 Answers

As mentioned in the documentation for Custom permissions:

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. (Function-based views will need to check object permissions explicitly, raising PermissionDenied on failure.)

You have implemented the put method yourself and get the object yourself instead of using the get_object method (which calls check_object_permissions itself) so check_object_permissions is never called. Instead you should set lookup_field as path in your view class and use get_object:

class ShopDetail(OwnerView):
    """Edit ou delete a shop"""
    queryset = Shop.objects.all()
    lookup_field = 'path'
    
    def put(self, request, path):
        """For admin or shop owner to edit a shop"""
        shop = self.get_object()
        serializer = ShopSerializer(shop, data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data)
        return Response(serializer.errors)

Also to do this OwnerView should inherit from GenericAPIView:

from rest_framework.generics import GenericAPIView


class OwnerView(GenericAPIView):
    """
    Check if a user is owner
    """
    permission_classes = (IsOwner,)
Related