summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFlorian Apolloner <florian@apolloner.eu>2013-09-24 20:52:20 +0200
committerFlorian Apolloner <florian@apolloner.eu>2013-10-21 20:33:03 +0200
commit823951ec55f17373561b4e97ecbe369c2cac364c (patch)
tree983b8661a22e4d27925b423b7bfa555905c997e5
parent37aea82b1cf1175cb5d8de80fb8b0902b3d303e5 (diff)
downloaddjango-823951ec55f17373561b4e97ecbe369c2cac364c.tar.gz
[1.6.x] Force update of the password on iteration count changes.
Backport of 7d0d0dbf26a3c0d16e9c2b930fd6d7b89f215946 from master.
-rw-r--r--django/contrib/auth/hashers.py9
-rw-r--r--django/contrib/auth/tests/test_hashers.py31
2 files changed, 40 insertions, 0 deletions
diff --git a/django/contrib/auth/hashers.py b/django/contrib/auth/hashers.py
index ae315ff6c3..e9e8215c09 100644
--- a/django/contrib/auth/hashers.py
+++ b/django/contrib/auth/hashers.py
@@ -56,6 +56,8 @@ def check_password(password, encoded, setter=None, preferred='default'):
hasher = identify_hasher(encoded)
must_update = hasher.algorithm != preferred.algorithm
+ if not must_update:
+ must_update = hasher.must_update(encoded)
is_correct = hasher.verify(password, encoded)
if setter and is_correct and must_update:
setter(password)
@@ -212,6 +214,9 @@ class BasePasswordHasher(object):
"""
raise NotImplementedError()
+ def must_update(self, encoded):
+ return False
+
class PBKDF2PasswordHasher(BasePasswordHasher):
"""
@@ -250,6 +255,10 @@ class PBKDF2PasswordHasher(BasePasswordHasher):
(_('hash'), mask_hash(hash)),
])
+ def must_update(self, encoded):
+ algorithm, iterations, salt, hash = encoded.split('$', 3)
+ return int(iterations) != self.iterations
+
class PBKDF2SHA1PasswordHasher(PBKDF2PasswordHasher):
"""
diff --git a/django/contrib/auth/tests/test_hashers.py b/django/contrib/auth/tests/test_hashers.py
index 0af475d353..f0132bd43f 100644
--- a/django/contrib/auth/tests/test_hashers.py
+++ b/django/contrib/auth/tests/test_hashers.py
@@ -244,6 +244,37 @@ class TestUtilsHashPass(unittest.TestCase):
self.assertFalse(check_password('WRONG', encoded, setter))
self.assertFalse(state['upgraded'])
+ def test_pbkdf2_upgrade(self):
+ self.assertEqual('pbkdf2_sha256', get_hasher('default').algorithm)
+ hasher = get_hasher('default')
+ self.assertNotEqual(hasher.iterations, 1)
+
+ old_iterations = hasher.iterations
+ try:
+ # Generate a password with 1 iteration.
+ hasher.iterations = 1
+ encoded = make_password('letmein')
+ algo, iterations, salt, hash = encoded.split('$', 3)
+ self.assertEqual(iterations, '1')
+
+ state = {'upgraded': False}
+ def setter(password):
+ state['upgraded'] = True
+
+ # Check that no upgrade is triggerd
+ self.assertTrue(check_password('letmein', encoded, setter))
+ self.assertFalse(state['upgraded'])
+
+ # Revert to the old iteration count and ...
+ hasher.iterations = old_iterations
+
+ # ... check if the password would get updated to the new iteration count.
+ self.assertTrue(check_password('letmein', encoded, setter))
+ self.assertTrue(state['upgraded'])
+ finally:
+ hasher.iterations = old_iterations
+
+
def test_load_library_no_algorithm(self):
with self.assertRaises(ValueError) as e:
BasePasswordHasher()._load_library()