From b89c2a5d9eb70ca36629ef657c98e3371e9a5c4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20G=C3=B3rny?= Date: Fri, 23 Aug 2013 17:49:42 +0200 Subject: [PATCH] Fixed #18171 -- Checked signature of authenticate() to avoid supressing TypeErrors. The current auth backend code catches TypeError to detect backends that do not support specified argumetnts. As a result, any TypeErrors raised within the actual backend code are silenced. In Python 2.7+ and 3.2+ this can be avoided by using inspect.getcallargs(). With this method, we can test whether arguments match the signature without actually calling the function. Thanks David Eyk for the report. --- django/contrib/auth/__init__.py | 6 +++- .../contrib/auth/tests/test_auth_backends.py | 29 +++++++++++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) 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')