Search code examples
django-rest-frameworkviewpermissionsdjango-users

How to set permissions classes for UserList in a secure way


With this implementation, the entire Userlist is visible for anyone in the API endpoint. If I change the permission classes to IsAdmin for instance, users does not have access to their own user. What would be a secure way to only return the current user? Is it possible to filter out just that user within the get_queryset() function?

class UserList(mixins.ListModelMixin, mixins.CreateModelMixin, generics.GenericAPIView):
    serializer_class = UserSerializer

    permission_classes = [IsAuthenticatedOrReadOnly]

    def get(self, request, *args, **kwargs):
        self.serializer_class = UserGetSerializer
        return self.list(request, *args, **kwargs)

    def post(self, request, *args, **kwargs):
        return self.create(request, *args, **kwargs)

    def get_queryset(self):
        qs = get_user_model().objects.all()

        if self.request.user:
            # Return the currently logged in user
            status = self.request.query_params.get("user", None)
            if status and status == "current":
                qs = get_user_model().objects.filter(pk=self.request.user.pk)
            return qs

Solution

  • To me it looks like you already accomplished your goal - you filter properly in get_queryset. Here's our implementation in Djoser which is the most popular REST Auth lib for DRF.

    Notice we have /me endpoint which is very common for asking for "yourself". For more info have a look at Designing URI for current logged in user in REST applications question.

    Anyway, everything user related is often weird. If I was trying to implement what you do I'd probably raise NotFound in get_queryset because in reality you don't need it.

    If your goal is to return user data when he requests GET user resource without pk, something like that should work

        def list(self, request, *args, **kwargs):
            serializer = self.serializer_class(self.request.user)
            return Response(serializer.data)
    

    and afaik create doesn't need a queryset. For username/mail duplication add validators in the serializer.