[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 a3ba2662cd.
This commit is contained in:
Tim Graham 2018-01-30 19:31:25 -05:00
parent ddc49820f7
commit 55e16f25e9
4 changed files with 38 additions and 0 deletions

View File

@ -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:

View File

@ -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`).

View File

@ -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`).

View File

@ -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: