From 7191e5aed1531fa8bd001521d1d1b98293f724ae Mon Sep 17 00:00:00 2001 From: Jaap Roes Date: Mon, 24 Jun 2024 10:07:00 +0200 Subject: [PATCH] Fixed #35530 -- Deprecated request.user fallback in auth.login. --- django/contrib/auth/__init__.py | 15 ++++++++++ docs/internals/deprecation.txt | 4 +++ docs/releases/5.2.txt | 4 +++ tests/async/test_async_auth.py | 41 ++++++++++++++++++++++----- tests/auth_tests/test_login_logout.py | 39 ++++++++++++++++++++----- 5 files changed, 89 insertions(+), 14 deletions(-) diff --git a/django/contrib/auth/__init__.py b/django/contrib/auth/__init__.py index 689567ca6c..ea8b285584 100644 --- a/django/contrib/auth/__init__.py +++ b/django/contrib/auth/__init__.py @@ -1,11 +1,13 @@ import inspect import re +import warnings from django.apps import apps as django_apps from django.conf import settings from django.core.exceptions import ImproperlyConfigured, PermissionDenied from django.middleware.csrf import rotate_token from django.utils.crypto import constant_time_compare +from django.utils.deprecation import RemovedInDjango61Warning from django.utils.module_loading import import_string from django.views.decorators.debug import sensitive_variables @@ -155,8 +157,15 @@ def login(request, user, backend=None): the anonymous session is retained when the user logs in. """ session_auth_hash = "" + # RemovedInDjango61Warning. if user is None: user = request.user + warnings.warn( + "Fallback to request.user when user is None will be removed.", + RemovedInDjango61Warning, + stacklevel=2, + ) + if hasattr(user, "get_session_auth_hash"): session_auth_hash = user.get_session_auth_hash() @@ -188,7 +197,13 @@ def login(request, user, backend=None): async def alogin(request, user, backend=None): """See login().""" session_auth_hash = "" + # RemovedInDjango61Warning. if user is None: + warnings.warn( + "Fallback to request.user when user is None will be removed.", + RemovedInDjango61Warning, + stacklevel=2, + ) user = await request.auser() if hasattr(user, "get_session_auth_hash"): session_auth_hash = user.get_session_auth_hash() diff --git a/docs/internals/deprecation.txt b/docs/internals/deprecation.txt index 85ad0d400f..0ad64b9027 100644 --- a/docs/internals/deprecation.txt +++ b/docs/internals/deprecation.txt @@ -18,6 +18,10 @@ details on these changes. * The ``all`` keyword argument of ``django.contrib.staticfiles.finders.find()`` will be removed. +* The fallback to ``request.user`` when ``user`` is ``None`` in + ``django.contrib.auth.login`` and ``django.contrib.auth.alogin`` + will be removed. + .. _deprecation-removed-in-6.0: 6.0 diff --git a/docs/releases/5.2.txt b/docs/releases/5.2.txt index 8327de7405..f6a24034f4 100644 --- a/docs/releases/5.2.txt +++ b/docs/releases/5.2.txt @@ -418,3 +418,7 @@ Miscellaneous * The ``all`` argument for the ``django.contrib.staticfiles.finders.find()`` function is deprecated in favor of the ``find_all`` argument. + +* The fallback to ``request.user`` when ``user`` is ``None`` in + ``django.contrib.auth.login`` and ``django.contrib.auth.alogin`` + will be removed. diff --git a/tests/async/test_async_auth.py b/tests/async/test_async_auth.py index 2dc2086445..db3fc8abec 100644 --- a/tests/async/test_async_auth.py +++ b/tests/async/test_async_auth.py @@ -8,6 +8,7 @@ from django.contrib.auth import ( from django.contrib.auth.models import AnonymousUser, User from django.http import HttpRequest from django.test import TestCase, override_settings +from django.utils.deprecation import RemovedInDjango61Warning class AsyncAuthTest(TestCase): @@ -60,15 +61,30 @@ class AsyncAuthTest(TestCase): self.assertIsInstance(user, User) self.assertEqual(user.username, second_user.username) + # RemovedInDjango61Warning: When the deprecation ends, replace with: + # async def test_alogin_without_user(self): async def test_alogin_without_user_no_request_user(self): request = HttpRequest() request.session = await self.client.asession() - with self.assertRaisesMessage( - AttributeError, - "'HttpRequest' object has no attribute 'auser'", + # RemovedInDjango61Warning: When the deprecation ends, replace with: + # with self.assertRaisesMessage( + # AttributeError, + # "'NoneType' object has no attribute '_meta'", + # ): + # await alogin(request, None) + with ( + self.assertRaisesMessage( + AttributeError, + "'HttpRequest' object has no attribute 'auser'", + ), + self.assertWarnsMessage( + RemovedInDjango61Warning, + "Fallback to request.user when user is None will be removed.", + ), ): await alogin(request, None) + # RemovedInDjango61Warning: When the deprecation ends, remove completely. async def test_alogin_without_user_anonymous_request(self): async def auser(): return AnonymousUser() @@ -77,12 +93,19 @@ class AsyncAuthTest(TestCase): request.user = AnonymousUser() request.auser = auser request.session = await self.client.asession() - with self.assertRaisesMessage( - AttributeError, - "'AnonymousUser' object has no attribute '_meta'", + with ( + self.assertRaisesMessage( + AttributeError, + "'AnonymousUser' object has no attribute '_meta'", + ), + self.assertWarnsMessage( + RemovedInDjango61Warning, + "Fallback to request.user when user is None will be removed.", + ), ): await alogin(request, None) + # RemovedInDjango61Warning: When the deprecation ends, remove completely. async def test_alogin_without_user_authenticated_request(self): async def auser(): return self.test_user @@ -91,7 +114,11 @@ class AsyncAuthTest(TestCase): request.user = self.test_user request.auser = auser request.session = await self.client.asession() - await alogin(request, None) + with self.assertWarnsMessage( + RemovedInDjango61Warning, + "Fallback to request.user when user is None will be removed.", + ): + await alogin(request, None) user = await aget_user(request) self.assertIsInstance(user, User) self.assertEqual(user.username, self.test_user.username) diff --git a/tests/auth_tests/test_login_logout.py b/tests/auth_tests/test_login_logout.py index 80f0563709..54ec44fedb 100644 --- a/tests/auth_tests/test_login_logout.py +++ b/tests/auth_tests/test_login_logout.py @@ -2,6 +2,7 @@ from django.contrib import auth from django.contrib.auth.models import AnonymousUser, User from django.http import HttpRequest from django.test import TestCase +from django.utils.deprecation import RemovedInDjango61Warning class TestLogin(TestCase): @@ -32,27 +33,51 @@ class TestLogin(TestCase): self.assertEqual(self.request.session[auth.SESSION_KEY], str(self.user.pk)) + # RemovedInDjango61Warning: When the deprecation ends, replace with: + # def test_without_user(self): def test_without_user_no_request_user(self): - with self.assertRaisesMessage( - AttributeError, - "'HttpRequest' object has no attribute 'user'", + # RemovedInDjango61Warning: When the deprecation ends, replace with: + # with self.assertRaisesMessage( + # AttributeError, + # "'NoneType' object has no attribute '_meta'", + # ): + # await auth.login(self.request, None) + with ( + self.assertRaisesMessage( + AttributeError, + "'HttpRequest' object has no attribute 'user'", + ), + self.assertWarnsMessage( + RemovedInDjango61Warning, + "Fallback to request.user when user is None will be removed.", + ), ): auth.login(self.request, None) + # RemovedInDjango61Warning: When the deprecation ends, remove completely. def test_without_user_anonymous_request(self): self.request.user = AnonymousUser() - with self.assertRaisesMessage( - AttributeError, - "'AnonymousUser' object has no attribute '_meta'", + with ( + self.assertRaisesMessage( + AttributeError, + "'AnonymousUser' object has no attribute '_meta'", + ), + self.assertWarnsMessage( + RemovedInDjango61Warning, + "Fallback to request.user when user is None will be removed.", + ), ): auth.login(self.request, None) + # RemovedInDjango61Warning: When the deprecation ends, remove completely. def test_without_user_authenticated_request(self): self.request.user = self.user self.assertNotIn(auth.SESSION_KEY, self.request.session) - auth.login(self.request, None) + msg = "Fallback to request.user when user is None will be removed." + with self.assertWarnsMessage(RemovedInDjango61Warning, msg): + auth.login(self.request, None) self.assertEqual(self.request.session[auth.SESSION_KEY], str(self.user.pk))