summaryrefslogtreecommitdiff
path: root/tests/admin_views
diff options
context:
space:
mode:
authorJon Dufresne <jon.dufresne@gmail.com>2021-01-12 05:37:56 -0800
committerGitHub <noreply@github.com>2021-01-12 14:37:56 +0100
commitba31b0103442ac891fb3cb98f316781254e366c3 (patch)
treec41bf2e135811961ca80477521d6e6df5eb17624 /tests/admin_views
parent3071660acfbdf4b5c59457c8e9dc345d5e8894c5 (diff)
Fixed #31747 -- Fixed model enumeration via admin URLs.
Co-authored-by: Carlton Gibson <carlton.gibson@noumenal.es>
Diffstat (limited to 'tests/admin_views')
-rw-r--r--tests/admin_views/admin.py31
-rw-r--r--tests/admin_views/tests.py211
-rw-r--r--tests/admin_views/urls.py10
3 files changed, 250 insertions, 2 deletions
diff --git a/tests/admin_views/admin.py b/tests/admin_views/admin.py
index 4a72e3070f..1140f03496 100644
--- a/tests/admin_views/admin.py
+++ b/tests/admin_views/admin.py
@@ -15,10 +15,11 @@ from django.core.files.storage import FileSystemStorage
from django.core.mail import EmailMessage
from django.db import models
from django.forms.models import BaseModelFormSet
-from django.http import HttpResponse, StreamingHttpResponse
+from django.http import HttpResponse, JsonResponse, StreamingHttpResponse
from django.urls import path
from django.utils.html import format_html
from django.utils.safestring import mark_safe
+from django.views.decorators.common import no_append_slash
from .forms import MediaActionForm
from .models import (
@@ -100,7 +101,19 @@ class ArticleForm(forms.ModelForm):
model = Article
-class ArticleAdmin(admin.ModelAdmin):
+class ArticleAdminWithExtraUrl(admin.ModelAdmin):
+ def get_urls(self):
+ urlpatterns = super().get_urls()
+ urlpatterns.append(
+ path('extra.json', self.admin_site.admin_view(self.extra_json), name='article_extra_json')
+ )
+ return urlpatterns
+
+ def extra_json(self, request):
+ return JsonResponse({})
+
+
+class ArticleAdmin(ArticleAdminWithExtraUrl):
list_display = (
'content', 'date', callable_year, 'model_year', 'modeladmin_year',
'model_year_reversed', 'section', lambda obj: obj.title,
@@ -1181,5 +1194,19 @@ class ArticleAdmin9(admin.ModelAdmin):
return obj is None
+class ActorAdmin9(admin.ModelAdmin):
+ def get_urls(self):
+ # Opt-out of append slash for single model.
+ urls = super().get_urls()
+ for pattern in urls:
+ pattern.callback = no_append_slash(pattern.callback)
+ return urls
+
+
site9 = admin.AdminSite(name='admin9')
site9.register(Article, ArticleAdmin9)
+site9.register(Actor, ActorAdmin9)
+
+site10 = admin.AdminSite(name='admin10')
+site10.final_catch_all_view = False
+site10.register(Article, ArticleAdminWithExtraUrl)
diff --git a/tests/admin_views/tests.py b/tests/admin_views/tests.py
index 6f47465c4a..297760f807 100644
--- a/tests/admin_views/tests.py
+++ b/tests/admin_views/tests.py
@@ -6470,3 +6470,214 @@ class GetFormsetsWithInlinesArgumentTest(TestCase):
post_data = {'name': '2'}
response = self.client.post(reverse('admin:admin_views_implicitlygeneratedpk_change', args=(1,)), post_data)
self.assertEqual(response.status_code, 302)
+
+
+@override_settings(ROOT_URLCONF='admin_views.urls')
+class AdminSiteFinalCatchAllPatternTests(TestCase):
+ """
+ Verifies the behaviour of the admin catch-all view.
+
+ * Anonynous/non-staff users are redirected to login for all URLs, whether
+ otherwise valid or not.
+ * APPEND_SLASH is applied for staff if needed.
+ * Otherwise Http404.
+ * Catch-all view disabled via AdminSite.final_catch_all_view.
+ """
+ def test_unknown_url_redirects_login_if_not_authenticated(self):
+ unknown_url = '/test_admin/admin/unknown/'
+ response = self.client.get(unknown_url)
+ self.assertRedirects(response, '%s?next=%s' % (reverse('admin:login'), unknown_url))
+
+ def test_unknown_url_404_if_authenticated(self):
+ superuser = User.objects.create_superuser(
+ username='super',
+ password='secret',
+ email='super@example.com',
+ )
+ self.client.force_login(superuser)
+ unknown_url = '/test_admin/admin/unknown/'
+ response = self.client.get(unknown_url)
+ self.assertEqual(response.status_code, 404)
+
+ def test_known_url_redirects_login_if_not_authenticated(self):
+ known_url = reverse('admin:admin_views_article_changelist')
+ response = self.client.get(known_url)
+ self.assertRedirects(response, '%s?next=%s' % (reverse('admin:login'), known_url))
+
+ def test_known_url_missing_slash_redirects_login_if_not_authenticated(self):
+ known_url = reverse('admin:admin_views_article_changelist')[:-1]
+ response = self.client.get(known_url)
+ # Redirects with the next URL also missing the slash.
+ self.assertRedirects(response, '%s?next=%s' % (reverse('admin:login'), known_url))
+
+ def test_non_admin_url_shares_url_prefix(self):
+ url = reverse('non_admin')[:-1]
+ response = self.client.get(url)
+ # Redirects with the next URL also missing the slash.
+ self.assertRedirects(response, '%s?next=%s' % (reverse('admin:login'), url))
+
+ def test_url_without_trailing_slash_if_not_authenticated(self):
+ url = reverse('admin:article_extra_json')
+ response = self.client.get(url)
+ self.assertRedirects(response, '%s?next=%s' % (reverse('admin:login'), url))
+
+ def test_unkown_url_without_trailing_slash_if_not_authenticated(self):
+ url = reverse('admin:article_extra_json')[:-1]
+ response = self.client.get(url)
+ self.assertRedirects(response, '%s?next=%s' % (reverse('admin:login'), url))
+
+ @override_settings(APPEND_SLASH=True)
+ def test_missing_slash_append_slash_true_unknown_url(self):
+ superuser = User.objects.create_user(
+ username='staff',
+ password='secret',
+ email='staff@example.com',
+ is_staff=True,
+ )
+ self.client.force_login(superuser)
+ unknown_url = '/test_admin/admin/unknown/'
+ response = self.client.get(unknown_url[:-1])
+ self.assertEqual(response.status_code, 404)
+
+ @override_settings(APPEND_SLASH=True)
+ def test_missing_slash_append_slash_true(self):
+ superuser = User.objects.create_user(
+ username='staff',
+ password='secret',
+ email='staff@example.com',
+ is_staff=True,
+ )
+ self.client.force_login(superuser)
+ known_url = reverse('admin:admin_views_article_changelist')
+ response = self.client.get(known_url[:-1])
+ self.assertRedirects(response, known_url, status_code=301, target_status_code=403)
+
+ @override_settings(APPEND_SLASH=True)
+ def test_missing_slash_append_slash_true_non_staff_user(self):
+ user = User.objects.create_user(
+ username='user',
+ password='secret',
+ email='user@example.com',
+ is_staff=False,
+ )
+ self.client.force_login(user)
+ known_url = reverse('admin:admin_views_article_changelist')
+ response = self.client.get(known_url[:-1])
+ self.assertRedirects(response, '/test_admin/admin/login/?next=/test_admin/admin/admin_views/article')
+
+ @override_settings(APPEND_SLASH=False)
+ def test_missing_slash_append_slash_false(self):
+ superuser = User.objects.create_user(
+ username='staff',
+ password='secret',
+ email='staff@example.com',
+ is_staff=True,
+ )
+ self.client.force_login(superuser)
+ known_url = reverse('admin:admin_views_article_changelist')
+ response = self.client.get(known_url[:-1])
+ self.assertEqual(response.status_code, 404)
+
+ @override_settings(APPEND_SLASH=True)
+ def test_single_model_no_append_slash(self):
+ superuser = User.objects.create_user(
+ username='staff',
+ password='secret',
+ email='staff@example.com',
+ is_staff=True,
+ )
+ self.client.force_login(superuser)
+ known_url = reverse('admin9:admin_views_actor_changelist')
+ response = self.client.get(known_url[:-1])
+ self.assertEqual(response.status_code, 404)
+
+ # Same tests above with final_catch_all_view=False.
+
+ def test_unknown_url_404_if_not_authenticated_without_final_catch_all_view(self):
+ unknown_url = '/test_admin/admin10/unknown/'
+ response = self.client.get(unknown_url)
+ self.assertEqual(response.status_code, 404)
+
+ def test_unknown_url_404_if_authenticated_without_final_catch_all_view(self):
+ superuser = User.objects.create_superuser(
+ username='super',
+ password='secret',
+ email='super@example.com',
+ )
+ self.client.force_login(superuser)
+ unknown_url = '/test_admin/admin10/unknown/'
+ response = self.client.get(unknown_url)
+ self.assertEqual(response.status_code, 404)
+
+ def test_known_url_redirects_login_if_not_authenticated_without_final_catch_all_view(self):
+ known_url = reverse('admin10:admin_views_article_changelist')
+ response = self.client.get(known_url)
+ self.assertRedirects(response, '%s?next=%s' % (reverse('admin10:login'), known_url))
+
+ def test_known_url_missing_slash_redirects_with_slash_if_not_authenticated_without_final_catch_all_view(self):
+ known_url = reverse('admin10:admin_views_article_changelist')
+ response = self.client.get(known_url[:-1])
+ self.assertRedirects(response, known_url, status_code=301, fetch_redirect_response=False)
+
+ def test_non_admin_url_shares_url_prefix_without_final_catch_all_view(self):
+ url = reverse('non_admin10')
+ response = self.client.get(url[:-1])
+ self.assertRedirects(response, url, status_code=301)
+
+ def test_url_without_trailing_slash_if_not_authenticated_without_final_catch_all_view(self):
+ url = reverse('admin10:article_extra_json')
+ response = self.client.get(url)
+ self.assertRedirects(response, '%s?next=%s' % (reverse('admin10:login'), url))
+
+ def test_unkown_url_without_trailing_slash_if_not_authenticated_without_final_catch_all_view(self):
+ url = reverse('admin10:article_extra_json')[:-1]
+ response = self.client.get(url)
+ # Matches test_admin/admin10/admin_views/article/<path:object_id>/
+ self.assertRedirects(response, url + '/', status_code=301, fetch_redirect_response=False)
+
+ @override_settings(APPEND_SLASH=True)
+ def test_missing_slash_append_slash_true_unknown_url_without_final_catch_all_view(self):
+ superuser = User.objects.create_user(
+ username='staff',
+ password='secret',
+ email='staff@example.com',
+ is_staff=True,
+ )
+ self.client.force_login(superuser)
+ unknown_url = '/test_admin/admin10/unknown/'
+ response = self.client.get(unknown_url[:-1])
+ self.assertEqual(response.status_code, 404)
+
+ @override_settings(APPEND_SLASH=True)
+ def test_missing_slash_append_slash_true_without_final_catch_all_view(self):
+ superuser = User.objects.create_user(
+ username='staff',
+ password='secret',
+ email='staff@example.com',
+ is_staff=True,
+ )
+ self.client.force_login(superuser)
+ known_url = reverse('admin10:admin_views_article_changelist')
+ response = self.client.get(known_url[:-1])
+ self.assertRedirects(response, known_url, status_code=301, target_status_code=403)
+
+ @override_settings(APPEND_SLASH=False)
+ def test_missing_slash_append_slash_false_without_final_catch_all_view(self):
+ superuser = User.objects.create_user(
+ username='staff',
+ password='secret',
+ email='staff@example.com',
+ is_staff=True,
+ )
+ self.client.force_login(superuser)
+ known_url = reverse('admin10:admin_views_article_changelist')
+ response = self.client.get(known_url[:-1])
+ self.assertEqual(response.status_code, 404)
+
+ # Outside admin.
+
+ def test_non_admin_url_404_if_not_authenticated(self):
+ unknown_url = '/unknown/'
+ response = self.client.get(unknown_url)
+ # Does not redirect to the admin login.
+ self.assertEqual(response.status_code, 404)
diff --git a/tests/admin_views/urls.py b/tests/admin_views/urls.py
index ca684b2f2e..355e082b68 100644
--- a/tests/admin_views/urls.py
+++ b/tests/admin_views/urls.py
@@ -1,8 +1,14 @@
+from django.http import HttpResponse
from django.urls import include, path
from . import admin, custom_has_permission_admin, customadmin, views
from .test_autocomplete_view import site as autocomplete_site
+
+def non_admin_view(request):
+ return HttpResponse()
+
+
urlpatterns = [
path('test_admin/admin/doc/', include('django.contrib.admindocs.urls')),
path('test_admin/admin/secure-view/', views.secure_view, name='secure_view'),
@@ -17,6 +23,10 @@ urlpatterns = [
# All admin views accept `extra_context` to allow adding it like this:
path('test_admin/admin8/', (admin.site.get_urls(), 'admin', 'admin-extra-context'), {'extra_context': {}}),
path('test_admin/admin9/', admin.site9.urls),
+ path('test_admin/admin10/', admin.site10.urls),
path('test_admin/has_permission_admin/', custom_has_permission_admin.site.urls),
path('test_admin/autocomplete_admin/', autocomplete_site.urls),
+ # Shares the admin URL prefix.
+ path('test_admin/admin/non_admin_view/', non_admin_view, name='non_admin'),
+ path('test_admin/admin10/non_admin_view/', non_admin_view, name='non_admin10'),
]