Fixed #20760 -- Reduced timing variation in ModelBackend.

Thanks jpaglier and erikr.
This commit is contained in:
Aymeric Augustin 2013-07-23 15:41:09 +02:00
parent e716518ad2
commit 5dbca13f3b
2 changed files with 27 additions and 2 deletions

View File

@ -17,7 +17,9 @@ class ModelBackend(object):
if user.check_password(password): if user.check_password(password):
return user return user
except UserModel.DoesNotExist: except UserModel.DoesNotExist:
return None # Run the default password hasher once to reduce the timing
# difference between an existing and a non-existing user (#20760).
UserModel().set_password(password)
def get_group_permissions(self, user_obj, obj=None): def get_group_permissions(self, user_obj, obj=None):
""" """

View File

@ -12,6 +12,17 @@ from django.contrib.auth import authenticate, get_user
from django.http import HttpRequest from django.http import HttpRequest
from django.test import TestCase from django.test import TestCase
from django.test.utils import override_settings from django.test.utils import override_settings
from django.contrib.auth.hashers import MD5PasswordHasher
class CountingMD5PasswordHasher(MD5PasswordHasher):
"""Hasher that counts how many times it computes a hash."""
calls = 0
def encode(self, *args, **kwargs):
type(self).calls += 1
return super(CountingMD5PasswordHasher, self).encode(*args, **kwargs)
class BaseModelBackendTest(object): class BaseModelBackendTest(object):
@ -107,10 +118,22 @@ class BaseModelBackendTest(object):
self.assertEqual(user.get_all_permissions(), set(['auth.test'])) self.assertEqual(user.get_all_permissions(), set(['auth.test']))
def test_get_all_superuser_permissions(self): def test_get_all_superuser_permissions(self):
"A superuser has all permissions. Refs #14795" """A superuser has all permissions. Refs #14795."""
user = self.UserModel._default_manager.get(pk=self.superuser.pk) user = self.UserModel._default_manager.get(pk=self.superuser.pk)
self.assertEqual(len(user.get_all_permissions()), len(Permission.objects.all())) self.assertEqual(len(user.get_all_permissions()), len(Permission.objects.all()))
@override_settings(PASSWORD_HASHERS=('django.contrib.auth.tests.test_auth_backends.CountingMD5PasswordHasher',))
def test_authentication_timing(self):
"""Hasher is run once regardless of whether the user exists. Refs #20760."""
CountingMD5PasswordHasher.calls = 0
username = getattr(self.user, self.UserModel.USERNAME_FIELD)
authenticate(username=username, password='test')
self.assertEqual(CountingMD5PasswordHasher.calls, 1)
CountingMD5PasswordHasher.calls = 0
authenticate(username='no_such_user', password='test')
self.assertEqual(CountingMD5PasswordHasher.calls, 1)
@skipIfCustomUser @skipIfCustomUser
class ModelBackendTest(BaseModelBackendTest, TestCase): class ModelBackendTest(BaseModelBackendTest, TestCase):