Skip to content

Commit

Permalink
Fixed #30237 -- Made Authentication/SessionMiddleware and ModelBacken…
Browse files Browse the repository at this point in the history
…d admin checks allow subclasses.
  • Loading branch information
hermansc authored and timgraham committed Mar 14, 2019
1 parent 782d85b commit 33d344e
Show file tree
Hide file tree
Showing 2 changed files with 74 additions and 6 deletions.
28 changes: 22 additions & 6 deletions django/contrib/admin/checks.py
Expand Up @@ -15,6 +15,7 @@
)
from django.template import engines
from django.template.backends.django import DjangoTemplates
from django.utils.module_loading import import_string


def _issubclass(cls, classinfo):
Expand All @@ -28,6 +29,23 @@ def _issubclass(cls, classinfo):
return False


def _contains_subclass(class_path, candidate_paths):
"""
Return whether or not a dotted class path (or a subclass of that class) is
found in a list of candidate paths.
"""
cls = import_string(class_path)
for path in candidate_paths:
try:
candidate_cls = import_string(path)
except ImportError:
# ImportErrors are raised elsewhere.
continue
if _issubclass(candidate_cls, cls):
return True
return False


def check_admin_app(app_configs, **kwargs):
from django.contrib.admin.sites import all_sites
errors = []
Expand Down Expand Up @@ -72,8 +90,7 @@ def check_dependencies(**kwargs):
else:
if ('django.contrib.auth.context_processors.auth'
not in django_templates_instance.context_processors and
'django.contrib.auth.backends.ModelBackend'
in settings.AUTHENTICATION_BACKENDS):
_contains_subclass('django.contrib.auth.backends.ModelBackend', settings.AUTHENTICATION_BACKENDS)):
errors.append(checks.Error(
"'django.contrib.auth.context_processors.auth' must be "
"enabled in DjangoTemplates (TEMPLATES) if using the default "
Expand All @@ -88,15 +105,14 @@ def check_dependencies(**kwargs):
"the admin application.",
id='admin.E404',
))
if ('django.contrib.auth.middleware.AuthenticationMiddleware'
not in settings.MIDDLEWARE):

if not _contains_subclass('django.contrib.auth.middleware.AuthenticationMiddleware', settings.MIDDLEWARE):
errors.append(checks.Error(
"'django.contrib.auth.middleware.AuthenticationMiddleware' must "
"be in MIDDLEWARE in order to use the admin application.",
id='admin.E408',
))
if ('django.contrib.messages.middleware.MessageMiddleware'
not in settings.MIDDLEWARE):
if not _contains_subclass('django.contrib.messages.middleware.MessageMiddleware', settings.MIDDLEWARE):
errors.append(checks.Error(
"'django.contrib.messages.middleware.MessageMiddleware' must "
"be in MIDDLEWARE in order to use the admin application.",
Expand Down
52 changes: 52 additions & 0 deletions tests/admin_checks/tests.py
@@ -1,7 +1,10 @@
from django import forms
from django.contrib import admin
from django.contrib.admin import AdminSite
from django.contrib.auth.backends import ModelBackend
from django.contrib.auth.middleware import AuthenticationMiddleware
from django.contrib.contenttypes.admin import GenericStackedInline
from django.contrib.messages.middleware import MessageMiddleware
from django.core import checks
from django.test import SimpleTestCase, override_settings

Expand Down Expand Up @@ -37,6 +40,18 @@ def check(self, **kwargs):
return ['error!']


class AuthenticationMiddlewareSubclass(AuthenticationMiddleware):
pass


class MessageMiddlewareSubclass(MessageMiddleware):
pass


class ModelBackendSubclass(ModelBackend):
pass


@override_settings(
SILENCED_SYSTEM_CHECKS=['fields.W342'], # ForeignKey(unique=True)
INSTALLED_APPS=[
Expand Down Expand Up @@ -129,6 +144,28 @@ def test_context_processor_dependencies(self):
with self.settings(AUTHENTICATION_BACKENDS=[]):
self.assertEqual(admin.checks.check_dependencies(), expected[1:])

@override_settings(
AUTHENTICATION_BACKENDS=['admin_checks.tests.ModelBackendSubclass'],
TEMPLATES=[{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': ['django.contrib.messages.context_processors.messages'],
},
}],
)
def test_context_processor_dependencies_model_backend_subclass(self):
expected = [
checks.Error(
"'django.contrib.auth.context_processors.auth' must be "
"enabled in DjangoTemplates (TEMPLATES) if using the default "
"auth backend in order to use the admin application.",
id='admin.E402',
),
]
self.assertEqual(admin.checks.check_dependencies(), expected)

@override_settings(
TEMPLATES=[
{
Expand Down Expand Up @@ -169,6 +206,21 @@ def test_middleware_dependencies(self):
]
self.assertEqual(errors, expected)

@override_settings(MIDDLEWARE=[
'admin_checks.tests.AuthenticationMiddlewareSubclass',
'admin_checks.tests.MessageMiddlewareSubclass',
])
def test_middleware_subclasses(self):
self.assertEqual(admin.checks.check_dependencies(), [])

@override_settings(MIDDLEWARE=[
'django.contrib.does.not.Exist',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
])
def test_admin_check_ignores_import_error_in_middleware(self):
self.assertEqual(admin.checks.check_dependencies(), [])

def test_custom_adminsite(self):
class CustomAdminSite(admin.AdminSite):
pass
Expand Down

0 comments on commit 33d344e

Please sign in to comment.