diff --git a/django/contrib/auth/__init__.py b/django/contrib/auth/__init__.py index 2f620a34fe..4ef5c0b2cd 100644 --- a/django/contrib/auth/__init__.py +++ b/django/contrib/auth/__init__.py @@ -1,3 +1,4 @@ +import inspect import re from django.conf import settings @@ -46,10 +47,13 @@ def authenticate(**credentials): """ for backend in get_backends(): try: - user = backend.authenticate(**credentials) + inspect.getcallargs(backend.authenticate, **credentials) except TypeError: # This backend doesn't accept these credentials as arguments. Try the next one. continue + + try: + user = backend.authenticate(**credentials) except PermissionDenied: # This backend says to stop in our tracks - this user should not be allowed in at all. return None diff --git a/django/contrib/auth/tests/test_auth_backends.py b/django/contrib/auth/tests/test_auth_backends.py index 4e83d786cf..4afe7c026c 100644 --- a/django/contrib/auth/tests/test_auth_backends.py +++ b/django/contrib/auth/tests/test_auth_backends.py @@ -480,3 +480,32 @@ class ChangedBackendSettingsTest(TestCase): # anonymous as the backend is not longer available. self.assertIsNotNone(user) self.assertTrue(user.is_anonymous()) + + +class TypeErrorBackend(object): + """ + Always raises TypeError. + """ + supports_object_permissions = True + supports_anonymous_user = True + supports_inactive_user = True + + def authenticate(self, username=None, password=None): + raise TypeError + + +@skipIfCustomUser +class TypeErrorBackendTest(TestCase): + """ + Tests that a TypeError within a backend is propagated properly. + + Regression test for ticket #18171 + """ + backend = 'django.contrib.auth.tests.test_auth_backends.TypeErrorBackend' + + def setUp(self): + self.user1 = User.objects.create_user('test', 'test@example.com', 'test') + + @override_settings(AUTHENTICATION_BACKENDS=(backend, )) + def test_type_error_raised(self): + self.assertRaises(TypeError, authenticate, username='test', password='test')