Fixed #20760 -- Reduced timing variation in ModelBackend.
Thanks jpaglier and erikr.
This commit is contained in:
parent
e716518ad2
commit
5dbca13f3b
|
@ -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):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -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):
|
||||||
|
|
Loading…
Reference in New Issue