mirror of
https://github.com/ansible-collections/community.general.git
synced 2026-07-08 19:49:09 +00:00
htpasswd: fix hash_scheme aliases and Apache-compatible bcrypt (#12123)
* fix(htpasswd): support HtpasswdFile aliases and Apache-compatible bcrypt CryptContext does not recognise HtpasswdFile alias names such as portable, portable_apache_24, host_apache_24, causing a KeyError. In addition, building a CryptContext for bcrypt produced $2b$ hashes that Apache rejects (it only accepts $2y$/$2a$). Use htpasswd_context for schemes it already supports, fall back to htpasswd_context on KeyError for aliases, and import CryptContext from module_utils/_crypt.py instead of passlib directly. Fixes #6135 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat(changelog): add fragment for PR 12123 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(_crypt): silence DeprecationWarning when importing stdlib crypt On Python 3.11/3.12, `import crypt` emits a DeprecationWarning that ansible-test sanity --test import treats as an error. Suppress it since the import is an intentional fallback when passlib is not available. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(htpasswd): fix sanity ignores and bcrypt version constraint - Revert _crypt.py DeprecationWarning suppression; add sanity ignore entries for htpasswd.py import-3.11/3.12 instead (mirrors existing entries for _crypt.py itself) - Pin bcrypt<4.2 in integration tests: bcrypt 4.2 removed __about__ which passlib 1.7.x uses, breaking passlib.apache import - Fix regex_search assertion to use 'is not none' for a boolean result - Add bcrypt version constraint note to module documentation Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(htpasswd): handle system-installed bcrypt in integration tests On Debian/Ubuntu, bcrypt may be installed by the system package manager with no RECORD file, making pip downgrade impossible. Move bcrypt installation into a self-contained block in test_schemes.yml with ignore_errors, a functional passlib+bcrypt check, and always-cleanup. Bcrypt tests are skipped when a compatible version cannot be used. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * refactor(htpasswd): extract obtain_crypt_context(); import CryptContext from passlib directly Extract context selection logic into obtain_crypt_context(). Import CryptContext inside the deps.declare("passlib") block instead of from module_utils/_crypt.py — passlib is already a hard dependency and other symbols are imported from it there. Remove now-unnecessary htpasswd.py sanity import ignore entries. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
2939d7fdd3
commit
49ca175f01
5 changed files with 141 additions and 5 deletions
|
|
@ -66,6 +66,8 @@ notes:
|
|||
- 'On Debian < 11, Ubuntu <= 20.04, or Fedora: install C(python-passlib).'
|
||||
- 'On Debian, Ubuntu: install C(python3-passlib).'
|
||||
- 'On RHEL or CentOS: Enable EPEL, then install C(python-passlib).'
|
||||
- To use V(bcrypt) as O(hash_scheme), the C(bcrypt) Python library must also be installed.
|
||||
Due to incompatibilities in C(passlib) 1.7.x, use C(bcrypt<4.2).
|
||||
requirements: [passlib>=1.6]
|
||||
author: "Ansible Core Team"
|
||||
extends_documentation_fragment:
|
||||
|
|
@ -109,7 +111,7 @@ with deps.declare("passlib"):
|
|||
# Apparently the type infos don't know htpasswd_context, which *does* exist
|
||||
# (but isn't mentioned in the documentation for some reason)
|
||||
from passlib.apache import HtpasswdFile, htpasswd_context # type: ignore[attr-defined]
|
||||
from passlib.context import CryptContext
|
||||
from passlib.context import CryptContext # type: ignore[assignment]
|
||||
|
||||
|
||||
apache_hashes = ["apr_md5_crypt", "des_crypt", "ldap_sha1", "plaintext"]
|
||||
|
|
@ -121,14 +123,24 @@ def create_missing_directories(dest):
|
|||
os.makedirs(destpath)
|
||||
|
||||
|
||||
def obtain_crypt_context(hash_scheme):
|
||||
if hash_scheme in apache_hashes or hash_scheme in htpasswd_context.schemes():
|
||||
# Use htpasswd_context for all officially-supported schemes, including bcrypt
|
||||
# (htpasswd_context produces Apache-compatible $2y$ bcrypt, not $2b$)
|
||||
return htpasswd_context
|
||||
try:
|
||||
return CryptContext(schemes=[hash_scheme] + apache_hashes)
|
||||
except KeyError:
|
||||
# hash_scheme is a HtpasswdFile alias (e.g. portable, portable_apache_24)
|
||||
# that is not a valid passlib scheme name; let HtpasswdFile resolve it natively
|
||||
return htpasswd_context
|
||||
|
||||
|
||||
def present(dest, username, password, hash_scheme, create, check_mode):
|
||||
"""Ensures user is present
|
||||
|
||||
Returns (msg, changed)"""
|
||||
if hash_scheme in apache_hashes:
|
||||
context = htpasswd_context
|
||||
else:
|
||||
context = CryptContext(schemes=[hash_scheme] + apache_hashes)
|
||||
context = obtain_crypt_context(hash_scheme)
|
||||
if not os.path.exists(dest):
|
||||
if not create:
|
||||
raise ValueError(f"Destination {dest} does not exist")
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue