diff options
| author | Herman S <hermansc@users.noreply.github.com> | 2019-03-05 22:22:09 +0100 |
|---|---|---|
| committer | Tim Graham <timograham@gmail.com> | 2019-03-14 20:36:11 -0400 |
| commit | d8704a4d4f6a3bc96cf26ffd1f14bf4b5c71c74d (patch) | |
| tree | 064dbbc6d14cc7ff132e4b7f4aa65bcc1b2bcfa3 | |
| parent | b150d9946013758a2a13eee751ac828d8582d944 (diff) | |
[2.2.x] Fixed #30237 -- Made Authentication/SessionMiddleware and ModelBackend admin checks allow subclasses.
Backport of f976ab1b117574db78d884c94e549a6b8e4c9f9b from master.
| -rw-r--r-- | django/contrib/admin/checks.py | 28 | ||||
| -rw-r--r-- | tests/admin_checks/tests.py | 51 |
2 files changed, 73 insertions, 6 deletions
diff --git a/django/contrib/admin/checks.py b/django/contrib/admin/checks.py index 3f8abdd476..aaf6ed2cf0 100644 --- a/django/contrib/admin/checks.py +++ b/django/contrib/admin/checks.py @@ -18,6 +18,7 @@ from django.template import engines from django.template.backends.django import DjangoTemplates from django.utils.deprecation import RemovedInDjango30Warning from django.utils.inspect import get_func_args +from django.utils.module_loading import import_string def _issubclass(cls, classinfo): @@ -31,6 +32,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 = [] @@ -75,8 +93,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 " @@ -91,15 +108,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.", diff --git a/tests/admin_checks/tests.py b/tests/admin_checks/tests.py index df1cd6f96f..c7fe39b91e 100644 --- a/tests/admin_checks/tests.py +++ b/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 @@ -37,6 +40,18 @@ class MyAdmin(admin.ModelAdmin): 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=[ @@ -130,6 +145,27 @@ class SystemChecksTestCase(SimpleTestCase): 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): + self.assertEqual(admin.checks.check_dependencies(), [ + 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', + ), + ]) + + @override_settings( TEMPLATES=[ { 'BACKEND': 'django.template.backends.dummy.TemplateStrings', @@ -169,6 +205,21 @@ class SystemChecksTestCase(SimpleTestCase): ] 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 |
