summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTim Graham <timograham@gmail.com>2015-02-26 14:04:24 -0500
committerTim Graham <timograham@gmail.com>2015-03-30 18:52:59 -0400
commitb86abbceb9a96d7a0fe18047c8fcd6fca90a2f3e (patch)
tree30da9fcf251ff7a7cb33a1329e76669c7772de83
parente4cf8c8420634d6f2dc8ce873246256ce635972d (diff)
Fixed #24115 -- Allowed bcrypt hashers to upgrade passwords on rounds change.
Thanks Florian Apolloner for the review.
-rw-r--r--django/contrib/auth/hashers.py4
-rw-r--r--docs/releases/1.9.txt3
-rw-r--r--tests/auth_tests/test_hashers.py32
3 files changed, 39 insertions, 0 deletions
diff --git a/django/contrib/auth/hashers.py b/django/contrib/auth/hashers.py
index b56f74f0d9..0188f4a4e7 100644
--- a/django/contrib/auth/hashers.py
+++ b/django/contrib/auth/hashers.py
@@ -337,6 +337,10 @@ class BCryptSHA256PasswordHasher(BasePasswordHasher):
(_('checksum'), mask_hash(checksum)),
])
+ def must_update(self, encoded):
+ algorithm, empty, algostr, rounds, data = encoded.split('$', 4)
+ return int(rounds) != self.rounds
+
class BCryptPasswordHasher(BCryptSHA256PasswordHasher):
"""
diff --git a/docs/releases/1.9.txt b/docs/releases/1.9.txt
index 41ac4f27c9..3bcbd8bbfe 100644
--- a/docs/releases/1.9.txt
+++ b/docs/releases/1.9.txt
@@ -56,6 +56,9 @@ Minor features
subclassed ``django.contrib.auth.hashers.PBKDF2PasswordHasher`` to change the
default value.
+* The ``BCryptSHA256PasswordHasher`` will now update passwords if its
+ ``rounds`` attribute is changed.
+
:mod:`django.contrib.gis`
^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/tests/auth_tests/test_hashers.py b/tests/auth_tests/test_hashers.py
index f264926aff..993010b2f4 100644
--- a/tests/auth_tests/test_hashers.py
+++ b/tests/auth_tests/test_hashers.py
@@ -177,6 +177,38 @@ class TestUtilsHashPass(SimpleTestCase):
self.assertTrue(check_password('', blank_encoded))
self.assertFalse(check_password(' ', blank_encoded))
+ @skipUnless(bcrypt, "bcrypt not installed")
+ def test_bcrypt_upgrade(self):
+ hasher = get_hasher('bcrypt')
+ self.assertEqual('bcrypt', hasher.algorithm)
+ self.assertNotEqual(hasher.rounds, 4)
+
+ old_rounds = hasher.rounds
+ try:
+ # Generate a password with 4 rounds.
+ hasher.rounds = 4
+ encoded = make_password('letmein', hasher='bcrypt')
+ rounds = hasher.safe_summary(encoded)['work factor']
+ self.assertEqual(rounds, '04')
+
+ state = {'upgraded': False}
+
+ def setter(password):
+ state['upgraded'] = True
+
+ # Check that no upgrade is triggered.
+ self.assertTrue(check_password('letmein', encoded, setter, 'bcrypt'))
+ self.assertFalse(state['upgraded'])
+
+ # Revert to the old rounds count and ...
+ hasher.rounds = old_rounds
+
+ # ... check if the password would get updated to the new count.
+ self.assertTrue(check_password('letmein', encoded, setter, 'bcrypt'))
+ self.assertTrue(state['upgraded'])
+ finally:
+ hasher.rounds = old_rounds
+
def test_unusable(self):
encoded = make_password(None)
self.assertEqual(len(encoded), len(UNUSABLE_PASSWORD_PREFIX) + UNUSABLE_PASSWORD_SUFFIX_LENGTH)