mirror of
https://github.com/django/django.git
synced 2025-03-12 02:12:38 +00:00
Refs #33691 -- Removed django.contrib.auth.hashers.CryptPasswordHasher per deprecation timeline.
This commit is contained in:
parent
ce7b4f39e3
commit
b5ac6e78f8
@ -17,7 +17,7 @@ from django.utils.crypto import (
|
|||||||
md5,
|
md5,
|
||||||
pbkdf2,
|
pbkdf2,
|
||||||
)
|
)
|
||||||
from django.utils.deprecation import RemovedInDjango50Warning, RemovedInDjango51Warning
|
from django.utils.deprecation import RemovedInDjango51Warning
|
||||||
from django.utils.module_loading import import_string
|
from django.utils.module_loading import import_string
|
||||||
from django.utils.translation import gettext_noop as _
|
from django.utils.translation import gettext_noop as _
|
||||||
|
|
||||||
@ -823,62 +823,3 @@ class UnsaltedMD5PasswordHasher(BasePasswordHasher):
|
|||||||
|
|
||||||
def harden_runtime(self, password, encoded):
|
def harden_runtime(self, password, encoded):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
# RemovedInDjango50Warning.
|
|
||||||
class CryptPasswordHasher(BasePasswordHasher):
|
|
||||||
"""
|
|
||||||
Password hashing using UNIX crypt (not recommended)
|
|
||||||
|
|
||||||
The crypt module is not supported on all platforms.
|
|
||||||
"""
|
|
||||||
|
|
||||||
algorithm = "crypt"
|
|
||||||
library = "crypt"
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
warnings.warn(
|
|
||||||
"django.contrib.auth.hashers.CryptPasswordHasher is deprecated.",
|
|
||||||
RemovedInDjango50Warning,
|
|
||||||
stacklevel=2,
|
|
||||||
)
|
|
||||||
super().__init__(*args, **kwargs)
|
|
||||||
|
|
||||||
def salt(self):
|
|
||||||
return get_random_string(2)
|
|
||||||
|
|
||||||
def encode(self, password, salt):
|
|
||||||
crypt = self._load_library()
|
|
||||||
if len(salt) != 2:
|
|
||||||
raise ValueError("salt must be of length 2.")
|
|
||||||
hash = crypt.crypt(password, salt)
|
|
||||||
if hash is None: # A platform like OpenBSD with a dummy crypt module.
|
|
||||||
raise TypeError("hash must be provided.")
|
|
||||||
# we don't need to store the salt, but Django used to do this
|
|
||||||
return "%s$%s$%s" % (self.algorithm, "", hash)
|
|
||||||
|
|
||||||
def decode(self, encoded):
|
|
||||||
algorithm, salt, hash = encoded.split("$", 2)
|
|
||||||
assert algorithm == self.algorithm
|
|
||||||
return {
|
|
||||||
"algorithm": algorithm,
|
|
||||||
"hash": hash,
|
|
||||||
"salt": salt,
|
|
||||||
}
|
|
||||||
|
|
||||||
def verify(self, password, encoded):
|
|
||||||
crypt = self._load_library()
|
|
||||||
decoded = self.decode(encoded)
|
|
||||||
data = crypt.crypt(password, decoded["hash"])
|
|
||||||
return constant_time_compare(decoded["hash"], data)
|
|
||||||
|
|
||||||
def safe_summary(self, encoded):
|
|
||||||
decoded = self.decode(encoded)
|
|
||||||
return {
|
|
||||||
_("algorithm"): decoded["algorithm"],
|
|
||||||
_("salt"): decoded["salt"],
|
|
||||||
_("hash"): mask_hash(decoded["hash"], show=3),
|
|
||||||
}
|
|
||||||
|
|
||||||
def harden_runtime(self, password, encoded):
|
|
||||||
pass
|
|
||||||
|
@ -340,3 +340,5 @@ to remove usage of these features.
|
|||||||
longer allowed.
|
longer allowed.
|
||||||
|
|
||||||
* The ``django.contrib.gis.admin.OpenLayersWidget`` is removed.
|
* The ``django.contrib.gis.admin.OpenLayersWidget`` is removed.
|
||||||
|
|
||||||
|
+ The ``django.contrib.auth.hashers.CryptPasswordHasher`` is removed.
|
||||||
|
@ -19,17 +19,7 @@ from django.contrib.auth.hashers import (
|
|||||||
)
|
)
|
||||||
from django.test import SimpleTestCase, ignore_warnings
|
from django.test import SimpleTestCase, ignore_warnings
|
||||||
from django.test.utils import override_settings
|
from django.test.utils import override_settings
|
||||||
from django.utils.deprecation import RemovedInDjango50Warning, RemovedInDjango51Warning
|
from django.utils.deprecation import RemovedInDjango51Warning
|
||||||
|
|
||||||
# RemovedInDjango50Warning.
|
|
||||||
try:
|
|
||||||
import crypt
|
|
||||||
except ImportError:
|
|
||||||
crypt = None
|
|
||||||
else:
|
|
||||||
# On some platforms (e.g. OpenBSD), crypt.crypt() always return None.
|
|
||||||
if crypt.crypt("") is None:
|
|
||||||
crypt = None
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import bcrypt
|
import bcrypt
|
||||||
@ -239,57 +229,6 @@ class TestUtilsHashPass(SimpleTestCase):
|
|||||||
with self.assertRaisesMessage(RemovedInDjango51Warning, msg):
|
with self.assertRaisesMessage(RemovedInDjango51Warning, msg):
|
||||||
get_hasher("unsalted_sha1")
|
get_hasher("unsalted_sha1")
|
||||||
|
|
||||||
@ignore_warnings(category=RemovedInDjango50Warning)
|
|
||||||
@skipUnless(crypt, "no crypt module to generate password.")
|
|
||||||
@override_settings(
|
|
||||||
PASSWORD_HASHERS=["django.contrib.auth.hashers.CryptPasswordHasher"]
|
|
||||||
)
|
|
||||||
def test_crypt(self):
|
|
||||||
encoded = make_password("lètmei", "ab", "crypt")
|
|
||||||
self.assertEqual(encoded, "crypt$$ab1Hv2Lg7ltQo")
|
|
||||||
self.assertTrue(is_password_usable(encoded))
|
|
||||||
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))
|
|
||||||
|
|
||||||
@ignore_warnings(category=RemovedInDjango50Warning)
|
|
||||||
@skipUnless(crypt, "no crypt module to generate password.")
|
|
||||||
@override_settings(
|
|
||||||
PASSWORD_HASHERS=["django.contrib.auth.hashers.CryptPasswordHasher"]
|
|
||||||
)
|
|
||||||
def test_crypt_encode_invalid_salt(self):
|
|
||||||
hasher = get_hasher("crypt")
|
|
||||||
msg = "salt must be of length 2."
|
|
||||||
with self.assertRaisesMessage(ValueError, msg):
|
|
||||||
hasher.encode("password", salt="a")
|
|
||||||
|
|
||||||
@ignore_warnings(category=RemovedInDjango50Warning)
|
|
||||||
@skipUnless(crypt, "no crypt module to generate password.")
|
|
||||||
@override_settings(
|
|
||||||
PASSWORD_HASHERS=["django.contrib.auth.hashers.CryptPasswordHasher"]
|
|
||||||
)
|
|
||||||
def test_crypt_encode_invalid_hash(self):
|
|
||||||
hasher = get_hasher("crypt")
|
|
||||||
msg = "hash must be provided."
|
|
||||||
with mock.patch("crypt.crypt", return_value=None):
|
|
||||||
with self.assertRaisesMessage(TypeError, msg):
|
|
||||||
hasher.encode("password", salt="ab")
|
|
||||||
|
|
||||||
@skipUnless(crypt, "no crypt module to generate password.")
|
|
||||||
@override_settings(
|
|
||||||
PASSWORD_HASHERS=["django.contrib.auth.hashers.CryptPasswordHasher"]
|
|
||||||
)
|
|
||||||
def test_crypt_deprecation_warning(self):
|
|
||||||
msg = "django.contrib.auth.hashers.CryptPasswordHasher is deprecated."
|
|
||||||
with self.assertRaisesMessage(RemovedInDjango50Warning, msg):
|
|
||||||
get_hasher("crypt")
|
|
||||||
|
|
||||||
@skipUnless(bcrypt, "bcrypt not installed")
|
@skipUnless(bcrypt, "bcrypt not installed")
|
||||||
def test_bcrypt_sha256(self):
|
def test_bcrypt_sha256(self):
|
||||||
encoded = make_password("lètmein", hasher="bcrypt_sha256")
|
encoded = make_password("lètmein", hasher="bcrypt_sha256")
|
||||||
|
Loading…
x
Reference in New Issue
Block a user