diff --git a/django/contrib/auth/__init__.py b/django/contrib/auth/__init__.py index d96300c503..b601fecc3e 100644 --- a/django/contrib/auth/__init__.py +++ b/django/contrib/auth/__init__.py @@ -1,13 +1,11 @@ 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 RemovedInDjango21Warning from django.utils.module_loading import import_string from django.utils.translation import LANGUAGE_SESSION_KEY @@ -67,7 +65,12 @@ def authenticate(request=None, **credentials): """ for backend, backend_path in _get_backends(return_tuples=True): try: - user = _authenticate_with_backend(backend, backend_path, request, credentials) + inspect.getcallargs(backend.authenticate, request, **credentials) + except TypeError: + # This backend doesn't accept these credentials as arguments. Try the next one. + continue + try: + user = backend.authenticate(request, **credentials) except PermissionDenied: # This backend says to stop in our tracks - this user should not be allowed in at all. break @@ -81,40 +84,6 @@ def authenticate(request=None, **credentials): user_login_failed.send(sender=__name__, credentials=_clean_credentials(credentials), request=request) -def _authenticate_with_backend(backend, backend_path, request, credentials): - args = (request,) - # Does the backend accept a request argument? - try: - inspect.getcallargs(backend.authenticate, request, **credentials) - except TypeError: - args = () - credentials.pop('request', None) - # Does the backend accept a request keyword argument? - try: - inspect.getcallargs(backend.authenticate, request=request, **credentials) - except TypeError: - # Does the backend accept credentials without request? - try: - inspect.getcallargs(backend.authenticate, **credentials) - except TypeError: - # This backend doesn't accept these credentials as arguments. Try the next one. - return None - else: - warnings.warn( - "Update %s.authenticate() to accept a positional " - "`request` argument." % backend_path, - RemovedInDjango21Warning - ) - else: - credentials['request'] = request - warnings.warn( - "In %s.authenticate(), move the `request` keyword argument " - "to the first positional argument." % backend_path, - RemovedInDjango21Warning - ) - return backend.authenticate(*args, **credentials) - - def login(request, user, backend=None): """ Persist a user id and a backend in the request. This way a user doesn't diff --git a/docs/releases/2.1.txt b/docs/releases/2.1.txt index 909d537c39..085ad3aa7e 100644 --- a/docs/releases/2.1.txt +++ b/docs/releases/2.1.txt @@ -246,3 +246,6 @@ how to remove usage of these features. tag is removed. * ``DatabaseIntrospection.get_indexes()`` is removed. + +* The ``authenticate()`` method of authentication backends requires ``request`` + as the first positional argument. diff --git a/docs/topics/auth/customizing.txt b/docs/topics/auth/customizing.txt index 84f0782eb5..a61d9f9c8b 100644 --- a/docs/topics/auth/customizing.txt +++ b/docs/topics/auth/customizing.txt @@ -167,11 +167,6 @@ object the first time a user authenticates:: except User.DoesNotExist: return None -.. versionchanged:: 1.11 - - The ``request`` parameter was added to ``authenticate()`` and support for - backends that don't accept it will be removed in Django 2.1. - .. _authorization_methods: Handling authorization in custom backends diff --git a/tests/auth_tests/test_auth_backends_deprecation.py b/tests/auth_tests/test_auth_backends_deprecation.py deleted file mode 100644 index 78a5d8945a..0000000000 --- a/tests/auth_tests/test_auth_backends_deprecation.py +++ /dev/null @@ -1,92 +0,0 @@ -import warnings - -from django.contrib.auth import authenticate -from django.test import SimpleTestCase, override_settings - -mock_request = object() -mock_backend = object() - - -class NoRequestBackend: - def authenticate(self, username=None, password=None): - # Doesn't accept a request parameter. - pass - - -class RequestNotPositionArgBackend: - def authenticate(self, username=None, password=None, request=None): - assert username == 'username' - assert password == 'pass' - assert request is mock_request - - -class RequestNotPositionArgWithUsedKwargBackend: - def authenticate(self, username=None, password=None, request=None, backend=None): - assert username == 'username' - assert password == 'pass' - assert request is mock_request - assert backend is mock_backend - - -class AcceptsRequestBackendTest(SimpleTestCase): - """ - A deprecation warning is shown for backends that have an authenticate() - method without a request parameter. - """ - no_request_backend = '%s.NoRequestBackend' % __name__ - request_not_positional_backend = '%s.RequestNotPositionArgBackend' % __name__ - request_not_positional_with_used_kwarg_backend = '%s.RequestNotPositionArgWithUsedKwargBackend' % __name__ - - @override_settings(AUTHENTICATION_BACKENDS=[no_request_backend]) - def test_no_request_deprecation_warning(self): - with warnings.catch_warnings(record=True) as warns: - warnings.simplefilter('always') - authenticate(username='test', password='test') - self.assertEqual(len(warns), 1) - self.assertEqual( - str(warns[0].message), - "Update %s.authenticate() to accept a positional `request` " - "argument." % self.no_request_backend - ) - - @override_settings(AUTHENTICATION_BACKENDS=[request_not_positional_backend]) - def test_request_keyword_arg_deprecation_warning(self): - with warnings.catch_warnings(record=True) as warns: - warnings.simplefilter('always') - authenticate(username='username', password='pass', request=mock_request) - 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.request_not_positional_backend - ) - - @override_settings(AUTHENTICATION_BACKENDS=[request_not_positional_backend, no_request_backend]) - def test_both_types_of_deprecation_warning(self): - with warnings.catch_warnings(record=True) as warns: - warnings.simplefilter('always') - authenticate(mock_request, username='username', password='pass') - - self.assertEqual(len(warns), 2) - self.assertEqual( - str(warns[0].message), - "In %s.authenticate(), move the `request` keyword argument to the " - "first positional argument." % self.request_not_positional_backend - ) - self.assertEqual( - str(warns[1].message), - "Update %s.authenticate() to accept a positional `request` " - "argument." % self.no_request_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: - warnings.simplefilter('always') - authenticate(username='username', password='pass', request=mock_request, backend=mock_backend) - 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.request_not_positional_with_used_kwarg_backend - )