5

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
Hamidreza
  • 1,465
  • 12
  • 31
  • 1
    Can you provide the code which raises this error? – Ayush Gupta Aug 11 '21 at 06:06
  • @AyushGupta 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. – Hamidreza Aug 11 '21 at 06:13
  • Why not create a forbidden page template and instead of raising exceptions send them to the forbidden page template? – Ayush Gupta Aug 11 '21 at 06:15
  • @AyushGupta We do have a forbidden page, but it's the session middleware responsibility to redirect user to this page (as it is doing) just like how 404 error is handled. However, in that case we have to write another decorator to check permissions instead of using the provided `permission_requiered` decorator from django itself. :) – Hamidreza Aug 11 '21 at 06:19
  • Did you have a custom `handler403`? Try creating an empty `403.html` in one of your templates – Brian Destura Aug 15 '21 at 12:01

1 Answers1

3

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!!!

JPG
  • 82,442
  • 19
  • 127
  • 206