diff --git a/test/integration/targets/filter_core/tasks/password_hash.yml b/test/integration/targets/filter_core/tasks/password_hash.yml index 2b607744e9c..558a1ba3058 100644 --- a/test/integration/targets/filter_core/tasks/password_hash.yml +++ b/test/integration/targets/filter_core/tasks/password_hash.yml @@ -87,3 +87,53 @@ assert: that: - yescrypt_hash_salt == '$y$j9T$V7qMYJaNbVKOeh4PhtqPk/$x7rTqZ.RpI07.dkBSxcg.jLM8ODUfx25rCN0cFsAUg0' + + - name: Test salt with invalid character + set_fact: + md5_hash: "{{ 'test' | password_hash('md5_crypt', salt='123^') }}" + ignore_errors: yes + register: bad_salt_result + + - name: Determine if bad salt character was recognized + assert: + that: bad_salt_result.msg is contains 'invalid characters in salt' + + - name: Test supplying invalid salt size with algo needing exact size + set_fact: + bcrypt_hash: "{{ 'test' | password_hash('bcrypt', salt='12345', salt_size=5) }}" + ignore_errors: yes + register: bad_salt_result + + - name: Determine if salt size mismatch caught + assert: + that: bad_salt_result.msg is contains 'invalid salt size supplied (5), expected 22' + + - name: Test salt with exact size mismatch + set_fact: + bcrypt_hash: "{{ 'test' | password_hash('bcrypt', salt='123') }}" + ignore_errors: yes + register: bad_salt_result + + - name: Determine if salt size mismatch caught + assert: + that: bad_salt_result.msg is contains 'invalid salt size supplied (3), expected 22' + + - name: Test supplying too big salt + set_fact: + bcrypt_hash: "{{ 'test' | password_hash('md5_crypt', salt='1234567890') }}" + ignore_errors: yes + register: bad_salt_result + + - name: Determine if too big salt caught + assert: + that: bad_salt_result.msg is contains 'invalid salt size supplied (10), expected at most 8' + + - name: Test supplying too big salt size + set_fact: + bcrypt_hash: "{{ 'test' | password_hash('md5_crypt', salt='1234567890', salt_size=10) }}" + ignore_errors: yes + register: bad_salt_result + + - name: Determine if too big salt size caught + assert: + that: bad_salt_result.msg is contains 'invalid salt size supplied (10), expected at most 8' diff --git a/test/units/utils/test_encrypt.py b/test/units/utils/test_encrypt.py index dac175ce25d..133d3b1d75a 100644 --- a/test/units/utils/test_encrypt.py +++ b/test/units/utils/test_encrypt.py @@ -7,6 +7,8 @@ import warnings import pytest +from pytest_mock import MockerFixture + from ansible.errors import AnsibleError from ansible.plugins.filter.core import get_encrypted_password @@ -249,3 +251,61 @@ def test_passlib_bcrypt_salt(recwarn): result = passlib_obj.hash(secret, salt=repaired_salt, ident=ident) assert result == expected + + +def test_do_encrypt_no_lib(mocker: MockerFixture) -> None: + """Test AnsibleError is raised when no encryption library is installed.""" + mocker.patch('ansible.utils.encrypt.HAS_CRYPT', False) + mocker.patch('ansible.utils.encrypt.PASSLIB_AVAILABLE', False) + + with pytest.raises(AnsibleError, match=r"Unable to encrypt nor hash, either libxcrypt \(recommended\), crypt, or passlib must be installed\."): + encrypt.do_encrypt("123", "sha256_crypt", salt="12345678") + + +class TestCryptHash: + """ + Tests for the CryptHash class. + + These tests are hitting code paths that are otherwise impossible to reach + through integration tests, but necessary for more complete code coverage. + """ + + def test_invalid_instantiation(self, mocker: MockerFixture) -> None: + """Should not be able to instantiate a CryptHash class without libxcrypt/libcrypt.""" + mocker.patch('ansible.utils.encrypt.HAS_CRYPT', False) + + with pytest.raises(AnsibleError, match=r"crypt cannot be used as the 'libxcrypt' library is not installed or is unusable\."): + encrypt.CryptHash("sha256_crypt") + + def test_ansible_unsupported_algorithm(self) -> None: + """Test AnsibleError is raised when Ansible does not support requested algorithm.""" + with pytest.raises(AnsibleError, match=r"crypt does not support 'foo' algorithm"): + encrypt.CryptHash("foo") + + def test_library_unsupported_algorithm(self, mocker: MockerFixture) -> None: + """Test AnsibleError is raised when crypt library does not support an Ansible supported algorithm.""" + # Pretend we have a crypt lib that doesn't like our algo + mocker.patch('ansible.utils.encrypt.HAS_CRYPT', True) + mocker.patch('ansible._internal._encryption._crypt.crypt', side_effect=ValueError) + + # instantiate with an Ansible supported algo + crypt_hash = encrypt.CryptHash("sha256_crypt") + + with pytest.raises(AnsibleError, match=r"crypt does not support 'sha256_crypt' algorithm"): + crypt_hash.hash("123", salt="12345678") + + +class TestPasslibHash: + """ + Tests for the PasslibHash class. + + These tests are hitting code paths that are otherwise impossible to reach + through integration tests, but necessary for more complete code coverage. + """ + + def test_invalid_instantiation(self, mocker: MockerFixture) -> None: + """Should not be able to instantiate a PasslibHash class without passlib.""" + mocker.patch('ansible.utils.encrypt.PASSLIB_AVAILABLE', False) + + with pytest.raises(AnsibleError, match=r"The passlib Python package must be installed to hash with the 'sha256_crypt' algorithm\."): + encrypt.PasslibHash("sha256_crypt")