java_keystore: fix keystore type (#2516)

* fix keystore type; update unit tests

* add changelog fragment

* document new param 'keystore_type'

* add keystore_type support (backward compatible)

* check JKS format with magic bytes
* update integration tests
* revert first changes in unit tests
* update changelog fragment

* fix magic bytes for python2/python3

* fix integration tests (irrelevant check_mode)

* fix unit test (keystore removed before failure => changed=true)

* fix typo

* fix spelling

* shorten a branch

* mock is_jks_or_pkcs12

* fix function path in unit tests

* Apply suggestions from code review (spelling)

Co-authored-by: Ajpantuso <ajpantuso@gmail.com>

* rename a method (module + unit tests)

* move ArgumentSpec class content to main()

* refactor create() to not loose existing keystore in case of error

* update unit tests

* add integration test (error handling)

* fix keystore backup cleanup

Co-authored-by: Ajpantuso <ajpantuso@gmail.com>
This commit is contained in:
quidame 2021-06-27 14:56:43 +02:00 committed by GitHub
parent 2fb0877577
commit 199ead85d0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 308 additions and 69 deletions

View file

@ -14,7 +14,25 @@ from ansible_collections.community.general.tests.unit.plugins.modules.utils impo
from ansible_collections.community.general.tests.unit.compat.mock import patch
from ansible_collections.community.general.tests.unit.compat.mock import Mock
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.general.plugins.modules.system.java_keystore import JavaKeystore, ArgumentSpec
from ansible_collections.community.general.plugins.modules.system.java_keystore import JavaKeystore
module_argument_spec = dict(
name=dict(type='str', required=True),
dest=dict(type='path', required=True),
certificate=dict(type='str', no_log=True),
certificate_path=dict(type='path'),
private_key=dict(type='str', no_log=True),
private_key_path=dict(type='path', no_log=False),
private_key_passphrase=dict(type='str', no_log=True),
password=dict(type='str', required=True, no_log=True),
ssl_backend=dict(type='str', default='openssl', choices=['openssl', 'cryptography']),
keystore_type=dict(type='str', choices=['jks', 'pkcs12']),
force=dict(type='bool', default=False),
)
module_supports_check_mode = True
module_choose_between = (['certificate', 'certificate_path'],
['private_key', 'private_key_path'])
class TestCreateJavaKeystore(ModuleTestCase):
@ -25,11 +43,13 @@ class TestCreateJavaKeystore(ModuleTestCase):
super(TestCreateJavaKeystore, self).setUp()
orig_exists = os.path.exists
self.spec = ArgumentSpec()
self.mock_create_file = patch('ansible_collections.community.general.plugins.modules.system.java_keystore.create_file')
self.mock_create_path = patch('ansible_collections.community.general.plugins.modules.system.java_keystore.create_path')
self.mock_current_type = patch('ansible_collections.community.general.plugins.modules.system.java_keystore.JavaKeystore.current_type')
self.mock_run_command = patch('ansible.module_utils.basic.AnsibleModule.run_command')
self.mock_get_bin_path = patch('ansible.module_utils.basic.AnsibleModule.get_bin_path')
self.mock_preserved_copy = patch('ansible.module_utils.basic.AnsibleModule.preserved_copy')
self.mock_atomic_move = patch('ansible.module_utils.basic.AnsibleModule.atomic_move')
self.mock_os_path_exists = patch('os.path.exists',
side_effect=lambda path: True if path == '/path/to/keystore.jks' else orig_exists(path))
self.mock_selinux_context = patch('ansible.module_utils.basic.AnsibleModule.selinux_context',
@ -38,8 +58,11 @@ class TestCreateJavaKeystore(ModuleTestCase):
side_effect=lambda path: (False, None))
self.run_command = self.mock_run_command.start()
self.get_bin_path = self.mock_get_bin_path.start()
self.preserved_copy = self.mock_preserved_copy.start()
self.atomic_move = self.mock_atomic_move.start()
self.create_file = self.mock_create_file.start()
self.create_path = self.mock_create_path.start()
self.current_type = self.mock_current_type.start()
self.selinux_context = self.mock_selinux_context.start()
self.is_special_selinux_path = self.mock_is_special_selinux_path.start()
self.os_path_exists = self.mock_os_path_exists.start()
@ -49,8 +72,11 @@ class TestCreateJavaKeystore(ModuleTestCase):
super(TestCreateJavaKeystore, self).tearDown()
self.mock_create_file.stop()
self.mock_create_path.stop()
self.mock_current_type.stop()
self.mock_run_command.stop()
self.mock_get_bin_path.stop()
self.mock_preserved_copy.stop()
self.mock_atomic_move.stop()
self.mock_selinux_context.stop()
self.mock_is_special_selinux_path.stop()
self.mock_os_path_exists.stop()
@ -65,8 +91,10 @@ class TestCreateJavaKeystore(ModuleTestCase):
))
module = AnsibleModule(
argument_spec=self.spec.argument_spec,
supports_check_mode=self.spec.supports_check_mode
argument_spec=module_argument_spec,
supports_check_mode=module_supports_check_mode,
mutually_exclusive=module_choose_between,
required_one_of=module_choose_between
)
with patch('os.remove', return_value=True):
@ -96,8 +124,10 @@ class TestCreateJavaKeystore(ModuleTestCase):
))
module = AnsibleModule(
argument_spec=self.spec.argument_spec,
supports_check_mode=self.spec.supports_check_mode
argument_spec=module_argument_spec,
supports_check_mode=module_supports_check_mode,
mutually_exclusive=module_choose_between,
required_one_of=module_choose_between
)
module.exit_json = Mock()
@ -106,7 +136,7 @@ class TestCreateJavaKeystore(ModuleTestCase):
with patch('os.remove', return_value=True):
self.create_path.side_effect = ['/tmp/tmp1cyp12xa']
self.create_file.side_effect = ['/tmp/tmpvalcrt32', '/tmp/tmpwh4key0c', '']
self.run_command.side_effect = [(1, '', ''), (0, '', '')]
self.run_command.side_effect = [(1, '', 'Oops'), (0, '', '')]
self.get_bin_path.side_effect = ['keytool', 'openssl', '']
jks = JavaKeystore(module)
jks.create()
@ -118,6 +148,7 @@ class TestCreateJavaKeystore(ModuleTestCase):
"-passout", "stdin",
"-passin", "stdin"],
msg='',
err='Oops',
rc=1
)
@ -131,8 +162,10 @@ class TestCreateJavaKeystore(ModuleTestCase):
))
module = AnsibleModule(
argument_spec=self.spec.argument_spec,
supports_check_mode=self.spec.supports_check_mode
argument_spec=module_argument_spec,
supports_check_mode=module_supports_check_mode,
mutually_exclusive=module_choose_between,
required_one_of=module_choose_between
)
module.exit_json = Mock()
@ -141,7 +174,7 @@ class TestCreateJavaKeystore(ModuleTestCase):
with patch('os.remove', return_value=True):
self.create_path.side_effect = ['/tmp/tmp1cyp12xa']
self.create_file.side_effect = ['/tmp/tmpvalcrt32', '/tmp/tmpwh4key0c', '']
self.run_command.side_effect = [(1, '', ''), (0, '', '')]
self.run_command.side_effect = [(1, '', 'Oops'), (0, '', '')]
self.get_bin_path.side_effect = ['keytool', 'openssl', '']
jks = JavaKeystore(module)
jks.create()
@ -152,6 +185,7 @@ class TestCreateJavaKeystore(ModuleTestCase):
"-out", "/tmp/tmp1cyp12xa",
"-passout", "stdin"],
msg='',
err='Oops',
rc=1
)
@ -165,8 +199,10 @@ class TestCreateJavaKeystore(ModuleTestCase):
))
module = AnsibleModule(
argument_spec=self.spec.argument_spec,
supports_check_mode=self.spec.supports_check_mode
argument_spec=module_argument_spec,
supports_check_mode=module_supports_check_mode,
mutually_exclusive=module_choose_between,
required_one_of=module_choose_between
)
module.exit_json = Mock()
@ -175,7 +211,7 @@ class TestCreateJavaKeystore(ModuleTestCase):
with patch('os.remove', return_value=True):
self.create_path.side_effect = ['/tmp/tmpgrzm2ah7']
self.create_file.side_effect = ['/tmp/etacifitrec', '/tmp/yek_etavirp', '']
self.run_command.side_effect = [(0, '', ''), (1, '', '')]
self.run_command.side_effect = [(0, '', ''), (1, '', 'Oops')]
self.get_bin_path.side_effect = ['keytool', 'openssl', '']
jks = JavaKeystore(module)
jks.create()
@ -185,6 +221,7 @@ class TestCreateJavaKeystore(ModuleTestCase):
"-srckeystore", "/tmp/tmpgrzm2ah7", "-srcstoretype", "pkcs12", "-alias", "test",
"-noprompt"],
msg='',
err='Oops',
rc=1
)
@ -195,20 +232,28 @@ class TestCertChanged(ModuleTestCase):
def setUp(self):
"""Setup."""
super(TestCertChanged, self).setUp()
self.spec = ArgumentSpec()
self.mock_create_file = patch('ansible_collections.community.general.plugins.modules.system.java_keystore.create_file')
self.mock_current_type = patch('ansible_collections.community.general.plugins.modules.system.java_keystore.JavaKeystore.current_type')
self.mock_run_command = patch('ansible.module_utils.basic.AnsibleModule.run_command')
self.mock_get_bin_path = patch('ansible.module_utils.basic.AnsibleModule.get_bin_path')
self.mock_preserved_copy = patch('ansible.module_utils.basic.AnsibleModule.preserved_copy')
self.mock_atomic_move = patch('ansible.module_utils.basic.AnsibleModule.atomic_move')
self.run_command = self.mock_run_command.start()
self.create_file = self.mock_create_file.start()
self.get_bin_path = self.mock_get_bin_path.start()
self.current_type = self.mock_current_type.start()
self.preserved_copy = self.mock_preserved_copy.start()
self.atomic_move = self.mock_atomic_move.start()
def tearDown(self):
"""Teardown."""
super(TestCertChanged, self).tearDown()
self.mock_create_file.stop()
self.mock_current_type.stop()
self.mock_run_command.stop()
self.mock_get_bin_path.stop()
self.mock_preserved_copy.stop()
self.mock_atomic_move.stop()
def test_cert_unchanged_same_fingerprint(self):
set_module_args(dict(
@ -220,14 +265,17 @@ class TestCertChanged(ModuleTestCase):
))
module = AnsibleModule(
argument_spec=self.spec.argument_spec,
supports_check_mode=self.spec.supports_check_mode
argument_spec=module_argument_spec,
supports_check_mode=module_supports_check_mode,
mutually_exclusive=module_choose_between,
required_one_of=module_choose_between
)
with patch('os.remove', return_value=True):
self.create_file.side_effect = ['/tmp/placeholder', '']
self.run_command.side_effect = [(0, 'foo=abcd:1234:efgh', ''), (0, 'SHA256: abcd:1234:efgh', '')]
self.get_bin_path.side_effect = ['keytool', 'openssl', '']
self.current_type.side_effect = ['jks']
jks = JavaKeystore(module)
result = jks.cert_changed()
self.assertFalse(result, 'Fingerprint is identical')
@ -242,19 +290,22 @@ class TestCertChanged(ModuleTestCase):
))
module = AnsibleModule(
argument_spec=self.spec.argument_spec,
supports_check_mode=self.spec.supports_check_mode
argument_spec=module_argument_spec,
supports_check_mode=module_supports_check_mode,
mutually_exclusive=module_choose_between,
required_one_of=module_choose_between
)
with patch('os.remove', return_value=True):
self.create_file.side_effect = ['/tmp/placeholder', '']
self.run_command.side_effect = [(0, 'foo=abcd:1234:efgh', ''), (0, 'SHA256: wxyz:9876:stuv', '')]
self.get_bin_path.side_effect = ['keytool', 'openssl', '']
self.current_type.side_effect = ['jks']
jks = JavaKeystore(module)
result = jks.cert_changed()
self.assertTrue(result, 'Fingerprint mismatch')
def test_cert_changed_fail_alias_does_not_exist(self):
def test_cert_changed_alias_does_not_exist(self):
set_module_args(dict(
certificate='cert-foo',
private_key='private-foo',
@ -264,8 +315,10 @@ class TestCertChanged(ModuleTestCase):
))
module = AnsibleModule(
argument_spec=self.spec.argument_spec,
supports_check_mode=self.spec.supports_check_mode
argument_spec=module_argument_spec,
supports_check_mode=module_supports_check_mode,
mutually_exclusive=module_choose_between,
required_one_of=module_choose_between
)
with patch('os.remove', return_value=True):
@ -287,8 +340,10 @@ class TestCertChanged(ModuleTestCase):
))
module = AnsibleModule(
argument_spec=self.spec.argument_spec,
supports_check_mode=self.spec.supports_check_mode
argument_spec=module_argument_spec,
supports_check_mode=module_supports_check_mode,
mutually_exclusive=module_choose_between,
required_one_of=module_choose_between
)
with patch('os.remove', return_value=True):
@ -310,8 +365,10 @@ class TestCertChanged(ModuleTestCase):
))
module = AnsibleModule(
argument_spec=self.spec.argument_spec,
supports_check_mode=self.spec.supports_check_mode
argument_spec=module_argument_spec,
supports_check_mode=module_supports_check_mode,
mutually_exclusive=module_choose_between,
required_one_of=module_choose_between
)
module.exit_json = Mock()
@ -321,6 +378,7 @@ class TestCertChanged(ModuleTestCase):
self.create_file.side_effect = ['/tmp/tmpdj6bvvme', '']
self.run_command.side_effect = [(1, '', 'Oops'), (0, 'SHA256: wxyz:9876:stuv', '')]
self.get_bin_path.side_effect = ['keytool', 'openssl', '']
self.current_type.side_effect = ['jks']
jks = JavaKeystore(module)
jks.cert_changed()
module.fail_json.assert_called_once_with(
@ -340,8 +398,10 @@ class TestCertChanged(ModuleTestCase):
))
module = AnsibleModule(
argument_spec=self.spec.argument_spec,
supports_check_mode=self.spec.supports_check_mode
argument_spec=module_argument_spec,
supports_check_mode=module_supports_check_mode,
mutually_exclusive=module_choose_between,
required_one_of=module_choose_between
)
module.exit_json = Mock()