Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django REST Framework - How to return 404 error instead of 403

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.

like image 861
Samuel Avatar asked Feb 21 '18 10:02

Samuel


People also ask

How do I return a 404 response in Django?

The Http404 exceptionIf you raise Http404 at any point in a view function, Django will catch it and return the standard error page for your application, along with an HTTP error code 404. In order to show customized HTML when Django returns a 404, you can create an HTML template named 404.

How do I create a custom exception in Django REST framework?

Custom exception handlingThe exception handler function should either return a Response object, or return None if the exception cannot be handled. If the handler returns None then the exception will be re-raised and Django will return a standard HTTP 500 'server error' response.

How does Django handle Page Not Found error?

You're seeing this error because you have DEBUG = True in your Django settings file. Change that to False, and Django will display a standard 404 page. Let's do that by turning off Django debug mode. For this, we need to update the settings.py file.

Can we use Django REST framework without Django?

REST framework requires the following: Python (3.6, 3.7, 3.8, 3.9, 3.10) Django (2.2, 3.0, 3.1, 3.2, 4.0, 4.1)


2 Answers

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)
like image 59
krs Avatar answered Nov 10 '22 21:11

krs


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
like image 34
JPG Avatar answered Nov 10 '22 21:11

JPG