diff --git a/django/conf/__init__.py b/django/conf/__init__.py index 1ac308a3ed..7ca099b47b 100644 --- a/django/conf/__init__.py +++ b/django/conf/__init__.py @@ -9,9 +9,11 @@ a list of all possible variables. import importlib import os import time # Needed for Windows +import warnings from django.conf import global_settings from django.core.exceptions import ImproperlyConfigured +from django.utils.deprecation import RemovedInDjango20Warning from django.utils.functional import LazyObject, empty from django.utils import six @@ -110,6 +112,16 @@ class Settings(BaseSettings): if not self.SECRET_KEY: raise ImproperlyConfigured("The SECRET_KEY setting must not be empty.") + if ('django.contrib.auth.middleware.AuthenticationMiddleware' in self.MIDDLEWARE_CLASSES and + 'django.contrib.auth.middleware.SessionAuthenticationMiddleware' not in self.MIDDLEWARE_CLASSES): + warnings.warn( + "Session verification will become mandatory in Django 2.0. " + "Please add 'django.contrib.auth.middleware.SessionAuthenticationMiddleware' " + "to your MIDDLEWARE_CLASSES setting when you are ready to opt-in after " + "reading the upgrade considerations in the 1.8 release notes.", + RemovedInDjango20Warning + ) + if hasattr(time, 'tzset') and self.TIME_ZONE: # When we can, attempt to validate the timezone. If we can't find # this file, no check happens and it's harmless. diff --git a/docs/internals/deprecation.txt b/docs/internals/deprecation.txt index 1db9e23d4e..15fbed1565 100644 --- a/docs/internals/deprecation.txt +++ b/docs/internals/deprecation.txt @@ -95,6 +95,10 @@ details on these changes. * ``django.shortcuts.render()`` * ``django.shortcuts.render_to_response()`` +* Session verification will be enabled regardless of whether or not + ``'django.contrib.auth.middleware.SessionAuthenticationMiddleware'`` is in + ``MIDDLEWARE_CLASSES``. + .. _deprecation-removed-in-1.9: 1.9 diff --git a/docs/releases/1.8.txt b/docs/releases/1.8.txt index 375a39920f..2ddcaf25bb 100644 --- a/docs/releases/1.8.txt +++ b/docs/releases/1.8.txt @@ -1158,6 +1158,21 @@ The default value of the :attr:`RedirectView.permanent ` attribute will change from ``True`` to ``False`` in Django 1.9. +Using ``AuthenticationMiddleware`` without ``SessionAuthenticationMiddleware`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +:class:`django.contrib.auth.middleware.SessionAuthenticationMiddleware` was +added in Django 1.7. In Django 1.7.2, its functionality was moved to +``auth.get_user()`` and, for backwards compatibility, enabled only if +``'django.contrib.auth.middleware.SessionAuthenticationMiddleware'`` appears in +:setting:`MIDDLEWARE_CLASSES`. + +In Django 2.0, session verification will be enabled regardless of whether or not +``SessionAuthenticationMiddleware`` is enabled (at which point +``SessionAuthenticationMiddleware`` will have no significance). You can add it +to your ``MIDDLEWARE_CLASSES`` sometime before then to opt-in. Please read the +:ref:`upgrade considerations ` first. + .. removed-features-1.8: Features removed in 1.8 diff --git a/docs/topics/auth/default.txt b/docs/topics/auth/default.txt index 8f19d12b48..62b070ee61 100644 --- a/docs/topics/auth/default.txt +++ b/docs/topics/auth/default.txt @@ -612,6 +612,12 @@ Session invalidation on password change is enabled in :setting:`MIDDLEWARE_CLASSES`. It's included if ``settings.py`` was generated by :djadmin:`startproject` on Django ≥ 1.7. + Session verification will become mandatory in Django 2.0 regardless of + whether or not ``SessionAuthenticationMiddleware`` is enabled. If you have + a pre-1.7 project or one generated using a template that doesn't include + ``SessionAuthenticationMiddleware``, consider enabling it before then after + reading the upgrade considerations below. + If your :setting:`AUTH_USER_MODEL` inherits from :class:`~django.contrib.auth.models.AbstractBaseUser` or implements its own :meth:`~django.contrib.auth.models.AbstractBaseUser.get_session_auth_hash()` diff --git a/tests/settings_tests/tests.py b/tests/settings_tests/tests.py index b2681f4317..c87f6b6204 100644 --- a/tests/settings_tests/tests.py +++ b/tests/settings_tests/tests.py @@ -10,6 +10,7 @@ from django.http import HttpRequest from django.test import (SimpleTestCase, TransactionTestCase, TestCase, modify_settings, override_settings, signals) from django.utils import six +from django.utils.encoding import force_text @modify_settings(ITEMS={ @@ -464,3 +465,47 @@ class TestTupleSettings(unittest.TestCase): finally: del sys.modules['fake_settings_module'] delattr(settings_module, setting) + + +class TestSessionVerification(unittest.TestCase): + + def setUp(self): + self.settings_module = ModuleType('fake_settings_module') + self.settings_module.SECRET_KEY = 'foo' + + def tearDown(self): + if 'fake_settings_module' in sys.modules: + del sys.modules['fake_settings_module'] + + def test_session_verification_deprecation_no_verification(self): + self.settings_module.MIDDLEWARE_CLASSES = ['django.contrib.auth.middleware.AuthenticationMiddleware'] + sys.modules['fake_settings_module'] = self.settings_module + with warnings.catch_warnings(record=True) as warn: + warnings.filterwarnings('always') + Settings('fake_settings_module') + self.assertEqual( + force_text(warn[0].message), + "Session verification will become mandatory in Django 2.0. " + "Please add 'django.contrib.auth.middleware.SessionAuthenticationMiddleware' " + "to your MIDDLEWARE_CLASSES setting when you are ready to opt-in after " + "reading the upgrade considerations in the 1.8 release notes.", + ) + + def test_session_verification_deprecation_both(self): + self.settings_module.MIDDLEWARE_CLASSES = [ + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.auth.middleware.SessionAuthenticationMiddleware', + ] + sys.modules['fake_settings_module'] = self.settings_module + with warnings.catch_warnings(record=True) as warn: + warnings.filterwarnings('always') + Settings('fake_settings_module') + self.assertEqual(len(warn), 0) + + def test_session_verification_deprecation_neither(self): + self.settings_module.MIDDLEWARE_CLASSES = [] + sys.modules['fake_settings_module'] = self.settings_module + with warnings.catch_warnings(record=True) as warn: + warnings.filterwarnings('always') + Settings('fake_settings_module') + self.assertEqual(len(warn), 0)