Search code examples
pythondjangodjango-middlewaredjango-sessions

Django prints error when PermissionDenied exception raises


In our project, we have used django SessionMiddleware to handle users sessions and it's working fine. The only problem here is when PermissionDenied exceptions happens, an error and its traceback will be printed out in the console! However as expected, by raising that exception, the 403 page will show to the user, but I think it doesn't seem rational, because the middleware here is handling the exception! Just like not found exception, I expect no error in the console. Is there anything wrong?!

here is the middleware settings:

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django_otp.middleware.OTPMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
    'axes.middleware.AxesMiddleware',
]

And here's the printed error:

Forbidden (Permission denied): /the/not_allowed/page
Traceback (most recent call last):
  File "/venv/lib/python3.8/site-packages/django/core/handlers/exception.py", line 34, in inner
    response = get_response(request)
  File "/venv/lib/python3.8/site-packages/django/core/handlers/base.py", line 115, in _get_response
    response = self.process_exception_by_middleware(e, request)
  File "/venv/lib/python3.8/site-packages/django/core/handlers/base.py", line 113, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "/usr/lib/python3.8/contextlib.py", line 75, in inner
    return func(*args, **kwds)
  File "/our_project/base/decorators.py", line 88, in wrapper
    return view_func(request, *args, **kwargs)
  File "/venv/lib/python3.8/site-packages/django/contrib/auth/decorators.py", line 21, in _wrapped_view
    return view_func(request, *args, **kwargs)
  File "/venv/lib/python3.8/site-packages/django/contrib/auth/decorators.py", line 20, in _wrapped_view
    if test_func(request.user):
  File "/venv/lib/python3.8/site-packages/django/contrib/auth/decorators.py", line 70, in check_perms
    raise PermissionDenied
django.core.exceptions.PermissionDenied

Solution

  • From this comment

    In this example the exception is raised from permission_required decorator in django.contrib.auth.decorators. I passed raise_exception=True to this decorator to make it raise exception instead of redirecting to login page

    So, it is clear that you have set raise_exception=True in your decorator.

    and from the doc

    If the raise_exception parameter is given, the decorator will raise PermissionDenied, prompting the 403 (HTTP Forbidden) view instead of redirecting to the login page.

    So, technically, when the conditions are not met, Django will raise an exception. But, depending on the value of 403.html, Django will show you either the plain 403 page or custom HTML response.


    I expect no error in the console. Is there anything wrong?

    There is nothing wrong here (or I couldn't see anything).

    So, if you want to omit the traceback from the console, you may need to write a error handling middleware to handle this exception.

    # error handling middleware
    from django.core.exceptions import PermissionDenied
    from django.shortcuts import render
    
    
    class PermissionDeniedErrorHandler:
        def __init__(self, get_response):
            self.get_response = get_response
    
        def __call__(self, request):
            response = self.get_response(request)
            return response
    
        def process_exception(self, request, exception):
            # This is the method that responsible for the safe-exception handling
            if isinstance(exception, PermissionDenied):
                return render(
                    request=request,
                    template_name="your_custom_403.html",
                    status=403
                )
            return None

    Note: Do not forgot to bind this middleware in your MIDDLEWARE settings.

    and thus, you will not get any error tracebacks in the console.

    Cheers!!!