Browse Source

Fixed #35530 -- Deprecated request.user fallback in auth.login and auth.alogin.

Jaap Roes 3 months ago
parent
commit
ceecd518b1

+ 21 - 0
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
 
@@ -154,9 +156,19 @@ def login(request, user, backend=None):
     have to reauthenticate on every request. Note that data set during
     the anonymous session is retained when the user logs in.
     """
+    # RemovedInDjango61Warning: When the deprecation ends, replace with:
+    # session_auth_hash = user.get_session_auth_hash()
     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,
+        )
+
+    # RemovedInDjango61Warning.
     if hasattr(user, "get_session_auth_hash"):
         session_auth_hash = user.get_session_auth_hash()
 
@@ -187,9 +199,18 @@ def login(request, user, backend=None):
 
 async def alogin(request, user, backend=None):
     """See login()."""
+    # RemovedInDjango61Warning: When the deprecation ends, replace with:
+    # session_auth_hash = user.get_session_auth_hash()
     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()
+    # RemovedInDjango61Warning.
     if hasattr(user, "get_session_auth_hash"):
         session_auth_hash = user.get_session_auth_hash()
 

+ 4 - 0
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

+ 4 - 0
docs/releases/5.2.txt

@@ -443,3 +443,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.

+ 52 - 2
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,7 +61,52 @@ class AsyncAuthTest(TestCase):
         self.assertIsInstance(user, User)
         self.assertEqual(user.username, second_user.username)
 
-    async def test_alogin_without_user(self):
+    # 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()
+        # RemovedInDjango61Warning: When the deprecation ends, replace with:
+        # with self.assertRaisesMessage(
+        #     AttributeError,
+        #     "'NoneType' object has no attribute 'get_session_auth_hash'",
+        # ):
+        #     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()
+
+        request = HttpRequest()
+        request.user = AnonymousUser()
+        request.auser = auser
+        request.session = await self.client.asession()
+        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
 
@@ -68,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)

+ 48 - 1
tests/auth_tests/test_login.py

@@ -1,7 +1,8 @@
 from django.contrib import auth
-from django.contrib.auth.models import User
+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):
@@ -23,3 +24,49 @@ class TestLogin(TestCase):
 
         auth.login(self.request, self.user)
         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):
+        # RemovedInDjango61Warning: When the deprecation ends, replace with:
+        # with self.assertRaisesMessage(
+        #     AttributeError,
+        #     "'NoneType' object has no attribute 'get_session_auth_hash'",
+        # ):
+        #     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'",
+            ),
+            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)
+
+        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))