Fixed #30064 -- Added form to validate admin search fields query input.

This commit is contained in:
Carlton Gibson 2019-08-22 14:09:49 +02:00 committed by GitHub
parent 6b16c91157
commit 5b4c6b58a0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 35 additions and 1 deletions

View File

@ -1,6 +1,8 @@
from datetime import datetime, timedelta from datetime import datetime, timedelta
from django import forms
from django.conf import settings from django.conf import settings
from django.contrib import messages
from django.contrib.admin import FieldListFilter from django.contrib.admin import FieldListFilter
from django.contrib.admin.exceptions import ( from django.contrib.admin.exceptions import (
DisallowedModelAdminLookup, DisallowedModelAdminToField, DisallowedModelAdminLookup, DisallowedModelAdminToField,
@ -34,7 +36,18 @@ IGNORED_PARAMS = (
ALL_VAR, ORDER_VAR, ORDER_TYPE_VAR, SEARCH_VAR, IS_POPUP_VAR, TO_FIELD_VAR) ALL_VAR, ORDER_VAR, ORDER_TYPE_VAR, SEARCH_VAR, IS_POPUP_VAR, TO_FIELD_VAR)
class ChangeListSearchForm(forms.Form):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# Populate "fields" dynamically because SEARCH_VAR is a variable:
self.fields = {
SEARCH_VAR: forms.CharField(required=False, strip=False),
}
class ChangeList: class ChangeList:
search_form_class = ChangeListSearchForm
def __init__(self, request, model, list_display, list_display_links, def __init__(self, request, model, list_display, list_display_links,
list_filter, date_hierarchy, search_fields, list_select_related, list_filter, date_hierarchy, search_fields, list_select_related,
list_per_page, list_max_show_all, list_editable, model_admin, sortable_by): list_per_page, list_max_show_all, list_editable, model_admin, sortable_by):
@ -56,6 +69,11 @@ class ChangeList:
self.sortable_by = sortable_by self.sortable_by = sortable_by
# Get search parameters from the query string. # Get search parameters from the query string.
_search_form = self.search_form_class(request.GET)
if not _search_form.is_valid():
for error in _search_form.errors.values():
messages.error(request, ', '.join(error))
self.query = _search_form.cleaned_data.get(SEARCH_VAR) or ''
try: try:
self.page_num = int(request.GET.get(PAGE_VAR, 0)) self.page_num = int(request.GET.get(PAGE_VAR, 0))
except ValueError: except ValueError:
@ -76,7 +94,6 @@ class ChangeList:
self.list_editable = () self.list_editable = ()
else: else:
self.list_editable = list_editable self.list_editable = list_editable
self.query = request.GET.get(SEARCH_VAR, '')
self.queryset = self.get_queryset(request) self.queryset = self.get_queryset(request)
self.get_results(request) self.get_results(request)
if self.is_popup: if self.is_popup:

View File

@ -8,6 +8,7 @@ from django.contrib.admin.tests import AdminSeleniumTestCase
from django.contrib.admin.views.main import ALL_VAR, SEARCH_VAR from django.contrib.admin.views.main import ALL_VAR, SEARCH_VAR
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from django.contrib.messages.storage.cookie import CookieStorage
from django.db import connection, models from django.db import connection, models
from django.db.models import F from django.db.models import F
from django.db.models.fields import Field, IntegerField from django.db.models.fields import Field, IntegerField
@ -406,6 +407,22 @@ class ChangeListTests(TestCase):
# Make sure distinct() was called # Make sure distinct() was called
self.assertEqual(cl.queryset.count(), 1) self.assertEqual(cl.queryset.count(), 1)
def test_changelist_search_form_validation(self):
m = ConcertAdmin(Concert, custom_site)
tests = [
({SEARCH_VAR: '\x00'}, 'Null characters are not allowed.'),
({SEARCH_VAR: 'some\x00thing'}, 'Null characters are not allowed.'),
]
for case, error in tests:
with self.subTest(case=case):
request = self.factory.get('/concert/', case)
request.user = self.superuser
request._messages = CookieStorage(request)
m.get_changelist_instance(request)
messages = [m.message for m in request._messages]
self.assertEqual(1, len(messages))
self.assertEqual(error, messages[0])
def test_distinct_for_non_unique_related_object_in_search_fields(self): def test_distinct_for_non_unique_related_object_in_search_fields(self):
""" """
Regressions tests for #15819: If a field listed in search_fields Regressions tests for #15819: If a field listed in search_fields