Loading django/contrib/auth/hashers.py +6 −6 Original line number Diff line number Diff line Loading @@ -47,7 +47,7 @@ def check_password(password, encoded, setter=None, preferred='default'): If setter is specified, it'll be called when you need to regenerate the password. """ if not password or not is_password_usable(encoded): if not is_password_usable(encoded): return False preferred = get_hasher(preferred) Loading @@ -65,10 +65,10 @@ def make_password(password, salt=None, hasher='default'): Turn a plain-text password into a hash for database storage Same as encode() but generates a new random salt. If password is None or blank then UNUSABLE_PASSWORD will be password is None then UNUSABLE_PASSWORD will be returned which disallows logins. """ if not password: if password is None: return UNUSABLE_PASSWORD hasher = get_hasher(hasher) Loading Loading @@ -222,7 +222,7 @@ class PBKDF2PasswordHasher(BasePasswordHasher): digest = hashlib.sha256 def encode(self, password, salt, iterations=None): assert password assert password is not None assert salt and '$' not in salt if not iterations: iterations = self.iterations Loading Loading @@ -350,7 +350,7 @@ class SHA1PasswordHasher(BasePasswordHasher): algorithm = "sha1" def encode(self, password, salt): assert password assert password is not None assert salt and '$' not in salt hash = hashlib.sha1(force_bytes(salt + password)).hexdigest() return "%s$%s$%s" % (self.algorithm, salt, hash) Loading Loading @@ -378,7 +378,7 @@ class MD5PasswordHasher(BasePasswordHasher): algorithm = "md5" def encode(self, password, salt): assert password assert password is not None assert salt and '$' not in salt hash = hashlib.md5(force_bytes(salt + password)).hexdigest() return "%s$%s$%s" % (self.algorithm, salt, hash) Loading django/contrib/auth/tests/test_hashers.py +53 −0 Original line number Diff line number Diff line Loading @@ -32,6 +32,12 @@ class TestUtilsHashPass(unittest.TestCase): self.assertTrue(is_password_usable(encoded)) self.assertTrue(check_password('lètmein', encoded)) self.assertFalse(check_password('lètmeinz', encoded)) # Blank passwords blank_encoded = make_password('') self.assertTrue(blank_encoded.startswith('pbkdf2_sha256$')) self.assertTrue(is_password_usable(blank_encoded)) self.assertTrue(check_password('', blank_encoded)) self.assertFalse(check_password(' ', blank_encoded)) def test_pkbdf2(self): encoded = make_password('lètmein', 'seasalt', 'pbkdf2_sha256') Loading @@ -41,6 +47,12 @@ class TestUtilsHashPass(unittest.TestCase): self.assertTrue(check_password('lètmein', encoded)) self.assertFalse(check_password('lètmeinz', encoded)) self.assertEqual(identify_hasher(encoded).algorithm, "pbkdf2_sha256") # Blank passwords blank_encoded = make_password('', 'seasalt', 'pbkdf2_sha256') self.assertTrue(blank_encoded.startswith('pbkdf2_sha256$')) self.assertTrue(is_password_usable(blank_encoded)) self.assertTrue(check_password('', blank_encoded)) self.assertFalse(check_password(' ', blank_encoded)) def test_sha1(self): encoded = make_password('lètmein', 'seasalt', 'sha1') Loading @@ -50,6 +62,12 @@ class TestUtilsHashPass(unittest.TestCase): self.assertTrue(check_password('lètmein', encoded)) self.assertFalse(check_password('lètmeinz', encoded)) self.assertEqual(identify_hasher(encoded).algorithm, "sha1") # Blank passwords blank_encoded = make_password('', 'seasalt', 'sha1') self.assertTrue(blank_encoded.startswith('sha1$')) self.assertTrue(is_password_usable(blank_encoded)) self.assertTrue(check_password('', blank_encoded)) self.assertFalse(check_password(' ', blank_encoded)) def test_md5(self): encoded = make_password('lètmein', 'seasalt', 'md5') Loading @@ -59,6 +77,12 @@ class TestUtilsHashPass(unittest.TestCase): self.assertTrue(check_password('lètmein', encoded)) self.assertFalse(check_password('lètmeinz', encoded)) self.assertEqual(identify_hasher(encoded).algorithm, "md5") # Blank passwords blank_encoded = make_password('', 'seasalt', 'md5') self.assertTrue(blank_encoded.startswith('md5$')) self.assertTrue(is_password_usable(blank_encoded)) self.assertTrue(check_password('', blank_encoded)) self.assertFalse(check_password(' ', blank_encoded)) def test_unsalted_md5(self): encoded = make_password('lètmein', '', 'unsalted_md5') Loading @@ -72,6 +96,11 @@ class TestUtilsHashPass(unittest.TestCase): self.assertTrue(is_password_usable(alt_encoded)) self.assertTrue(check_password('lètmein', alt_encoded)) self.assertFalse(check_password('lètmeinz', alt_encoded)) # Blank passwords blank_encoded = make_password('', '', 'unsalted_md5') self.assertTrue(is_password_usable(blank_encoded)) self.assertTrue(check_password('', blank_encoded)) self.assertFalse(check_password(' ', blank_encoded)) def test_unsalted_sha1(self): encoded = make_password('lètmein', '', 'unsalted_sha1') Loading @@ -83,6 +112,12 @@ class TestUtilsHashPass(unittest.TestCase): # Raw SHA1 isn't acceptable alt_encoded = encoded[6:] self.assertFalse(check_password('lètmein', alt_encoded)) # Blank passwords blank_encoded = make_password('', '', 'unsalted_sha1') self.assertTrue(blank_encoded.startswith('sha1$')) self.assertTrue(is_password_usable(blank_encoded)) self.assertTrue(check_password('', blank_encoded)) self.assertFalse(check_password(' ', blank_encoded)) @skipUnless(crypt, "no crypt module to generate password.") def test_crypt(self): Loading @@ -92,6 +127,12 @@ class TestUtilsHashPass(unittest.TestCase): self.assertTrue(check_password('lètmei', encoded)) self.assertFalse(check_password('lètmeiz', encoded)) self.assertEqual(identify_hasher(encoded).algorithm, "crypt") # Blank passwords blank_encoded = make_password('', 'ab', 'crypt') self.assertTrue(blank_encoded.startswith('crypt$')) self.assertTrue(is_password_usable(blank_encoded)) self.assertTrue(check_password('', blank_encoded)) self.assertFalse(check_password(' ', blank_encoded)) @skipUnless(bcrypt, "bcrypt not installed") def test_bcrypt_sha256(self): Loading @@ -108,6 +149,12 @@ class TestUtilsHashPass(unittest.TestCase): encoded = make_password(password, hasher='bcrypt_sha256') self.assertTrue(check_password(password, encoded)) self.assertFalse(check_password(password[:72], encoded)) # Blank passwords blank_encoded = make_password('', hasher='bcrypt_sha256') self.assertTrue(blank_encoded.startswith('bcrypt_sha256$')) self.assertTrue(is_password_usable(blank_encoded)) self.assertTrue(check_password('', blank_encoded)) self.assertFalse(check_password(' ', blank_encoded)) @skipUnless(bcrypt, "bcrypt not installed") def test_bcrypt(self): Loading @@ -117,6 +164,12 @@ class TestUtilsHashPass(unittest.TestCase): self.assertTrue(check_password('lètmein', encoded)) self.assertFalse(check_password('lètmeinz', encoded)) self.assertEqual(identify_hasher(encoded).algorithm, "bcrypt") # Blank passwords blank_encoded = make_password('', hasher='bcrypt') self.assertTrue(blank_encoded.startswith('bcrypt$')) self.assertTrue(is_password_usable(blank_encoded)) self.assertTrue(check_password('', blank_encoded)) self.assertFalse(check_password(' ', blank_encoded)) def test_unusable(self): encoded = make_password(None) Loading docs/ref/contrib/auth.txt +16 −0 Original line number Diff line number Diff line Loading @@ -132,12 +132,28 @@ Methods password hashing. Doesn't save the :class:`~django.contrib.auth.models.User` object. When the ``raw_password`` is ``None``, the password will be set to an unusable password, as if :meth:`~django.contrib.auth.models.User.set_unusable_password()` were used. .. versionchanged:: 1.6 In Django 1.4 and 1.5, a blank string was unintentionally stored as an unsable password. .. method:: check_password(raw_password) Returns ``True`` if the given raw string is the correct password for the user. (This takes care of the password hashing in making the comparison.) .. versionchanged:: 1.6 In Django 1.4 and 1.5, a blank string was unintentionally considered to be an unusable password, resulting in this method returning ``False`` for such a password. .. method:: set_unusable_password() Marks the user as having no password set. This isn't the same as Loading docs/releases/1.6.txt +9 −0 Original line number Diff line number Diff line Loading @@ -701,6 +701,15 @@ Miscellaneous * :class:`~django.views.generic.base.RedirectView` now has a `pattern_name` attribute which allows it to choose the target by reversing the URL. * In Django 1.4 and 1.5, a blank string was unintentionally not considered to be a valid password. This meant :meth:`~django.contrib.auth.models.User.set_password()` would save a blank password as an unusable password like :meth:`~django.contrib.auth.models.User.set_unusable_password()` does, and thus :meth:`~django.contrib.auth.models.User.check_password()` always returned ``False`` for blank passwords. This has been corrected in this release: blank passwords are now valid. Features deprecated in 1.6 ========================== Loading docs/topics/auth/customizing.txt +16 −0 Original line number Diff line number Diff line Loading @@ -583,12 +583,28 @@ The following methods are available on any subclass of password hashing. Doesn't save the :class:`~django.contrib.auth.models.AbstractBaseUser` object. When the raw_password is ``None``, the password will be set to an unusable password, as if :meth:`~django.contrib.auth.models.AbstractBaseUser.set_unusable_password()` were used. .. versionchanged:: 1.6 In Django 1.4 and 1.5, a blank string was unintentionally stored as an unsable password as well. .. method:: models.AbstractBaseUser.check_password(raw_password) Returns ``True`` if the given raw string is the correct password for the user. (This takes care of the password hashing in making the comparison.) .. versionchanged:: 1.6 In Django 1.4 and 1.5, a blank string was unintentionally considered to be an unusable password, resulting in this method returning ``False`` for such a password. .. method:: models.AbstractBaseUser.set_unusable_password() Marks the user as having no password set. This isn't the same as Loading Loading
django/contrib/auth/hashers.py +6 −6 Original line number Diff line number Diff line Loading @@ -47,7 +47,7 @@ def check_password(password, encoded, setter=None, preferred='default'): If setter is specified, it'll be called when you need to regenerate the password. """ if not password or not is_password_usable(encoded): if not is_password_usable(encoded): return False preferred = get_hasher(preferred) Loading @@ -65,10 +65,10 @@ def make_password(password, salt=None, hasher='default'): Turn a plain-text password into a hash for database storage Same as encode() but generates a new random salt. If password is None or blank then UNUSABLE_PASSWORD will be password is None then UNUSABLE_PASSWORD will be returned which disallows logins. """ if not password: if password is None: return UNUSABLE_PASSWORD hasher = get_hasher(hasher) Loading Loading @@ -222,7 +222,7 @@ class PBKDF2PasswordHasher(BasePasswordHasher): digest = hashlib.sha256 def encode(self, password, salt, iterations=None): assert password assert password is not None assert salt and '$' not in salt if not iterations: iterations = self.iterations Loading Loading @@ -350,7 +350,7 @@ class SHA1PasswordHasher(BasePasswordHasher): algorithm = "sha1" def encode(self, password, salt): assert password assert password is not None assert salt and '$' not in salt hash = hashlib.sha1(force_bytes(salt + password)).hexdigest() return "%s$%s$%s" % (self.algorithm, salt, hash) Loading Loading @@ -378,7 +378,7 @@ class MD5PasswordHasher(BasePasswordHasher): algorithm = "md5" def encode(self, password, salt): assert password assert password is not None assert salt and '$' not in salt hash = hashlib.md5(force_bytes(salt + password)).hexdigest() return "%s$%s$%s" % (self.algorithm, salt, hash) Loading
django/contrib/auth/tests/test_hashers.py +53 −0 Original line number Diff line number Diff line Loading @@ -32,6 +32,12 @@ class TestUtilsHashPass(unittest.TestCase): self.assertTrue(is_password_usable(encoded)) self.assertTrue(check_password('lètmein', encoded)) self.assertFalse(check_password('lètmeinz', encoded)) # Blank passwords blank_encoded = make_password('') self.assertTrue(blank_encoded.startswith('pbkdf2_sha256$')) self.assertTrue(is_password_usable(blank_encoded)) self.assertTrue(check_password('', blank_encoded)) self.assertFalse(check_password(' ', blank_encoded)) def test_pkbdf2(self): encoded = make_password('lètmein', 'seasalt', 'pbkdf2_sha256') Loading @@ -41,6 +47,12 @@ class TestUtilsHashPass(unittest.TestCase): self.assertTrue(check_password('lètmein', encoded)) self.assertFalse(check_password('lètmeinz', encoded)) self.assertEqual(identify_hasher(encoded).algorithm, "pbkdf2_sha256") # Blank passwords blank_encoded = make_password('', 'seasalt', 'pbkdf2_sha256') self.assertTrue(blank_encoded.startswith('pbkdf2_sha256$')) self.assertTrue(is_password_usable(blank_encoded)) self.assertTrue(check_password('', blank_encoded)) self.assertFalse(check_password(' ', blank_encoded)) def test_sha1(self): encoded = make_password('lètmein', 'seasalt', 'sha1') Loading @@ -50,6 +62,12 @@ class TestUtilsHashPass(unittest.TestCase): self.assertTrue(check_password('lètmein', encoded)) self.assertFalse(check_password('lètmeinz', encoded)) self.assertEqual(identify_hasher(encoded).algorithm, "sha1") # Blank passwords blank_encoded = make_password('', 'seasalt', 'sha1') self.assertTrue(blank_encoded.startswith('sha1$')) self.assertTrue(is_password_usable(blank_encoded)) self.assertTrue(check_password('', blank_encoded)) self.assertFalse(check_password(' ', blank_encoded)) def test_md5(self): encoded = make_password('lètmein', 'seasalt', 'md5') Loading @@ -59,6 +77,12 @@ class TestUtilsHashPass(unittest.TestCase): self.assertTrue(check_password('lètmein', encoded)) self.assertFalse(check_password('lètmeinz', encoded)) self.assertEqual(identify_hasher(encoded).algorithm, "md5") # Blank passwords blank_encoded = make_password('', 'seasalt', 'md5') self.assertTrue(blank_encoded.startswith('md5$')) self.assertTrue(is_password_usable(blank_encoded)) self.assertTrue(check_password('', blank_encoded)) self.assertFalse(check_password(' ', blank_encoded)) def test_unsalted_md5(self): encoded = make_password('lètmein', '', 'unsalted_md5') Loading @@ -72,6 +96,11 @@ class TestUtilsHashPass(unittest.TestCase): self.assertTrue(is_password_usable(alt_encoded)) self.assertTrue(check_password('lètmein', alt_encoded)) self.assertFalse(check_password('lètmeinz', alt_encoded)) # Blank passwords blank_encoded = make_password('', '', 'unsalted_md5') self.assertTrue(is_password_usable(blank_encoded)) self.assertTrue(check_password('', blank_encoded)) self.assertFalse(check_password(' ', blank_encoded)) def test_unsalted_sha1(self): encoded = make_password('lètmein', '', 'unsalted_sha1') Loading @@ -83,6 +112,12 @@ class TestUtilsHashPass(unittest.TestCase): # Raw SHA1 isn't acceptable alt_encoded = encoded[6:] self.assertFalse(check_password('lètmein', alt_encoded)) # Blank passwords blank_encoded = make_password('', '', 'unsalted_sha1') self.assertTrue(blank_encoded.startswith('sha1$')) self.assertTrue(is_password_usable(blank_encoded)) self.assertTrue(check_password('', blank_encoded)) self.assertFalse(check_password(' ', blank_encoded)) @skipUnless(crypt, "no crypt module to generate password.") def test_crypt(self): Loading @@ -92,6 +127,12 @@ class TestUtilsHashPass(unittest.TestCase): self.assertTrue(check_password('lètmei', encoded)) self.assertFalse(check_password('lètmeiz', encoded)) self.assertEqual(identify_hasher(encoded).algorithm, "crypt") # Blank passwords blank_encoded = make_password('', 'ab', 'crypt') self.assertTrue(blank_encoded.startswith('crypt$')) self.assertTrue(is_password_usable(blank_encoded)) self.assertTrue(check_password('', blank_encoded)) self.assertFalse(check_password(' ', blank_encoded)) @skipUnless(bcrypt, "bcrypt not installed") def test_bcrypt_sha256(self): Loading @@ -108,6 +149,12 @@ class TestUtilsHashPass(unittest.TestCase): encoded = make_password(password, hasher='bcrypt_sha256') self.assertTrue(check_password(password, encoded)) self.assertFalse(check_password(password[:72], encoded)) # Blank passwords blank_encoded = make_password('', hasher='bcrypt_sha256') self.assertTrue(blank_encoded.startswith('bcrypt_sha256$')) self.assertTrue(is_password_usable(blank_encoded)) self.assertTrue(check_password('', blank_encoded)) self.assertFalse(check_password(' ', blank_encoded)) @skipUnless(bcrypt, "bcrypt not installed") def test_bcrypt(self): Loading @@ -117,6 +164,12 @@ class TestUtilsHashPass(unittest.TestCase): self.assertTrue(check_password('lètmein', encoded)) self.assertFalse(check_password('lètmeinz', encoded)) self.assertEqual(identify_hasher(encoded).algorithm, "bcrypt") # Blank passwords blank_encoded = make_password('', hasher='bcrypt') self.assertTrue(blank_encoded.startswith('bcrypt$')) self.assertTrue(is_password_usable(blank_encoded)) self.assertTrue(check_password('', blank_encoded)) self.assertFalse(check_password(' ', blank_encoded)) def test_unusable(self): encoded = make_password(None) Loading
docs/ref/contrib/auth.txt +16 −0 Original line number Diff line number Diff line Loading @@ -132,12 +132,28 @@ Methods password hashing. Doesn't save the :class:`~django.contrib.auth.models.User` object. When the ``raw_password`` is ``None``, the password will be set to an unusable password, as if :meth:`~django.contrib.auth.models.User.set_unusable_password()` were used. .. versionchanged:: 1.6 In Django 1.4 and 1.5, a blank string was unintentionally stored as an unsable password. .. method:: check_password(raw_password) Returns ``True`` if the given raw string is the correct password for the user. (This takes care of the password hashing in making the comparison.) .. versionchanged:: 1.6 In Django 1.4 and 1.5, a blank string was unintentionally considered to be an unusable password, resulting in this method returning ``False`` for such a password. .. method:: set_unusable_password() Marks the user as having no password set. This isn't the same as Loading
docs/releases/1.6.txt +9 −0 Original line number Diff line number Diff line Loading @@ -701,6 +701,15 @@ Miscellaneous * :class:`~django.views.generic.base.RedirectView` now has a `pattern_name` attribute which allows it to choose the target by reversing the URL. * In Django 1.4 and 1.5, a blank string was unintentionally not considered to be a valid password. This meant :meth:`~django.contrib.auth.models.User.set_password()` would save a blank password as an unusable password like :meth:`~django.contrib.auth.models.User.set_unusable_password()` does, and thus :meth:`~django.contrib.auth.models.User.check_password()` always returned ``False`` for blank passwords. This has been corrected in this release: blank passwords are now valid. Features deprecated in 1.6 ========================== Loading
docs/topics/auth/customizing.txt +16 −0 Original line number Diff line number Diff line Loading @@ -583,12 +583,28 @@ The following methods are available on any subclass of password hashing. Doesn't save the :class:`~django.contrib.auth.models.AbstractBaseUser` object. When the raw_password is ``None``, the password will be set to an unusable password, as if :meth:`~django.contrib.auth.models.AbstractBaseUser.set_unusable_password()` were used. .. versionchanged:: 1.6 In Django 1.4 and 1.5, a blank string was unintentionally stored as an unsable password as well. .. method:: models.AbstractBaseUser.check_password(raw_password) Returns ``True`` if the given raw string is the correct password for the user. (This takes care of the password hashing in making the comparison.) .. versionchanged:: 1.6 In Django 1.4 and 1.5, a blank string was unintentionally considered to be an unusable password, resulting in this method returning ``False`` for such a password. .. method:: models.AbstractBaseUser.set_unusable_password() Marks the user as having no password set. This isn't the same as Loading