mirror of
https://github.com/ansible-collections/community.general.git
synced 2026-04-21 00:01:08 +00:00
* udm_user - replace crypt/legacycrypt with passlib The stdlib crypt module was removed in Python 3.13. Replace the crypt/legacycrypt import chain with passlib (already used elsewhere in the collection) and use CryptContext.verify() for password comparison. Fixes #4690 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Add changelog fragment for PR 11860 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * remove redundant ignore file entries * udm_user, homectl - replace crypt/legacycrypt with _crypt module utils Add a new _crypt module_utils that abstracts password hashing and verification. It uses passlib when available, falling back to the stdlib crypt or legacycrypt, and raises ImportError if none of them can be imported. Both udm_user and homectl now use this shared utility, fixing compatibility with Python 3.13+. Fixes #4690 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Add BOTMETA entry for _crypt module utils Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * _crypt - fix mypy errors and handle complete unavailability Replace CryptContext = object fallback (rejected by mypy) with a proper dummy class definition. Add has_crypt_context flag so modules can detect when no backend is available. Update both modules to import and check has_crypt_context instead of testing for None. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * adjsutments from review * Update plugins/modules/homectl.py Co-authored-by: Felix Fontein <felix@fontein.de> * Update plugins/modules/udm_user.py Co-authored-by: Felix Fontein <felix@fontein.de> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> Co-authored-by: Felix Fontein <felix@fontein.de>
46 lines
1.5 KiB
Python
46 lines
1.5 KiB
Python
# Copyright (c) 2026, Alexei Znamensky <russoz@gmail.com>
|
|
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
|
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
from __future__ import annotations
|
|
|
|
__all__ = ["CryptContext", "has_crypt_context"]
|
|
|
|
has_crypt_context = True
|
|
try:
|
|
from passlib.context import CryptContext
|
|
|
|
except ImportError:
|
|
try:
|
|
try:
|
|
import crypt as _crypt_mod
|
|
except ImportError:
|
|
import legacycrypt as _crypt_mod
|
|
|
|
_SCHEME_TO_METHOD = {
|
|
"sha512_crypt": _crypt_mod.METHOD_SHA512,
|
|
"sha256_crypt": _crypt_mod.METHOD_SHA256,
|
|
"md5_crypt": _crypt_mod.METHOD_MD5,
|
|
"des_crypt": _crypt_mod.METHOD_CRYPT,
|
|
}
|
|
|
|
class CryptContext: # type: ignore[no-redef]
|
|
@staticmethod
|
|
def verify(password, password_hash):
|
|
return _crypt_mod.crypt(password, password_hash) == password_hash
|
|
|
|
@staticmethod
|
|
def hash(password, scheme="sha512_crypt", rounds=10000):
|
|
method = _SCHEME_TO_METHOD.get(scheme)
|
|
if method is None:
|
|
raise ValueError(f"Unsupported scheme: {scheme}")
|
|
salt = _crypt_mod.mksalt(method, rounds=rounds)
|
|
return _crypt_mod.crypt(password, salt)
|
|
|
|
except ImportError:
|
|
|
|
class CryptContext: # type: ignore[no-redef]
|
|
pass
|
|
|
|
has_crypt_context = False
|