diff options
| author | Sezer BOZKIR <admin@sezerbozkir.com> | 2026-04-07 11:20:16 +0300 |
|---|---|---|
| committer | Jacob Walls <jacobtylerwalls@gmail.com> | 2026-06-11 10:10:47 -0400 |
| commit | dc4e5461faa390991e6a0f6d3ca47690e8f5c4e1 (patch) | |
| tree | ff68f0dab17b27839da4cbdff0c40e077cf0a6d1 | |
| parent | c6f81b383251dc9fd0918b4ba040c993444b931e (diff) | |
Fixed #36837 -- Skipped backends not implementing (a)get_user() in (a)force_login().
Co-authored-by: Mykhailo Havelia <Arfey17.mg@gmail.com>
| -rw-r--r-- | django/test/client.py | 7 | ||||
| -rw-r--r-- | docs/releases/6.2.txt | 4 | ||||
| -rw-r--r-- | docs/topics/testing/tools.txt | 12 | ||||
| -rw-r--r-- | tests/test_client/auth_backends.py | 8 | ||||
| -rw-r--r-- | tests/test_client/tests.py | 19 |
5 files changed, 44 insertions, 6 deletions
diff --git a/django/test/client.py b/django/test/client.py index 0f986d5a6c..d499045363 100644 --- a/django/test/client.py +++ b/django/test/client.py @@ -865,10 +865,15 @@ class ClientMixin: def _get_backend(self): from django.contrib.auth import load_backend + from django.contrib.auth.backends import BaseBackend + + def overrides(backend, name): + base = getattr(BaseBackend, name) + return getattr(type(backend), name, base) is not base for backend_path in settings.AUTHENTICATION_BACKENDS: backend = load_backend(backend_path) - if hasattr(backend, "get_user"): + if overrides(backend, "get_user") or overrides(backend, "aget_user"): return backend_path def _login(self, user, backend=None): diff --git a/docs/releases/6.2.txt b/docs/releases/6.2.txt index 74ba31f171..b949b0aa24 100644 --- a/docs/releases/6.2.txt +++ b/docs/releases/6.2.txt @@ -222,7 +222,9 @@ Templates Tests ~~~~~ -* ... +* :meth:`~django.test.Client.force_login` now skips members of + :setting:`AUTHENTICATION_BACKENDS` not implementing ``(a)get_user()``, e.g. + permission-only backends. URLs ~~~~ diff --git a/docs/topics/testing/tools.txt b/docs/topics/testing/tools.txt index da9dd38eec..6eeb96817b 100644 --- a/docs/topics/testing/tools.txt +++ b/docs/topics/testing/tools.txt @@ -479,15 +479,21 @@ Use the ``django.test.Client`` class to make requests. The user will have its ``backend`` attribute set to the value of the ``backend`` argument (which should be a dotted Python path string), or - to ``settings.AUTHENTICATION_BACKENDS[0]`` if a value isn't provided. - The :func:`~django.contrib.auth.authenticate` function called by - :meth:`login` normally annotates the user like this. + if a value isn't provided, the first backend from + :setting:`AUTHENTICATION_BACKENDS` implementing ``get_user()`` or + ``aget_user()``. The :func:`~django.contrib.auth.authenticate` function + called by :meth:`login` normally annotates the user like this. This method is faster than ``login()`` since the expensive password hashing algorithms are bypassed. Also, you can speed up ``login()`` by :ref:`using a weaker hasher while testing <speeding-up-tests-auth-hashers>`. + .. versionchanged:: 6.2 + + On older versions, backends not implementing ``(a)get_user()`` were + eligible to be selected. + .. method:: Client.logout() .. method:: Client.alogout() diff --git a/tests/test_client/auth_backends.py b/tests/test_client/auth_backends.py index 97a2763aaa..a20b0be4de 100644 --- a/tests/test_client/auth_backends.py +++ b/tests/test_client/auth_backends.py @@ -1,4 +1,4 @@ -from django.contrib.auth.backends import ModelBackend +from django.contrib.auth.backends import BaseBackend, ModelBackend class TestClientBackend(ModelBackend): @@ -7,3 +7,9 @@ class TestClientBackend(ModelBackend): class BackendWithoutGetUserMethod: pass + + +class PermissionOnlyBackend(BaseBackend): + """This class inherits from BaseBackend but does not implement get_user.""" + + pass diff --git a/tests/test_client/tests.py b/tests/test_client/tests.py index bc1472d88b..2ba9455d0c 100644 --- a/tests/test_client/tests.py +++ b/tests/test_client/tests.py @@ -794,6 +794,25 @@ class ClientTest(TestCase): self.client.force_login(self.u1) self.assertEqual(self.u1.backend, "django.contrib.auth.backends.ModelBackend") + @override_settings( + AUTHENTICATION_BACKENDS=[ + "test_client.auth_backends.PermissionOnlyBackend", + "django.contrib.auth.backends.ModelBackend", + ] + ) + def test_force_login_skips_noop_get_user_backend(self): + """force_login() skips auth backends without concrete get_user().""" + self.client.force_login(self.u1) + self.assertEqual(self.u1.backend, "django.contrib.auth.backends.ModelBackend") + + @override_settings( + AUTHENTICATION_BACKENDS=[ + "test_client.auth_backends.PermissionOnlyBackend", + ] + ) + def test_force_login_all_backends_noop(self): + self.assertIsNone(self.client._get_backend()) + @override_settings(SESSION_ENGINE="django.contrib.sessions.backends.signed_cookies") def test_logout_cookie_sessions(self): self.test_logout() |
