Force update of the password on iteration count changes.
This commit is contained in:
parent
1e4f53a6eb
commit
7d0d0dbf26
|
@ -56,6 +56,8 @@ def check_password(password, encoded, setter=None, preferred='default'):
|
||||||
hasher = identify_hasher(encoded)
|
hasher = identify_hasher(encoded)
|
||||||
|
|
||||||
must_update = hasher.algorithm != preferred.algorithm
|
must_update = hasher.algorithm != preferred.algorithm
|
||||||
|
if not must_update:
|
||||||
|
must_update = hasher.must_update(encoded)
|
||||||
is_correct = hasher.verify(password, encoded)
|
is_correct = hasher.verify(password, encoded)
|
||||||
if setter and is_correct and must_update:
|
if setter and is_correct and must_update:
|
||||||
setter(password)
|
setter(password)
|
||||||
|
@ -212,6 +214,9 @@ class BasePasswordHasher(object):
|
||||||
"""
|
"""
|
||||||
raise NotImplementedError('subclasses of BasePasswordHasher must provide a safe_summary() method')
|
raise NotImplementedError('subclasses of BasePasswordHasher must provide a safe_summary() method')
|
||||||
|
|
||||||
|
def must_update(self, encoded):
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
class PBKDF2PasswordHasher(BasePasswordHasher):
|
class PBKDF2PasswordHasher(BasePasswordHasher):
|
||||||
"""
|
"""
|
||||||
|
@ -250,6 +255,10 @@ class PBKDF2PasswordHasher(BasePasswordHasher):
|
||||||
(_('hash'), mask_hash(hash)),
|
(_('hash'), mask_hash(hash)),
|
||||||
])
|
])
|
||||||
|
|
||||||
|
def must_update(self, encoded):
|
||||||
|
algorithm, iterations, salt, hash = encoded.split('$', 3)
|
||||||
|
return int(iterations) != self.iterations
|
||||||
|
|
||||||
|
|
||||||
class PBKDF2SHA1PasswordHasher(PBKDF2PasswordHasher):
|
class PBKDF2SHA1PasswordHasher(PBKDF2PasswordHasher):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -245,6 +245,37 @@ class TestUtilsHashPass(unittest.TestCase):
|
||||||
self.assertFalse(check_password('WRONG', encoded, setter))
|
self.assertFalse(check_password('WRONG', encoded, setter))
|
||||||
self.assertFalse(state['upgraded'])
|
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):
|
def test_load_library_no_algorithm(self):
|
||||||
with self.assertRaises(ValueError) as e:
|
with self.assertRaises(ValueError) as e:
|
||||||
BasePasswordHasher()._load_library()
|
BasePasswordHasher()._load_library()
|
||||||
|
|
Loading…
Reference in New Issue