Commit 2f42bbab authored by Tim Graham's avatar Tim Graham
Browse files

[1.6.x] Fixed #21535 -- Fixed password hash iteration upgrade.

Thanks jared_mess for the report.

Backport of fddb0131 from master
parent 14ddc1b5
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -57,7 +57,7 @@ def check_password(password, encoded, setter=None, preferred='default'):

    must_update = hasher.algorithm != preferred.algorithm
    if not must_update:
        must_update = hasher.must_update(encoded)
        must_update = preferred.must_update(encoded)
    is_correct = hasher.verify(password, encoded)
    if setter and is_correct and must_update:
        setter(password)
+33 −1
Original line number Diff line number Diff line
@@ -5,6 +5,7 @@ from django.conf.global_settings import PASSWORD_HASHERS as default_hashers
from django.contrib.auth.hashers import (is_password_usable, BasePasswordHasher,
    check_password, make_password, PBKDF2PasswordHasher, load_hashers, PBKDF2SHA1PasswordHasher,
    get_hasher, identify_hasher, UNUSABLE_PASSWORD_PREFIX, UNUSABLE_PASSWORD_SUFFIX_LENGTH)
from django.test import SimpleTestCase
from django.utils import six
from django.utils import unittest
from django.utils.unittest import skipUnless
@@ -21,7 +22,11 @@ except ImportError:
    bcrypt = None


class TestUtilsHashPass(unittest.TestCase):
class PBKDF2SingleIterationHasher(PBKDF2PasswordHasher):
    iterations = 1


class TestUtilsHashPass(SimpleTestCase):

    def setUp(self):
        load_hashers(password_hashers=default_hashers)
@@ -274,6 +279,33 @@ class TestUtilsHashPass(unittest.TestCase):
        finally:
            hasher.iterations = old_iterations

    def test_pbkdf2_upgrade_new_hasher(self):
        self.assertEqual('pbkdf2_sha256', get_hasher('default').algorithm)
        hasher = get_hasher('default')
        self.assertNotEqual(hasher.iterations, 1)

        state = {'upgraded': False}

        def setter(password):
            state['upgraded'] = True

        with self.settings(PASSWORD_HASHERS=[
                'django.contrib.auth.tests.test_hashers.PBKDF2SingleIterationHasher']):
            encoded = make_password('letmein')
            algo, iterations, salt, hash = encoded.split('$', 3)
            self.assertEqual(iterations, '1')

            # Check that no upgrade is triggerd
            self.assertTrue(check_password('letmein', encoded, setter))
            self.assertFalse(state['upgraded'])

        # Revert to the old iteration count and check if the password would get
        # updated to the new iteration count.
        with self.settings(PASSWORD_HASHERS=[
                'django.contrib.auth.hashers.PBKDF2PasswordHasher',
                'django.contrib.auth.tests.test_hashers.PBKDF2SingleIterationHasher']):
            self.assertTrue(check_password('letmein', encoded, setter))
            self.assertTrue(state['upgraded'])

    def test_load_library_no_algorithm(self):
        with self.assertRaises(ValueError) as e:
+1 −0
Original line number Diff line number Diff line
@@ -40,3 +40,4 @@ Bug fixes
* Fixed test client ``logout()`` method when using the cookie-based session
  backend (#21448).
* Fixed a crash when a ``GeometryField`` uses a non-geometric widget (#21496).
* Fixed password hash upgrade when changing the iteration count (#21535).