DRF Viewset remove permission for detail route
Asked Answered
U

2

8

I have a basic Viewset:

class UsersViewSet(viewsets.ModelViewSet):
    permission_classes = (OnlyStaff,)
    queryset = User.objects.all()
    serializer_class = UserSerializer

It is bind to the /api/users/ endpoint. I want to create a user profile page, so I need only a particular user, so I can retrieve it from /api/users/<id>/, but the problem is that I want /api/users/<id>/ to be allowed to anyone, but /api/users/ to keep its permission OnlyStaff, so no one can have access to the full list of users.

Note: Perhaps it's not such a good implementation, since anyone could brute force the data incremeting the id, but I'm willing to change it from <id> to <slug>.

How can I delete the permission from detail route?

Thanks in advance.

Uproot answered 27/9, 2018 at 19:25 Comment(0)
S
18

Override the get_permissions() method as below

from rest_framework.permissions import AllowAny


class UsersViewSet(viewsets.ModelViewSet):
    permission_classes = (OnlyStaff,)
    queryset = User.objects.all()
    serializer_class = UserSerializer

    def get_permissions(self):
        if self.action == 'retrieve':
            return [AllowAny(), ]        
        return super(UsersViewSet, self).get_permissions()
Seko answered 28/9, 2018 at 5:19 Comment(1)
Thanks once again. So the effective way to override the permission for a specific method is to override get_permissions(), not the attribute permission_classes.Uproot
I
2

It would help if you posted the permission class.

But going off what you posted, it appears that only staff users can have access to the endpoints bound to that viewset. Meaning no other user type/role can access those endpoints.

Going off your question, it seems like you want to setup a IsOwnerOrStaffOrReadOnly permission and over ride the list route function of the ModelViewSet and replace permission_classes and then call super

class UsersViewSet(viewsets.ModelViewSet):
    permission_classes = (IsOwnerOrStaffOrReadOnly,)
    queryset = User.objects.all()
    serializer_class = UserSerializer

def list(self, request, *arg, **kwargs):
   self.permission_classes = (OnlyStaffCanReadList,)
   super(UsersViewSet, self).list(request, *args, **kwargs)  // python3 super().list(request, *args, **kwargs)

is Owner object permission class

class IsOwnerOrStaffOrReadOnly(permissions.BasePermission):

    def has_object_permission(self, request, view, obj):
        # Read permissions are allowed to any request,
        # so we'll always allow GET, HEAD or OPTIONS requests.
        if request.method in permissions.SAFE_METHODS:
            return True

        if request.user.role == 'staff': 
           return True
        # Instance must have an attribute named `owner`.
        return obj.owner == request.user

only staff can read permission class

class OnlyStaffCanReadList(permissions.BasePermission):

    def has_object_permission(self, request, view, obj):
        if request.user.role == 'Staff': 
           return True
        else:
           return False

as provided in the comments, your user model must have the owner role. if you are using the django user model you can just do a obj.id == request.user.id comparison

Insomnolence answered 27/9, 2018 at 19:43 Comment(2)
overriding list() method does not work, it does not get executed. IsOwnerOrStaffOrReadOnly gets executed in both endpoints.Uproot
@Uproot very odd. i made a jdoodle to validate the over riding of a tuple in an inherited class to validate that it should be the case. check this out jdoodle.com/a/GQg. After looking further it seems there may be an issue with overriding perms on a single route. Have a look at this. #25284297Insomnolence

© 2022 - 2024 — McMap. All rights reserved.