From 55e16f25e9d2050e95e448f9ab2e4b9fc845a9e5 Mon Sep 17 00:00:00 2001 From: Tim Graham Date: Tue, 30 Jan 2018 19:31:25 -0500 Subject: [PATCH] [2.0.x] Fixed #29071 -- Fixed contrib.auth.authenticate() crash if a backend doesn't accept a request but a later one does. Regression in a3ba2662cdaa36183fdfb8a26dfa157e26fca76a. --- django/contrib/auth/__init__.py | 1 + docs/releases/1.11.10.txt | 4 +++ docs/releases/2.0.2.txt | 4 +++ .../test_auth_backends_deprecation.py | 29 +++++++++++++++++++ 4 files changed, 38 insertions(+) diff --git a/django/contrib/auth/__init__.py b/django/contrib/auth/__init__.py index d96300c503..891be59657 100644 --- a/django/contrib/auth/__init__.py +++ b/django/contrib/auth/__init__.py @@ -82,6 +82,7 @@ def authenticate(request=None, **credentials): def _authenticate_with_backend(backend, backend_path, request, credentials): + credentials = credentials.copy() # Prevent a mutation from propagating. args = (request,) # Does the backend accept a request argument? try: diff --git a/docs/releases/1.11.10.txt b/docs/releases/1.11.10.txt index 49e19614a5..88d2d007bf 100644 --- a/docs/releases/1.11.10.txt +++ b/docs/releases/1.11.10.txt @@ -11,3 +11,7 @@ Bugfixes * Fixed incorrect foreign key nullification if a model has two foreign keys to the same model and a target model is deleted (:ticket:`29016`). + +* Fixed a regression where ``contrib.auth.authenticate()`` crashes if an + authentication backend doesn't accept ``request`` and a later one does + (:ticket:`29071`). diff --git a/docs/releases/2.0.2.txt b/docs/releases/2.0.2.txt index 06968d4ba3..000236af2c 100644 --- a/docs/releases/2.0.2.txt +++ b/docs/releases/2.0.2.txt @@ -20,3 +20,7 @@ Bugfixes * Fixed a regression where a queryset that annotates with geometry objects crashes (:ticket:`29054`). + +* Fixed a regression where ``contrib.auth.authenticate()`` crashes if an + authentication backend doesn't accept ``request`` and a later one does + (:ticket:`29071`). diff --git a/tests/auth_tests/test_auth_backends_deprecation.py b/tests/auth_tests/test_auth_backends_deprecation.py index 78a5d8945a..195239b686 100644 --- a/tests/auth_tests/test_auth_backends_deprecation.py +++ b/tests/auth_tests/test_auth_backends_deprecation.py @@ -13,6 +13,18 @@ class NoRequestBackend: pass +class NoRequestWithKwargs: + def authenticate(self, username=None, password=None, **kwargs): + pass + + +class RequestPositionalArg: + def authenticate(self, request, username=None, password=None, **kwargs): + assert username == 'username' + assert password == 'pass' + assert request is mock_request + + class RequestNotPositionArgBackend: def authenticate(self, username=None, password=None, request=None): assert username == 'username' @@ -34,6 +46,8 @@ class AcceptsRequestBackendTest(SimpleTestCase): method without a request parameter. """ no_request_backend = '%s.NoRequestBackend' % __name__ + no_request_with_kwargs_backend = '%s.NoRequestWithKwargs' % __name__ + request_positional_arg_backend = '%s.RequestPositionalArg' % __name__ request_not_positional_backend = '%s.RequestNotPositionArgBackend' % __name__ request_not_positional_with_used_kwarg_backend = '%s.RequestNotPositionArgWithUsedKwargBackend' % __name__ @@ -79,6 +93,21 @@ class AcceptsRequestBackendTest(SimpleTestCase): "argument." % self.no_request_backend ) + @override_settings(AUTHENTICATION_BACKENDS=[no_request_with_kwargs_backend, request_positional_arg_backend]) + def test_credentials_not_mutated(self): + """ + No problem if a backend doesn't accept `request` and a later one does. + """ + with warnings.catch_warnings(record=True) as warns: + warnings.simplefilter('always') + authenticate(mock_request, username='username', password='pass') + self.assertEqual(len(warns), 1) + self.assertEqual( + str(warns[0].message), + "In %s.authenticate(), move the `request` keyword argument to the " + "first positional argument." % self.no_request_with_kwargs_backend + ) + @override_settings(AUTHENTICATION_BACKENDS=[request_not_positional_with_used_kwarg_backend]) def test_handles_backend_in_kwargs(self): with warnings.catch_warnings(record=True) as warns: