Commit b89c2a5d authored by Michał Górny's avatar Michał Górny Committed by Tim Graham
Browse files

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.
parent 5b889b24
Loading
Loading
Loading
Loading
+5 −1
Original line number Diff line number Diff line
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
+29 −0
Original line number Diff line number Diff line
@@ -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')