6

My API allows access (any request) to certain objects only when a user is authenticated and certain other conditions are satisfied.

class SomethingViewSet(viewsets.ModelViewSet):
    queryset = Something.objects.filter(query_hiding_non_authorized_objects)
    serializer_class = SomethingSerializer

    permission_classes = (permissions.IsAuthenticated, SomePermission)

If a user attempts to view a non-authorized object DRF returns a 403 error, however, this gives away that an object with the requested id exists. How can I return 404 errors in these cases?

Note: I also use a custom queryset to hide non-authorized objects from being listed.

4
  • If you already hide them in get_queryset then just removing the permission will net you a 404 instead. Commented Feb 21, 2018 at 10:19
  • Haha, great, problem solved in the easiest way possible, would you mind creating an answer so I can accept it? Commented Feb 21, 2018 at 10:42
  • sure, it's done! Commented Feb 21, 2018 at 10:52
  • I actually have the opposite behavior. I use ModelViewSet. If I had a permission checking the user is the owner of the object, then it return 403, as expected. But if I narrow the get_queryset to only display objects owned, then I have, as expected, a 404 on the List retrieval. But also a 404 when trying to access an existing object not owned. On the last one, I'd expect a 403... Commented Oct 11, 2018 at 19:03

2 Answers 2

6

As you already hide them in get_queryset just removing the permission will net you a 404 instead.

edit: you can also override the permission_denied method in your View class to throw another exception, this is the default implementation:

def permission_denied(self, request, message=None):
    """ If request is not permitted, determine what kind of exception to raise.
    """
    if request.authenticators and not request.successful_authenticator:
        raise exceptions.NotAuthenticated()
    raise exceptions.PermissionDenied(detail=message)
Sign up to request clarification or add additional context in comments.

2 Comments

I noticed a problem with this approach: if the queryset includes an object but the permission restricts what operations you can perform on it, this approach will not work and one would have to, e.g., add the restrictions to the query set method
See my edit, you can try override the function that handles permission denied
3

I think you can use custom exception handler in this case,

from rest_framework.views import exception_handler

def custom_exception_handler(exc, context):
    response = exception_handler(exc, context)

    if response.status_code == 403:            
        response.status_code = 404

    return response

In settings:

REST_FRAMEWORK = {
    'EXCEPTION_HANDLER': 
'my_project.my_app.utils.custom_exception_handler'
}



Second Method

from rest_framework.exceptions import APIException


class CustomForbidden(APIException):
    status_code = status.HTTP_404_NOT_FOUND


class CustomPermission(permissions.BasePermission):
    def has_permission(self, request, view):
        if not_allowed:
            raise CustomForbidden

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.