Snackoverflow
Snackoverflow

Reputation: 828

How to return a 403 in a ViewSet instead of 404

I'm working on an API and have this ViewSet:

class ProjectViewSet(viewsets.ModelViewSet):
    # API endpoint that allows projects to be viewed or edited.
    queryset = Project.objects.all()
    serializer_class = ProjectSerializer
    authentication_classes = used_authentication_classes
    permission_classes = (IsOwner,)

    @detail_route(methods=['get'])
    def functions(self, request, pk=None):
        project = self.get_object()
        if project is None:
            return Response({'detail': 'Missing project id'}, status=404)
        return Response([FunctionSerializer(x).data for x in Function.objects.filter(project=project)])

A permission system is attached to this API. The permissions work fine for a single resource. But when I call api/projects which should return all of the projects the user has access to, it does in fact return all of the projects, regardless whether the user should be able to GET a certain project in the list or not.

So I overwrote the get_queryset method to only return the projects the user has access to:

def get_queryset(self):
    if self.request.user.is_superuser or self.request.user.is_staff:
        return Project.objects.all()
    else:
        return Project.objects.filter(user=self.request.user.user)

This works, but now the API returns a 404 instead of a 403 when I ask for a specific resource I don't have access to.

I understand why, because the PK from the resource I try to get is undefined since I only return projects the user has access to. What I don't understand is how to fix this.

Does anyone know how I can make it return a 403, or maybe an idea towards where I should look?

Upvotes: 3

Views: 632

Answers (1)

user8060120
user8060120

Reputation:

as @Alasdair say the 404 is a deliberate choice, but if you still want to get 403 you can try:

def get_queryset(self):
    user = self.request.user
    allow_all = user.is_superuser or user.is_staff
    if self.action == 'list' and not allow_all:
        return Project.objects.filter(user=user)
    return Project.objects.all()

Upvotes: 4

Related Questions