radicale/utils: drop bcrypt compat check

We now support passlib and bcrypt5 so the test can be dropped.

Related-to: #1980
Signed-off-by: Henning Schild <henning@hennsch.de>
This commit is contained in:
Henning Schild
2026-04-14 12:09:57 +02:00
parent 7b5c0304bb
commit 6690e9e3ca
6 changed files with 7 additions and 55 deletions

View File

@@ -1052,7 +1052,6 @@ Available methods:
* `bcrypt`
This uses a modified version of the Blowfish stream cipher, which is considered very secure.
The installation of Python's **bcrypt** module is required for this to work.
Also consider version of passlib(libpass): bcrypt >= 5.0.0 requires passlib(libpass) >= 1.9.3
* `md5`
Use an iterated MD5 digest of the password with salt (nowadays insecure).

View File

@@ -28,7 +28,7 @@ classifiers = [
]
urls = {Homepage = "https://radicale.org/"}
requires-python = ">=3.9.0"
# Hint: if bcyrpt < 5.0.0 is used, passlib(libpass) dependency can be downgraded/reverted by: sed -i 's|libpass[^"]*|passlib|' pyproject.toml
# Hint: passlib(libpass) dependency can be downgraded/reverted by: sed -i 's|libpass[^"]*|passlib|' pyproject.toml
dependencies = [
"defusedxml",
"libpass>=1.9.3",

View File

@@ -43,7 +43,7 @@ out-of-the-box:
- SHA256 (htpasswd -2 ...)
- SHA512 (htpasswd -5 ...)
When bcrypt is installed (bcrypt >= 5.0.0 requires passlib(libpass) >= 1.9.3):
When bcrypt is installed:
- BCRYPT (htpasswd -B ...) -- Requires htpasswd 2.4.x
When argon2 is installed:
@@ -61,7 +61,7 @@ from typing import Any, Tuple
from passlib.hash import apr_md5_crypt, sha256_crypt, sha512_crypt
from radicale import auth, config, logger, utils
from radicale import auth, config, logger
class Auth(auth.BaseAuth):
@@ -122,22 +122,12 @@ class Auth(auth.BaseAuth):
"The htpasswd encryption method 'bcrypt' or 'autodetect' requires "
"the bcrypt module (entries found: %d)." % self._htpasswd_bcrypt_use) from e
else:
[bcrypt_usable, info] = utils.passlib_libpass_supports_bcrypt()
if bcrypt_usable:
self._has_bcrypt = True
logger.info(info)
else:
logger.warning(info)
self._has_bcrypt = True
if self._encryption == "autodetect":
if self._htpasswd_bcrypt_use == 0:
logger.info("auth htpasswd encryption is 'radicale.auth.htpasswd_encryption.%s' and bcrypt module found, but currently not required", self._encryption)
else:
logger.info("auth htpasswd encryption is 'radicale.auth.htpasswd_encryption.%s' and bcrypt module found (bcrypt entries found: %d)", self._encryption, self._htpasswd_bcrypt_use)
if not bcrypt_usable:
raise RuntimeError("The htpasswd encryption 'autodetect' requires the bcrypt module but not usuable")
else:
if not bcrypt_usable:
raise RuntimeError("The htpasswd encryption method 'bcrypt' requires the bcrypt module but not usuable")
if self._encryption == "bcrypt":
self._verify = functools.partial(self._bcrypt, bcrypt)
else:

View File

