diff --git a/debug_toolbar/_compat.py b/debug_toolbar/_compat.py new file mode 100644 index 000000000..0e0ab8c1b --- /dev/null +++ b/debug_toolbar/_compat.py @@ -0,0 +1,10 @@ +try: + from django.contrib.auth.decorators import login_not_required +except ImportError: + # For Django < 5.1, copy the current Django implementation + def login_not_required(view_func): + """ + Decorator for views that allows access to unauthenticated requests. + """ + view_func.login_required = False + return view_func diff --git a/debug_toolbar/panels/history/views.py b/debug_toolbar/panels/history/views.py index 3fcbd9b32..fb6e28c93 100644 --- a/debug_toolbar/panels/history/views.py +++ b/debug_toolbar/panels/history/views.py @@ -1,11 +1,13 @@ from django.http import HttpResponseBadRequest, JsonResponse from django.template.loader import render_to_string +from debug_toolbar._compat import login_not_required from debug_toolbar.decorators import render_with_toolbar_language, require_show_toolbar from debug_toolbar.panels.history.forms import HistoryStoreForm from debug_toolbar.toolbar import DebugToolbar +@login_not_required @require_show_toolbar @render_with_toolbar_language def history_sidebar(request): @@ -37,6 +39,7 @@ def history_sidebar(request): return HttpResponseBadRequest("Form errors") +@login_not_required @require_show_toolbar @render_with_toolbar_language def history_refresh(request): diff --git a/debug_toolbar/panels/sql/views.py b/debug_toolbar/panels/sql/views.py index 4b6ced9da..b3ad6debb 100644 --- a/debug_toolbar/panels/sql/views.py +++ b/debug_toolbar/panels/sql/views.py @@ -2,6 +2,7 @@ from django.template.loader import render_to_string from django.views.decorators.csrf import csrf_exempt +from debug_toolbar._compat import login_not_required from debug_toolbar.decorators import render_with_toolbar_language, require_show_toolbar from debug_toolbar.forms import SignedDataForm from debug_toolbar.panels.sql.forms import SQLSelectForm @@ -17,6 +18,7 @@ def get_signed_data(request): @csrf_exempt +@login_not_required @require_show_toolbar @render_with_toolbar_language def sql_select(request): @@ -47,6 +49,7 @@ def sql_select(request): @csrf_exempt +@login_not_required @require_show_toolbar @render_with_toolbar_language def sql_explain(request): @@ -86,6 +89,7 @@ def sql_explain(request): @csrf_exempt +@login_not_required @require_show_toolbar @render_with_toolbar_language def sql_profile(request): diff --git a/debug_toolbar/panels/templates/views.py b/debug_toolbar/panels/templates/views.py index e65d1a9d5..be6893e0f 100644 --- a/debug_toolbar/panels/templates/views.py +++ b/debug_toolbar/panels/templates/views.py @@ -5,9 +5,11 @@ from django.template.loader import render_to_string from django.utils.html import format_html, mark_safe +from debug_toolbar._compat import login_not_required from debug_toolbar.decorators import render_with_toolbar_language, require_show_toolbar +@login_not_required @require_show_toolbar @render_with_toolbar_language def template_source(request): diff --git a/debug_toolbar/views.py b/debug_toolbar/views.py index b93acbeed..467d7485c 100644 --- a/debug_toolbar/views.py +++ b/debug_toolbar/views.py @@ -2,10 +2,12 @@ from django.utils.html import escape from django.utils.translation import gettext as _ +from debug_toolbar._compat import login_not_required from debug_toolbar.decorators import render_with_toolbar_language, require_show_toolbar from debug_toolbar.toolbar import DebugToolbar +@login_not_required @require_show_toolbar @render_with_toolbar_language def render_panel(request): diff --git a/docs/changes.rst b/docs/changes.rst index b7df20707..abf709b45 100644 --- a/docs/changes.rst +++ b/docs/changes.rst @@ -11,6 +11,7 @@ Pending `Google Summer of Code Project 2024 `__. * Added Django 5.1 to the CI matrix. +* Added support for the ``LoginRequiredMiddleware`` introduced in Django 5.1. * Support select and explain buttons for ``UNION`` queries on PostgreSQL. * Fixed internal toolbar requests being instrumented if the Django setting ``FORCE_SCRIPT_NAME`` was set. diff --git a/tests/test_login_not_required.py b/tests/test_login_not_required.py new file mode 100644 index 000000000..f3406d6b4 --- /dev/null +++ b/tests/test_login_not_required.py @@ -0,0 +1,39 @@ +import unittest + +import django +from django.test import SimpleTestCase, override_settings +from django.urls import reverse + + +@unittest.skipIf( + django.VERSION < (5, 1), + "Valid on Django 5.1 and above, requires LoginRequiredMiddleware", +) +@override_settings( + DEBUG=True, + MIDDLEWARE=[ + "django.contrib.sessions.middleware.SessionMiddleware", + "django.contrib.auth.middleware.AuthenticationMiddleware", + "django.contrib.auth.middleware.LoginRequiredMiddleware", + "debug_toolbar.middleware.DebugToolbarMiddleware", + ], +) +class LoginNotRequiredTestCase(SimpleTestCase): + def test_panels(self): + for uri in ( + "history_sidebar", + "history_refresh", + "sql_select", + "sql_explain", + "sql_profile", + "template_source", + ): + with self.subTest(uri=uri): + response = self.client.get(reverse(f"djdt:{uri}")) + self.assertNotEqual(response.status_code, 200) + + def test_render_panel(self): + response = self.client.get( + reverse("djdt:render_panel"), query_params={"store_id": "store_id"} + ) + self.assertEqual(response.status_code, 200)