From 30f2358db83c3e183d8072532df38c5dc2445107 Mon Sep 17 00:00:00 2001 From: Valentin Samir Date: Sun, 18 Aug 2024 12:56:48 +0200 Subject: [PATCH] Make the crypt module optional. Fix #67 The python stdlid crypt module is deprecated since version 3.11 and will be removed in version 3.13. Check for the availability of the crypt module. All password checks using the crypt module will stop to work on python 3.13. --- CHANGELOG.rst | 10 ++++++++++ README.rst | 20 ++++++++++++-------- cas_server/tests/test_utils.py | 3 +++ cas_server/utils.py | 12 +++++++++++- 4 files changed, 36 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 2218aaa..978d3df 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -15,6 +15,16 @@ Added * Support for Django 4.2 * Allow forms to be overridden from settings +Deprecated +---------- + +* Make the crypt module optional and deprecate it's usage. + The python stdlid crypt module is deprecated since version 3.11 and + will be removed in version 3.13. Check for the availability of the + crypt module. + All password checks using the crypt module will stop to work on + python 3.13. + v2.0.0 - 2022-10-17 =================== diff --git a/README.rst b/README.rst index 4612a2c..05ee8bf 100644 --- a/README.rst +++ b/README.rst @@ -383,16 +383,17 @@ Only useful if you are using the mysql authentication backend: * ``CAS_SQL_PASSWORD_CHECK``: The method used to check the user password. Must be one of the following: * ``"crypt"`` (see ), the password in the database - should begin with $ + should begin with $. This method is deprecated and will stop to work in python 3.13. * ``"ldap"`` (see https://tools.ietf.org/id/draft-stroeder-hashed-userpassword-values-01.html) the password in the database must begin with one of {MD5}, {SMD5}, {SHA}, {SSHA}, {SHA256}, - {SSHA256}, {SHA384}, {SSHA384}, {SHA512}, {SSHA512}, {CRYPT}. + {SSHA256}, {SHA384}, {SSHA384}, {SHA512}, {SSHA512}, {CRYPT}. {CRYPT} is deprecated + and will stop to work in python 3.13. * ``"hex_HASH_NAME"`` with ``HASH_NAME`` in md5, sha1, sha224, sha256, sha384, sha512. The hashed password in the database is compared to the hexadecimal digest of the clear password hashed with the corresponding algorithm. * ``"plain"``, the password in the database must be in clear. - The default is ``"crypt"``. + The default is ``"crypt"``. This default is deprecated and will stop to work in python 3.13. Sql backend settings @@ -409,16 +410,18 @@ used by the sql backend. * ``CAS_SQL_PASSWORD_CHECK``: The method used to check the user password. Must be one of the following: * ``"crypt"`` (see ), the password in the database - should begin with $ + should begin with $. This method is deprecated and will stop to work in python 3.13. * ``"ldap"`` (see https://tools.ietf.org/id/draft-stroeder-hashed-userpassword-values-01.html) the password in the database must begin with one of {MD5}, {SMD5}, {SHA}, {SSHA}, {SHA256}, - {SSHA256}, {SHA384}, {SSHA384}, {SHA512}, {SSHA512}, {CRYPT}. + {SSHA256}, {SHA384}, {SSHA384}, {SHA512}, {SSHA512}, {CRYPT}. {CRYPT} is deprecated + and will stop to work in python 3.13. * ``"hex_HASH_NAME"`` with ``HASH_NAME`` in md5, sha1, sha224, sha256, sha384, sha512. The hashed password in the database is compared to the hexadecimal digest of the clear password hashed with the corresponding algorithm. * ``"plain"``, the password in the database must be in clear. - The default is ``"crypt"``. + The default is ``"crypt"``. This default is deprecated and will stop to work in python 3.13. + * ``CAS_SQL_PASSWORD_CHARSET``: Charset the SQL users passwords was hash with. This is needed to encode the user submitted password before hashing it for comparison. The default is ``"utf-8"``. @@ -439,10 +442,11 @@ Only useful if you are using the ldap authentication backend: * ``CAS_LDAP_PASSWORD_CHECK``: The method used to check the user password. Must be one of the following: * ``"crypt"`` (see ), the password in the database - should begin with $ + should begin with $. This method is deprecated and will stop to work in python 3.13. * ``"ldap"`` (see https://tools.ietf.org/id/draft-stroeder-hashed-userpassword-values-01.html) the password in the database must begin with one of {MD5}, {SMD5}, {SHA}, {SSHA}, {SHA256}, - {SSHA256}, {SHA384}, {SSHA384}, {SHA512}, {SSHA512}, {CRYPT}. + {SSHA256}, {SHA384}, {SSHA384}, {SHA512}, {SSHA512}, {CRYPT}. {CRYPT} is deprecated and + will stop to work in python 3.13. * ``"hex_HASH_NAME"`` with ``HASH_NAME`` in md5, sha1, sha224, sha256, sha384, sha512. The hashed password in the database is compared to the hexadecimal digest of the clear password hashed with the corresponding algorithm. diff --git a/cas_server/tests/test_utils.py b/cas_server/tests/test_utils.py index d690724..2cd2084 100644 --- a/cas_server/tests/test_utils.py +++ b/cas_server/tests/test_utils.py @@ -63,6 +63,9 @@ def test_plain_unicode(self): def test_crypt(self): """test the crypt auth method""" + # Only run test if crypt is available + if utils.crypt is None: + return salts = ["$6$UVVAQvrMyXMF3FF3", "aa"] hashed_password1 = [] for salt in salts: diff --git a/cas_server/utils.py b/cas_server/utils.py index 31d923e..dd518da 100644 --- a/cas_server/utils.py +++ b/cas_server/utils.py @@ -30,13 +30,17 @@ import string import json import hashlib -import crypt import base64 import six import requests import time import logging import binascii +# The crypt module is deprecated and will be removed in version 3.13 +try: + import crypt +except ImportError: + crypt = None from importlib import import_module from datetime import datetime, timedelta @@ -411,6 +415,8 @@ def crypt_salt_is_valid(salt): :return: ``True`` if ``salt`` is a valid crypt salt on this system, ``False`` otherwise :rtype: bool """ + if crypt is None: + return False if len(salt) < 2: return False else: @@ -567,6 +573,8 @@ def hash(cls, scheme, password, salt=None, charset="utf8"): cls._schemes_to_hash[scheme](password + salt).digest() + salt ) except KeyError: + if crypt is None: + raise cls.BadScheme("Crypt is not available on the system") if six.PY3: password = password.decode(charset) salt = salt.decode(charset) @@ -646,6 +654,8 @@ def check_password(method, password, hashed_password, charset): if method == "plain": return password == hashed_password elif method == "crypt": + if crypt is None: + raise ValueError("Crypt is not available on the system") if hashed_password.startswith(b'$'): salt = b'$'.join(hashed_password.split(b'$', 3)[:-1]) elif hashed_password.startswith(b'_'): # pragma: no cover old BSD format not supported