diff --git a/django/contrib/auth/hashers.py b/django/contrib/auth/hashers.py index 3cdbaa75b09..42159531dc8 100644 --- a/django/contrib/auth/hashers.py +++ b/django/contrib/auth/hashers.py @@ -17,6 +17,7 @@ from django.utils.crypto import ( md5, pbkdf2, ) +from django.utils.deprecation import RemovedInDjango50Warning from django.utils.module_loading import import_string from django.utils.translation import gettext_noop as _ @@ -797,6 +798,7 @@ class UnsaltedMD5PasswordHasher(BasePasswordHasher): pass +# RemovedInDjango50Warning. class CryptPasswordHasher(BasePasswordHasher): """ Password hashing using UNIX crypt (not recommended) @@ -807,6 +809,14 @@ class CryptPasswordHasher(BasePasswordHasher): 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) diff --git a/docs/internals/deprecation.txt b/docs/internals/deprecation.txt index 903f2f71612..39007eb3e9f 100644 --- a/docs/internals/deprecation.txt +++ b/docs/internals/deprecation.txt @@ -103,6 +103,8 @@ details on these changes. * The ``django.contrib.gis.admin.OpenLayersWidget`` will be removed. +* The ``django.contrib.auth.hashers.CryptPasswordHasher`` will be removed. + .. _deprecation-removed-in-4.1: 4.1 diff --git a/docs/releases/4.1.txt b/docs/releases/4.1.txt index a9e6d71cd15..af129a149e4 100644 --- a/docs/releases/4.1.txt +++ b/docs/releases/4.1.txt @@ -683,6 +683,8 @@ Miscellaneous * The undocumented ``django.contrib.gis.admin.OpenLayersWidget`` is deprecated. +* ``django.contrib.auth.hashers.CryptPasswordHasher`` is deprecated. + Features removed in 4.1 ======================= diff --git a/docs/topics/auth/passwords.txt b/docs/topics/auth/passwords.txt index 33a57a8be50..fbac4e567d6 100644 --- a/docs/topics/auth/passwords.txt +++ b/docs/topics/auth/passwords.txt @@ -439,7 +439,6 @@ The full list of hashers included in Django is:: 'django.contrib.auth.hashers.MD5PasswordHasher', 'django.contrib.auth.hashers.UnsaltedSHA1PasswordHasher', 'django.contrib.auth.hashers.UnsaltedMD5PasswordHasher', - 'django.contrib.auth.hashers.CryptPasswordHasher', ] The corresponding algorithm names are: @@ -454,7 +453,6 @@ The corresponding algorithm names are: * ``md5`` * ``unsalted_sha1`` * ``unsalted_md5`` -* ``crypt`` .. _write-your-own-password-hasher: diff --git a/tests/auth_tests/test_hashers.py b/tests/auth_tests/test_hashers.py index b767c2823d0..2faf2499b0c 100644 --- a/tests/auth_tests/test_hashers.py +++ b/tests/auth_tests/test_hashers.py @@ -18,9 +18,11 @@ from django.contrib.auth.hashers import ( is_password_usable, make_password, ) -from django.test import SimpleTestCase +from django.test import SimpleTestCase, ignore_warnings from django.test.utils import override_settings +from django.utils.deprecation import RemovedInDjango50Warning +# RemovedInDjango50Warning. try: import crypt except ImportError: @@ -201,6 +203,7 @@ class TestUtilsHashPass(SimpleTestCase): with self.assertRaisesMessage(ValueError, msg): hasher.encode("password", salt="salt") + @ignore_warnings(category=RemovedInDjango50Warning) @skipUnless(crypt, "no crypt module to generate password.") @override_settings( PASSWORD_HASHERS=["django.contrib.auth.hashers.CryptPasswordHasher"] @@ -219,6 +222,7 @@ class TestUtilsHashPass(SimpleTestCase): 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"] @@ -229,6 +233,7 @@ class TestUtilsHashPass(SimpleTestCase): 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"] @@ -240,6 +245,15 @@ class TestUtilsHashPass(SimpleTestCase): 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") def test_bcrypt_sha256(self): encoded = make_password("lètmein", hasher="bcrypt_sha256")