@@ -31,7 +31,7 @@ from typing import Iterable, Tuple, Union
import pytest
from radicale import utils, xmlutils
from radicale import xmlutils
from radicale.auth import htpasswd
from radicale.tests import BaseTest
@@ -133,52 +133,42 @@ class TestBaseAuthRequests(BaseTest):
self._test_htpasswd("autodetect", "tmp:$6$rounds=2500$A1H/cZUl3CBnsplz$bSKYCDQ/YGR..YhxaZcM1eKmAi/jlnpbENKU8a.9kE95JBIpyUss3.cUyss0xQnhjD4PReN4sAzmdziWmoCsg/")
@pytest.mark.skipif(has_bcrypt == 0, reason="No bcrypt module installed")
@pytest.mark.skipif(not utils.passlib_libpass_supports_bcrypt()[0], reason="bcrypt module incompatible with passlib(libpass) module")
def test_htpasswd_bcrypt_2a(self) -> None:
self._test_htpasswd("bcrypt", "tmp:$2a$10$Mj4A9vMecAp/K7.0fMKoVOk1SjgR.RBhl06a52nvzXhxlT3HB7Reu")
@pytest.mark.skipif(has_bcrypt == 0, reason="No bcrypt module installed or incompatibe")
@pytest.mark.skipif(not utils.passlib_libpass_supports_bcrypt()[0], reason="bcrypt module incompatible with passlib(libpass) module")
@pytest.mark.skipif(has_bcrypt == 0, reason="No bcrypt module installed")
def test_htpasswd_bcrypt_2a_autodetect(self) -> None:
self._test_htpasswd("autodetect", "tmp:$2a$10$Mj4A9vMecAp/K7.0fMKoVOk1SjgR.RBhl06a52nvzXhxlT3HB7Reu")
@pytest.mark.skipif(has_bcrypt == 0, reason="No bcrypt module installed")
@pytest.mark.skipif(not utils.passlib_libpass_supports_bcrypt()[0], reason="bcrypt module incompatible with passlib(libpass) module")
def test_htpasswd_bcrypt_2b(self) -> None:
self._test_htpasswd("bcrypt", "tmp:$2b$12$7a4z/fdmXlBIfkz0smvzW.1Nds8wpgC/bo2DVOb4OSQKWCDL1A1wu")
@pytest.mark.skipif(has_bcrypt == 0, reason="No bcrypt module installed")
@pytest.mark.skipif(not utils.passlib_libpass_supports_bcrypt()[0], reason="bcrypt module incompatible with passlib(libpass) module")
def test_htpasswd_bcrypt_2b_autodetect(self) -> None:
self._test_htpasswd("autodetect", "tmp:$2b$12$7a4z/fdmXlBIfkz0smvzW.1Nds8wpgC/bo2DVOb4OSQKWCDL1A1wu")
@pytest.mark.skipif(has_bcrypt == 0, reason="No bcrypt module installed")
@pytest.mark.skipif(not utils.passlib_libpass_supports_bcrypt()[0], reason="bcrypt module incompatible with passlib(libpass) module")
def test_htpasswd_bcrypt_2y(self) -> None:
self._test_htpasswd("bcrypt", "tmp:$2y$05$oD7hbiQFQlvCM7zoalo/T.MssV3VNTRI3w5KDnj8NTUKJNWfVpvRq")
@pytest.mark.skipif(has_bcrypt == 0, reason="No bcrypt module installed")
@pytest.mark.skipif(not utils.passlib_libpass_supports_bcrypt()[0], reason="bcrypt module incompatible with passlib(libpass) module")
def test_htpasswd_bcrypt_2y_autodetect(self) -> None:
self._test_htpasswd("autodetect", "tmp:$2y$05$oD7hbiQFQlvCM7zoalo/T.MssV3VNTRI3w5KDnj8NTUKJNWfVpvRq")
@pytest.mark.skipif(has_bcrypt == 0, reason="No bcrypt module installed")
@pytest.mark.skipif(not utils.passlib_libpass_supports_bcrypt()[0], reason="bcrypt module incompatible with passlib(libpass) module")
def test_htpasswd_bcrypt_C10(self) -> None:
self._test_htpasswd("bcrypt", "tmp:$2y$10$bZsWq06ECzxqi7RmulQvC.T1YHUnLW2E3jn.MU2pvVTGn1dfORt2a")
@pytest.mark.skipif(has_bcrypt == 0, reason="No bcrypt module installed")
@pytest.mark.skipif(not utils.passlib_libpass_supports_bcrypt()[0], reason="bcrypt module incompatible with passlib(libpass) module")
def test_htpasswd_bcrypt_C10_autodetect(self) -> None:
self._test_htpasswd("autodetect", "tmp:$2y$10$bZsWq06ECzxqi7RmulQvC.T1YHUnLW2E3jn.MU2pvVTGn1dfORt2a")
@pytest.mark.skipif(has_bcrypt == 0, reason="No bcrypt module installed")
@pytest.mark.skipif(not utils.passlib_libpass_supports_bcrypt()[0], reason="bcrypt module incompatible with passlib(libpass) module")
def test_htpasswd_bcrypt_unicode(self) -> None:
self._test_htpasswd("bcrypt", "😀:$2y$10$Oyz5aHV4MD9eQJbk6GPemOs4T6edK6U9Sqlzr.W1mMVCS8wJUftnW", "unicode")
@pytest.mark.skipif(has_bcrypt == 0, reason="No bcrypt module installed")
@pytest.mark.skipif(not utils.passlib_libpass_supports_bcrypt()[0], reason="bcrypt module incompatible with passlib(libpass) module")
def test_htpasswd_bcrypt_long(self) -> None:
import bcrypt
m = (
@@ -193,7 +183,6 @@ class TestBaseAuthRequests(BaseTest):
self._test_htpasswd("bcrypt", "tmp:" + hash.decode('utf-8'), m)
@pytest.mark.skipif(has_bcrypt == 0, reason="No bcrypt module installed")
@pytest.mark.skipif(not utils.passlib_libpass_supports_bcrypt()[0], reason="bcrypt module incompatible with passlib(libpass) module")
def test_bcrypt_hash_long(self) -> None:
import bcrypt
salt = bcrypt.gensalt()

View File

@@ -27,8 +27,6 @@ from importlib import import_module, metadata
from string import ascii_letters, digits, punctuation
from typing import Callable, Sequence, Tuple, Type, TypeVar, Union
from packaging.version import Version
from radicale import config
from radicale.log import logger
@@ -109,30 +107,6 @@ def vobject_supports_vcard4() -> bool:
return False
def passlib_libpass_supports_bcrypt() -> Tuple[bool, str]:
"""Check if passlib(libpass) version supports bcrypt version."""
info = ""
try:
version_bcrypt = package_version("bcrypt")
version_bcrypt_check = "5.0.0"
version_passlib = package_version("passlib")
version_passlib_check = "1.9.3"
if Version(version_bcrypt) >= Version(version_bcrypt_check):
# bcrypt >= 5.0.0 has issues with passlib(libpass) < 1.9.3
if Version(version_passlib) < Version(version_passlib_check):
info = "bcrypt module version %r >= %r and passlib(libpass) module version %r < %r found => incompatible, downgrade bcrypt or upgrade passlib(libpass)" % (version_bcrypt, version_bcrypt_check, version_passlib, version_passlib_check)
return (False, info)
else:
info = "bcrypt module version %r >= %r and passlib(libpass) module version %r >= %r found => ok" % (version_bcrypt, version_bcrypt_check, version_passlib, version_passlib_check)
return (True, info)
else:
info = "bcrypt module version %r < %r and passlib(libpass) module version %r found => ok" % (version_bcrypt, version_bcrypt_check, version_passlib)
return (True, info)
except Exception:
info = "bcrypt module version or passlib(libpass) module version %r not found => problem"
return (False, info)
def packages_version():
versions = []
versions.append("python=%s.%s.%s" % (sys.version_info[0], sys.version_info[1], sys.version_info[2]))

View File

@@ -37,7 +37,7 @@ web_files = ["web/internal_data/css/icon.png",
"web/internal_data/js/utils/*.js",
"web/internal_data/index.html"]
# Hint: if bcyrpt < 5.0.0 is used, passlib(libpass) dependency can be downgraded/reverted by: sed -i 's|libpass[^"]*|passlib|' setup.py.legacy
# Hint: passlib(libpass) dependency can be downgraded/reverted by: sed -i 's|libpass[^"]*|passlib|' setup.py.legacy
install_requires = ["defusedxml", "libpass>=1.9.3", "vobject>=0.9.6",
"pika>=1.1.0",
"requests",