Merge pull request #2089 from henning-schild/henning/staging1
deal with bcrypt>=5 72 chars limit for libpass+passlib
This commit is contained in:
20
.github/workflows/test.yml
vendored
20
.github/workflows/test.yml
vendored
@@ -47,6 +47,26 @@ jobs:
|
|||||||
- name: Test with newest Python on latest Ubuntu
|
- name: Test with newest Python on latest Ubuntu
|
||||||
run: tox -c pyproject.toml -e py
|
run: tox -c pyproject.toml -e py
|
||||||
|
|
||||||
|
test-ubuntu-python-newest-with-passlib:
|
||||||
|
name: Test Ubuntu:latest Python:newest passlib
|
||||||
|
needs: lint
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
os: [ubuntu-latest]
|
||||||
|
python-version: ['3.14']
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v5
|
||||||
|
- uses: actions/setup-python@v6
|
||||||
|
with:
|
||||||
|
python-version: ${{ matrix.python-version }}
|
||||||
|
- name: Install Test dependencies
|
||||||
|
run: pip install tox
|
||||||
|
- name: Switch back to passlib
|
||||||
|
run: sed -i 's|libpass[^"]*|passlib|' pyproject.toml
|
||||||
|
- name: Test with newest Python on latest Ubuntu
|
||||||
|
run: tox -c pyproject.toml -e py
|
||||||
|
|
||||||
test-python-32bit:
|
test-python-32bit:
|
||||||
name: Test Ubuntu:latest Python:32-bit
|
name: Test Ubuntu:latest Python:32-bit
|
||||||
needs: [lint, test-ubuntu-python-newest, integ-test]
|
needs: [lint, test-ubuntu-python-newest, integ-test]
|
||||||
|
|||||||
@@ -1052,7 +1052,6 @@ Available methods:
|
|||||||
* `bcrypt`
|
* `bcrypt`
|
||||||
This uses a modified version of the Blowfish stream cipher, which is considered very secure.
|
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.
|
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`
|
* `md5`
|
||||||
Use an iterated MD5 digest of the password with salt (nowadays insecure).
|
Use an iterated MD5 digest of the password with salt (nowadays insecure).
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ classifiers = [
|
|||||||
]
|
]
|
||||||
urls = {Homepage = "https://radicale.org/"}
|
urls = {Homepage = "https://radicale.org/"}
|
||||||
requires-python = ">=3.9.0"
|
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 = [
|
dependencies = [
|
||||||
"defusedxml",
|
"defusedxml",
|
||||||
"libpass>=1.9.3",
|
"libpass>=1.9.3",
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ out-of-the-box:
|
|||||||
- SHA256 (htpasswd -2 ...)
|
- SHA256 (htpasswd -2 ...)
|
||||||
- SHA512 (htpasswd -5 ...)
|
- 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
|
- BCRYPT (htpasswd -B ...) -- Requires htpasswd 2.4.x
|
||||||
|
|
||||||
When argon2 is installed:
|
When argon2 is installed:
|
||||||
@@ -61,11 +61,13 @@ from typing import Any, Tuple
|
|||||||
|
|
||||||
from passlib.hash import apr_md5_crypt, sha256_crypt, sha512_crypt
|
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):
|
class Auth(auth.BaseAuth):
|
||||||
|
|
||||||
|
BCRYPT_MAX_PWLEN = 72
|
||||||
|
|
||||||
_filename: str
|
_filename: str
|
||||||
_encoding: str
|
_encoding: str
|
||||||
_htpasswd: dict # login -> digest
|
_htpasswd: dict # login -> digest
|
||||||
@@ -120,22 +122,12 @@ class Auth(auth.BaseAuth):
|
|||||||
"The htpasswd encryption method 'bcrypt' or 'autodetect' requires "
|
"The htpasswd encryption method 'bcrypt' or 'autodetect' requires "
|
||||||
"the bcrypt module (entries found: %d)." % self._htpasswd_bcrypt_use) from e
|
"the bcrypt module (entries found: %d)." % self._htpasswd_bcrypt_use) from e
|
||||||
else:
|
else:
|
||||||
[bcrypt_usable, info] = utils.passlib_libpass_supports_bcrypt()
|
|
||||||
if bcrypt_usable:
|
|
||||||
self._has_bcrypt = True
|
self._has_bcrypt = True
|
||||||
logger.info(info)
|
|
||||||
else:
|
|
||||||
logger.warning(info)
|
|
||||||
if self._encryption == "autodetect":
|
if self._encryption == "autodetect":
|
||||||
if self._htpasswd_bcrypt_use == 0:
|
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)
|
logger.info("auth htpasswd encryption is 'radicale.auth.htpasswd_encryption.%s' and bcrypt module found, but currently not required", self._encryption)
|
||||||
else:
|
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)
|
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":
|
if self._encryption == "bcrypt":
|
||||||
self._verify = functools.partial(self._bcrypt, bcrypt)
|
self._verify = functools.partial(self._bcrypt, bcrypt)
|
||||||
else:
|
else:
|
||||||
@@ -176,16 +168,21 @@ class Auth(auth.BaseAuth):
|
|||||||
"""Check if ``hash_value`` and ``password`` match, plain method."""
|
"""Check if ``hash_value`` and ``password`` match, plain method."""
|
||||||
return ("PLAIN", hmac.compare_digest(hash_value.encode(), password.encode()))
|
return ("PLAIN", hmac.compare_digest(hash_value.encode(), password.encode()))
|
||||||
|
|
||||||
def _plain_fallback(self, method_orig, hash_value: str, password: str) -> tuple[str, bool]:
|
def _plain_fallback(self, method_orig, hash_value: bytes, password: bytes) -> tuple[str, bool]:
|
||||||
"""Check if ``hash_value`` and ``password`` match, plain method / fallback in case of hash length is not matching on autodetection."""
|
"""Check if ``hash_value`` and ``password`` match, plain method / fallback in case of hash length is not matching on autodetection."""
|
||||||
info = "PLAIN/fallback as hash length not matching for " + method_orig + ": " + str(len(hash_value))
|
info = "PLAIN/fallback as hash length not matching for " + method_orig + ": " + str(len(hash_value))
|
||||||
return (info, hmac.compare_digest(hash_value.encode(), password.encode()))
|
return (info, hmac.compare_digest(hash_value, password))
|
||||||
|
|
||||||
def _bcrypt(self, bcrypt: Any, hash_value: str, password: str) -> tuple[str, bool]:
|
def _bcrypt(self, bcrypt: Any, hash_value: str, password: str) -> tuple[str, bool]:
|
||||||
if self._encryption == "autodetect" and len(hash_value) != 60:
|
pw = password.encode()
|
||||||
return self._plain_fallback("BCRYPT", hash_value, password)
|
hv = hash_value.encode()
|
||||||
|
if len(pw) > self.BCRYPT_MAX_PWLEN:
|
||||||
|
pw = pw[:self.BCRYPT_MAX_PWLEN]
|
||||||
|
logger.warning("Bcrypt passwords can not be longer than %d characters, truncated" % self.BCRYPT_MAX_PWLEN)
|
||||||
|
if self._encryption == "autodetect" and len(hv) != 60:
|
||||||
|
return self._plain_fallback("BCRYPT", hv, pw)
|
||||||
else:
|
else:
|
||||||
return ("BCRYPT", bcrypt.checkpw(password=password.encode('utf-8'), hashed_password=hash_value.encode()))
|
return ("BCRYPT", bcrypt.checkpw(password=pw, hashed_password=hv))
|
||||||
|
|
||||||
def _argon2(self, argon2: Any, hash_value: str, password: str) -> tuple[str, bool]:
|
def _argon2(self, argon2: Any, hash_value: str, password: str) -> tuple[str, bool]:
|
||||||
return ("ARGON2", argon2.verify(password, hash_value.strip()))
|
return ("ARGON2", argon2.verify(password, hash_value.strip()))
|
||||||
|
|||||||
@@ -31,7 +31,8 @@ from typing import Iterable, Tuple, Union
|
|||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from radicale import utils, xmlutils
|
from radicale import xmlutils
|
||||||
|
from radicale.auth import htpasswd
|
||||||
from radicale.tests import BaseTest
|
from radicale.tests import BaseTest
|
||||||
|
|
||||||
|
|
||||||
@@ -42,6 +43,8 @@ class TestBaseAuthRequests(BaseTest):
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
BCRYPT_MAX_PWLEN = htpasswd.Auth.BCRYPT_MAX_PWLEN
|
||||||
|
|
||||||
# test for available bcrypt module
|
# test for available bcrypt module
|
||||||
try:
|
try:
|
||||||
import bcrypt
|
import bcrypt
|
||||||
@@ -130,50 +133,69 @@ class TestBaseAuthRequests(BaseTest):
|
|||||||
self._test_htpasswd("autodetect", "tmp:$6$rounds=2500$A1H/cZUl3CBnsplz$bSKYCDQ/YGR..YhxaZcM1eKmAi/jlnpbENKU8a.9kE95JBIpyUss3.cUyss0xQnhjD4PReN4sAzmdziWmoCsg/")
|
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(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:
|
def test_htpasswd_bcrypt_2a(self) -> None:
|
||||||
self._test_htpasswd("bcrypt", "tmp:$2a$10$Mj4A9vMecAp/K7.0fMKoVOk1SjgR.RBhl06a52nvzXhxlT3HB7Reu")
|
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(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_autodetect(self) -> None:
|
def test_htpasswd_bcrypt_2a_autodetect(self) -> None:
|
||||||
self._test_htpasswd("autodetect", "tmp:$2a$10$Mj4A9vMecAp/K7.0fMKoVOk1SjgR.RBhl06a52nvzXhxlT3HB7Reu")
|
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(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:
|
def test_htpasswd_bcrypt_2b(self) -> None:
|
||||||
self._test_htpasswd("bcrypt", "tmp:$2b$12$7a4z/fdmXlBIfkz0smvzW.1Nds8wpgC/bo2DVOb4OSQKWCDL1A1wu")
|
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(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:
|
def test_htpasswd_bcrypt_2b_autodetect(self) -> None:
|
||||||
self._test_htpasswd("autodetect", "tmp:$2b$12$7a4z/fdmXlBIfkz0smvzW.1Nds8wpgC/bo2DVOb4OSQKWCDL1A1wu")
|
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(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:
|
def test_htpasswd_bcrypt_2y(self) -> None:
|
||||||
self._test_htpasswd("bcrypt", "tmp:$2y$05$oD7hbiQFQlvCM7zoalo/T.MssV3VNTRI3w5KDnj8NTUKJNWfVpvRq")
|
self._test_htpasswd("bcrypt", "tmp:$2y$05$oD7hbiQFQlvCM7zoalo/T.MssV3VNTRI3w5KDnj8NTUKJNWfVpvRq")
|
||||||
|
|
||||||
@pytest.mark.skipif(has_bcrypt == 0, reason="No bcrypt module installed")
|
@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:
|
def test_htpasswd_bcrypt_2y_autodetect(self) -> None:
|
||||||
self._test_htpasswd("autodetect", "tmp:$2y$05$oD7hbiQFQlvCM7zoalo/T.MssV3VNTRI3w5KDnj8NTUKJNWfVpvRq")
|
self._test_htpasswd("autodetect", "tmp:$2y$05$oD7hbiQFQlvCM7zoalo/T.MssV3VNTRI3w5KDnj8NTUKJNWfVpvRq")
|
||||||
|
|
||||||
@pytest.mark.skipif(has_bcrypt == 0, reason="No bcrypt module installed")
|
@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:
|
def test_htpasswd_bcrypt_C10(self) -> None:
|
||||||
self._test_htpasswd("bcrypt", "tmp:$2y$10$bZsWq06ECzxqi7RmulQvC.T1YHUnLW2E3jn.MU2pvVTGn1dfORt2a")
|
self._test_htpasswd("bcrypt", "tmp:$2y$10$bZsWq06ECzxqi7RmulQvC.T1YHUnLW2E3jn.MU2pvVTGn1dfORt2a")
|
||||||
|
|
||||||
@pytest.mark.skipif(has_bcrypt == 0, reason="No bcrypt module installed")
|
@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:
|
def test_htpasswd_bcrypt_C10_autodetect(self) -> None:
|
||||||
self._test_htpasswd("autodetect", "tmp:$2y$10$bZsWq06ECzxqi7RmulQvC.T1YHUnLW2E3jn.MU2pvVTGn1dfORt2a")
|
self._test_htpasswd("autodetect", "tmp:$2y$10$bZsWq06ECzxqi7RmulQvC.T1YHUnLW2E3jn.MU2pvVTGn1dfORt2a")
|
||||||
|
|
||||||
@pytest.mark.skipif(has_bcrypt == 0, reason="No bcrypt module installed")
|
@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:
|
def test_htpasswd_bcrypt_unicode(self) -> None:
|
||||||
self._test_htpasswd("bcrypt", "😀:$2y$10$Oyz5aHV4MD9eQJbk6GPemOs4T6edK6U9Sqlzr.W1mMVCS8wJUftnW", "unicode")
|
self._test_htpasswd("bcrypt", "😀:$2y$10$Oyz5aHV4MD9eQJbk6GPemOs4T6edK6U9Sqlzr.W1mMVCS8wJUftnW", "unicode")
|
||||||
|
|
||||||
|
@pytest.mark.skipif(has_bcrypt == 0, reason="No bcrypt module installed")
|
||||||
|
def test_htpasswd_bcrypt_long(self) -> None:
|
||||||
|
import bcrypt
|
||||||
|
m = (
|
||||||
|
# full available len
|
||||||
|
("tmp", "a" * self.BCRYPT_MAX_PWLEN, True),
|
||||||
|
# longer than valid -> same password
|
||||||
|
("tmp", "a" * (self.BCRYPT_MAX_PWLEN + 42), True),
|
||||||
|
# shorter that before -> some other password
|
||||||
|
("tmp", "a" * (self.BCRYPT_MAX_PWLEN - 1), False),
|
||||||
|
)
|
||||||
|
hash = bcrypt.hashpw(("a" * self.BCRYPT_MAX_PWLEN).encode(), bcrypt.gensalt())
|
||||||
|
self._test_htpasswd("bcrypt", "tmp:" + hash.decode('utf-8'), m)
|
||||||
|
|
||||||
|
@pytest.mark.skipif(has_bcrypt == 0, reason="No bcrypt module installed")
|
||||||
|
def test_bcrypt_hash_long(self) -> None:
|
||||||
|
import bcrypt
|
||||||
|
salt = bcrypt.gensalt()
|
||||||
|
h0 = bcrypt.hashpw(("a" * self.BCRYPT_MAX_PWLEN).encode(), salt)
|
||||||
|
try:
|
||||||
|
h1 = bcrypt.hashpw(("a" * (self.BCRYPT_MAX_PWLEN + 1)).encode(), salt)
|
||||||
|
# bcrypt >= 5, truncated for us
|
||||||
|
except ValueError:
|
||||||
|
return
|
||||||
|
|
||||||
|
# overlong password produced same hash as maxlen
|
||||||
|
assert h0 == h1
|
||||||
|
|
||||||
@pytest.mark.skipif(has_argon2 == 0, reason="No argon2 module installed")
|
@pytest.mark.skipif(has_argon2 == 0, reason="No argon2 module installed")
|
||||||
def test_htpasswd_argon2_i(self) -> None:
|
def test_htpasswd_argon2_i(self) -> None:
|
||||||
self._test_htpasswd("argon2", "tmp:$argon2i$v=19$m=65536,t=3,p=4$NgZg7F1rzRkDoNSaMwag9A$qmsvMKEn5zOXHm8e3O5fKzzcRo0UESwaDr/cETe5YPI")
|
self._test_htpasswd("argon2", "tmp:$argon2i$v=19$m=65536,t=3,p=4$NgZg7F1rzRkDoNSaMwag9A$qmsvMKEn5zOXHm8e3O5fKzzcRo0UESwaDr/cETe5YPI")
|
||||||
|
|||||||
@@ -27,8 +27,6 @@ from importlib import import_module, metadata
|
|||||||
from string import ascii_letters, digits, punctuation
|
from string import ascii_letters, digits, punctuation
|
||||||
from typing import Callable, Sequence, Tuple, Type, TypeVar, Union
|
from typing import Callable, Sequence, Tuple, Type, TypeVar, Union
|
||||||
|
|
||||||
from packaging.version import Version
|
|
||||||
|
|
||||||
from radicale import config
|
from radicale import config
|
||||||
from radicale.log import logger
|
from radicale.log import logger
|
||||||
|
|
||||||
@@ -109,30 +107,6 @@ def vobject_supports_vcard4() -> bool:
|
|||||||
return False
|
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():
|
def packages_version():
|
||||||
versions = []
|
versions = []
|
||||||
versions.append("python=%s.%s.%s" % (sys.version_info[0], sys.version_info[1], sys.version_info[2]))
|
versions.append("python=%s.%s.%s" % (sys.version_info[0], sys.version_info[1], sys.version_info[2]))
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ web_files = ["web/internal_data/css/icon.png",
|
|||||||
"web/internal_data/js/utils/*.js",
|
"web/internal_data/js/utils/*.js",
|
||||||
"web/internal_data/index.html"]
|
"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",
|
install_requires = ["defusedxml", "libpass>=1.9.3", "vobject>=0.9.6",
|
||||||
"pika>=1.1.0",
|
"pika>=1.1.0",
|
||||||
"requests",
|
"requests",
|
||||||
|
|||||||
Reference in New Issue
Block a user