Merge pull request #1958 from pbiering/htpasswd-improve-autodetection
Htpasswd improve autodetection
This commit is contained in:
@@ -11,6 +11,7 @@
|
||||
* Fix: hook for server-side e-mail notification
|
||||
* Change: dependency PyPI/passlib (stale since 2020) replaced with PyPI/libpass >= 1.9.3
|
||||
* Extend: add a check whether bcrypt version is compatible with passlib(libpass) version (requires "packaging")
|
||||
* Improve: autodetection of hashes in htpasswd (SHA256/SHA512 "rounds" are now supported)
|
||||
|
||||
## 3.5.10
|
||||
* Improve: logging of broken calendar items during PUT
|
||||
|
||||
@@ -191,37 +191,28 @@ class Auth(auth.BaseAuth):
|
||||
return ("ARGON2", argon2.verify(password, hash_value.strip()))
|
||||
|
||||
def _md5apr1(self, hash_value: str, password: str) -> tuple[str, bool]:
|
||||
if self._encryption == "autodetect" and len(hash_value) != 37:
|
||||
return self._plain_fallback("MD5-APR1", hash_value, password)
|
||||
else:
|
||||
return ("MD5-APR1", apr_md5_crypt.verify(password, hash_value.strip()))
|
||||
return ("MD5-APR1", apr_md5_crypt.verify(password, hash_value.strip()))
|
||||
|
||||
def _sha256(self, hash_value: str, password: str) -> tuple[str, bool]:
|
||||
if self._encryption == "autodetect" and len(hash_value) != 63:
|
||||
return self._plain_fallback("SHA-256", hash_value, password)
|
||||
else:
|
||||
return ("SHA-256", sha256_crypt.verify(password, hash_value.strip()))
|
||||
return ("SHA-256", sha256_crypt.verify(password, hash_value.strip()))
|
||||
|
||||
def _sha512(self, hash_value: str, password: str) -> tuple[str, bool]:
|
||||
if self._encryption == "autodetect" and len(hash_value) != 106:
|
||||
return self._plain_fallback("SHA-512", hash_value, password)
|
||||
else:
|
||||
return ("SHA-512", sha512_crypt.verify(password, hash_value.strip()))
|
||||
return ("SHA-512", sha512_crypt.verify(password, hash_value.strip()))
|
||||
|
||||
def _autodetect(self, hash_value: str, password: str) -> tuple[str, bool]:
|
||||
if hash_value.startswith("$apr1$", 0, 6):
|
||||
if re.match(r"^\$apr1\$[A-Za-z0-9/.]{8}\$[A-Za-z0-9/.]{22}", hash_value):
|
||||
# MD5-APR1
|
||||
return self._md5apr1(hash_value, password)
|
||||
elif re.match(r"^\$2(a|b|x|y)?\$", hash_value):
|
||||
elif re.match(r"^\$2(a|b|x|y)?\$[0-9]{2}\$[A-Za-z0-9/.]{53}", hash_value):
|
||||
# BCRYPT
|
||||
return self._verify_bcrypt(hash_value, password)
|
||||
elif re.match(r"^\$argon2(i|d|id)\$", hash_value):
|
||||
# ARGON2
|
||||
return self._verify_argon2(hash_value, password)
|
||||
elif hash_value.startswith("$5$", 0, 3):
|
||||
elif re.match(r"^\$5\$(rounds=[0-9]+\$)?[A-Za-z0-9/.]{16}\$[A-Za-z0-9/.]{42}", hash_value):
|
||||
# SHA-256
|
||||
return self._sha256(hash_value, password)
|
||||
elif hash_value.startswith("$6$", 0, 3):
|
||||
elif re.match(r"^\$6\$(rounds=[0-9]+\$)?[A-Za-z0-9/.]{16}\$[A-Za-z0-9/.]{85}", hash_value):
|
||||
# SHA-512
|
||||
return self._sha512(hash_value, password)
|
||||
else:
|
||||
|
||||
@@ -114,12 +114,18 @@ class TestBaseAuthRequests(BaseTest):
|
||||
def test_htpasswd_sha256_autodetect(self) -> None:
|
||||
self._test_htpasswd("autodetect", "tmp:$5$i4Ni4TQq6L5FKss5$ilpTjkmnxkwZeV35GB9cYSsDXTALBn6KtWRJAzNlCL/")
|
||||
|
||||
def test_htpasswd_sha256_autodetect_with_rounds(self) -> None:
|
||||
self._test_htpasswd("autodetect", "tmp:$5$rounds=2500$9QD/kpJlV71PCXWy$/AbUzxa6kjDWHJ8BLU1hyQUBN/8wsGEf.rNjuKDHA24")
|
||||
|
||||
def test_htpasswd_sha512(self) -> None:
|
||||
self._test_htpasswd("sha512", "tmp:$6$3Qhl8r6FLagYdHYa$UCH9yXCed4A.J9FQsFPYAOXImzZUMfvLa0lwcWOxWYLOF5sE/lF99auQ4jKvHY2vijxmefl7G6kMqZ8JPdhIJ/")
|
||||
|
||||
def test_htpasswd_sha512_autodetect(self) -> None:
|
||||
self._test_htpasswd("autodetect", "tmp:$6$3Qhl8r6FLagYdHYa$UCH9yXCed4A.J9FQsFPYAOXImzZUMfvLa0lwcWOxWYLOF5sE/lF99auQ4jKvHY2vijxmefl7G6kMqZ8JPdhIJ/")
|
||||
|
||||
def test_htpasswd_sha512_autodetect_with_rounds(self) -> None:
|
||||
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:
|
||||
|
||||
Reference in New Issue
Block a user