From cd0aa35c19a57d67adb1940516ed29b02a2709cd Mon Sep 17 00:00:00 2001 From: Abhijeet Kasurde Date: Thu, 7 Sep 2023 14:44:26 -0700 Subject: [PATCH] encrypt: deprecate passlib_or_crypt (#81571) * deprecate passlib_or_crypt in favor of do_encrypt Fixes: #55839 --- changelogs/fragments/passlib_or_crypt.yml | 3 +++ lib/ansible/plugins/filter/core.py | 4 ++-- lib/ansible/utils/display.py | 2 +- lib/ansible/utils/encrypt.py | 15 ++++++++------- test/units/utils/test_encrypt.py | 13 +++++++++++-- 5 files changed, 25 insertions(+), 12 deletions(-) create mode 100644 changelogs/fragments/passlib_or_crypt.yml diff --git a/changelogs/fragments/passlib_or_crypt.yml b/changelogs/fragments/passlib_or_crypt.yml new file mode 100644 index 00000000000..9b36fad0d57 --- /dev/null +++ b/changelogs/fragments/passlib_or_crypt.yml @@ -0,0 +1,3 @@ +--- +bugfixes: +- encrypt - deprecate passlib_or_crypt API (https://github.com/ansible/ansible/issues/55839). diff --git a/lib/ansible/plugins/filter/core.py b/lib/ansible/plugins/filter/core.py index 8fe04f66531..09820faa7cd 100644 --- a/lib/ansible/plugins/filter/core.py +++ b/lib/ansible/plugins/filter/core.py @@ -34,7 +34,7 @@ from ansible.parsing.ajson import AnsibleJSONEncoder from ansible.parsing.yaml.dumper import AnsibleDumper from ansible.template import recursive_check_defined from ansible.utils.display import Display -from ansible.utils.encrypt import passlib_or_crypt, PASSLIB_AVAILABLE +from ansible.utils.encrypt import do_encrypt, PASSLIB_AVAILABLE from ansible.utils.hashing import md5s, checksum_s from ansible.utils.unicode import unicode_wrap from ansible.utils.vars import merge_hash @@ -292,7 +292,7 @@ def get_encrypted_password(password, hashtype='sha512', salt=None, salt_size=Non ) try: - return passlib_or_crypt(password, hashtype, salt=salt, salt_size=salt_size, rounds=rounds, ident=ident) + return do_encrypt(password, hashtype, salt=salt, salt_size=salt_size, rounds=rounds, ident=ident) except AnsibleError as e: reraise(AnsibleFilterError, AnsibleFilterError(to_native(e), orig_exc=e), sys.exc_info()[2]) except Exception as e: diff --git a/lib/ansible/utils/display.py b/lib/ansible/utils/display.py index c7e09dc2b98..3f331ad8e73 100644 --- a/lib/ansible/utils/display.py +++ b/lib/ansible/utils/display.py @@ -645,7 +645,7 @@ class Display(metaclass=Singleton): if encrypt: # Circular import because encrypt needs a display class from ansible.utils.encrypt import do_encrypt - result = do_encrypt(result, encrypt, salt_size, salt) + result = do_encrypt(result, encrypt, salt_size=salt_size, salt=salt) # handle utf-8 chars result = to_text(result, errors='surrogate_or_strict') diff --git a/lib/ansible/utils/encrypt.py b/lib/ansible/utils/encrypt.py index 4efcdde429b..541c5c82834 100644 --- a/lib/ansible/utils/encrypt.py +++ b/lib/ansible/utils/encrypt.py @@ -163,7 +163,7 @@ class CryptHash(BaseHash): result = None orig_exc = e - # None as result would be interpreted by the some modules (user module) + # None as result would be interpreted by some modules (user module) # as no password at all. if not result: raise AnsibleError( @@ -267,12 +267,13 @@ class PasslibHash(BaseHash): def passlib_or_crypt(secret, algorithm, salt=None, salt_size=None, rounds=None, ident=None): + display.deprecated("passlib_or_crypt API is deprecated in favor of do_encrypt", version='2.20') + return do_encrypt(secret, algorithm, salt=salt, salt_size=salt_size, rounds=rounds, ident=ident) + + +def do_encrypt(result, encrypt, salt_size=None, salt=None, ident=None, rounds=None): if PASSLIB_AVAILABLE: - return PasslibHash(algorithm).hash(secret, salt=salt, salt_size=salt_size, rounds=rounds, ident=ident) + return PasslibHash(encrypt).hash(result, salt=salt, salt_size=salt_size, rounds=rounds, ident=ident) if HAS_CRYPT: - return CryptHash(algorithm).hash(secret, salt=salt, salt_size=salt_size, rounds=rounds, ident=ident) + return CryptHash(encrypt).hash(result, salt=salt, salt_size=salt_size, rounds=rounds, ident=ident) raise AnsibleError("Unable to encrypt nor hash, either crypt or passlib must be installed.", orig_exc=CRYPT_E) - - -def do_encrypt(result, encrypt, salt_size=None, salt=None, ident=None): - return passlib_or_crypt(result, encrypt, salt_size=salt_size, salt=salt, ident=ident) diff --git a/test/units/utils/test_encrypt.py b/test/units/utils/test_encrypt.py index 72fe3b07a05..be325790194 100644 --- a/test/units/utils/test_encrypt.py +++ b/test/units/utils/test_encrypt.py @@ -27,16 +27,25 @@ class passlib_off(object): def assert_hash(expected, secret, algorithm, **settings): + assert encrypt.do_encrypt(secret, algorithm, **settings) == expected if encrypt.PASSLIB_AVAILABLE: - assert encrypt.passlib_or_crypt(secret, algorithm, **settings) == expected assert encrypt.PasslibHash(algorithm).hash(secret, **settings) == expected else: - assert encrypt.passlib_or_crypt(secret, algorithm, **settings) == expected with pytest.raises(AnsibleError) as excinfo: encrypt.PasslibHash(algorithm).hash(secret, **settings) assert excinfo.value.args[0] == "passlib must be installed and usable to hash with '%s'" % algorithm +@pytest.mark.skipif(sys.platform.startswith('darwin'), reason='macOS requires passlib') +def test_passlib_or_crypt(): + with passlib_off(): + expected = "$5$rounds=5000$12345678$uAZsE3BenI2G.nA8DpTl.9Dc8JiqacI53pEqRr5ppT7" + assert encrypt.passlib_or_crypt("123", "sha256_crypt", salt="12345678", rounds=5000) == expected + + expected = "$5$12345678$uAZsE3BenI2G.nA8DpTl.9Dc8JiqacI53pEqRr5ppT7" + assert encrypt.passlib_or_crypt("123", "sha256_crypt", salt="12345678", rounds=5000) == expected + + @pytest.mark.skipif(sys.platform.startswith('darwin'), reason='macOS requires passlib') def test_encrypt_with_rounds_no_passlib(): with passlib_off():