From 318462fa2445953d22e48e35dc06a1099c4e8f37 Mon Sep 17 00:00:00 2001 From: Felix Fontein Date: Sun, 18 May 2025 00:57:28 +0200 Subject: [PATCH] Work on issues found by pylint (#896) * Look at possibly-used-before-assignment. * Use latest beta releases of ansible-core 2.19 for mypy and pylint. * Look at unsupported-*. * Look at unknown-option-value. * Look at redefined-builtin. * Look at superfluous-parens. * Look at unspecified-encoding. * Adjust to new cryptography version and to ansible-core 2.17's pylint. * Look at super-with-arguments. * Look at no-else-*. * Look at try-except-raise. * Look at inconsistent-return-statements. * Look at redefined-outer-name. * Look at redefined-argument-from-local. * Look at attribute-defined-outside-init. * Look at unused-variable. * Look at protected-access. * Look at raise-missing-from. * Look at arguments-differ. * Look at useless-suppression and use-symbolic-message-instead. * Look at consider-using-dict-items. * Look at consider-using-in. * Look at consider-using-set-comprehension. * Look at consider-using-with. * Look at use-dict-literal. --- .pylintrc | 29 +-- antsibull-nox.toml | 3 +- plugins/action/openssl_privatekey_pipe.py | 30 +-- plugins/filter/gpg_fingerprint.py | 12 +- plugins/filter/openssl_csr_info.py | 2 +- plugins/filter/openssl_privatekey_info.py | 4 +- plugins/filter/openssl_publickey_info.py | 4 +- plugins/filter/parse_serial.py | 10 +- plugins/filter/to_serial.py | 12 +- plugins/filter/x509_certificate_info.py | 2 +- plugins/filter/x509_crl_info.py | 2 +- plugins/lookup/gpg_fingerprint.py | 2 +- plugins/module_utils/_acme/account.py | 25 +- plugins/module_utils/_acme/acme.py | 46 ++-- .../_acme/backend_cryptography.py | 25 +- .../module_utils/_acme/backend_openssl_cli.py | 17 +- plugins/module_utils/_acme/backends.py | 6 +- plugins/module_utils/_acme/certificate.py | 12 +- plugins/module_utils/_acme/certificates.py | 2 +- plugins/module_utils/_acme/challenges.py | 10 +- plugins/module_utils/_acme/errors.py | 14 +- plugins/module_utils/_acme/io.py | 6 +- plugins/module_utils/_acme/utils.py | 4 +- plugins/module_utils/_crypto/_objects.py | 6 +- .../module_utils/_crypto/cryptography_crl.py | 10 +- .../_crypto/cryptography_support.py | 112 ++++---- .../_crypto/module_backends/certificate.py | 43 ++-- .../module_backends/certificate_acme.py | 25 +- .../module_backends/certificate_entrust.py | 49 ++-- .../module_backends/certificate_info.py | 62 ++--- .../module_backends/certificate_ownca.py | 42 ++- .../module_backends/certificate_selfsigned.py | 48 ++-- .../_crypto/module_backends/crl_info.py | 27 +- .../_crypto/module_backends/csr.py | 241 ++++++++++-------- .../_crypto/module_backends/csr_info.py | 58 +++-- .../_crypto/module_backends/privatekey.py | 117 ++++----- .../module_backends/privatekey_convert.py | 61 +++-- .../module_backends/privatekey_info.py | 16 +- .../_crypto/module_backends/publickey_info.py | 10 +- plugins/module_utils/_crypto/support.py | 32 +-- plugins/module_utils/_ecs/api.py | 31 ++- .../module_utils/_openssh/backends/common.py | 37 ++- .../_openssh/backends/keypair_backend.py | 24 +- plugins/module_utils/_openssh/certificate.py | 82 +++--- plugins/module_utils/_openssh/cryptography.py | 74 +++--- plugins/module_utils/_openssh/utils.py | 9 +- plugins/module_utils/_serial.py | 2 +- plugins/module_utils/_time.py | 3 +- plugins/modules/acme_account.py | 54 ++-- plugins/modules/acme_account_info.py | 8 +- plugins/modules/acme_ari_info.py | 4 +- plugins/modules/acme_certificate.py | 102 ++++---- .../acme_certificate_deactivate_authz.py | 2 +- .../modules/acme_certificate_order_create.py | 18 +- .../acme_certificate_order_finalize.py | 50 ++-- .../modules/acme_certificate_order_info.py | 2 +- .../acme_certificate_order_validate.py | 20 +- .../modules/acme_certificate_renewal_info.py | 36 +-- plugins/modules/acme_certificate_revoke.py | 14 +- plugins/modules/acme_challenge_cert_helper.py | 18 +- plugins/modules/acme_inspect.py | 26 +- plugins/modules/certificate_complete_chain.py | 22 +- plugins/modules/crypto_info.py | 61 ++--- plugins/modules/ecs_certificate.py | 155 ++++++----- plugins/modules/ecs_domain.py | 34 +-- plugins/modules/get_certificate.py | 54 ++-- plugins/modules/luks_device.py | 122 +++++---- plugins/modules/openssh_cert.py | 72 +++--- plugins/modules/openssh_keypair.py | 62 ++--- plugins/modules/openssl_csr.py | 22 +- plugins/modules/openssl_csr_info.py | 24 +- plugins/modules/openssl_csr_pipe.py | 6 +- plugins/modules/openssl_dhparam.py | 34 ++- plugins/modules/openssl_pkcs12.py | 100 ++++---- plugins/modules/openssl_privatekey.py | 26 +- plugins/modules/openssl_privatekey_convert.py | 10 +- plugins/modules/openssl_privatekey_info.py | 32 +-- plugins/modules/openssl_publickey.py | 67 ++--- plugins/modules/openssl_publickey_info.py | 26 +- plugins/modules/openssl_signature.py | 26 +- plugins/modules/openssl_signature_info.py | 26 +- plugins/modules/x509_certificate.py | 28 +- plugins/modules/x509_certificate_convert.py | 32 +-- plugins/modules/x509_certificate_info.py | 28 +- plugins/modules/x509_certificate_pipe.py | 6 +- plugins/modules/x509_crl.py | 155 +++++------ plugins/modules/x509_crl_info.py | 18 +- plugins/plugin_utils/_action_module.py | 8 +- plugins/plugin_utils/_gnupg.py | 26 +- tests/sanity/ignore-2.17.txt | 1 + .../module_utils/_acme/backend_data.py | 101 +++++--- .../_acme/test_backend_cryptography.py | 6 +- .../_acme/test_backend_openssl_cli.py | 8 +- .../plugins/module_utils/_acme/test_errors.py | 12 +- tests/unit/plugins/module_utils/test__time.py | 80 +++--- .../unit/plugins/modules/test_luks_device.py | 2 +- 96 files changed, 1748 insertions(+), 1598 deletions(-) diff --git a/.pylintrc b/.pylintrc index 56ecb468..a3489d0f 100644 --- a/.pylintrc +++ b/.pylintrc @@ -356,11 +356,10 @@ disable=raw-checker-failed, missing-module-docstring, locally-disabled, suppressed-message, - useless-suppression, - use-symbolic-message-instead, use-implicit-booleaness-not-comparison, use-implicit-booleaness-not-comparison-to-string, use-implicit-booleaness-not-comparison-to-zero, + superfluous-parens, too-few-public-methods, too-many-arguments, too-many-boolean-expressions, @@ -378,35 +377,13 @@ disable=raw-checker-failed, wrong-import-order, wrong-import-position, # To clean up: - arguments-differ, - attribute-defined-outside-init, broad-exception-caught, broad-exception-raised, - consider-using-dict-items, - consider-using-in, - consider-using-set-comprehension, - consider-using-with, fixme, - inconsistent-return-statements, invalid-name, - no-else-raise, - no-else-return, - possibly-used-before-assignment, - protected-access, - raise-missing-from, - redefined-argument-from-local, - redefined-builtin, - redefined-outer-name, - superfluous-parens, - super-with-arguments, - try-except-raise, - unknown-option-value, - unspecified-encoding, - unsupported-assignment-operation, - unsupported-binary-operation, unused-argument, - unused-variable, - use-dict-literal, + # Cannot remove yet due to inadequacy of rules + inconsistent-return-statements, # doesn't notice that fail_json() does not return # Enable the message, report, category or checker with the given id(s). You can # either give multiple identifier separated by comma (,) or put this option diff --git a/antsibull-nox.toml b/antsibull-nox.toml index 73162e06..40c227f0 100644 --- a/antsibull-nox.toml +++ b/antsibull-nox.toml @@ -15,12 +15,13 @@ run_flake8 = true flake8_config = ".flake8" run_pylint = true pylint_rcfile = ".pylintrc" +pylint_ansible_core_package = "ansible-core>=2.19.0b4" run_yamllint = true yamllint_config = ".yamllint" yamllint_config_plugins = ".yamllint-docs" yamllint_config_plugins_examples = ".yamllint-examples" run_mypy = true -mypy_ansible_core_package = "ansible-core>=2.19.0b3" +mypy_ansible_core_package = "ansible-core>=2.19.0b4" mypy_config = ".mypy.ini" mypy_extra_deps = [ "cryptography", diff --git a/plugins/action/openssl_privatekey_pipe.py b/plugins/action/openssl_privatekey_pipe.py index bee58137..33842e6b 100644 --- a/plugins/action/openssl_privatekey_pipe.py +++ b/plugins/action/openssl_privatekey_pipe.py @@ -60,14 +60,14 @@ class PrivateKeyModule: if self.module_backend.needs_regeneration(): # Regenerate self.module_backend.generate_private_key() - privatekey_data = self.module_backend.get_private_key_data() - self.privatekey_bytes = privatekey_data + # Call get_private_key_data() to make sure that exceptions are raised now: + self.module_backend.get_private_key_data() self.changed = True elif self.module_backend.needs_conversion(): # Convert self.module_backend.convert_private_key() - privatekey_data = self.module_backend.get_private_key_data() - self.privatekey_bytes = privatekey_data + # Call get_private_key_data() to make sure that exceptions are raised now: + self.module_backend.get_private_key_data() self.changed = True def dump(self) -> dict[str, t.Any]: @@ -80,22 +80,20 @@ class PrivateKeyModule: class ActionModule(ActionModuleBase): - @staticmethod - def setup_module() -> tuple[ArgumentSpec, dict[str, t.Any]]: + def setup_module(self) -> tuple[ArgumentSpec, dict[str, t.Any]]: argument_spec = get_privatekey_argument_spec() argument_spec.argument_spec.update( - dict( - content=dict(type="str", no_log=True), - content_base64=dict(type="bool", default=False), - return_current_key=dict(type="bool", default=False), - ) - ) - return argument_spec, dict( - supports_check_mode=True, + { + "content": {"type": "str", "no_log": True}, + "content_base64": {"type": "bool", "default": False}, + "return_current_key": {"type": "bool", "default": False}, + } ) + return argument_spec, { + "supports_check_mode": True, + } - @staticmethod - def run_module(module: AnsibleActionModule) -> None: + def run_module(self, module: AnsibleActionModule) -> None: module_backend = select_backend(module=module) try: diff --git a/plugins/filter/gpg_fingerprint.py b/plugins/filter/gpg_fingerprint.py index 41edc6e7..7ab14d77 100644 --- a/plugins/filter/gpg_fingerprint.py +++ b/plugins/filter/gpg_fingerprint.py @@ -52,16 +52,18 @@ from ansible_collections.community.crypto.plugins.plugin_utils._gnupg import ( ) -def gpg_fingerprint(input: str | bytes) -> str: - if not isinstance(input, (str, bytes)): +def gpg_fingerprint(gpg_key_content: str | bytes) -> str: + if not isinstance(gpg_key_content, (str, bytes)): raise AnsibleFilterError( - f"The input for the community.crypto.gpg_fingerprint filter must be a string; got {type(input)} instead" + f"The input for the community.crypto.gpg_fingerprint filter must be a string; got {type(gpg_key_content)} instead" ) try: gpg = PluginGPGRunner() - return get_fingerprint_from_bytes(gpg_runner=gpg, content=to_bytes(input)) + return get_fingerprint_from_bytes( + gpg_runner=gpg, content=to_bytes(gpg_key_content) + ) except GPGError as exc: - raise AnsibleFilterError(str(exc)) + raise AnsibleFilterError(str(exc)) from exc class FilterModule: diff --git a/plugins/filter/openssl_csr_info.py b/plugins/filter/openssl_csr_info.py index 9a04c616..63b889d6 100644 --- a/plugins/filter/openssl_csr_info.py +++ b/plugins/filter/openssl_csr_info.py @@ -313,7 +313,7 @@ def openssl_csr_info_filter( module=module, content=to_bytes(data), validate_signature=True ) except OpenSSLObjectError as exc: - raise AnsibleFilterError(str(exc)) + raise AnsibleFilterError(str(exc)) from exc class FilterModule: diff --git a/plugins/filter/openssl_privatekey_info.py b/plugins/filter/openssl_privatekey_info.py index 5009d0dd..62975f98 100644 --- a/plugins/filter/openssl_privatekey_info.py +++ b/plugins/filter/openssl_privatekey_info.py @@ -193,9 +193,9 @@ def openssl_privatekey_info_filter( result.pop("key_is_consistent", None) return result except PrivateKeyParseError as exc: - raise AnsibleFilterError(exc.error_message) + raise AnsibleFilterError(exc.error_message) from exc except OpenSSLObjectError as exc: - raise AnsibleFilterError(str(exc)) + raise AnsibleFilterError(str(exc)) from exc class FilterModule: diff --git a/plugins/filter/openssl_publickey_info.py b/plugins/filter/openssl_publickey_info.py index 62c7bf2d..964e23c2 100644 --- a/plugins/filter/openssl_publickey_info.py +++ b/plugins/filter/openssl_publickey_info.py @@ -150,9 +150,9 @@ def openssl_publickey_info_filter(data: str | bytes) -> dict[str, t.Any]: try: return get_publickey_info(module=module, content=to_bytes(data)) except PublicKeyParseError as exc: - raise AnsibleFilterError(exc.error_message) + raise AnsibleFilterError(exc.error_message) from exc except OpenSSLObjectError as exc: - raise AnsibleFilterError(str(exc)) + raise AnsibleFilterError(str(exc)) from exc class FilterModule: diff --git a/plugins/filter/parse_serial.py b/plugins/filter/parse_serial.py index f7e086a2..9175f65e 100644 --- a/plugins/filter/parse_serial.py +++ b/plugins/filter/parse_serial.py @@ -48,15 +48,15 @@ from ansible_collections.community.crypto.plugins.module_utils._serial import ( ) -def parse_serial_filter(input: str | bytes) -> int: - if not isinstance(input, (str, bytes)): +def parse_serial_filter(serial_str: str | bytes) -> int: + if not isinstance(serial_str, (str, bytes)): raise AnsibleFilterError( - f"The input for the community.crypto.parse_serial filter must be a string; got {type(input)} instead" + f"The input for the community.crypto.parse_serial filter must be a string; got {type(serial_str)} instead" ) try: - return parse_serial(to_native(input)) + return parse_serial(to_native(serial_str)) except ValueError as exc: - raise AnsibleFilterError(str(exc)) + raise AnsibleFilterError(str(exc)) from exc class FilterModule: diff --git a/plugins/filter/to_serial.py b/plugins/filter/to_serial.py index f2c72b91..0f99d3aa 100644 --- a/plugins/filter/to_serial.py +++ b/plugins/filter/to_serial.py @@ -45,19 +45,19 @@ from ansible.errors import AnsibleFilterError from ansible_collections.community.crypto.plugins.module_utils._serial import to_serial -def to_serial_filter(input: int) -> str: - if not isinstance(input, int): +def to_serial_filter(serial_int: int) -> str: + if not isinstance(serial_int, int): raise AnsibleFilterError( - f"The input for the community.crypto.to_serial filter must be an integer; got {type(input)} instead" + f"The input for the community.crypto.to_serial filter must be an integer; got {type(serial_int)} instead" ) - if input < 0: + if serial_int < 0: raise AnsibleFilterError( "The input for the community.crypto.to_serial filter must not be negative" ) try: - return to_serial(input) + return to_serial(serial_int) except ValueError as exc: - raise AnsibleFilterError(str(exc)) + raise AnsibleFilterError(str(exc)) from exc class FilterModule: diff --git a/plugins/filter/x509_certificate_info.py b/plugins/filter/x509_certificate_info.py index 2b2bdc70..51fca7d4 100644 --- a/plugins/filter/x509_certificate_info.py +++ b/plugins/filter/x509_certificate_info.py @@ -345,7 +345,7 @@ def x509_certificate_info_filter( try: return get_certificate_info(module=module, content=to_bytes(data)) except OpenSSLObjectError as exc: - raise AnsibleFilterError(str(exc)) + raise AnsibleFilterError(str(exc)) from exc class FilterModule: diff --git a/plugins/filter/x509_crl_info.py b/plugins/filter/x509_crl_info.py index 1164aa72..73a24a9c 100644 --- a/plugins/filter/x509_crl_info.py +++ b/plugins/filter/x509_crl_info.py @@ -212,7 +212,7 @@ def x509_crl_info_filter( list_revoked_certificates=list_revoked_certificates, ) except OpenSSLObjectError as exc: - raise AnsibleFilterError(str(exc)) + raise AnsibleFilterError(str(exc)) from exc class FilterModule: diff --git a/plugins/lookup/gpg_fingerprint.py b/plugins/lookup/gpg_fingerprint.py index 5d108b6c..74572960 100644 --- a/plugins/lookup/gpg_fingerprint.py +++ b/plugins/lookup/gpg_fingerprint.py @@ -76,4 +76,4 @@ class LookupModule(LookupBase): ) return result except GPGError as exc: - raise AnsibleLookupError(str(exc)) + raise AnsibleLookupError(str(exc)) from exc diff --git a/plugins/module_utils/_acme/account.py b/plugins/module_utils/_acme/account.py index aeb22911..43516a7b 100644 --- a/plugins/module_utils/_acme/account.py +++ b/plugins/module_utils/_acme/account.py @@ -119,7 +119,7 @@ class ACMEAccount: if "location" in info: self.client.set_account_uri(info["location"]) return True, result - elif info["status"] == 200: + if info["status"] == 200: # Account did exist if result.get("status") == "deactivated": # A bug in Pebble (https://github.com/letsencrypt/pebble/issues/179) and @@ -130,12 +130,11 @@ class ACMEAccount: # requests authorized by that account's key." if not allow_creation: return False, None - else: - raise ModuleFailException("Account is deactivated") + raise ModuleFailException("Account is deactivated") if "location" in info: self.client.set_account_uri(info["location"]) return False, result - elif ( + if ( info["status"] in (400, 404) and result["type"] == "urn:ietf:params:acme:error:accountDoesNotExist" and not allow_creation @@ -144,7 +143,7 @@ class ACMEAccount: # (According to RFC 8555, Section 7.3.1, the HTTP status code MUST be 400. # Unfortunately Digicert does not care and sends 404 instead.) return False, None - elif ( + if ( info["status"] == 403 and result["type"] == "urn:ietf:params:acme:error:unauthorized" and "deactivated" in (result.get("detail") or "") @@ -154,15 +153,13 @@ class ACMEAccount: # might need adjustment in error detection. if not allow_creation: return False, None - else: - raise ModuleFailException("Account is deactivated") - else: - raise ACMEProtocolException( - module=self.client.module, - msg="Registering ACME account failed", - info=info, - content_json=result, - ) + raise ModuleFailException("Account is deactivated") + raise ACMEProtocolException( + module=self.client.module, + msg="Registering ACME account failed", + info=info, + content_json=result, + ) def get_account_data(self) -> dict[str, t.Any] | None: """ diff --git a/plugins/module_utils/_acme/acme.py b/plugins/module_utils/_acme/acme.py index 0a035c87..94039a57 100644 --- a/plugins/module_utils/_acme/acme.py +++ b/plugins/module_utils/_acme/acme.py @@ -244,7 +244,9 @@ class ACMEClient: passphrase=self.account_key_passphrase, ) except KeyParsingError as e: - raise ModuleFailException(f"Error while parsing account key: {e.msg}") + raise ModuleFailException( + f"Error while parsing account key: {e.msg}" + ) from e self.account_jwk = self.account_key_data["jwk"] self.account_jws_header = { "alg": self.account_key_data["alg"], @@ -307,7 +309,7 @@ class ACMEClient: except Exception as e: raise ModuleFailException( f"Failed to encode payload / headers as JSON: {e}" - ) + ) from e return self.backend.sign( payload64=payload64, protected64=protected64, key_data=key_data @@ -456,10 +458,10 @@ class ACMEClient: result = decoded_result else: result = content - except ValueError: + except ValueError as exc: raise NetworkException( f"Failed to parse the ACME response: {url} {content}" - ) + ) from exc else: result = content @@ -569,10 +571,10 @@ class ACMEClient: try: result = self.module.from_json(content.decode("utf8")) parsed_json_result = True - except ValueError: + except ValueError as exc: raise NetworkException( f"Failed to parse the ACME response: {uri} {content!r}" - ) + ) from exc else: result = content else: @@ -642,30 +644,32 @@ def create_default_argspec( Provides default argument spec for the options documented in the acme doc fragment. """ result = ArgumentSpec( - argument_spec=dict( - acme_directory=dict(type="str", required=True), - acme_version=dict(type="int", choices=[2], default=2), - validate_certs=dict(type="bool", default=True), - select_crypto_backend=dict( - type="str", default="auto", choices=["auto", "openssl", "cryptography"] - ), - request_timeout=dict(type="int", default=10), - ), + argument_spec={ + "acme_directory": {"type": "str", "required": True}, + "acme_version": {"type": "int", "choices": [2], "default": 2}, + "validate_certs": {"type": "bool", "default": True}, + "select_crypto_backend": { + "type": "str", + "default": "auto", + "choices": ["auto", "openssl", "cryptography"], + }, + "request_timeout": {"type": "int", "default": 10}, + }, ) if with_account: result.update_argspec( - account_key_src=dict(type="path", aliases=["account_key"]), - account_key_content=dict(type="str", no_log=True), - account_key_passphrase=dict(type="str", no_log=True), - account_uri=dict(type="str"), + account_key_src={"type": "path", "aliases": ["account_key"]}, + account_key_content={"type": "str", "no_log": True}, + account_key_passphrase={"type": "str", "no_log": True}, + account_uri={"type": "str"}, ) if require_account_key: result.update(required_one_of=[["account_key_src", "account_key_content"]]) result.update(mutually_exclusive=[["account_key_src", "account_key_content"]]) if with_certificate: result.update_argspec( - csr=dict(type="path"), - csr_content=dict(type="str"), + csr={"type": "path"}, + csr_content={"type": "str"}, ) result.update( required_one_of=[["csr", "csr_content"]], diff --git a/plugins/module_utils/_acme/backend_cryptography.py b/plugins/module_utils/_acme/backend_cryptography.py index 8c3a89f6..53e699c9 100644 --- a/plugins/module_utils/_acme/backend_cryptography.py +++ b/plugins/module_utils/_acme/backend_cryptography.py @@ -211,9 +211,7 @@ class CryptographyChainMatcher(ChainMatcher): class CryptographyBackend(CryptoBackend): def __init__(self, *, module: AnsibleModule) -> None: - super(CryptographyBackend, self).__init__( - module=module, with_timezone=CRYPTOGRAPHY_TIMEZONE - ) + super().__init__(module=module, with_timezone=CRYPTOGRAPHY_TIMEZONE) def parse_key( self, @@ -242,7 +240,7 @@ class CryptographyBackend(CryptoBackend): password=to_bytes(passphrase) if passphrase is not None else None, ) except Exception as e: - raise KeyParsingError(f"error while loading key: {e}") + raise KeyParsingError(f"error while loading key: {e}") from e if isinstance(key, cryptography.hazmat.primitives.asymmetric.rsa.RSAPrivateKey): rsa_pk = key.public_key().public_numbers() return { @@ -256,7 +254,7 @@ class CryptographyBackend(CryptoBackend): }, "hash": "sha256", } - elif isinstance( + if isinstance( key, cryptography.hazmat.primitives.asymmetric.ec.EllipticCurvePrivateKey ): ec_pk = key.public_key().public_numbers() @@ -296,8 +294,7 @@ class CryptographyBackend(CryptoBackend): "hash": hashalg, "point_size": point_size, } - else: - raise KeyParsingError(f'unknown key type "{type(key)}"') + raise KeyParsingError(f'unknown key type "{type(key)}"') def sign( self, *, payload64: str, protected64: str, key_data: dict[str, t.Any] @@ -332,6 +329,8 @@ class CryptographyBackend(CryptoBackend): rr = convert_int_to_hex(r, digits=2 * key_data["point_size"]) ss = convert_int_to_hex(s, digits=2 * key_data["point_size"]) signature = binascii.unhexlify(rr) + binascii.unhexlify(ss) + else: + raise AssertionError("Can never be reached") # pragma: no cover return { "protected": protected64, @@ -472,8 +471,10 @@ class CryptographyBackend(CryptoBackend): cert = cryptography.x509.load_pem_x509_certificate(b_cert_content) except Exception as e: if cert_filename is None: - raise BackendException(f"Cannot parse certificate: {e}") - raise BackendException(f"Cannot parse certificate {cert_filename}: {e}") + raise BackendException(f"Cannot parse certificate: {e}") from e + raise BackendException( + f"Cannot parse certificate {cert_filename}: {e}" + ) from e if now is None: now = self.get_now() @@ -508,8 +509,10 @@ class CryptographyBackend(CryptoBackend): cert = cryptography.x509.load_pem_x509_certificate(b_cert_content) except Exception as e: if cert_filename is None: - raise BackendException(f"Cannot parse certificate: {e}") - raise BackendException(f"Cannot parse certificate {cert_filename}: {e}") + raise BackendException(f"Cannot parse certificate: {e}") from e + raise BackendException( + f"Cannot parse certificate {cert_filename}: {e}" + ) from e ski = None try: diff --git a/plugins/module_utils/_acme/backend_openssl_cli.py b/plugins/module_utils/_acme/backend_openssl_cli.py index 151e3902..10df3440 100644 --- a/plugins/module_utils/_acme/backend_openssl_cli.py +++ b/plugins/module_utils/_acme/backend_openssl_cli.py @@ -45,7 +45,12 @@ if t.TYPE_CHECKING: ) -_OPENSSL_ENVIRONMENT_UPDATE = dict(LANG="C", LC_ALL="C", LC_MESSAGES="C", LC_CTYPE="C") +_OPENSSL_ENVIRONMENT_UPDATE = { + "LANG": "C", + "LC_ALL": "C", + "LC_MESSAGES": "C", + "LC_CTYPE": "C", +} def _extract_date( @@ -66,7 +71,7 @@ def _extract_date( except ValueError as exc: raise BackendException( f"Failed to parse '{name}' date{cert_filename_suffix}: {exc}" - ) + ) from exc def _decode_octets(octets_text: str) -> bytes: @@ -118,7 +123,7 @@ class OpenSSLCLIBackend(CryptoBackend): def __init__( self, *, module: AnsibleModule, openssl_binary: str | None = None ) -> None: - super(OpenSSLCLIBackend, self).__init__(module=module, with_timezone=True) + super().__init__(module=module, with_timezone=True) if openssl_binary is None: openssl_binary = module.get_bin_path("openssl", True) self.openssl_binary = openssl_binary @@ -156,11 +161,11 @@ class OpenSSLCLIBackend(CryptoBackend): raise KeyParsingError( f"failed to create temporary content file: {err}", exception=traceback.format_exc(), - ) + ) from err f.close() # Parse key account_key_type = None - with open(key_file, "rt") as fi: + with open(key_file, "r", encoding="utf-8") as fi: for line in fi: m = re.match( r"^\s*-{5,}BEGIN\s+(EC|RSA)\s+PRIVATE\s+KEY-{5,}\s*$", line @@ -228,7 +233,7 @@ class OpenSSLCLIBackend(CryptoBackend): }, "hash": "sha256", } - elif account_key_type == "ec": + if account_key_type == "ec": pub_data = re.search( r"pub:\s*\n\s+04:([a-f0-9\:\s]+?)\nASN1 OID: (\S+)(?:\nNIST CURVE: (\S+))?", out_text, diff --git a/plugins/module_utils/_acme/backends.py b/plugins/module_utils/_acme/backends.py index 309ddf35..7ccbf796 100644 --- a/plugins/module_utils/_acme/backends.py +++ b/plugins/module_utils/_acme/backends.py @@ -77,14 +77,14 @@ def _parse_acme_timestamp( """ # RFC 3339 (https://www.rfc-editor.org/info/rfc3339) timestamp_str = _reduce_fractional_digits(timestamp_str) - for format in ( + for time_format in ( "%Y-%m-%dT%H:%M:%SZ", "%Y-%m-%dT%H:%M:%S.%fZ", "%Y-%m-%dT%H:%M:%S%z", "%Y-%m-%dT%H:%M:%S.%f%z", ): try: - result = datetime.datetime.strptime(timestamp_str, format) + result = datetime.datetime.strptime(timestamp_str, time_format) except ValueError: pass else: @@ -117,7 +117,7 @@ class CryptoBackend(metaclass=abc.ABCMeta): raise BackendException(f"Invalid value for {name}: {value!r}") return result except OpenSSLObjectError as exc: - raise BackendException(str(exc)) + raise BackendException(str(exc)) from exc def interpolate_timestamp( self, diff --git a/plugins/module_utils/_acme/certificate.py b/plugins/module_utils/_acme/certificate.py index ed0570b5..8d3dd752 100644 --- a/plugins/module_utils/_acme/certificate.py +++ b/plugins/module_utils/_acme/certificate.py @@ -166,11 +166,11 @@ class ACMECertificateClient: continue challenge_data = authz.get_challenge_data(client=self.client) data.append( - dict( - identifier=authz.identifier, - identifier_type=authz.identifier_type, - challenges=challenge_data, - ) + { + "identifier": authz.identifier, + "identifier_type": authz.identifier_type, + "challenges": challenge_data, + } ) dns_challenge = challenge_data.get(dns_challenge_type) if dns_challenge: @@ -321,7 +321,7 @@ class ACMECertificateClient: """ if self.csr is None and self.csr_content is None: raise ModuleFailException("No CSR has been provided") - for identifier, authz in order.authorizations.items(): + for authz in order.authorizations.values(): if authz.status != "valid": authz.raise_error( error_msg=f'Status is {authz.status!r} and not "valid"', diff --git a/plugins/module_utils/_acme/certificates.py b/plugins/module_utils/_acme/certificates.py index 02aac3e9..df189389 100644 --- a/plugins/module_utils/_acme/certificates.py +++ b/plugins/module_utils/_acme/certificates.py @@ -71,7 +71,7 @@ class CertificateChain: process_links( info=info, - callback=lambda link, relation: result._process_links( + callback=lambda link, relation: result._process_links( # pylint: disable=protected-access client=client, link=link, relation=relation ), ) diff --git a/plugins/module_utils/_acme/challenges.py b/plugins/module_utils/_acme/challenges.py index 34bf3688..c00df34b 100644 --- a/plugins/module_utils/_acme/challenges.py +++ b/plugins/module_utils/_acme/challenges.py @@ -295,10 +295,10 @@ class Authorization: raise ACMEProtocolException( module=module, msg=f"Failed to validate challenge for {self.combined_identifier}: {error_msg}. {'; '.join(error_details)}", - extras=dict( - identifier=self.combined_identifier, - authorization=self.data, - ), + extras={ + "identifier": self.combined_identifier, + "authorization": self.data, + }, ) def find_challenge(self, *, challenge_type: str) -> Challenge | None: @@ -374,7 +374,7 @@ class Authorization: """ authz = cls(url=url) authz_deactivate = {"status": "deactivated"} - result, info = client.send_signed_request( + result, _info = client.send_signed_request( url, authz_deactivate, fail_on_error=True ) authz._setup(client=client, data=result) diff --git a/plugins/module_utils/_acme/errors.py b/plugins/module_utils/_acme/errors.py index 57338923..55474209 100644 --- a/plugins/module_utils/_acme/errors.py +++ b/plugins/module_utils/_acme/errors.py @@ -40,12 +40,12 @@ def format_error_problem( subproblems = problem.get("subproblems") if subproblems is not None: msg = f"{msg} Subproblems:" - for index, problem in enumerate(subproblems): + for index, subproblem in enumerate(subproblems): index_str = f"{subproblem_prefix}{index}" - problem_str = format_error_problem( - problem, subproblem_prefix=f"{index_str}." + subproblem_str = format_error_problem( + subproblem, subproblem_prefix=f"{index_str}." ) - msg = f"{msg}\n({index_str}) {problem_str}" + msg = f"{msg}\n({index_str}) {subproblem_str}" return msg @@ -55,7 +55,7 @@ class ModuleFailException(Exception): """ def __init__(self, msg: str, **args: t.Any) -> None: - super(ModuleFailException, self).__init__(self, msg) + super().__init__(self, msg) self.msg = msg self.module_fail_args = args @@ -100,7 +100,7 @@ class ACMEProtocolException(ModuleFailException): except Exception: pass - extras = extras or dict() + extras = extras or {} error_code = None error_type = None @@ -152,7 +152,7 @@ class ACMEProtocolException(ModuleFailException): elif content is not None: add_msg = f" The raw result: {to_text(content)}" - super(ACMEProtocolException, self).__init__(f"{msg}.{add_msg}", **extras) + super().__init__(f"{msg}.{add_msg}", **extras) self.problem: dict[str, t.Any] = {} self.subproblems: list[dict[str, t.Any]] = [] self.error_code = error_code diff --git a/plugins/module_utils/_acme/io.py b/plugins/module_utils/_acme/io.py index 130405f0..20ae1930 100644 --- a/plugins/module_utils/_acme/io.py +++ b/plugins/module_utils/_acme/io.py @@ -29,7 +29,7 @@ def read_file(fn: str | os.PathLike) -> bytes: with open(fn, "rb") as f: return f.read() except Exception as e: - raise ModuleFailException(f'Error while reading file "{fn}": {e}') + raise ModuleFailException(f'Error while reading file "{fn}": {e}') from e # This function was adapted from an earlier version of https://github.com/ansible/ansible/blob/devel/lib/ansible/modules/uri.py @@ -55,7 +55,7 @@ def write_file( raise ModuleFailException( f"failed to create temporary content file: {err}", exception=traceback.format_exc(), - ) + ) from err f.close() checksum_src = None checksum_dest = None @@ -94,7 +94,7 @@ def write_file( raise ModuleFailException( f"failed to copy {tmpsrc} to {dest}: {err}", exception=traceback.format_exc(), - ) + ) from err os.remove(tmpsrc) return changed diff --git a/plugins/module_utils/_acme/utils.py b/plugins/module_utils/_acme/utils.py index e9287581..f7e253e2 100644 --- a/plugins/module_utils/_acme/utils.py +++ b/plugins/module_utils/_acme/utils.py @@ -60,13 +60,13 @@ def pem_to_der( lines = pem_content.splitlines() elif pem_filename is not None: try: - with open(pem_filename, "rt") as f: + with open(pem_filename, "r", encoding="utf-8") as f: lines = list(f) except Exception as err: raise ModuleFailException( f"cannot load PEM file {pem_filename}: {err}", exception=traceback.format_exc(), - ) + ) from err else: raise ModuleFailException( "One of pem_filename and pem_content must be provided" diff --git a/plugins/module_utils/_crypto/_objects.py b/plugins/module_utils/_crypto/_objects.py index b535ccf7..d15a4b5b 100644 --- a/plugins/module_utils/_crypto/_objects.py +++ b/plugins/module_utils/_crypto/_objects.py @@ -12,9 +12,9 @@ from ansible_collections.community.crypto.plugins.module_utils._crypto._objects_ ) -OID_LOOKUP: dict[str, str] = dict() -NORMALIZE_NAMES: dict[str, str] = dict() -NORMALIZE_NAMES_SHORT: dict[str, str] = dict() +OID_LOOKUP: dict[str, str] = {} +NORMALIZE_NAMES: dict[str, str] = {} +NORMALIZE_NAMES_SHORT: dict[str, str] = {} for dotted, names in OID_MAP.items(): for name in names: diff --git a/plugins/module_utils/_crypto/cryptography_crl.py b/plugins/module_utils/_crypto/cryptography_crl.py index 1710261c..a2158697 100644 --- a/plugins/module_utils/_crypto/cryptography_crl.py +++ b/plugins/module_utils/_crypto/cryptography_crl.py @@ -62,13 +62,13 @@ if HAS_CRYPTOGRAPHY: "aa_compromise": x509.ReasonFlags.aa_compromise, "remove_from_crl": x509.ReasonFlags.remove_from_crl, } - REVOCATION_REASON_MAP_INVERSE = dict() + REVOCATION_REASON_MAP_INVERSE = {} for k, v in REVOCATION_REASON_MAP.items(): REVOCATION_REASON_MAP_INVERSE[v] = k else: - REVOCATION_REASON_MAP = dict() - REVOCATION_REASON_MAP_INVERSE = dict() + REVOCATION_REASON_MAP = {} + REVOCATION_REASON_MAP_INVERSE = {} def cryptography_decode_revoked_certificate( @@ -145,7 +145,9 @@ def cryptography_get_signature_algorithm_oid_from_crl( except AttributeError: # Older cryptography versions do not have signature_algorithm_oid yet dotted = obj2txt( - crl._backend._lib, crl._backend._ffi, crl._x509_crl.sig_alg.algorithm # type: ignore + crl._backend._lib, # type: ignore[attr-defined] # pylint: disable=protected-access + crl._backend._ffi, # type: ignore[attr-defined] # pylint: disable=protected-access + crl._x509_crl.sig_alg.algorithm, # type: ignore[attr-defined] # pylint: disable=protected-access ) return x509.oid.ObjectIdentifier(dotted) diff --git a/plugins/module_utils/_crypto/cryptography_support.py b/plugins/module_utils/_crypto/cryptography_support.py index e8108580..e4f6db0e 100644 --- a/plugins/module_utils/_crypto/cryptography_support.py +++ b/plugins/module_utils/_crypto/cryptography_support.py @@ -113,17 +113,17 @@ if t.TYPE_CHECKING: PKCS12KeyAndCertificates, ) - CertificatePrivateKeyTypes = ( - CertificateIssuerPrivateKeyTypes - | cryptography.hazmat.primitives.asymmetric.x25519.X25519PrivateKey - | cryptography.hazmat.primitives.asymmetric.x448.X448PrivateKey - ) - PublicKeyTypesWOEdwards = ( - DHPublicKey | DSAPublicKey | EllipticCurvePublicKey | RSAPublicKey - ) - PrivateKeyTypesWOEdwards = ( - DHPrivateKey | DSAPrivateKey | EllipticCurvePrivateKey | RSAPrivateKey - ) + CertificatePrivateKeyTypes = t.Union[ + CertificateIssuerPrivateKeyTypes, + cryptography.hazmat.primitives.asymmetric.x25519.X25519PrivateKey, + cryptography.hazmat.primitives.asymmetric.x448.X448PrivateKey, + ] + PublicKeyTypesWOEdwards = t.Union[ + DHPublicKey, DSAPublicKey, EllipticCurvePublicKey, RSAPublicKey + ] + PrivateKeyTypesWOEdwards = t.Union[ + DHPrivateKey, DSAPrivateKey, EllipticCurvePrivateKey, RSAPrivateKey + ] else: PublicKeyTypesWOEdwards = None PrivateKeyTypesWOEdwards = None @@ -146,14 +146,14 @@ DOTTED_OID = re.compile(r"^\d+(?:\.\d+)+$") def cryptography_get_extensions_from_cert( cert: x509.Certificate, ) -> dict[str, dict[str, bool | str]]: - result = dict() + result = {} if _CRYPTOGRAPHY_36_0_OR_NEWER: for ext in cert.extensions: - result[ext.oid.dotted_string] = dict( - critical=ext.critical, - value=base64.b64encode(ext.value.public_bytes()).decode("ascii"), - ) + result[ext.oid.dotted_string] = { + "critical": ext.critical, + "value": base64.b64encode(ext.value.public_bytes()).decode("ascii"), + } else: # Since cryptography will not give us the DER value for an extension # (that is only stored for unrecognized extensions), we have to re-do @@ -162,6 +162,9 @@ def cryptography_get_extensions_from_cert( backend = default_backend() + # We access a *lot* of internal APIs here, so let's disable that message... + # pylint: disable=protected-access + x509_obj = cert._x509 # type: ignore # With cryptography 35.0.0, we can no longer use obj2txt. Unfortunately it still does # not allow to get the raw value of an extension, so we have to use this ugly hack: @@ -175,10 +178,10 @@ def cryptography_get_extensions_from_cert( data = backend._lib.X509_EXTENSION_get_data(ext) backend.openssl_assert(data != backend._ffi.NULL) der = backend._ffi.buffer(data.data, data.length)[:] - entry = dict( - critical=(crit == 1), - value=base64.b64encode(der).decode("ascii"), - ) + entry = { + "critical": (crit == 1), + "value": base64.b64encode(der).decode("ascii"), + } try: oid = obj2txt( backend._lib, @@ -195,14 +198,14 @@ def cryptography_get_extensions_from_cert( def cryptography_get_extensions_from_csr( csr: x509.CertificateSigningRequest, ) -> dict[str, dict[str, bool | str]]: - result = dict() + result = {} if _CRYPTOGRAPHY_36_0_OR_NEWER: for ext in csr.extensions: - result[ext.oid.dotted_string] = dict( - critical=ext.critical, - value=base64.b64encode(ext.value.public_bytes()).decode("ascii"), - ) + result[ext.oid.dotted_string] = { + "critical": ext.critical, + "value": base64.b64encode(ext.value.public_bytes()).decode("ascii"), + } else: # Since cryptography will not give us the DER value for an extension @@ -212,6 +215,9 @@ def cryptography_get_extensions_from_csr( backend = default_backend() + # We access a *lot* of internal APIs here, so let's disable that message... + # pylint: disable=protected-access + extensions = backend._lib.X509_REQ_get_extensions(csr._x509_req) # type: ignore extensions = backend._ffi.gc( extensions, @@ -235,10 +241,10 @@ def cryptography_get_extensions_from_csr( data = backend._lib.X509_EXTENSION_get_data(ext) backend.openssl_assert(data != backend._ffi.NULL) der: bytes = backend._ffi.buffer(data.data, data.length)[:] # type: ignore - entry = dict( - critical=(crit == 1), - value=base64.b64encode(der).decode("ascii"), - ) + entry = { + "critical": (crit == 1), + "value": base64.b64encode(der).decode("ascii"), + } try: oid = obj2txt( backend._lib, @@ -269,13 +275,15 @@ def cryptography_oid_to_name( if names: name = names[0] else: - name = oid._name - if name == "Unknown OID": + try: + name = oid._name # pylint: disable=protected-access + if name == "Unknown OID": + name = dotted_string + except AttributeError: name = dotted_string if short: return NORMALIZE_NAMES_SHORT.get(name, name) - else: - return NORMALIZE_NAMES.get(name, name) + return NORMALIZE_NAMES.get(name, name) def _get_hex(bytesstr: bytes) -> str: @@ -393,7 +401,7 @@ def _parse_dn(name: bytes) -> list[x509.NameAttribute]: except OpenSSLObjectError as e: raise OpenSSLObjectError( f"Error while parsing distinguished name {to_text(original_name)!r}: {e}" - ) + ) from e result.append(attribute) if name: if name[0:1] != sep or len(name) < 2: @@ -414,7 +422,7 @@ def cryptography_parse_relative_distinguished_name( except OpenSSLObjectError as e: raise OpenSSLObjectError( f"Error while parsing relative distinguished name {to_text(part)!r}: {e}" - ) + ) from e return cryptography.x509.RelativeDistinguishedName(names) @@ -468,7 +476,7 @@ def _adjust_idn( raise OpenSSLObjectError( f'Error while transforming part "{part}" of {what} DNS name "{value}" to {dest}.' f' IDNA2008 transformation resulted in "{exc2008}", IDNA2003 transformation resulted in "{exc2003}".' - ) + ) from exc2003 return ".".join(parts) @@ -561,7 +569,7 @@ def cryptography_get_name( x509.Name(reversed(_parse_dn(to_bytes(name[8:])))) ) except Exception as e: - raise OpenSSLObjectError(f'Cannot parse {what} "{name}": {e}') + raise OpenSSLObjectError(f'Cannot parse {what} "{name}": {e}') from e if ":" not in name: raise OpenSSLObjectError( f'Cannot parse {what} "{name}" (forgot "DNS:" prefix?)' @@ -656,17 +664,17 @@ def cryptography_parse_key_usage_params(usages: t.Iterable[str]) -> dict[str, bo Given a list of key usage identifier strings, returns the parameters for cryptography's x509.KeyUsage(). Raises an OpenSSLObjectError if an identifier is unknown. """ - params = dict( - digital_signature=False, - content_commitment=False, - key_encipherment=False, - data_encipherment=False, - key_agreement=False, - key_cert_sign=False, - crl_sign=False, - encipher_only=False, - decipher_only=False, - ) + params = { + "digital_signature": False, + "content_commitment": False, + "key_encipherment": False, + "data_encipherment": False, + "key_agreement": False, + "key_cert_sign": False, + "crl_sign": False, + "encipher_only": False, + "decipher_only": False, + } for usage in usages: params[_cryptography_get_keyusage(usage)] = True return params @@ -699,7 +707,7 @@ def cryptography_get_basic_constraints( except Exception as e: raise OpenSSLObjectError( f'Cannot parse path length constraint "{v}" ({e})' - ) + ) from e else: raise OpenSSLObjectError(f'Unknown basic constraint "{constraint}"') return ca, path_length @@ -901,6 +909,9 @@ def _parse_pkcs12_35_0_0( backend = default_backend() + # We access a *lot* of internal APIs here, so let's disable that message... + # pylint: disable=protected-access + # This code basically does what load_key_and_certificates() does, but without error-checking. # Since load_key_and_certificates succeeded, it should not fail. pkcs12 = backend._ffi.gc( @@ -944,6 +955,9 @@ def _parse_pkcs12_legacy( pkcs12_bytes, passphrase ) + # We access a *lot* of internal APIs here, so let's disable that message... + # pylint: disable=protected-access + friendly_name = None if certificate: # See https://github.com/pyca/cryptography/issues/5760#issuecomment-842687238 diff --git a/plugins/module_utils/_crypto/module_backends/certificate.py b/plugins/module_utils/_crypto/module_backends/certificate.py index 7ee09b5a..c5f2c332 100644 --- a/plugins/module_utils/_crypto/module_backends/certificate.py +++ b/plugins/module_utils/_crypto/module_backends/certificate.py @@ -109,7 +109,7 @@ class CertificateBackend(metaclass=abc.ABCMeta): result["can_parse_certificate"] = True return result except Exception: - return dict(can_parse_certificate=False) + return {"can_parse_certificate": False} @abc.abstractmethod def generate_certificate(self) -> None: @@ -143,7 +143,7 @@ class CertificateBackend(metaclass=abc.ABCMeta): passphrase=self.privatekey_passphrase, ) except OpenSSLBadPassphraseError as exc: - raise CertificateError(exc) + raise CertificateError(exc) from exc def _ensure_csr_loaded(self) -> None: """Load the CSR into self.csr.""" @@ -380,25 +380,28 @@ def select_backend( def get_certificate_argument_spec() -> ArgumentSpec: return ArgumentSpec( - argument_spec=dict( - provider=dict( - type="str", choices=[] - ), # choices will be filled by add_XXX_provider_to_argument_spec() in certificate_xxx.py - force=dict( - type="bool", - default=False, - ), - csr_path=dict(type="path"), - csr_content=dict(type="str"), - ignore_timestamps=dict(type="bool", default=True), - select_crypto_backend=dict( - type="str", default="auto", choices=["auto", "cryptography"] - ), + argument_spec={ + "provider": { + "type": "str", + "choices": [], + }, # choices will be filled by add_XXX_provider_to_argument_spec() in certificate_xxx.py + "force": { + "type": "bool", + "default": False, + }, + "csr_path": {"type": "path"}, + "csr_content": {"type": "str"}, + "ignore_timestamps": {"type": "bool", "default": True}, + "select_crypto_backend": { + "type": "str", + "default": "auto", + "choices": ["auto", "cryptography"], + }, # General properties of a certificate - privatekey_path=dict(type="path"), - privatekey_content=dict(type="str", no_log=True), - privatekey_passphrase=dict(type="str", no_log=True), - ), + "privatekey_path": {"type": "path"}, + "privatekey_content": {"type": "str", "no_log": True}, + "privatekey_passphrase": {"type": "str", "no_log": True}, + }, mutually_exclusive=[ ["csr_path", "csr_content"], ["privatekey_path", "privatekey_content"], diff --git a/plugins/module_utils/_crypto/module_backends/certificate_acme.py b/plugins/module_utils/_crypto/module_backends/certificate_acme.py index 0c832f96..69c7c425 100644 --- a/plugins/module_utils/_crypto/module_backends/certificate_acme.py +++ b/plugins/module_utils/_crypto/module_backends/certificate_acme.py @@ -30,7 +30,7 @@ if t.TYPE_CHECKING: class AcmeCertificateBackend(CertificateBackend): def __init__(self, *, module: AnsibleModule) -> None: - super(AcmeCertificateBackend, self).__init__(module=module) + super().__init__(module=module) self.accountkey_path: str = module.params["acme_accountkey_path"] self.challenge_path: str = module.params["acme_challenge_path"] self.use_chain: bool = module.params["acme_chain"] @@ -94,7 +94,7 @@ class AcmeCertificateBackend(CertificateBackend): self.module.run_command(command, check_rc=True)[1] ) except OSError as exc: - raise CertificateError(exc) + raise CertificateError(exc) from exc def get_certificate_data(self) -> bytes: """Return bytes for self.cert.""" @@ -103,9 +103,7 @@ class AcmeCertificateBackend(CertificateBackend): return self.cert_bytes def dump(self, *, include_certificate: bool) -> dict[str, t.Any]: - result = super(AcmeCertificateBackend, self).dump( - include_certificate=include_certificate - ) + result = super().dump(include_certificate=include_certificate) result["accountkey"] = self.accountkey_path return result @@ -128,14 +126,15 @@ class AcmeCertificateProvider(CertificateProvider): def add_acme_provider_to_argument_spec(argument_spec: ArgumentSpec) -> None: argument_spec.argument_spec["provider"]["choices"].append("acme") argument_spec.argument_spec.update( - dict( - acme_accountkey_path=dict(type="path"), - acme_challenge_path=dict(type="path"), - acme_chain=dict(type="bool", default=False), - acme_directory=dict( - type="str", default="https://acme-v02.api.letsencrypt.org/directory" - ), - ) + { + "acme_accountkey_path": {"type": "path"}, + "acme_challenge_path": {"type": "path"}, + "acme_chain": {"type": "bool", "default": False}, + "acme_directory": { + "type": "str", + "default": "https://acme-v02.api.letsencrypt.org/directory", + }, + } ) diff --git a/plugins/module_utils/_crypto/module_backends/certificate_entrust.py b/plugins/module_utils/_crypto/module_backends/certificate_entrust.py index 11285a85..02cf52c5 100644 --- a/plugins/module_utils/_crypto/module_backends/certificate_entrust.py +++ b/plugins/module_utils/_crypto/module_backends/certificate_entrust.py @@ -51,13 +51,14 @@ except ImportError: class EntrustCertificateBackend(CertificateBackend): def __init__(self, *, module: AnsibleModule) -> None: - super(EntrustCertificateBackend, self).__init__(module=module) + super().__init__(module=module) self.trackingId = None self.notAfter = get_relative_time_option( module.params["entrust_not_after"], input_name="entrust_not_after", with_timezone=CRYPTOGRAPHY_TIMEZONE, ) + self.cert_bytes: bytes | None = None if self.csr_content is None: if self.csr_path is None: @@ -119,7 +120,7 @@ class EntrustCertificateBackend(CertificateBackend): body["csr"] = to_native(self.csr_content) else: assert self.csr_path is not None - with open(self.csr_path, "r") as csr_file: + with open(self.csr_path, "r", encoding="utf-8") as csr_file: body["csr"] = csr_file.read() body["certType"] = self.module.params["entrust_cert_type"] @@ -157,6 +158,8 @@ class EntrustCertificateBackend(CertificateBackend): def get_certificate_data(self) -> bytes: """Return bytes for self.cert.""" + if self.cert_bytes is None: + raise AssertionError("Contract violation: cert_bytes not set") return self.cert_bytes def needs_regeneration( @@ -165,7 +168,7 @@ class EntrustCertificateBackend(CertificateBackend): not_before: datetime.datetime | None = None, not_after: datetime.datetime | None = None, ) -> bool: - parent_check = super(EntrustCertificateBackend, self).needs_regeneration() + parent_check = super().needs_regeneration() try: cert_details = self._get_cert_details() @@ -176,7 +179,7 @@ class EntrustCertificateBackend(CertificateBackend): # Always issue a new certificate if the certificate is expired, suspended or revoked status = cert_details.get("status", False) - if status == "EXPIRED" or status == "SUSPENDED" or status == "REVOKED": + if status in ("EXPIRED", "SUSPENDED", "REVOKED"): return True # If the requested cert type was specified and it is for a different certificate type than the initial certificate, a new one is needed @@ -239,11 +242,11 @@ class EntrustCertificateProvider(CertificateProvider): def add_entrust_provider_to_argument_spec(argument_spec: ArgumentSpec) -> None: argument_spec.argument_spec["provider"]["choices"].append("entrust") argument_spec.argument_spec.update( - dict( - entrust_cert_type=dict( - type="str", - default="STANDARD_SSL", - choices=[ + { + "entrust_cert_type": { + "type": "str", + "default": "STANDARD_SSL", + "choices": [ "STANDARD_SSL", "ADVANTAGE_SSL", "UC_SSL", @@ -255,20 +258,20 @@ def add_entrust_provider_to_argument_spec(argument_spec: ArgumentSpec) -> None: "CDS_ENT_PRO", "SMIME_ENT", ], - ), - entrust_requester_email=dict(type="str"), - entrust_requester_name=dict(type="str"), - entrust_requester_phone=dict(type="str"), - entrust_api_user=dict(type="str"), - entrust_api_key=dict(type="str", no_log=True), - entrust_api_client_cert_path=dict(type="path"), - entrust_api_client_cert_key_path=dict(type="path", no_log=True), - entrust_api_specification_path=dict( - type="path", - default="https://cloud.entrust.net/EntrustCloud/documentation/cms-api-2.1.0.yaml", - ), - entrust_not_after=dict(type="str", default="+365d"), - ) + }, + "entrust_requester_email": {"type": "str"}, + "entrust_requester_name": {"type": "str"}, + "entrust_requester_phone": {"type": "str"}, + "entrust_api_user": {"type": "str"}, + "entrust_api_key": {"type": "str", "no_log": True}, + "entrust_api_client_cert_path": {"type": "path"}, + "entrust_api_client_cert_key_path": {"type": "path", "no_log": True}, + "entrust_api_specification_path": { + "type": "path", + "default": "https://cloud.entrust.net/EntrustCloud/documentation/cms-api-2.1.0.yaml", + }, + "entrust_not_after": {"type": "str", "default": "+365d"}, + } ) argument_spec.required_if.append( ( diff --git a/plugins/module_utils/_crypto/module_backends/certificate_info.py b/plugins/module_utils/_crypto/module_backends/certificate_info.py index a9181e98..88293dab 100644 --- a/plugins/module_utils/_crypto/module_backends/certificate_info.py +++ b/plugins/module_utils/_crypto/module_backends/certificate_info.py @@ -70,6 +70,8 @@ TIMESTAMP_FORMAT = "%Y%m%d%H%M%SZ" class CertificateInfoRetrieval(metaclass=abc.ABCMeta): + cert: x509.Certificate + def __init__(self, *, module: GeneralAnsibleModule, content: bytes) -> None: # content must be a bytes string self.module = module @@ -169,11 +171,11 @@ class CertificateInfoRetrieval(metaclass=abc.ABCMeta): result["signature_algorithm"] = self._get_signature_algorithm() subject = self._get_subject_ordered() issuer = self._get_issuer_ordered() - result["subject"] = dict() + result["subject"] = {} for k, v in subject: result["subject"][k] = v result["subject_ordered"] = subject - result["issuer"] = dict() + result["issuer"] = {} for k, v in issuer: result["issuer"][k] = v result["issuer_ordered"] = issuer @@ -249,9 +251,7 @@ class CertificateInfoRetrievalCryptography(CertificateInfoRetrieval): """Validate the supplied cert, using the cryptography backend""" def __init__(self, *, module: GeneralAnsibleModule, content: bytes) -> None: - super(CertificateInfoRetrievalCryptography, self).__init__( - module=module, content=content - ) + super().__init__(module=module, content=content) self.name_encoding = module.params.get("name_encoding", "ignore") def _get_der_bytes(self) -> bytes: @@ -289,36 +289,36 @@ class CertificateInfoRetrievalCryptography(CertificateInfoRetrieval): x509.KeyUsage ) current_key_usage = current_key_ext.value - key_usage = dict( - digital_signature=current_key_usage.digital_signature, - content_commitment=current_key_usage.content_commitment, - key_encipherment=current_key_usage.key_encipherment, - data_encipherment=current_key_usage.data_encipherment, - key_agreement=current_key_usage.key_agreement, - key_cert_sign=current_key_usage.key_cert_sign, - crl_sign=current_key_usage.crl_sign, - encipher_only=False, - decipher_only=False, - ) + key_usage = { + "digital_signature": current_key_usage.digital_signature, + "content_commitment": current_key_usage.content_commitment, + "key_encipherment": current_key_usage.key_encipherment, + "data_encipherment": current_key_usage.data_encipherment, + "key_agreement": current_key_usage.key_agreement, + "key_cert_sign": current_key_usage.key_cert_sign, + "crl_sign": current_key_usage.crl_sign, + "encipher_only": False, + "decipher_only": False, + } if key_usage["key_agreement"]: key_usage.update( - dict( - encipher_only=current_key_usage.encipher_only, - decipher_only=current_key_usage.decipher_only, - ) + { + "encipher_only": current_key_usage.encipher_only, + "decipher_only": current_key_usage.decipher_only, + } ) - key_usage_names = dict( - digital_signature="Digital Signature", - content_commitment="Non Repudiation", - key_encipherment="Key Encipherment", - data_encipherment="Data Encipherment", - key_agreement="Key Agreement", - key_cert_sign="Certificate Sign", - crl_sign="CRL Sign", - encipher_only="Encipher Only", - decipher_only="Decipher Only", - ) + key_usage_names = { + "digital_signature": "Digital Signature", + "content_commitment": "Non Repudiation", + "key_encipherment": "Key Encipherment", + "data_encipherment": "Data Encipherment", + "key_agreement": "Key Agreement", + "key_cert_sign": "Certificate Sign", + "crl_sign": "CRL Sign", + "encipher_only": "Encipher Only", + "decipher_only": "Decipher Only", + } return ( sorted( [ diff --git a/plugins/module_utils/_crypto/module_backends/certificate_ownca.py b/plugins/module_utils/_crypto/module_backends/certificate_ownca.py index 5e08f278..e7472969 100644 --- a/plugins/module_utils/_crypto/module_backends/certificate_ownca.py +++ b/plugins/module_utils/_crypto/module_backends/certificate_ownca.py @@ -63,7 +63,7 @@ except ImportError: class OwnCACertificateBackendCryptography(CertificateBackend): def __init__(self, *, module: AnsibleModule) -> None: - super(OwnCACertificateBackendCryptography, self).__init__(module=module) + super().__init__(module=module) self.create_subject_key_identifier: t.Literal[ "create_if_not_provided", "always_create", "never_create" @@ -223,7 +223,7 @@ class OwnCACertificateBackendCryptography(CertificateBackend): not_before: datetime.datetime | None = None, not_after: datetime.datetime | None = None, ) -> bool: - if super(OwnCACertificateBackendCryptography, self).needs_regeneration( + if super().needs_regeneration( not_before=self.notBefore, not_after=self.notAfter ): return True @@ -272,9 +272,7 @@ class OwnCACertificateBackendCryptography(CertificateBackend): return False def dump(self, *, include_certificate: bool) -> dict[str, t.Any]: - result = super(OwnCACertificateBackendCryptography, self).dump( - include_certificate=include_certificate - ) + result = super().dump(include_certificate=include_certificate) result.update( { "ca_cert": self.ca_cert_path, @@ -343,23 +341,23 @@ class OwnCACertificateProvider(CertificateProvider): def add_ownca_provider_to_argument_spec(argument_spec: ArgumentSpec) -> None: argument_spec.argument_spec["provider"]["choices"].append("ownca") argument_spec.argument_spec.update( - dict( - ownca_path=dict(type="path"), - ownca_content=dict(type="str"), - ownca_privatekey_path=dict(type="path"), - ownca_privatekey_content=dict(type="str", no_log=True), - ownca_privatekey_passphrase=dict(type="str", no_log=True), - ownca_digest=dict(type="str", default="sha256"), - ownca_version=dict(type="int", default=3, choices=[3]), # not used - ownca_not_before=dict(type="str", default="+0s"), - ownca_not_after=dict(type="str", default="+3650d"), - ownca_create_subject_key_identifier=dict( - type="str", - default="create_if_not_provided", - choices=["create_if_not_provided", "always_create", "never_create"], - ), - ownca_create_authority_key_identifier=dict(type="bool", default=True), - ) + { + "ownca_path": {"type": "path"}, + "ownca_content": {"type": "str"}, + "ownca_privatekey_path": {"type": "path"}, + "ownca_privatekey_content": {"type": "str", "no_log": True}, + "ownca_privatekey_passphrase": {"type": "str", "no_log": True}, + "ownca_digest": {"type": "str", "default": "sha256"}, + "ownca_version": {"type": "int", "default": 3, "choices": [3]}, # not used + "ownca_not_before": {"type": "str", "default": "+0s"}, + "ownca_not_after": {"type": "str", "default": "+3650d"}, + "ownca_create_subject_key_identifier": { + "type": "str", + "default": "create_if_not_provided", + "choices": ["create_if_not_provided", "always_create", "never_create"], + }, + "ownca_create_authority_key_identifier": {"type": "bool", "default": True}, + } ) argument_spec.mutually_exclusive.extend( [ diff --git a/plugins/module_utils/_crypto/module_backends/certificate_selfsigned.py b/plugins/module_utils/_crypto/module_backends/certificate_selfsigned.py index b37b3bf9..88fe6b6b 100644 --- a/plugins/module_utils/_crypto/module_backends/certificate_selfsigned.py +++ b/plugins/module_utils/_crypto/module_backends/certificate_selfsigned.py @@ -59,7 +59,7 @@ class SelfSignedCertificateBackendCryptography(CertificateBackend): privatekey: CertificateIssuerPrivateKeyTypes def __init__(self, *, module: AnsibleModule) -> None: - super(SelfSignedCertificateBackendCryptography, self).__init__(module=module) + super().__init__(module=module) self.create_subject_key_identifier: t.Literal[ "create_if_not_provided", "always_create", "never_create" @@ -144,7 +144,7 @@ class SelfSignedCertificateBackendCryptography(CertificateBackend): critical=False, ) except ValueError as e: - raise CertificateError(str(e)) + raise CertificateError(str(e)) from e certificate = cert_builder.sign( private_key=self.privatekey, @@ -167,7 +167,7 @@ class SelfSignedCertificateBackendCryptography(CertificateBackend): ) -> bool: assert self.privatekey is not None - if super(SelfSignedCertificateBackendCryptography, self).needs_regeneration( + if super().needs_regeneration( not_before=self.notBefore, not_after=self.notAfter ): return True @@ -185,9 +185,7 @@ class SelfSignedCertificateBackendCryptography(CertificateBackend): return False def dump(self, *, include_certificate: bool) -> dict[str, t.Any]: - result = super(SelfSignedCertificateBackendCryptography, self).dump( - include_certificate=include_certificate - ) + result = super().dump(include_certificate=include_certificate) if self.module.check_mode: result.update( @@ -243,21 +241,29 @@ class SelfSignedCertificateProvider(CertificateProvider): def add_selfsigned_provider_to_argument_spec(argument_spec: ArgumentSpec) -> None: argument_spec.argument_spec["provider"]["choices"].append("selfsigned") argument_spec.argument_spec.update( - dict( - selfsigned_version=dict(type="int", default=3, choices=[3]), # not used - selfsigned_digest=dict(type="str", default="sha256"), - selfsigned_not_before=dict( - type="str", default="+0s", aliases=["selfsigned_notBefore"] - ), - selfsigned_not_after=dict( - type="str", default="+3650d", aliases=["selfsigned_notAfter"] - ), - selfsigned_create_subject_key_identifier=dict( - type="str", - default="create_if_not_provided", - choices=["create_if_not_provided", "always_create", "never_create"], - ), - ) + { + "selfsigned_version": { + "type": "int", + "default": 3, + "choices": [3], + }, # not used + "selfsigned_digest": {"type": "str", "default": "sha256"}, + "selfsigned_not_before": { + "type": "str", + "default": "+0s", + "aliases": ["selfsigned_notBefore"], + }, + "selfsigned_not_after": { + "type": "str", + "default": "+3650d", + "aliases": ["selfsigned_notAfter"], + }, + "selfsigned_create_subject_key_identifier": { + "type": "str", + "default": "create_if_not_provided", + "choices": ["create_if_not_provided", "always_create", "never_create"], + }, + } ) diff --git a/plugins/module_utils/_crypto/module_backends/crl_info.py b/plugins/module_utils/_crypto/module_backends/crl_info.py index e9df7dc0..fb902137 100644 --- a/plugins/module_utils/_crypto/module_backends/crl_info.py +++ b/plugins/module_utils/_crypto/module_backends/crl_info.py @@ -67,18 +67,18 @@ class CRLInfoRetrieval: self.name_encoding = module.params.get("name_encoding", "ignore") def get_info(self) -> dict[str, t.Any]: - self.crl_pem = identify_pem_format(self.content) + crl_pem = identify_pem_format(self.content) try: - if self.crl_pem: - self.crl = x509.load_pem_x509_crl(self.content) + if crl_pem: + crl = x509.load_pem_x509_crl(self.content) else: - self.crl = x509.load_der_x509_crl(self.content) + crl = x509.load_der_x509_crl(self.content) except ValueError as e: self.module.fail_json(msg=f"Error while decoding CRL: {e}") result: dict[str, t.Any] = { "changed": False, - "format": "pem" if self.crl_pem else "der", + "format": "pem" if crl_pem else "der", "last_update": None, "next_update": None, "digest": None, @@ -86,25 +86,24 @@ class CRLInfoRetrieval: "issuer": None, } - result["last_update"] = self.crl.last_update.strftime(TIMESTAMP_FORMAT) + result["last_update"] = crl.last_update.strftime(TIMESTAMP_FORMAT) result["next_update"] = ( - self.crl.next_update.strftime(TIMESTAMP_FORMAT) - if self.crl.next_update - else None + crl.next_update.strftime(TIMESTAMP_FORMAT) if crl.next_update else None ) result["digest"] = cryptography_oid_to_name( - cryptography_get_signature_algorithm_oid_from_crl(self.crl) + cryptography_get_signature_algorithm_oid_from_crl(crl) ) issuer = [] - for attribute in self.crl.issuer: + for attribute in crl.issuer: issuer.append([cryptography_oid_to_name(attribute.oid), attribute.value]) result["issuer_ordered"] = issuer - result["issuer"] = {} + issuer_dict = {} for k, v in issuer: - result["issuer"][k] = v + issuer_dict[k] = v + result["issuer"] = issuer_dict if self.list_revoked_certificates: result["revoked_certificates"] = [] - for cert in self.crl: + for cert in crl: entry = cryptography_decode_revoked_certificate(cert) result["revoked_certificates"].append( cryptography_dump_revoked(entry, idn_rewrite=self.name_encoding) diff --git a/plugins/module_utils/_crypto/module_backends/csr.py b/plugins/module_utils/_crypto/module_backends/csr.py index 3ca2d245..426ae8f9 100644 --- a/plugins/module_utils/_crypto/module_backends/csr.py +++ b/plugins/module_utils/_crypto/module_backends/csr.py @@ -170,7 +170,7 @@ class CertificateSigningRequestBackend(metaclass=abc.ABCMeta): ) self.ordered_subject = True except ValueError as exc: - raise CertificateSigningRequestError(str(exc)) + raise CertificateSigningRequestError(str(exc)) from exc self.using_common_name_for_san = False if not self.subjectAltName and module.params["use_common_name_for_san"]: @@ -189,7 +189,7 @@ class CertificateSigningRequestBackend(metaclass=abc.ABCMeta): except Exception as e: raise CertificateSigningRequestError( f"Cannot parse subject_key_identifier: {e}" - ) + ) from e self.authority_key_identifier: bytes | None = None if authority_key_identifier is not None: @@ -200,7 +200,7 @@ class CertificateSigningRequestBackend(metaclass=abc.ABCMeta): except Exception as e: raise CertificateSigningRequestError( f"Cannot parse authority_key_identifier: {e}" - ) + ) from e self.existing_csr: cryptography.x509.CertificateSigningRequest | None = None self.existing_csr_bytes: bytes | None = None @@ -221,7 +221,7 @@ class CertificateSigningRequestBackend(metaclass=abc.ABCMeta): result["can_parse_csr"] = True return result except Exception: - return dict(can_parse_csr=False) + return {"can_parse_csr": False} @abc.abstractmethod def generate_csr(self) -> None: @@ -253,7 +253,7 @@ class CertificateSigningRequestBackend(metaclass=abc.ABCMeta): passphrase=self.privatekey_passphrase, ) except OpenSSLBadPassphraseError as exc: - raise CertificateSigningRequestError(exc) + raise CertificateSigningRequestError(exc) from exc @abc.abstractmethod def _check_csr(self) -> bool: @@ -294,10 +294,10 @@ class CertificateSigningRequestBackend(metaclass=abc.ABCMeta): # Store result result["csr"] = csr_bytes.decode("utf-8") if csr_bytes else None - result["diff"] = dict( - before=self.diff_before, - after=self.diff_after, - ) + result["diff"] = { + "before": self.diff_before, + "after": self.diff_after, + } return result @@ -347,16 +347,14 @@ def parse_crl_distribution_points( except (OpenSSLObjectError, ValueError) as e: raise OpenSSLObjectError( f"Error while parsing CRL distribution point #{index}: {e}" - ) + ) from e return result # Implementation with using cryptography class CertificateSigningRequestCryptographyBackend(CertificateSigningRequestBackend): def __init__(self, *, module: AnsibleModule) -> None: - super(CertificateSigningRequestCryptographyBackend, self).__init__( - module=module - ) + super().__init__(module=module) if self.version != 1: module.warn( "The cryptography backend only supports version 1. (The only valid value according to RFC 2986.)" @@ -388,7 +386,7 @@ class CertificateSigningRequestCryptographyBackend(CertificateSigningRequestBack ) ) except ValueError as e: - raise CertificateSigningRequestError(e) + raise CertificateSigningRequestError(e) from e if self.subjectAltName: csr = csr.add_extension( @@ -451,7 +449,9 @@ class CertificateSigningRequestCryptographyBackend(CertificateSigningRequestBack critical=self.name_constraints_critical, ) except TypeError as e: - raise OpenSSLObjectError(f"Error while parsing name constraint: {e}") + raise OpenSSLObjectError( + f"Error while parsing name constraint: {e}" + ) from e if self.create_subject_key_identifier: if not is_potential_certificate_issuer_public_key( @@ -556,8 +556,7 @@ class CertificateSigningRequestCryptographyBackend(CertificateSigningRequestBack current_subject = [(sub.oid, sub.value) for sub in csr.subject] if self.ordered_subject: return subject == current_subject - else: - return set(subject) == set(current_subject) + return set(subject) == set(current_subject) def _find_extension( extensions: cryptography.x509.Extensions, exttype: type[_ET] @@ -596,15 +595,14 @@ class CertificateSigningRequestCryptographyBackend(CertificateSigningRequestBack ) if not self.keyUsage: return current_keyusage_ext is None - elif current_keyusage_ext is None: + if current_keyusage_ext is None: return False params = cryptography_parse_key_usage_params(self.keyUsage) - for param in params: - if getattr(current_keyusage_ext.value, "_" + param) != params[param]: + for param, value in params.items(): + # TODO: check whether getattr() with '_' prepended is really needed + if getattr(current_keyusage_ext.value, "_" + param) != value: return False - if current_keyusage_ext.critical != self.keyUsage_critical: - return False - return True + return current_keyusage_ext.critical == self.keyUsage_critical def _check_extenededKeyUsage(extensions: cryptography.x509.Extensions) -> bool: current_usages_ext = _find_extension( @@ -647,8 +645,7 @@ class CertificateSigningRequestCryptographyBackend(CertificateSigningRequestBack bc_ext is not None and bc_ext.critical == self.basicConstraints_critical ) - else: - return bc_ext is None + return bc_ext is None def _check_ocspMustStaple(extensions: cryptography.x509.Extensions) -> bool: tlsfeature_ext = _find_extension(extensions, cryptography.x509.TLSFeature) @@ -662,8 +659,7 @@ class CertificateSigningRequestCryptographyBackend(CertificateSigningRequestBack cryptography.x509.TLSFeatureType.status_request in tlsfeature_ext.value ) - else: - return tlsfeature_ext is None + return tlsfeature_ext is None def _check_nameConstraints(extensions: cryptography.x509.Extensions) -> bool: current_nc_ext = _find_extension( @@ -722,10 +718,8 @@ class CertificateSigningRequestCryptographyBackend(CertificateSigningRequestBack self.privatekey.public_key() ).digest return ext.value.digest == digest - else: - return ext.value.digest == self.subject_key_identifier - else: - return ext is None + return ext.value.digest == self.subject_key_identifier + return ext is None def _check_authority_key_identifier( extensions: cryptography.x509.Extensions, @@ -753,8 +747,7 @@ class CertificateSigningRequestCryptographyBackend(CertificateSigningRequestBack and ext.value.authority_cert_serial_number == self.authority_cert_serial_number ) - else: - return ext is None + return ext is None def _check_crl_distribution_points( extensions: cryptography.x509.Extensions, @@ -814,77 +807,97 @@ def select_backend( def get_csr_argument_spec() -> ArgumentSpec: return ArgumentSpec( - argument_spec=dict( - digest=dict(type="str", default="sha256"), - privatekey_path=dict(type="path"), - privatekey_content=dict(type="str", no_log=True), - privatekey_passphrase=dict(type="str", no_log=True), - version=dict(type="int", default=1, choices=[1]), - subject=dict(type="dict"), - subject_ordered=dict(type="list", elements="dict"), - country_name=dict(type="str", aliases=["C", "countryName"]), - state_or_province_name=dict( - type="str", aliases=["ST", "stateOrProvinceName"] - ), - locality_name=dict(type="str", aliases=["L", "localityName"]), - organization_name=dict(type="str", aliases=["O", "organizationName"]), - organizational_unit_name=dict( - type="str", aliases=["OU", "organizationalUnitName"] - ), - common_name=dict(type="str", aliases=["CN", "commonName"]), - email_address=dict(type="str", aliases=["E", "emailAddress"]), - subject_alt_name=dict( - type="list", elements="str", aliases=["subjectAltName"] - ), - subject_alt_name_critical=dict( - type="bool", default=False, aliases=["subjectAltName_critical"] - ), - use_common_name_for_san=dict( - type="bool", default=True, aliases=["useCommonNameForSAN"] - ), - key_usage=dict(type="list", elements="str", aliases=["keyUsage"]), - key_usage_critical=dict( - type="bool", default=False, aliases=["keyUsage_critical"] - ), - extended_key_usage=dict( - type="list", elements="str", aliases=["extKeyUsage", "extendedKeyUsage"] - ), - extended_key_usage_critical=dict( - type="bool", - default=False, - aliases=["extKeyUsage_critical", "extendedKeyUsage_critical"], - ), - basic_constraints=dict( - type="list", elements="str", aliases=["basicConstraints"] - ), - basic_constraints_critical=dict( - type="bool", default=False, aliases=["basicConstraints_critical"] - ), - ocsp_must_staple=dict( - type="bool", default=False, aliases=["ocspMustStaple"] - ), - ocsp_must_staple_critical=dict( - type="bool", default=False, aliases=["ocspMustStaple_critical"] - ), - name_constraints_permitted=dict(type="list", elements="str"), - name_constraints_excluded=dict(type="list", elements="str"), - name_constraints_critical=dict(type="bool", default=False), - create_subject_key_identifier=dict(type="bool", default=False), - subject_key_identifier=dict(type="str"), - authority_key_identifier=dict(type="str"), - authority_cert_issuer=dict(type="list", elements="str"), - authority_cert_serial_number=dict(type="int"), - crl_distribution_points=dict( - type="list", - elements="dict", - options=dict( - full_name=dict(type="list", elements="str"), - relative_name=dict(type="list", elements="str"), - crl_issuer=dict(type="list", elements="str"), - reasons=dict( - type="list", - elements="str", - choices=[ + argument_spec={ + "digest": {"type": "str", "default": "sha256"}, + "privatekey_path": {"type": "path"}, + "privatekey_content": {"type": "str", "no_log": True}, + "privatekey_passphrase": {"type": "str", "no_log": True}, + "version": {"type": "int", "default": 1, "choices": [1]}, + "subject": {"type": "dict"}, + "subject_ordered": {"type": "list", "elements": "dict"}, + "country_name": {"type": "str", "aliases": ["C", "countryName"]}, + "state_or_province_name": { + "type": "str", + "aliases": ["ST", "stateOrProvinceName"], + }, + "locality_name": {"type": "str", "aliases": ["L", "localityName"]}, + "organization_name": {"type": "str", "aliases": ["O", "organizationName"]}, + "organizational_unit_name": { + "type": "str", + "aliases": ["OU", "organizationalUnitName"], + }, + "common_name": {"type": "str", "aliases": ["CN", "commonName"]}, + "email_address": {"type": "str", "aliases": ["E", "emailAddress"]}, + "subject_alt_name": { + "type": "list", + "elements": "str", + "aliases": ["subjectAltName"], + }, + "subject_alt_name_critical": { + "type": "bool", + "default": False, + "aliases": ["subjectAltName_critical"], + }, + "use_common_name_for_san": { + "type": "bool", + "default": True, + "aliases": ["useCommonNameForSAN"], + }, + "key_usage": {"type": "list", "elements": "str", "aliases": ["keyUsage"]}, + "key_usage_critical": { + "type": "bool", + "default": False, + "aliases": ["keyUsage_critical"], + }, + "extended_key_usage": { + "type": "list", + "elements": "str", + "aliases": ["extKeyUsage", "extendedKeyUsage"], + }, + "extended_key_usage_critical": { + "type": "bool", + "default": False, + "aliases": ["extKeyUsage_critical", "extendedKeyUsage_critical"], + }, + "basic_constraints": { + "type": "list", + "elements": "str", + "aliases": ["basicConstraints"], + }, + "basic_constraints_critical": { + "type": "bool", + "default": False, + "aliases": ["basicConstraints_critical"], + }, + "ocsp_must_staple": { + "type": "bool", + "default": False, + "aliases": ["ocspMustStaple"], + }, + "ocsp_must_staple_critical": { + "type": "bool", + "default": False, + "aliases": ["ocspMustStaple_critical"], + }, + "name_constraints_permitted": {"type": "list", "elements": "str"}, + "name_constraints_excluded": {"type": "list", "elements": "str"}, + "name_constraints_critical": {"type": "bool", "default": False}, + "create_subject_key_identifier": {"type": "bool", "default": False}, + "subject_key_identifier": {"type": "str"}, + "authority_key_identifier": {"type": "str"}, + "authority_cert_issuer": {"type": "list", "elements": "str"}, + "authority_cert_serial_number": {"type": "int"}, + "crl_distribution_points": { + "type": "list", + "elements": "dict", + "options": { + "full_name": {"type": "list", "elements": "str"}, + "relative_name": {"type": "list", "elements": "str"}, + "crl_issuer": {"type": "list", "elements": "str"}, + "reasons": { + "type": "list", + "elements": "str", + "choices": [ "key_compromise", "ca_compromise", "affiliation_changed", @@ -894,15 +907,17 @@ def get_csr_argument_spec() -> ArgumentSpec: "privilege_withdrawn", "aa_compromise", ], - ), - ), - mutually_exclusive=[("full_name", "relative_name")], - required_one_of=[("full_name", "relative_name", "crl_issuer")], - ), - select_crypto_backend=dict( - type="str", default="auto", choices=["auto", "cryptography"] - ), - ), + }, + }, + "mutually_exclusive": [("full_name", "relative_name")], + "required_one_of": [("full_name", "relative_name", "crl_issuer")], + }, + "select_crypto_backend": { + "type": "str", + "default": "auto", + "choices": ["auto", "cryptography"], + }, + }, required_together=[ ["authority_cert_issuer", "authority_cert_serial_number"], ], diff --git a/plugins/module_utils/_crypto/module_backends/csr_info.py b/plugins/module_utils/_crypto/module_backends/csr_info.py index a66b30e2..c97c2224 100644 --- a/plugins/module_utils/_crypto/module_backends/csr_info.py +++ b/plugins/module_utils/_crypto/module_backends/csr_info.py @@ -61,6 +61,8 @@ TIMESTAMP_FORMAT = "%Y%m%d%H%M%SZ" class CSRInfoRetrieval(metaclass=abc.ABCMeta): + csr: x509.CertificateSigningRequest + def __init__( self, *, module: GeneralAnsibleModule, content: bytes, validate_signature: bool ) -> None: @@ -129,7 +131,7 @@ class CSRInfoRetrieval(metaclass=abc.ABCMeta): ) subject = self._get_subject_ordered() - result["subject"] = dict() + result["subject"] = {} for k, v in subject: result["subject"][k] = v result["subject_ordered"] = subject @@ -197,7 +199,7 @@ class CSRInfoRetrievalCryptography(CSRInfoRetrieval): def __init__( self, *, module: GeneralAnsibleModule, content: bytes, validate_signature: bool ) -> None: - super(CSRInfoRetrievalCryptography, self).__init__( + super().__init__( module=module, content=content, validate_signature=validate_signature ) self.name_encoding: t.Literal["ignore", "idna", "unicode"] = module.params.get( @@ -216,36 +218,36 @@ class CSRInfoRetrievalCryptography(CSRInfoRetrieval): try: current_key_ext = self.csr.extensions.get_extension_for_class(x509.KeyUsage) current_key_usage = current_key_ext.value - key_usage = dict( - digital_signature=current_key_usage.digital_signature, - content_commitment=current_key_usage.content_commitment, - key_encipherment=current_key_usage.key_encipherment, - data_encipherment=current_key_usage.data_encipherment, - key_agreement=current_key_usage.key_agreement, - key_cert_sign=current_key_usage.key_cert_sign, - crl_sign=current_key_usage.crl_sign, - encipher_only=False, - decipher_only=False, - ) + key_usage = { + "digital_signature": current_key_usage.digital_signature, + "content_commitment": current_key_usage.content_commitment, + "key_encipherment": current_key_usage.key_encipherment, + "data_encipherment": current_key_usage.data_encipherment, + "key_agreement": current_key_usage.key_agreement, + "key_cert_sign": current_key_usage.key_cert_sign, + "crl_sign": current_key_usage.crl_sign, + "encipher_only": False, + "decipher_only": False, + } if key_usage["key_agreement"]: key_usage.update( - dict( - encipher_only=current_key_usage.encipher_only, - decipher_only=current_key_usage.decipher_only, - ) + { + "encipher_only": current_key_usage.encipher_only, + "decipher_only": current_key_usage.decipher_only, + } ) - key_usage_names = dict( - digital_signature="Digital Signature", - content_commitment="Non Repudiation", - key_encipherment="Key Encipherment", - data_encipherment="Data Encipherment", - key_agreement="Key Agreement", - key_cert_sign="Certificate Sign", - crl_sign="CRL Sign", - encipher_only="Encipher Only", - decipher_only="Decipher Only", - ) + key_usage_names = { + "digital_signature": "Digital Signature", + "content_commitment": "Non Repudiation", + "key_encipherment": "Key Encipherment", + "data_encipherment": "Data Encipherment", + "key_agreement": "Key Agreement", + "key_cert_sign": "Certificate Sign", + "crl_sign": "CRL Sign", + "encipher_only": "Encipher Only", + "decipher_only": "Decipher Only", + } return ( sorted( [ diff --git a/plugins/module_utils/_crypto/module_backends/privatekey.py b/plugins/module_utils/_crypto/module_backends/privatekey.py index e3c77229..17651524 100644 --- a/plugins/module_utils/_crypto/module_backends/privatekey.py +++ b/plugins/module_utils/_crypto/module_backends/privatekey.py @@ -265,10 +265,10 @@ class PrivateKeyBackend(metaclass=abc.ABCMeta): else: result["privatekey"] = None - result["diff"] = dict( - before=self.diff_before, - after=self.diff_after, - ) + result["diff"] = { + "before": self.diff_before, + "after": self.diff_after, + } return result @@ -323,7 +323,7 @@ class PrivateKeyCryptographyBackend(PrivateKeyBackend): self.curves[name] = _Curve(name=name, ectype=ectype, deprecated=deprecated) def __init__(self, module: GeneralAnsibleModule) -> None: - super(PrivateKeyCryptographyBackend, self).__init__(module=module) + super().__init__(module=module) self.curves: dict[str, _Curve] = {} self._add_curve("secp224r1", "SECP224R1") @@ -351,8 +351,7 @@ class PrivateKeyCryptographyBackend(PrivateKeyBackend): return self.format # type: ignore if self.type in ("X25519", "X448", "Ed25519", "Ed448"): return "pkcs8" - else: - return "pkcs1" + return "pkcs1" def generate_private_key(self) -> None: """(Re-)Generate private key.""" @@ -427,6 +426,9 @@ class PrivateKeyCryptographyBackend(PrivateKeyBackend): export_encoding = ( cryptography.hazmat.primitives.serialization.Encoding.Raw ) + else: + # pylint does not notice that all possible values for export_format_txt have been covered. + raise AssertionError("Can never be reached") # pragma: no cover except AttributeError: self.module.fail_json( msg=f'Cryptography backend does not support the selected output format "{self.format}"' @@ -469,8 +471,8 @@ class PrivateKeyCryptographyBackend(PrivateKeyBackend): raise AssertionError("existing_private_key_bytes not set") try: # Interpret bytes depending on format. - format = identify_private_key_format(data) - if format == "raw": + key_format = identify_private_key_format(data) + if key_format == "raw": if len(data) == 56: return cryptography.hazmat.primitives.asymmetric.x448.X448PrivateKey.from_private_bytes( data @@ -497,15 +499,13 @@ class PrivateKeyCryptographyBackend(PrivateKeyBackend): data ) raise PrivateKeyError("Cannot load raw key") - else: - return ( - cryptography.hazmat.primitives.serialization.load_pem_private_key( - data, - None if self.passphrase is None else to_bytes(self.passphrase), - ) - ) + + return cryptography.hazmat.primitives.serialization.load_pem_private_key( + data, + None if self.passphrase is None else to_bytes(self.passphrase), + ) except Exception as e: - raise PrivateKeyError(e) + raise PrivateKeyError(e) from e def _ensure_existing_private_key_loaded(self) -> None: if self.existing_private_key is None and self.has_existing(): @@ -515,21 +515,20 @@ class PrivateKeyCryptographyBackend(PrivateKeyBackend): if self.existing_private_key_bytes is None: raise AssertionError("existing_private_key_bytes not set") try: - format = identify_private_key_format(self.existing_private_key_bytes) - if format == "raw": + key_format = identify_private_key_format(self.existing_private_key_bytes) + if key_format == "raw": # Raw keys cannot be encrypted. To avoid incompatibilities, we try to # actually load the key (and return False when this fails). self._load_privatekey() # Loading the key succeeded. Only return True when no passphrase was # provided. return self.passphrase is None - else: - return bool( - cryptography.hazmat.primitives.serialization.load_pem_private_key( - self.existing_private_key_bytes, - None if self.passphrase is None else to_bytes(self.passphrase), - ) + return bool( + cryptography.hazmat.primitives.serialization.load_pem_private_key( + self.existing_private_key_bytes, + None if self.passphrase is None else to_bytes(self.passphrase), ) + ) except Exception: return False @@ -588,8 +587,8 @@ class PrivateKeyCryptographyBackend(PrivateKeyBackend): if self.format == "auto_ignore": return True try: - format = identify_private_key_format(self.existing_private_key_bytes) - return format == self._get_wanted_format() + key_format = identify_private_key_format(self.existing_private_key_bytes) + return key_format == self._get_wanted_format() except Exception: return False @@ -603,16 +602,16 @@ def select_backend(module: GeneralAnsibleModule) -> PrivateKeyBackend: def get_privatekey_argument_spec() -> ArgumentSpec: return ArgumentSpec( - argument_spec=dict( - size=dict(type="int", default=4096), - type=dict( - type="str", - default="RSA", - choices=["DSA", "ECC", "Ed25519", "Ed448", "RSA", "X25519", "X448"], - ), - curve=dict( - type="str", - choices=[ + argument_spec={ + "size": {"type": "int", "default": 4096}, + "type": { + "type": "str", + "default": "RSA", + "choices": ["DSA", "ECC", "Ed25519", "Ed448", "RSA", "X25519", "X448"], + }, + "curve": { + "type": "str", + "choices": [ "secp224r1", "secp256k1", "secp256r1", @@ -633,32 +632,36 @@ def get_privatekey_argument_spec() -> ArgumentSpec: "sect571k1", "sect571r1", ], - ), - passphrase=dict(type="str", no_log=True), - cipher=dict(type="str", default="auto"), - format=dict( - type="str", - default="auto_ignore", - choices=["pkcs1", "pkcs8", "raw", "auto", "auto_ignore"], - ), - format_mismatch=dict( - type="str", default="regenerate", choices=["regenerate", "convert"] - ), - select_crypto_backend=dict( - type="str", choices=["auto", "cryptography"], default="auto" - ), - regenerate=dict( - type="str", - default="full_idempotence", - choices=[ + }, + "passphrase": {"type": "str", "no_log": True}, + "cipher": {"type": "str", "default": "auto"}, + "format": { + "type": "str", + "default": "auto_ignore", + "choices": ["pkcs1", "pkcs8", "raw", "auto", "auto_ignore"], + }, + "format_mismatch": { + "type": "str", + "default": "regenerate", + "choices": ["regenerate", "convert"], + }, + "select_crypto_backend": { + "type": "str", + "choices": ["auto", "cryptography"], + "default": "auto", + }, + "regenerate": { + "type": "str", + "default": "full_idempotence", + "choices": [ "never", "fail", "partial_idempotence", "full_idempotence", "always", ], - ), - ), + }, + }, required_if=[ ("type", "ECC", ["curve"]), ], diff --git a/plugins/module_utils/_crypto/module_backends/privatekey_convert.py b/plugins/module_utils/_crypto/module_backends/privatekey_convert.py index 6ee92dec..18826710 100644 --- a/plugins/module_utils/_crypto/module_backends/privatekey_convert.py +++ b/plugins/module_utils/_crypto/module_backends/privatekey_convert.py @@ -121,7 +121,7 @@ class PrivateKeyConvertBackend(metaclass=abc.ABCMeta): assert self.dest_private_key_bytes is not None try: - format, self.dest_private_key = self._load_private_key( + key_format, self.dest_private_key = self._load_private_key( data=self.dest_private_key_bytes, passphrase=self.dest_passphrase, current_hint=self.src_private_key, @@ -129,7 +129,7 @@ class PrivateKeyConvertBackend(metaclass=abc.ABCMeta): except Exception: return True - return format != self.format or not cryptography_compare_private_keys( + return key_format != self.format or not cryptography_compare_private_keys( self.dest_private_key, self.src_private_key ) @@ -141,7 +141,7 @@ class PrivateKeyConvertBackend(metaclass=abc.ABCMeta): # Implementation with using cryptography class PrivateKeyConvertCryptographyBackend(PrivateKeyConvertBackend): def __init__(self, *, module: AnsibleModule) -> None: - super(PrivateKeyConvertCryptographyBackend, self).__init__(module=module) + super().__init__(module=module) def get_private_key_data(self) -> bytes: """Return bytes for self.src_private_key in output format""" @@ -166,6 +166,9 @@ class PrivateKeyConvertCryptographyBackend(PrivateKeyConvertBackend): export_encoding = ( cryptography.hazmat.primitives.serialization.Encoding.Raw ) + else: + # pylint does not notice that all possible values for self.format have been covered. + raise AssertionError("Can never be reached") # pragma: no cover except AttributeError: self.module.fail_json( msg=f'Cryptography backend does not support the selected output format "{self.format}"' @@ -208,20 +211,20 @@ class PrivateKeyConvertCryptographyBackend(PrivateKeyConvertBackend): ) -> tuple[str, PrivateKeyTypes]: try: # Interpret bytes depending on format. - format = identify_private_key_format(data) - if format == "raw": + key_format = identify_private_key_format(data) + if key_format == "raw": if passphrase is not None: raise PrivateKeyError("Cannot load raw key with passphrase") if len(data) == 56: return ( - format, + key_format, cryptography.hazmat.primitives.asymmetric.x448.X448PrivateKey.from_private_bytes( data ), ) if len(data) == 57: return ( - format, + key_format, cryptography.hazmat.primitives.asymmetric.ed448.Ed448PrivateKey.from_private_bytes( data ), @@ -233,14 +236,14 @@ class PrivateKeyConvertCryptographyBackend(PrivateKeyConvertBackend): ): try: return ( - format, + key_format, cryptography.hazmat.primitives.asymmetric.x25519.X25519PrivateKey.from_private_bytes( data ), ) except Exception: return ( - format, + key_format, cryptography.hazmat.primitives.asymmetric.ed25519.Ed25519PrivateKey.from_private_bytes( data ), @@ -248,29 +251,29 @@ class PrivateKeyConvertCryptographyBackend(PrivateKeyConvertBackend): else: try: return ( - format, + key_format, cryptography.hazmat.primitives.asymmetric.ed25519.Ed25519PrivateKey.from_private_bytes( data ), ) except Exception: return ( - format, + key_format, cryptography.hazmat.primitives.asymmetric.x25519.X25519PrivateKey.from_private_bytes( data ), ) raise PrivateKeyError("Cannot load raw key") - else: - return ( - format, - cryptography.hazmat.primitives.serialization.load_pem_private_key( - data, - None if passphrase is None else to_bytes(passphrase), - ), - ) + + return ( + key_format, + cryptography.hazmat.primitives.serialization.load_pem_private_key( + data, + None if passphrase is None else to_bytes(passphrase), + ), + ) except Exception as e: - raise PrivateKeyError(e) + raise PrivateKeyError(e) from e def select_backend(module: AnsibleModule) -> PrivateKeyConvertBackend: @@ -282,13 +285,17 @@ def select_backend(module: AnsibleModule) -> PrivateKeyConvertBackend: def get_privatekey_argument_spec() -> ArgumentSpec: return ArgumentSpec( - argument_spec=dict( - src_path=dict(type="path"), - src_content=dict(type="str"), - src_passphrase=dict(type="str", no_log=True), - dest_passphrase=dict(type="str", no_log=True), - format=dict(type="str", required=True, choices=["pkcs1", "pkcs8", "raw"]), - ), + argument_spec={ + "src_path": {"type": "path"}, + "src_content": {"type": "str"}, + "src_passphrase": {"type": "str", "no_log": True}, + "dest_passphrase": {"type": "str", "no_log": True}, + "format": { + "type": "str", + "required": True, + "choices": ["pkcs1", "pkcs8", "raw"], + }, + }, mutually_exclusive=[ ["src_path", "src_content"], ], diff --git a/plugins/module_utils/_crypto/module_backends/privatekey_info.py b/plugins/module_utils/_crypto/module_backends/privatekey_info.py index f2018190..f3bd10f9 100644 --- a/plugins/module_utils/_crypto/module_backends/privatekey_info.py +++ b/plugins/module_utils/_crypto/module_backends/privatekey_info.py @@ -134,7 +134,7 @@ def _is_cryptography_key_consistent( # key._backend was removed in cryptography 42.0.0 backend = getattr(key, "_backend", None) if backend is not None: - return bool(backend._lib.RSA_check_key(key._rsa_cdata)) # type: ignore + return bool(backend._lib.RSA_check_key(key._rsa_cdata)) # type: ignore # pylint: disable=protected-access if isinstance(key, cryptography.hazmat.primitives.asymmetric.dsa.DSAPrivateKey): result = _check_dsa_consistency( key_public_data=key_public_data, key_private_data=key_private_data @@ -195,19 +195,21 @@ def _is_cryptography_key_consistent( class PrivateKeyConsistencyError(OpenSSLObjectError): def __init__(self, msg: str, *, result: dict[str, t.Any]) -> None: - super(PrivateKeyConsistencyError, self).__init__(msg) + super().__init__(msg) self.error_message = msg self.result = result class PrivateKeyParseError(OpenSSLObjectError): def __init__(self, msg: str, *, result: dict[str, t.Any]) -> None: - super(PrivateKeyParseError, self).__init__(msg) + super().__init__(msg) self.error_message = msg self.result = result class PrivateKeyInfoRetrieval(metaclass=abc.ABCMeta): + key: PrivateKeyTypes + def __init__( self, *, @@ -257,14 +259,14 @@ class PrivateKeyInfoRetrieval(metaclass=abc.ABCMeta): ) result["can_parse_key"] = True except OpenSSLObjectError as exc: - raise PrivateKeyParseError(str(exc), result=result) + raise PrivateKeyParseError(str(exc), result=result) from exc result["public_key"] = to_native(self._get_public_key(binary=False)) pk = self._get_public_key(binary=True) result["public_key_fingerprints"] = ( get_fingerprint_of_bytes(pk, prefer_one=prefer_one_fingerprint) if pk is not None - else dict() + else {} ) key_type, key_public_data, key_private_data = self._get_key_info( @@ -295,9 +297,7 @@ class PrivateKeyInfoRetrievalCryptography(PrivateKeyInfoRetrieval): def __init__( self, *, module: GeneralAnsibleModule, content: bytes, **kwargs ) -> None: - super(PrivateKeyInfoRetrievalCryptography, self).__init__( - module=module, content=content, **kwargs - ) + super().__init__(module=module, content=content, **kwargs) def _get_public_key(self, *, binary: bool) -> bytes: return self.key.public_key().public_bytes( diff --git a/plugins/module_utils/_crypto/module_backends/publickey_info.py b/plugins/module_utils/_crypto/module_backends/publickey_info.py index 80f87a74..6cef9dd6 100644 --- a/plugins/module_utils/_crypto/module_backends/publickey_info.py +++ b/plugins/module_utils/_crypto/module_backends/publickey_info.py @@ -100,7 +100,7 @@ def _get_cryptography_public_key_info( class PublicKeyParseError(OpenSSLObjectError): def __init__(self, msg: str, *, result: dict[str, t.Any]) -> None: - super(PublicKeyParseError, self).__init__(msg) + super().__init__(msg) self.error_message = msg self.result = result @@ -132,13 +132,13 @@ class PublicKeyInfoRetrieval(metaclass=abc.ABCMeta): try: self.key = load_publickey(content=self.content) except OpenSSLObjectError as e: - raise PublicKeyParseError(str(e), result={}) + raise PublicKeyParseError(str(e), result={}) from e pk = self._get_public_key(binary=True) result["fingerprints"] = ( get_fingerprint_of_bytes(pk, prefer_one=prefer_one_fingerprint) if pk is not None - else dict() + else {} ) key_type, key_public_data = self._get_key_info() @@ -157,9 +157,7 @@ class PublicKeyInfoRetrievalCryptography(PublicKeyInfoRetrieval): content: bytes | None = None, key: PublicKeyTypes | None = None, ) -> None: - super(PublicKeyInfoRetrievalCryptography, self).__init__( - module=module, content=content, key=key - ) + super().__init__(module=module, content=content, key=key) def _get_public_key(self, binary: bool) -> bytes: if self.key is None: diff --git a/plugins/module_utils/_crypto/support.py b/plugins/module_utils/_crypto/support.py index 96e1befa..6a58eabb 100644 --- a/plugins/module_utils/_crypto/support.py +++ b/plugins/module_utils/_crypto/support.py @@ -161,19 +161,21 @@ def load_privatekey( else: priv_key_detail = content except (IOError, OSError) as exc: - raise OpenSSLObjectError(exc) + raise OpenSSLObjectError(exc) from exc try: return load_pem_private_key( priv_key_detail, None if passphrase is None else to_bytes(passphrase), ) - except TypeError: + except TypeError as exc: raise OpenSSLBadPassphraseError( "Wrong or empty passphrase provided for private key" - ) - except ValueError: - raise OpenSSLBadPassphraseError("Wrong passphrase provided for private key") + ) from exc + except ValueError as exc: + raise OpenSSLBadPassphraseError( + "Wrong passphrase provided for private key" + ) from exc def load_certificate_privatekey( @@ -232,12 +234,12 @@ def load_publickey( with open(path, "rb") as b_priv_key_fh: content = b_priv_key_fh.read() except (IOError, OSError) as exc: - raise OpenSSLObjectError(exc) + raise OpenSSLObjectError(exc) from exc try: return serialization.load_pem_public_key(content) except Exception as e: - raise OpenSSLObjectError(f"Error while deserializing key: {e}") + raise OpenSSLObjectError(f"Error while deserializing key: {e}") from e def load_certificate( @@ -257,17 +259,17 @@ def load_certificate( else: cert_content = content except (IOError, OSError) as exc: - raise OpenSSLObjectError(exc) + raise OpenSSLObjectError(exc) from exc if der_support_enabled is False or identify_pem_format(cert_content): try: return x509.load_pem_x509_certificate(cert_content) except ValueError as exc: - raise OpenSSLObjectError(exc) + raise OpenSSLObjectError(exc) from exc elif der_support_enabled: try: return x509.load_der_x509_certificate(cert_content) except ValueError as exc: - raise OpenSSLObjectError(f"Cannot parse DER certificate: {exc}") + raise OpenSSLObjectError(f"Cannot parse DER certificate: {exc}") from exc def load_certificate_request( @@ -283,11 +285,11 @@ def load_certificate_request( else: csr_content = content except (IOError, OSError) as exc: - raise OpenSSLObjectError(exc) + raise OpenSSLObjectError(exc) from exc try: return x509.load_pem_x509_csr(csr_content) except ValueError as exc: - raise OpenSSLObjectError(exc) + raise OpenSSLObjectError(exc) from exc def parse_name_field( @@ -344,7 +346,7 @@ def parse_ordered_name_field( except (TypeError, ValueError) as exc: raise ValueError( f"Error while processing entry #{index + 1} in {name_field_name}: {exc}" - ) + ) from exc return result @@ -425,9 +427,7 @@ class OpenSSLObject(metaclass=abc.ABCMeta): self.changed = True except OSError as exc: if exc.errno != errno.ENOENT: - raise OpenSSLObjectError(exc) - else: - pass + raise OpenSSLObjectError(exc) from exc __all__ = ( diff --git a/plugins/module_utils/_ecs/api.py b/plugins/module_utils/_ecs/api.py index 5b789767..8d4254ec 100644 --- a/plugins/module_utils/_ecs/api.py +++ b/plugins/module_utils/_ecs/api.py @@ -43,16 +43,20 @@ valid_file_format = re.compile(r".*(\.)(yml|yaml|json)$") def ecs_client_argument_spec() -> dict[str, t.Any]: - return dict( - entrust_api_user=dict(type="str", required=True), - entrust_api_key=dict(type="str", required=True, no_log=True), - entrust_api_client_cert_path=dict(type="path", required=True), - entrust_api_client_cert_key_path=dict(type="path", required=True, no_log=True), - entrust_api_specification_path=dict( - type="path", - default="https://cloud.entrust.net/EntrustCloud/documentation/cms-api-2.1.0.yaml", - ), - ) + return { + "entrust_api_user": {"type": "str", "required": True}, + "entrust_api_key": {"type": "str", "required": True, "no_log": True}, + "entrust_api_client_cert_path": {"type": "path", "required": True}, + "entrust_api_client_cert_key_path": { + "type": "path", + "required": True, + "no_log": True, + }, + "entrust_api_specification_path": { + "type": "path", + "default": "https://cloud.entrust.net/EntrustCloud/documentation/cms-api-2.1.0.yaml", + }, + } class SessionConfigurationException(Exception): @@ -182,8 +186,7 @@ class RestOperation: if result or result == {}: if result_code and result_code < 400: return result - else: - raise RestOperationException(result) + raise RestOperationException(result) # Raise a generic RestOperationException if this fails raise RestOperationException( @@ -323,9 +326,9 @@ class ECSSession: except HTTPError as e: raise SessionConfigurationException( f"Error downloading specification from address '{entrust_api_specification_path}', received error code '{e.getcode()}'" - ) + ) from e else: - with open(entrust_api_specification_path) as f: + with open(entrust_api_specification_path, "rb") as f: if ".json" in entrust_api_specification_path: self._spec = json.load(f) elif ( diff --git a/plugins/module_utils/_openssh/backends/common.py b/plugins/module_utils/_openssh/backends/common.py index e41763ac..72d58536 100644 --- a/plugins/module_utils/_openssh/backends/common.py +++ b/plugins/module_utils/_openssh/backends/common.py @@ -45,8 +45,7 @@ def restore_on_failure( if backup_file is not None: module.atomic_move(os.path.abspath(backup_file), os.path.abspath(path)) raise - else: - module.add_cleanup_file(backup_file) + module.add_cleanup_file(backup_file) return backup_and_restore @@ -91,9 +90,8 @@ def _restore_all_on_failure( os.path.abspath(backup), os.path.abspath(destination) ) raise - else: - for destination, backup in backups: - self.module.add_cleanup_file(backup) + for destination, backup in backups: + self.module.add_cleanup_file(backup) return backup_and_restore @@ -126,7 +124,7 @@ class OpensshModule(metaclass=abc.ABCMeta): result["changed"] = self.changed - if self.module._diff: + if self.module._diff: # pylint: disable=protected-access result["diff"] = self.diff return result @@ -219,7 +217,7 @@ class KeygenCommand: serial_number: int | None, signature_algorithm: str | None, signing_key_path: str, - type: t.Literal["host", "user"] | None, + cert_type: t.Literal["host", "user"] | None, time_parameters: OpensshCertificateTimeParameters, use_agent: bool, **kwargs, @@ -235,7 +233,7 @@ class KeygenCommand: args.extend(["-n", ",".join(principals)]) if serial_number is not None: args.extend(["-z", str(serial_number)]) - if type == "host": + if cert_type == "host": args.extend(["-h"]) if use_agent: args.extend(["-U"]) @@ -252,7 +250,7 @@ class KeygenCommand: *, private_key_path: str, size: int, - type: str, + key_type: str, comment: str | None, **kwargs, ) -> tuple[int, str, str]: @@ -264,7 +262,7 @@ class KeygenCommand: "-b", str(size), "-t", - type, + key_type, "-f", private_key_path, "-C", @@ -313,7 +311,7 @@ class KeygenCommand: except (IOError, OSError) as e: raise ValueError( f"The private key at {private_key_path} is not writeable preventing a comment update ({e})" - ) + ) from e command = [self._bin_path, "-q"] if force_new_format: @@ -327,12 +325,12 @@ _PrivateKey = t.TypeVar("_PrivateKey", bound="PrivateKey") class PrivateKey: def __init__( - self, *, size: int, key_type: str, fingerprint: str, format: str = "" + self, *, size: int, key_type: str, fingerprint: str, key_format: str = "" ) -> None: self._size = size self._type = key_type self._fingerprint = fingerprint - self._format = format + self._format = key_format @property def size(self) -> int: @@ -428,11 +426,8 @@ class PublicKey: @classmethod def load(cls: t.Type[_PublicKey], path: str | os.PathLike) -> _PublicKey | None: - try: - with open(path, "r") as f: - properties = f.read().strip(" \n").split(" ", 2) - except (IOError, OSError): - raise + with open(path, "r", encoding="utf-8") as f: + properties = f.read().strip(" \n").split(" ", 2) if len(properties) < 2: return None @@ -454,14 +449,14 @@ def parse_private_key_format( *, path: str | os.PathLike, ) -> t.Literal["SSH", "PKCS8", "PKCS1", ""]: - with open(path, "r") as file: + with open(path, "r", encoding="utf-8") as file: header = file.readline().strip() if header == "-----BEGIN OPENSSH PRIVATE KEY-----": return "SSH" - elif header == "-----BEGIN PRIVATE KEY-----": + if header == "-----BEGIN PRIVATE KEY-----": return "PKCS8" - elif header == "-----BEGIN RSA PRIVATE KEY-----": + if header == "-----BEGIN RSA PRIVATE KEY-----": return "PKCS1" return "" diff --git a/plugins/module_utils/_openssh/backends/keypair_backend.py b/plugins/module_utils/_openssh/backends/keypair_backend.py index a7f87091..13363729 100644 --- a/plugins/module_utils/_openssh/backends/keypair_backend.py +++ b/plugins/module_utils/_openssh/backends/keypair_backend.py @@ -54,7 +54,7 @@ if t.TYPE_CHECKING: class KeypairBackend(OpensshModule, metaclass=abc.ABCMeta): def __init__(self, *, module: AnsibleModule) -> None: - super(KeypairBackend, self).__init__(module=module) + super().__init__(module=module) self.comment: str | None = self.module.params["comment"] self.private_key_path: str = self.module.params["path"] @@ -189,9 +189,9 @@ class KeypairBackend(OpensshModule, metaclass=abc.ABCMeta): def _should_generate(self) -> bool: if self.original_private_key is None: return True - elif self.regenerate == "never": + if self.regenerate == "never": return False - elif self.regenerate == "fail": + if self.regenerate == "fail": if not self._private_key_valid(): self.module.fail_json( msg="Key has wrong type and/or size. Will not proceed. " @@ -199,10 +199,9 @@ class KeypairBackend(OpensshModule, metaclass=abc.ABCMeta): + "`partial_idempotence`, `full_idempotence` or `always`, or with `force=true`." ) return False - elif self.regenerate in ("partial_idempotence", "full_idempotence"): + if self.regenerate in ("partial_idempotence", "full_idempotence"): return not self._private_key_valid() - else: - return True + return True def _private_key_valid(self) -> bool: if self.original_private_key is None: @@ -358,7 +357,7 @@ class KeypairBackend(OpensshModule, metaclass=abc.ABCMeta): class KeypairBackendOpensshBin(KeypairBackend): def __init__(self, *, module: AnsibleModule) -> None: - super(KeypairBackendOpensshBin, self).__init__(module=module) + super().__init__(module=module) if self.module.params["private_key_format"] != "auto": self.module.fail_json( @@ -371,7 +370,7 @@ class KeypairBackendOpensshBin(KeypairBackend): self.ssh_keygen.generate_keypair( private_key_path=private_key_path, size=self.size, - type=self.type, + key_type=self.type, comment=self.comment, check_rc=True, ) @@ -391,7 +390,7 @@ class KeypairBackendOpensshBin(KeypairBackend): return PublicKey.from_string(public_key_content) def _private_key_readable(self) -> bool: - rc, stdout, stderr = self.ssh_keygen.get_matching_public_key( + rc, _stdout, stderr = self.ssh_keygen.get_matching_public_key( private_key_path=self.private_key_path, check_rc=False ) return not ( @@ -425,7 +424,7 @@ class KeypairBackendOpensshBin(KeypairBackend): class KeypairBackendCryptography(KeypairBackend): def __init__(self, *, module: AnsibleModule) -> None: - super(KeypairBackendCryptography, self).__init__(module=module) + super().__init__(module=module) if self.type == "rsa1": self.module.fail_json( @@ -489,7 +488,7 @@ class KeypairBackendCryptography(KeypairBackend): size=keypair.size, key_type=keypair.key_type, fingerprint=keypair.fingerprint, - format=parse_private_key_format(path=self.private_key_path), + key_format=parse_private_key_format(path=self.private_key_path), ) def _get_public_key(self) -> PublicKey | t.Literal[""]: @@ -522,10 +521,9 @@ class KeypairBackendCryptography(KeypairBackend): OpensshKeypair.load( path=self.private_key_path, passphrase=None, no_public_key=True ) + return False except (InvalidPrivateKeyFileError, InvalidPassphraseError): return True - else: - return False return True diff --git a/plugins/module_utils/_openssh/certificate.py b/plugins/module_utils/_openssh/certificate.py index 9c08c669..c86bcbd8 100644 --- a/plugins/module_utils/_openssh/certificate.py +++ b/plugins/module_utils/_openssh/certificate.py @@ -124,11 +124,9 @@ class OpensshCertificateTimeParameters: def __eq__(self, other: object) -> bool: if not isinstance(other, type(self)): return NotImplemented - else: - return ( - self._valid_from == other._valid_from - and self._valid_to == other._valid_to - ) + return ( + self._valid_from == other._valid_from and self._valid_to == other._valid_to + ) def __ne__(self, other: object) -> bool: return not self == other @@ -188,12 +186,11 @@ class OpensshCertificateTimeParameters: return "always" if dt == _FOREVER: return "forever" - else: - return ( - dt.isoformat().replace("+00:00", "") - if date_format == "human_readable" - else dt.strftime("%Y%m%d%H%M%S") - ) + return ( + dt.isoformat().replace("+00:00", "") + if date_format == "human_readable" + else dt.strftime("%Y%m%d%H%M%S") + ) if date_format == "timestamp": td = dt - _ALWAYS return int( @@ -203,22 +200,17 @@ class OpensshCertificateTimeParameters: @staticmethod def to_datetime(time_string_or_timestamp: str | bytes | int) -> datetime: - try: - if isinstance(time_string_or_timestamp, (str, bytes)): - result = OpensshCertificateTimeParameters._time_string_to_datetime( - to_text(time_string_or_timestamp.strip()) - ) - elif isinstance(time_string_or_timestamp, int): - result = OpensshCertificateTimeParameters._timestamp_to_datetime( - time_string_or_timestamp - ) - else: - raise ValueError( - f"Value must be of type (str, unicode, int) not {type(time_string_or_timestamp)}" - ) - except ValueError: - raise - return result + if isinstance(time_string_or_timestamp, (str, bytes)): + return OpensshCertificateTimeParameters._time_string_to_datetime( + to_text(time_string_or_timestamp.strip()) + ) + if isinstance(time_string_or_timestamp, int): + return OpensshCertificateTimeParameters._timestamp_to_datetime( + time_string_or_timestamp + ) + raise ValueError( + f"Value must be of type (str, unicode, int) not {type(time_string_or_timestamp)}" + ) @staticmethod def _timestamp_to_datetime(timestamp: int) -> datetime: @@ -228,8 +220,8 @@ class OpensshCertificateTimeParameters: return _FOREVER try: return datetime.fromtimestamp(timestamp, tz=_datetime.timezone.utc) - except OverflowError: - raise ValueError + except OverflowError as e: + raise ValueError from e @staticmethod def _time_string_to_datetime(time_string: str) -> datetime: @@ -382,16 +374,15 @@ class OpensshCertificateInfo(metaclass=abc.ABCMeta): def cert_type(self) -> t.Literal["user", "host", ""]: if self._cert_type == _USER_TYPE: return "user" - elif self._cert_type == _HOST_TYPE: + if self._cert_type == _HOST_TYPE: return "host" - else: - return "" + return "" @cert_type.setter def cert_type(self, cert_type: t.Literal["user", "host"] | int) -> None: - if cert_type == "user" or cert_type == _USER_TYPE: + if cert_type in ("user", _USER_TYPE): self._cert_type = _USER_TYPE - elif cert_type == "host" or cert_type == _HOST_TYPE: + elif cert_type in ("host", _HOST_TYPE): self._cert_type = _HOST_TYPE else: raise ValueError(f"{cert_type} is not a valid certificate type") @@ -412,7 +403,7 @@ class OpensshCertificateInfo(metaclass=abc.ABCMeta): class OpensshRSACertificateInfo(OpensshCertificateInfo): def __init__(self, *, e: int | None = None, n: int | None = None, **kwargs) -> None: - super(OpensshRSACertificateInfo, self).__init__(**kwargs) + super().__init__(**kwargs) self.type_string = _SSH_TYPE_STRINGS["rsa"] + _CERT_SUFFIX_V01 self.e = e self.n = n @@ -444,7 +435,7 @@ class OpensshDSACertificateInfo(OpensshCertificateInfo): y: int | None = None, **kwargs, ) -> None: - super(OpensshDSACertificateInfo, self).__init__(**kwargs) + super().__init__(**kwargs) self.type_string = _SSH_TYPE_STRINGS["dsa"] + _CERT_SUFFIX_V01 self.p = p self.q = q @@ -476,7 +467,7 @@ class OpensshECDSACertificateInfo(OpensshCertificateInfo): def __init__( self, *, curve: bytes | None = None, public_key: bytes | None = None, **kwargs ): - super(OpensshECDSACertificateInfo, self).__init__(**kwargs) + super().__init__(**kwargs) self._curve = None if curve is not None: self.curve = curve @@ -519,7 +510,7 @@ class OpensshECDSACertificateInfo(OpensshCertificateInfo): class OpensshED25519CertificateInfo(OpensshCertificateInfo): def __init__(self, *, pk: bytes | None = None, **kwargs) -> None: - super(OpensshED25519CertificateInfo, self).__init__(**kwargs) + super().__init__(**kwargs) self.type_string = _SSH_TYPE_STRINGS["ed25519"] + _CERT_SUFFIX_V01 self.pk = pk @@ -559,13 +550,13 @@ class OpensshCertificate: with open(path, "rb") as cert_file: data = cert_file.read() except (IOError, OSError) as e: - raise ValueError(f"{path} cannot be opened for reading: {e}") + raise ValueError(f"{path} cannot be opened for reading: {e}") from e try: format_identifier, b64_cert = data.split(b" ")[:2] cert = binascii.a2b_base64(b64_cert) - except (binascii.Error, ValueError): - raise ValueError("Certificate not in OpenSSH format") + except (binascii.Error, ValueError) as e: + raise ValueError("Certificate not in OpenSSH format") from e for key_type, string in _SSH_TYPE_STRINGS.items(): if format_identifier == string + _CERT_SUFFIX_V01: @@ -585,7 +576,7 @@ class OpensshCertificate: cert_info = cls._parse_cert_info(pub_key_type, parser) signature = parser.string() except (TypeError, ValueError) as e: - raise ValueError(f"Invalid certificate data: {e}") + raise ValueError(f"Invalid certificate data: {e}") from e if parser.remaining_bytes(): raise ValueError( @@ -751,10 +742,9 @@ def apply_directives(directives: t.Iterable[str]) -> list[OpensshCertificateOpti if "clear" in directives: return [] - else: - return list( - set(default_options()) - set(directive_to_option[d] for d in directives) - ) + return list( + set(default_options()) - set(directive_to_option[d] for d in directives) + ) def default_options() -> list[OpensshCertificateOption]: diff --git a/plugins/module_utils/_openssh/cryptography.py b/plugins/module_utils/_openssh/cryptography.py index 6d9be5fd..93a6ca85 100644 --- a/plugins/module_utils/_openssh/cryptography.py +++ b/plugins/module_utils/_openssh/cryptography.py @@ -19,6 +19,7 @@ try: from cryptography.exceptions import InvalidSignature, UnsupportedAlgorithm from cryptography.hazmat.primitives import hashes, serialization from cryptography.hazmat.primitives.asymmetric import dsa, ec, padding, rsa + from cryptography.hazmat.primitives.asymmetric.ed448 import Ed448PrivateKey from cryptography.hazmat.primitives.asymmetric.ed25519 import ( Ed25519PrivateKey, Ed25519PublicKey, @@ -68,6 +69,10 @@ except ImportError: CRYPTOGRAPHY_VERSION = "0.0" _ALGORITHM_PARAMETERS = {} +from ansible_collections.community.crypto.plugins.module_utils._crypto.cryptography_support import ( + is_potential_certificate_issuer_private_key, +) + if t.TYPE_CHECKING: KeyFormat = t.Literal["SSH", "PKCS8", "PKCS1"] @@ -309,10 +314,10 @@ class AsymmetricKeypair: try: self.verify(signature=self.sign(b"message"), data=b"message") - except InvalidSignatureError: + except InvalidSignatureError as e: raise InvalidPublicKeyFileError( "The private key and public key of this keypair do not match" - ) + ) from e def __eq__(self, other: object) -> bool: if not isinstance(other, AsymmetricKeypair): @@ -368,7 +373,7 @@ class AsymmetricKeypair: data, **_ALGORITHM_PARAMETERS[self.__keytype]["signer_params"] # type: ignore ) except TypeError as e: - raise InvalidDataError(e) + raise InvalidDataError(e) from e def verify(self, *, signature: bytes, data: bytes) -> None: """Verifies that the signature associated with the provided data was signed @@ -383,8 +388,8 @@ class AsymmetricKeypair: data, **_ALGORITHM_PARAMETERS[self.__keytype]["signer_params"], # type: ignore ) - except InvalidSignature: - raise InvalidSignatureError + except InvalidSignature as e: + raise InvalidSignatureError from e def update_passphrase(self, passphrase: bytes | None = None) -> None: """Updates the encryption algorithm of this key pair @@ -661,10 +666,10 @@ def load_privatekey( try: privatekey_loader = privatekey_loaders[key_format] - except KeyError: + except KeyError as e: raise InvalidKeyFormatError( f"{key_format} is not a valid key format ({','.join(privatekey_loaders)})" - ) + ) from e if not os.path.exists(path): raise InvalidPrivateKeyFileError(f"No file was found at {path}") @@ -673,32 +678,33 @@ def load_privatekey( with open(path, "rb") as f: content = f.read() - privatekey = privatekey_loader( # type: ignore + try: + privatekey = privatekey_loader( data=content, password=passphrase, ) - - except ValueError as exc: - # Revert to PEM if key could not be loaded in SSH format - if key_format == "SSH": - try: - privatekey = privatekey_loaders["PEM"]( # type: ignore + except ValueError as exc: + # Revert to PEM if key could not be loaded in SSH format + if key_format == "SSH": + privatekey = privatekey_loaders["PEM"]( data=content, password=passphrase, ) - except ValueError as e: - raise InvalidPrivateKeyFileError(e) - except TypeError as e: - raise InvalidPassphraseError(e) - except UnsupportedAlgorithm as e: - raise InvalidAlgorithmError(e) - else: - raise InvalidPrivateKeyFileError(exc) + else: + raise InvalidPrivateKeyFileError(exc) from exc + except ValueError as e: + raise InvalidPrivateKeyFileError(e) from e except TypeError as e: - raise InvalidPassphraseError(e) + raise InvalidPassphraseError(e) from e except UnsupportedAlgorithm as e: - raise InvalidAlgorithmError(e) + raise InvalidAlgorithmError(e) from e + if not is_potential_certificate_issuer_private_key(privatekey) or isinstance( + privatekey, Ed448PrivateKey + ): + raise InvalidPrivateKeyFileError( + f"{privatekey} is not a supported private key type" + ) return privatekey @@ -713,10 +719,10 @@ def load_publickey( try: publickey_loader = publickey_loaders[key_format] - except KeyError: + except KeyError as e: raise InvalidKeyFormatError( f"{key_format} is not a valid key format ({','.join(publickey_loaders)})" - ) + ) from e if not os.path.exists(path): raise InvalidPublicKeyFileError(f"No file was found at {path}") @@ -729,9 +735,9 @@ def load_publickey( data=content, ) except ValueError as e: - raise InvalidPublicKeyFileError(e) + raise InvalidPublicKeyFileError(e) from e except UnsupportedAlgorithm as e: - raise InvalidAlgorithmError(e) + raise InvalidAlgorithmError(e) from e return publickey @@ -749,8 +755,7 @@ def compare_publickeys(pk1: PublicKeyTypes, pk2: PublicKeyTypes) -> bool: serialization.Encoding.Raw, serialization.PublicFormat.Raw ) return a_bytes == b_bytes - else: - return pk1.public_numbers() == pk2.public_numbers() # type: ignore + return pk1.public_numbers() == pk2.public_numbers() # type: ignore def compare_encryption_algorithms( @@ -761,12 +766,11 @@ def compare_encryption_algorithms( ea2, serialization.NoEncryption ): return True - elif isinstance(ea1, serialization.BestAvailableEncryption) and isinstance( + if isinstance(ea1, serialization.BestAvailableEncryption) and isinstance( ea2, serialization.BestAvailableEncryption ): return ea1.password == ea2.password - else: - return False + return False def get_encryption_algorithm( @@ -775,7 +779,7 @@ def get_encryption_algorithm( try: return serialization.BestAvailableEncryption(passphrase) except ValueError as e: - raise InvalidPassphraseError(e) + raise InvalidPassphraseError(e) from e def validate_comment(comment: str) -> None: @@ -796,7 +800,7 @@ def extract_comment(path: str | os.PathLike) -> str: else: comment = "" except (IOError, OSError) as e: - raise InvalidPublicKeyFileError(e) + raise InvalidPublicKeyFileError(e) from e return comment diff --git a/plugins/module_utils/_openssh/utils.py b/plugins/module_utils/_openssh/utils.py index 1cd0181e..452f5c8c 100644 --- a/plugins/module_utils/_openssh/utils.py +++ b/plugins/module_utils/_openssh/utils.py @@ -177,10 +177,9 @@ class OpensshParser: def _check_position(self, offset: int) -> int: if self._pos + offset > len(self._data): raise ValueError(f"Insufficient data remaining at position: {self._pos}") - elif self._pos + offset < 0: + if self._pos + offset < 0: raise ValueError("Position cannot be less than zero.") - else: - return self._pos + offset + return self._pos + offset @classmethod def signature_data(cls, *, signature_string: bytes) -> dict[str, bytes | int]: @@ -306,7 +305,9 @@ class _OpensshWriter: try: self.string(",".join(value).encode("ASCII")) except UnicodeEncodeError as e: - raise ValueError(f"Name-list's must consist of US-ASCII characters: {e}") + raise ValueError( + f"Name-list's must consist of US-ASCII characters: {e}" + ) from e return self diff --git a/plugins/module_utils/_serial.py b/plugins/module_utils/_serial.py index c6994224..a5b45c88 100644 --- a/plugins/module_utils/_serial.py +++ b/plugins/module_utils/_serial.py @@ -41,7 +41,7 @@ def parse_serial(value: str | bytes) -> int: except ValueError as exc: raise ValueError( f"The {i + 1}{th(i + 1)} part {part!r} is not a hexadecimal number in range [0, 255]: {exc}" - ) + ) from exc result = (result << 8) | part_value return result diff --git a/plugins/module_utils/_time.py b/plugins/module_utils/_time.py index 3e2f903d..ee99b51e 100644 --- a/plugins/module_utils/_time.py +++ b/plugins/module_utils/_time.py @@ -102,8 +102,7 @@ def convert_relative_to_datetime( if parsed_result.group("prefix") == "+": return now + offset - else: - return now - offset + return now - offset def get_relative_time_option( diff --git a/plugins/modules/acme_account.py b/plugins/modules/acme_account.py index f566219a..fa1a2e3c 100644 --- a/plugins/modules/acme_account.py +++ b/plugins/modules/acme_account.py @@ -184,25 +184,29 @@ from ansible_collections.community.crypto.plugins.module_utils._acme.errors impo def main() -> t.NoReturn: argument_spec = create_default_argspec() argument_spec.update_argspec( - terms_agreed=dict(type="bool", default=False), - state=dict( - type="str", required=True, choices=["absent", "present", "changed_key"] - ), - allow_creation=dict(type="bool", default=True), - contact=dict(type="list", elements="str", default=[]), - new_account_key_src=dict(type="path"), - new_account_key_content=dict(type="str", no_log=True), - new_account_key_passphrase=dict(type="str", no_log=True), - external_account_binding=dict( - type="dict", - options=dict( - kid=dict(type="str", required=True), - alg=dict( - type="str", required=True, choices=["HS256", "HS384", "HS512"] - ), - key=dict(type="str", required=True, no_log=True), - ), - ), + terms_agreed={"type": "bool", "default": False}, + state={ + "type": "str", + "required": True, + "choices": ["absent", "present", "changed_key"], + }, + allow_creation={"type": "bool", "default": True}, + contact={"type": "list", "elements": "str", "default": []}, + new_account_key_src={"type": "path"}, + new_account_key_content={"type": "str", "no_log": True}, + new_account_key_passphrase={"type": "str", "no_log": True}, + external_account_binding={ + "type": "dict", + "options": { + "kid": {"type": "str", "required": True}, + "alg": { + "type": "str", + "required": True, + "choices": ["HS256", "HS384", "HS512"], + }, + "key": {"type": "str", "required": True, "no_log": True}, + }, + }, ) argument_spec.update( mutually_exclusive=[("new_account_key_src", "new_account_key_content")], @@ -254,7 +258,7 @@ def main() -> t.NoReturn: if not module.check_mode: # Deactivate it deactivate_payload = {"status": "deactivated"} - result, info = client.send_signed_request( + result, _info = client.send_signed_request( t.cast(str, client.account_uri), deactivate_payload, error_msg="Failed to deactivate account", @@ -302,7 +306,7 @@ def main() -> t.NoReturn: except KeyParsingError as e: raise ModuleFailException( f"Error while parsing new account key: {e.msg}" - ) + ) from e # Verify that the account exists and has not been deactivated created, account_data = account.setup_account(allow_creation=False) if created: @@ -335,18 +339,18 @@ def main() -> t.NoReturn: key_data=new_key_data, ) # Send request and verify result - result, info = client.send_signed_request( + result, _info = client.send_signed_request( url, data, error_msg="Failed to rollover account key", expected_status_codes=[200], ) - if module._diff: + if module._diff: # pylint: disable=protected-access client.account_key_data = new_key_data if client.account_jws_header: client.account_jws_header["alg"] = new_key_data["alg"] diff_after = account.get_account_data() or {} - elif module._diff: + elif module._diff: # pylint: disable=protected-access # Kind of fake diff_after diff_after = dict(diff_before) diff_after["public_account_key"] = new_key_data["jwk"] @@ -355,7 +359,7 @@ def main() -> t.NoReturn: "changed": changed, "account_uri": client.account_uri, } - if module._diff: + if module._diff: # pylint: disable=protected-access result["diff"] = { "before": diff_before, "after": diff_after, diff --git a/plugins/modules/acme_account_info.py b/plugins/modules/acme_account_info.py index 8f7f881a..9b9bc24c 100644 --- a/plugins/modules/acme_account_info.py +++ b/plugins/modules/acme_account_info.py @@ -273,9 +273,11 @@ def get_order(client: ACMEClient, order_url: str) -> dict[str, t.Any]: def main() -> t.NoReturn: argument_spec = create_default_argspec() argument_spec.update_argspec( - retrieve_orders=dict( - type="str", default="ignore", choices=["ignore", "url_list", "object_list"] - ), + retrieve_orders={ + "type": "str", + "default": "ignore", + "choices": ["ignore", "url_list", "object_list"], + }, ) module = argument_spec.create_ansible_module(supports_check_mode=True) backend = create_backend(module, needs_acme_v2=True) diff --git a/plugins/modules/acme_ari_info.py b/plugins/modules/acme_ari_info.py index 32123768..ae141eef 100644 --- a/plugins/modules/acme_ari_info.py +++ b/plugins/modules/acme_ari_info.py @@ -109,8 +109,8 @@ from ansible_collections.community.crypto.plugins.module_utils._acme.errors impo def main() -> t.NoReturn: argument_spec = create_default_argspec(with_account=False) argument_spec.update_argspec( - certificate_path=dict(type="path"), - certificate_content=dict(type="str"), + certificate_path={"type": "path"}, + certificate_content={"type": "str"}, ) argument_spec.update( required_one_of=[("certificate_path", "certificate_content")], diff --git a/plugins/modules/acme_certificate.py b/plugins/modules/acme_certificate.py index 4e1bb050..e8655d93 100644 --- a/plugins/modules/acme_certificate.py +++ b/plugins/modules/acme_certificate.py @@ -578,7 +578,6 @@ from ansible_collections.community.crypto.plugins.module_utils._acme.certificate from ansible_collections.community.crypto.plugins.module_utils._acme.challenges import ( combine_identifier, normalize_combined_identifier, - split_identifier, wait_for_validation, ) from ansible_collections.community.crypto.plugins.module_utils._acme.errors import ( @@ -760,7 +759,6 @@ class ACMECertificateClient: data: dict[str, t.Any] = {} data_dns: dict[str, list[str]] = {} for type_identifier, authz in self.authorizations.items(): - identifier_type, identifier = split_identifier(type_identifier) # Skip valid authentications: their challenges are already valid # and do not need to be returned if authz.status == "valid": @@ -802,7 +800,7 @@ class ACMECertificateClient: # Step 2: validate pending challenges authzs_to_wait_for = [] - for type_identifier, authz in self.authorizations.items(): + for authz in self.authorizations.values(): if authz.status == "pending": if self.challenge is not None: authz.call_validate( @@ -951,52 +949,54 @@ def main() -> t.NoReturn: argument_spec = create_default_argspec(with_certificate=True) argument_spec.argument_spec["csr"]["aliases"] = ["src"] argument_spec.update_argspec( - modify_account=dict(type="bool", default=True), - account_email=dict(type="str"), - agreement=dict( - type="str", - removed_in_version="4.0.0", - removed_from_collection="community.crypto", - ), - terms_agreed=dict(type="bool", default=False), - challenge=dict( - type="str", - default="http-01", - choices=["http-01", "dns-01", "tls-alpn-01", NO_CHALLENGE], - ), - data=dict(type="dict"), - dest=dict(type="path", aliases=["cert"]), - fullchain_dest=dict(type="path", aliases=["fullchain"]), - chain_dest=dict(type="path", aliases=["chain"]), - remaining_days=dict(type="int", default=10), - deactivate_authzs=dict(type="bool", default=False), - force=dict(type="bool", default=False), - retrieve_all_alternates=dict(type="bool", default=False), - select_chain=dict( - type="list", - elements="dict", - options=dict( - test_certificates=dict( - type="str", default="all", choices=["first", "last", "all"] - ), - issuer=dict(type="dict"), - subject=dict(type="dict"), - subject_key_identifier=dict(type="str"), - authority_key_identifier=dict(type="str"), - ), - ), - include_renewal_cert_id=dict( - type="str", - choices=["never", "when_ari_supported", "always"], - default="never", - ), - profile=dict(type="str"), - order_creation_error_strategy=dict( - type="str", - default="auto", - choices=["auto", "always", "fail", "retry_without_replaces_cert_id"], - ), - order_creation_max_retries=dict(type="int", default=3), + modify_account={"type": "bool", "default": True}, + account_email={"type": "str"}, + agreement={ + "type": "str", + "removed_in_version": "4.0.0", + "removed_from_collection": "community.crypto", + }, + terms_agreed={"type": "bool", "default": False}, + challenge={ + "type": "str", + "default": "http-01", + "choices": ["http-01", "dns-01", "tls-alpn-01", NO_CHALLENGE], + }, + data={"type": "dict"}, + dest={"type": "path", "aliases": ["cert"]}, + fullchain_dest={"type": "path", "aliases": ["fullchain"]}, + chain_dest={"type": "path", "aliases": ["chain"]}, + remaining_days={"type": "int", "default": 10}, + deactivate_authzs={"type": "bool", "default": False}, + force={"type": "bool", "default": False}, + retrieve_all_alternates={"type": "bool", "default": False}, + select_chain={ + "type": "list", + "elements": "dict", + "options": { + "test_certificates": { + "type": "str", + "default": "all", + "choices": ["first", "last", "all"], + }, + "issuer": {"type": "dict"}, + "subject": {"type": "dict"}, + "subject_key_identifier": {"type": "str"}, + "authority_key_identifier": {"type": "str"}, + }, + }, + include_renewal_cert_id={ + "type": "str", + "choices": ["never", "when_ari_supported", "always"], + "default": "never", + }, + profile={"type": "str"}, + order_creation_error_strategy={ + "type": "str", + "default": "auto", + "choices": ["auto", "always", "fail", "retry_without_replaces_cert_id"], + }, + order_creation_max_retries={"type": "int", "default": 3}, ) argument_spec.update( required_one_of=[ @@ -1045,9 +1045,9 @@ def main() -> t.NoReturn: if module.params["deactivate_authzs"]: client.deactivate_authzs() data, data_dns = client.get_challenges_data(first_step=is_first_step) - auths = dict() + auths = {} assert client.authorizations is not None - for k, v in client.authorizations.items(): + for v in client.authorizations.values(): # Remove "type:" from key auths[v.identifier] = v.to_json() module.exit_json( diff --git a/plugins/modules/acme_certificate_deactivate_authz.py b/plugins/modules/acme_certificate_deactivate_authz.py index 0f51c3b5..de2aa9bc 100644 --- a/plugins/modules/acme_certificate_deactivate_authz.py +++ b/plugins/modules/acme_certificate_deactivate_authz.py @@ -70,7 +70,7 @@ from ansible_collections.community.crypto.plugins.module_utils._acme.orders impo def main() -> t.NoReturn: argument_spec = create_default_argspec() argument_spec.update_argspec( - order_uri=dict(type="str", required=True), + order_uri={"type": "str", "required": True}, ) module = argument_spec.create_ansible_module(supports_check_mode=True) diff --git a/plugins/modules/acme_certificate_order_create.py b/plugins/modules/acme_certificate_order_create.py index 2b63ce75..b412e978 100644 --- a/plugins/modules/acme_certificate_order_create.py +++ b/plugins/modules/acme_certificate_order_create.py @@ -388,15 +388,15 @@ from ansible_collections.community.crypto.plugins.module_utils._acme.errors impo def main() -> t.NoReturn: argument_spec = create_default_argspec(with_certificate=True) argument_spec.update_argspec( - deactivate_authzs=dict(type="bool", default=True), - replaces_cert_id=dict(type="str"), - profile=dict(type="str"), - order_creation_error_strategy=dict( - type="str", - default="auto", - choices=["auto", "always", "fail", "retry_without_replaces_cert_id"], - ), - order_creation_max_retries=dict(type="int", default=3), + deactivate_authzs={"type": "bool", "default": True}, + replaces_cert_id={"type": "str"}, + profile={"type": "str"}, + order_creation_error_strategy={ + "type": "str", + "default": "auto", + "choices": ["auto", "always", "fail", "retry_without_replaces_cert_id"], + }, + order_creation_max_retries={"type": "int", "default": 3}, ) module = argument_spec.create_ansible_module() diff --git a/plugins/modules/acme_certificate_order_finalize.py b/plugins/modules/acme_certificate_order_finalize.py index 855975a2..3189d756 100644 --- a/plugins/modules/acme_certificate_order_finalize.py +++ b/plugins/modules/acme_certificate_order_finalize.py @@ -340,29 +340,31 @@ if t.TYPE_CHECKING: def main() -> t.NoReturn: argument_spec = create_default_argspec(with_certificate=True) argument_spec.update_argspec( - order_uri=dict(type="str", required=True), - cert_dest=dict(type="path"), - fullchain_dest=dict(type="path"), - chain_dest=dict(type="path"), - deactivate_authzs=dict( - type="str", - default="always", - choices=["never", "always", "on_error", "on_success"], - ), - retrieve_all_alternates=dict(type="bool", default=False), - select_chain=dict( - type="list", - elements="dict", - options=dict( - test_certificates=dict( - type="str", default="all", choices=["first", "last", "all"] - ), - issuer=dict(type="dict"), - subject=dict(type="dict"), - subject_key_identifier=dict(type="str"), - authority_key_identifier=dict(type="str"), - ), - ), + order_uri={"type": "str", "required": True}, + cert_dest={"type": "path"}, + fullchain_dest={"type": "path"}, + chain_dest={"type": "path"}, + deactivate_authzs={ + "type": "str", + "default": "always", + "choices": ["never", "always", "on_error", "on_success"], + }, + retrieve_all_alternates={"type": "bool", "default": False}, + select_chain={ + "type": "list", + "elements": "dict", + "options": { + "test_certificates": { + "type": "str", + "default": "all", + "choices": ["first", "last", "all"], + }, + "issuer": {"type": "dict"}, + "subject": {"type": "dict"}, + "subject_key_identifier": {"type": "str"}, + "authority_key_identifier": {"type": "str"}, + }, + }, ) module = argument_spec.create_ansible_module() @@ -371,7 +373,7 @@ def main() -> t.NoReturn: try: client = ACMECertificateClient(module=module, backend=backend) select_chain_matcher = client.parse_select_chain(module.params["select_chain"]) - other = dict() + other = {} done = False order = None try: diff --git a/plugins/modules/acme_certificate_order_info.py b/plugins/modules/acme_certificate_order_info.py index 5a5df275..61eecb0c 100644 --- a/plugins/modules/acme_certificate_order_info.py +++ b/plugins/modules/acme_certificate_order_info.py @@ -374,7 +374,7 @@ from ansible_collections.community.crypto.plugins.module_utils._acme.errors impo def main() -> t.NoReturn: argument_spec = create_default_argspec(with_certificate=False) argument_spec.update_argspec( - order_uri=dict(type="str", required=True), + order_uri={"type": "str", "required": True}, ) module = argument_spec.create_ansible_module(supports_check_mode=True) diff --git a/plugins/modules/acme_certificate_order_validate.py b/plugins/modules/acme_certificate_order_validate.py index efa06c12..3299e09d 100644 --- a/plugins/modules/acme_certificate_order_validate.py +++ b/plugins/modules/acme_certificate_order_validate.py @@ -252,9 +252,9 @@ if t.TYPE_CHECKING: def main() -> t.NoReturn: argument_spec = create_default_argspec(with_certificate=False) argument_spec.update_argspec( - order_uri=dict(type="str", required=True), - challenge=dict(type="str", choices=["http-01", "dns-01", "tls-alpn-01"]), - deactivate_authzs=dict(type="bool", default=True), + order_uri={"type": "str", "required": True}, + challenge={"type": "str", "choices": ["http-01", "dns-01", "tls-alpn-01"]}, + deactivate_authzs={"type": "bool", "default": True}, ) module = argument_spec.create_ansible_module() @@ -330,13 +330,13 @@ def main() -> t.NoReturn: changed=len(authzs_with_challenges_to_wait_for) > 0, account_uri=client.client.account_uri, validating_challenges=[ - dict( - identifier=authz.identifier, - identifier_type=authz.identifier_type, - authz_url=authz.url, - challenge_type=challenge_type, - challenge_url=challenge.url if challenge else None, - ) + { + "identifier": authz.identifier, + "identifier_type": authz.identifier_type, + "authz_url": authz.url, + "challenge_type": challenge_type, + "challenge_url": challenge.url if challenge else None, + } for authz, challenge_type, challenge in authzs_with_challenges_to_wait_for ], ) diff --git a/plugins/modules/acme_certificate_renewal_info.py b/plugins/modules/acme_certificate_renewal_info.py index e2c1d68a..5ed5df7d 100644 --- a/plugins/modules/acme_certificate_renewal_info.py +++ b/plugins/modules/acme_certificate_renewal_info.py @@ -179,16 +179,18 @@ from ansible_collections.community.crypto.plugins.module_utils._acme.utils impor def main() -> t.NoReturn: argument_spec = create_default_argspec(with_account=False) argument_spec.update_argspec( - certificate_path=dict(type="path"), - certificate_content=dict(type="str"), - use_ari=dict(type="bool", default=True), - ari_algorithm=dict( - type="str", choices=["standard", "start"], default="standard" - ), - remaining_days=dict(type="int"), - remaining_percentage=dict(type="float"), - now=dict(type="str"), - treat_parsing_error_as_non_existing=dict(type="bool", default=False), + certificate_path={"type": "path"}, + certificate_content={"type": "str"}, + use_ari={"type": "bool", "default": True}, + ari_algorithm={ + "type": "str", + "choices": ["standard", "start"], + "default": "standard", + }, + remaining_days={"type": "int"}, + remaining_percentage={"type": "float"}, + now={"type": "str"}, + treat_parsing_error_as_non_existing={"type": "bool", "default": False}, ) argument_spec.update( mutually_exclusive=[("certificate_path", "certificate_content")], @@ -196,13 +198,13 @@ def main() -> t.NoReturn: module = argument_spec.create_ansible_module(supports_check_mode=True) backend = create_backend(module, needs_acme_v2=True) - result = dict( - changed=False, - msg="The certificate is still valid and no condition was reached", - exists=False, - parsable=False, - supports_ari=False, - ) + result = { + "changed": False, + "msg": "The certificate is still valid and no condition was reached", + "exists": False, + "parsable": False, + "supports_ari": False, + } def complete(should_renew: bool, **kwargs) -> t.NoReturn: result["should_renew"] = should_renew diff --git a/plugins/modules/acme_certificate_revoke.py b/plugins/modules/acme_certificate_revoke.py index ae227d48..1d89364f 100644 --- a/plugins/modules/acme_certificate_revoke.py +++ b/plugins/modules/acme_certificate_revoke.py @@ -134,11 +134,11 @@ from ansible_collections.community.crypto.plugins.module_utils._acme.utils impor def main() -> t.NoReturn: argument_spec = create_default_argspec(require_account_key=False) argument_spec.update_argspec( - private_key_src=dict(type="path"), - private_key_content=dict(type="str", no_log=True), - private_key_passphrase=dict(type="str", no_log=True), - certificate=dict(type="path", required=True), - revoke_reason=dict(type="int"), + private_key_src={"type": "path"}, + private_key_content={"type": "str", "no_log": True}, + private_key_passphrase={"type": "str", "no_log": True}, + certificate={"type": "path", "required": True}, + revoke_reason={"type": "int"}, ) argument_spec.update( required_one_of=[ @@ -186,7 +186,9 @@ def main() -> t.NoReturn: passphrase=passphrase, ) except KeyParsingError as e: - raise ModuleFailException(f"Error while parsing private key: {e.msg}") + raise ModuleFailException( + f"Error while parsing private key: {e.msg}" + ) from e # Step 2: sign revokation request with private key jws_header = { "alg": private_key_data["alg"], diff --git a/plugins/modules/acme_challenge_cert_helper.py b/plugins/modules/acme_challenge_cert_helper.py index 85ad53fc..43eb6a1f 100644 --- a/plugins/modules/acme_challenge_cert_helper.py +++ b/plugins/modules/acme_challenge_cert_helper.py @@ -200,13 +200,13 @@ def encode_octet_string(octet_string: bytes) -> bytes: def main() -> t.NoReturn: module = AnsibleModule( - argument_spec=dict( - challenge=dict(type="str", required=True, choices=["tls-alpn-01"]), - challenge_data=dict(type="dict", required=True), - private_key_src=dict(type="path"), - private_key_content=dict(type="str", no_log=True), - private_key_passphrase=dict(type="str", no_log=True), - ), + argument_spec={ + "challenge": {"type": "str", "required": True, "choices": ["tls-alpn-01"]}, + "challenge_data": {"type": "dict", "required": True}, + "private_key_src": {"type": "path"}, + "private_key_content": {"type": "str", "no_log": True}, + "private_key_passphrase": {"type": "str", "no_log": True}, + }, required_one_of=(["private_key_src", "private_key_content"],), mutually_exclusive=(["private_key_src", "private_key_content"],), ) @@ -239,7 +239,7 @@ def main() -> t.NoReturn: ) ) except Exception as e: - raise ModuleFailException(f"Error while loading private key: {e}") + raise ModuleFailException(f"Error while loading private key: {e}") from e if isinstance( private_key, ( @@ -317,6 +317,8 @@ def main() -> t.NoReturn: private_key, cryptography.hazmat.primitives.hashes.SHA256(), ) + else: + raise AssertionError("Can never be reached") # pragma: no cover module.exit_json( changed=True, diff --git a/plugins/modules/acme_inspect.py b/plugins/modules/acme_inspect.py index fff91453..36a10ac6 100644 --- a/plugins/modules/acme_inspect.py +++ b/plugins/modules/acme_inspect.py @@ -240,12 +240,14 @@ from ansible_collections.community.crypto.plugins.module_utils._acme.errors impo def main() -> t.NoReturn: argument_spec = create_default_argspec(require_account_key=False) argument_spec.update_argspec( - url=dict(type="str"), - method=dict( - type="str", choices=["get", "post", "directory-only"], default="get" - ), - content=dict(type="str"), - fail_on_acme_error=dict(type="bool", default=True), + url={"type": "str"}, + method={ + "type": "str", + "choices": ["get", "post", "directory-only"], + "default": "get", + }, + content={"type": "str"}, + fail_on_acme_error={"type": "bool", "default": True}, ) argument_spec.update( required_if=[ @@ -263,7 +265,7 @@ def main() -> t.NoReturn: try: # Get hold of ACMEClient and ACMEAccount objects (includes directory) client = ACMEClient(module=module, backend=backend) - method = module.params["method"] + method: t.Literal["get", "post", "directory-only"] = module.params["method"] result["directory"] = client.directory.directory # Do we have to do more requests? if method != "directory-only": @@ -283,12 +285,14 @@ def main() -> t.NoReturn: encode_payload=False, fail_on_error=False, ) + else: + raise AssertionError("Can never be reached") # pragma: no cover # Update results result.update( - dict( - headers=info, - output_text=to_native(data), - ) + { + "headers": info, + "output_text": to_native(data), + } ) # See if we can parse the result as JSON try: diff --git a/plugins/modules/certificate_complete_chain.py b/plugins/modules/certificate_complete_chain.py index 428bd50d..c7a505ca 100644 --- a/plugins/modules/certificate_complete_chain.py +++ b/plugins/modules/certificate_complete_chain.py @@ -328,12 +328,12 @@ def format_cert(cert: Certificate) -> str: def check_cycle( module: AnsibleModule, occured_certificates: set[cryptography.x509.Certificate], - next: Certificate, + next_certificate: Certificate, ) -> None: """ - Make sure that next is not in occured_certificates so far, and add it. + Make sure that next_certificate is not in occured_certificates so far, and add it. """ - next_cert = next.cert + next_cert = next_certificate.cert if next_cert in occured_certificates: module.fail_json(msg="Found cycle while building certificate chain") occured_certificates.add(next_cert) @@ -341,11 +341,15 @@ def check_cycle( def main() -> t.NoReturn: module = AnsibleModule( - argument_spec=dict( - input_chain=dict(type="str", required=True), - root_certificates=dict(type="list", required=True, elements="path"), - intermediate_certificates=dict(type="list", default=[], elements="path"), - ), + argument_spec={ + "input_chain": {"type": "str", "required": True}, + "root_certificates": {"type": "list", "required": True, "elements": "path"}, + "intermediate_certificates": { + "type": "list", + "default": [], + "elements": "path", + }, + }, supports_check_mode=True, ) @@ -382,7 +386,7 @@ def main() -> t.NoReturn: # Try to complete chain current: Certificate | None = chain[-1] completed = [] - occured_certificates = set([cert.cert for cert in chain]) + occured_certificates = {cert.cert for cert in chain} if current and current.cert in roots.certificate_by_cert: # Do not try to complete the chain when it is already ending with a root certificate current = None diff --git a/plugins/modules/crypto_info.py b/plugins/modules/crypto_info.py index 3c38ba9f..64a046d1 100644 --- a/plugins/modules/crypto_info.py +++ b/plugins/modules/crypto_info.py @@ -161,6 +161,7 @@ CRYPTOGRAPHY_VERSION: str | None CRYPTOGRAPHY_IMP_ERR: str | None try: import cryptography + import cryptography.hazmat.primitives.asymmetric from cryptography.exceptions import UnsupportedAlgorithm try: @@ -216,13 +217,12 @@ def add_crypto_information(module: AnsibleModule) -> dict[str, t.Any]: has_dsa_sign = False try: # added in 0.5 - https://cryptography.io/en/latest/hazmat/primitives/asymmetric/dsa/ - import cryptography.hazmat.primitives.asymmetric.dsa + from cryptography.hazmat.primitives.asymmetric import dsa has_dsa = True try: # added later in 1.5 - # pylint: disable-next=pointless-statement - cryptography.hazmat.primitives.asymmetric.dsa.DSAPrivateKey.sign + dsa.DSAPrivateKey.sign # pylint: disable=pointless-statement has_dsa_sign = True except AttributeError: pass @@ -234,13 +234,12 @@ def add_crypto_information(module: AnsibleModule) -> dict[str, t.Any]: has_rsa_sign = False try: # added in 0.5 - https://cryptography.io/en/latest/hazmat/primitives/asymmetric/rsa/ - import cryptography.hazmat.primitives.asymmetric.rsa + from cryptography.hazmat.primitives.asymmetric import rsa has_rsa = True try: # added later in 1.4 - # pylint: disable-next=pointless-statement - cryptography.hazmat.primitives.asymmetric.rsa.RSAPrivateKey.sign + rsa.RSAPrivateKey.sign # pylint: disable=pointless-statement has_rsa_sign = True except AttributeError: pass @@ -252,21 +251,17 @@ def add_crypto_information(module: AnsibleModule) -> dict[str, t.Any]: has_ed25519_sign = False try: # added in 2.6 - https://cryptography.io/en/latest/hazmat/primitives/asymmetric/ed25519/ - import cryptography.hazmat.primitives.asymmetric.ed25519 - from cryptography.hazmat.primitives.asymmetric.ed25519 import ( - Ed25519PrivateKey, - ) + from cryptography.hazmat.primitives.asymmetric import ed25519 try: - Ed25519PrivateKey.from_private_bytes(b"") + ed25519.Ed25519PrivateKey.from_private_bytes(b"") except ValueError: pass has_ed25519 = True try: # added with the primitive in 2.6 - # pylint: disable-next=pointless-statement - cryptography.hazmat.primitives.asymmetric.ed25519.Ed25519PrivateKey.sign + ed25519.Ed25519PrivateKey.sign # pylint: disable=pointless-statement has_ed25519_sign = True except AttributeError: pass @@ -278,19 +273,17 @@ def add_crypto_information(module: AnsibleModule) -> dict[str, t.Any]: has_ed448_sign = False try: # added in 2.6 - https://cryptography.io/en/latest/hazmat/primitives/asymmetric/ed448/ - import cryptography.hazmat.primitives.asymmetric.ed448 - from cryptography.hazmat.primitives.asymmetric.ed448 import Ed448PrivateKey + from cryptography.hazmat.primitives.asymmetric import ed448 try: - Ed448PrivateKey.from_private_bytes(b"") + ed448.Ed448PrivateKey.from_private_bytes(b"") except ValueError: pass has_ed448 = True try: # added with the primitive in 2.6 - # pylint: disable-next=pointless-statement - cryptography.hazmat.primitives.asymmetric.ed448.Ed448PrivateKey.sign + ed448.Ed448PrivateKey.sign # pylint: disable=pointless-statement has_ed448_sign = True except AttributeError: pass @@ -302,24 +295,21 @@ def add_crypto_information(module: AnsibleModule) -> dict[str, t.Any]: has_x25519_full = False try: # added in 2.0 - https://cryptography.io/en/latest/hazmat/primitives/asymmetric/x25519/ - import cryptography.hazmat.primitives.asymmetric.x25519 + from cryptography.hazmat.primitives.asymmetric import x25519 try: # added later in 2.5 - # pylint: disable-next=pointless-statement - cryptography.hazmat.primitives.asymmetric.x25519.X25519PrivateKey.private_bytes + x25519.X25519PrivateKey.private_bytes # pylint: disable=pointless-statement full = True except AttributeError: full = False try: if full: - cryptography.hazmat.primitives.asymmetric.x25519.X25519PrivateKey.from_private_bytes( - b"" - ) + x25519.X25519PrivateKey.from_private_bytes(b"") else: # Some versions do not support serialization and deserialization - use generate() instead - cryptography.hazmat.primitives.asymmetric.x25519.X25519PrivateKey.generate() + x25519.X25519PrivateKey.generate() except ValueError: pass @@ -332,12 +322,10 @@ def add_crypto_information(module: AnsibleModule) -> dict[str, t.Any]: has_x448 = False try: # added in 2.5 - https://cryptography.io/en/latest/hazmat/primitives/asymmetric/x448/ - import cryptography.hazmat.primitives.asymmetric.x448 + from cryptography.hazmat.primitives.asymmetric import x448 try: - from cryptography.hazmat.primitives.asymmetric.x448 import X448PrivateKey - - X448PrivateKey.from_private_bytes(b"") + x448.X448PrivateKey.from_private_bytes(b"") except ValueError: pass @@ -351,13 +339,12 @@ def add_crypto_information(module: AnsibleModule) -> dict[str, t.Any]: curves = [] try: # added in 0.5 - https://cryptography.io/en/latest/hazmat/primitives/asymmetric/ec/ - import cryptography.hazmat.primitives.asymmetric.ec + from cryptography.hazmat.primitives.asymmetric import ec has_ec = True try: # added later in 1.5 - # pylint: disable-next=pointless-statement - cryptography.hazmat.primitives.asymmetric.ec.EllipticCurvePrivateKey.sign + ec.EllipticCurvePrivateKey.sign # pylint: disable=pointless-statement has_ec_sign = True except AttributeError: pass @@ -365,14 +352,10 @@ def add_crypto_information(module: AnsibleModule) -> dict[str, t.Any]: pass else: for curve_name, constructor_name in CURVES: - ecclass = cryptography.hazmat.primitives.asymmetric.ec.__dict__.get( - constructor_name - ) + ecclass = ec.__dict__.get(constructor_name) if ecclass: try: - cryptography.hazmat.primitives.asymmetric.ec.generate_private_key( - curve=ecclass() - ) + ec.generate_private_key(curve=ecclass()) curves.append(curve_name) except UnsupportedAlgorithm: pass @@ -419,7 +402,7 @@ def add_openssl_information(module: AnsibleModule) -> dict[str, t.Any]: } result["openssl"] = openssl_result - rc, out, err = module.run_command([openssl_binary, "version"]) + rc, out, _err = module.run_command([openssl_binary, "version"]) if rc == 0: openssl_result["version_output"] = out parts = out.split(None, 2) diff --git a/plugins/modules/ecs_certificate.py b/plugins/modules/ecs_certificate.py index e02bced7..e4ff182d 100644 --- a/plugins/modules/ecs_certificate.py +++ b/plugins/modules/ecs_certificate.py @@ -786,11 +786,7 @@ class EcsCertificate: self.set_cert_details(module) assert self.cert_details is not None - if ( - self.cert_status == "EXPIRED" - or self.cert_status == "SUSPENDED" - or self.cert_status == "REVOKED" - ): + if self.cert_status in ("EXPIRED", "SUSPENDED", "REVOKED"): return False if self.cert_days < module.params["remaining_days"]: return False @@ -803,7 +799,7 @@ class EcsCertificate: # Read the CSR contents if self.csr and os.path.exists(self.csr): - with open(self.csr, "r") as csr_file: + with open(self.csr, "r", encoding="utf-8") as csr_file: body["csr"] = csr_file.read() # Check if the path is already a cert @@ -849,6 +845,8 @@ class EcsCertificate: result = self.ecs_client.ReissueCertRequest( # type: ignore[attr-defined] # pylint: disable=no-member trackingId=self.tracking_id, Body=body ) + else: + raise AssertionError("Can never be reached") # pragma: no cover self.tracking_id = result.get("trackingId") self.set_cert_details(module) assert self.cert_details is not None @@ -914,61 +912,61 @@ class EcsCertificate: def custom_fields_spec() -> dict[str, dict[str, str]]: - return dict( - text1=dict(type="str"), - text2=dict(type="str"), - text3=dict(type="str"), - text4=dict(type="str"), - text5=dict(type="str"), - text6=dict(type="str"), - text7=dict(type="str"), - text8=dict(type="str"), - text9=dict(type="str"), - text10=dict(type="str"), - text11=dict(type="str"), - text12=dict(type="str"), - text13=dict(type="str"), - text14=dict(type="str"), - text15=dict(type="str"), - number1=dict(type="float"), - number2=dict(type="float"), - number3=dict(type="float"), - number4=dict(type="float"), - number5=dict(type="float"), - date1=dict(type="str"), - date2=dict(type="str"), - date3=dict(type="str"), - date4=dict(type="str"), - date5=dict(type="str"), - email1=dict(type="str"), - email2=dict(type="str"), - email3=dict(type="str"), - email4=dict(type="str"), - email5=dict(type="str"), - dropdown1=dict(type="str"), - dropdown2=dict(type="str"), - dropdown3=dict(type="str"), - dropdown4=dict(type="str"), - dropdown5=dict(type="str"), - ) + return { + "text1": {"type": "str"}, + "text2": {"type": "str"}, + "text3": {"type": "str"}, + "text4": {"type": "str"}, + "text5": {"type": "str"}, + "text6": {"type": "str"}, + "text7": {"type": "str"}, + "text8": {"type": "str"}, + "text9": {"type": "str"}, + "text10": {"type": "str"}, + "text11": {"type": "str"}, + "text12": {"type": "str"}, + "text13": {"type": "str"}, + "text14": {"type": "str"}, + "text15": {"type": "str"}, + "number1": {"type": "float"}, + "number2": {"type": "float"}, + "number3": {"type": "float"}, + "number4": {"type": "float"}, + "number5": {"type": "float"}, + "date1": {"type": "str"}, + "date2": {"type": "str"}, + "date3": {"type": "str"}, + "date4": {"type": "str"}, + "date5": {"type": "str"}, + "email1": {"type": "str"}, + "email2": {"type": "str"}, + "email3": {"type": "str"}, + "email4": {"type": "str"}, + "email5": {"type": "str"}, + "dropdown1": {"type": "str"}, + "dropdown2": {"type": "str"}, + "dropdown3": {"type": "str"}, + "dropdown4": {"type": "str"}, + "dropdown5": {"type": "str"}, + } def ecs_certificate_argument_spec() -> dict[str, dict[str, t.Any]]: - return dict( - backup=dict(type="bool", default=False), - force=dict(type="bool", default=False), - path=dict(type="path", required=True), - full_chain_path=dict(type="path"), - tracking_id=dict(type="int"), - remaining_days=dict(type="int", default=30), - request_type=dict( - type="str", - default="new", - choices=["new", "renew", "reissue", "validate_only"], - ), - cert_type=dict( - type="str", - choices=[ + return { + "backup": {"type": "bool", "default": False}, + "force": {"type": "bool", "default": False}, + "path": {"type": "path", "required": True}, + "full_chain_path": {"type": "path"}, + "tracking_id": {"type": "int"}, + "remaining_days": {"type": "int", "default": 30}, + "request_type": { + "type": "str", + "default": "new", + "choices": ["new", "renew", "reissue", "validate_only"], + }, + "cert_type": { + "type": "str", + "choices": [ "STANDARD_SSL", "ADVANTAGE_SSL", "UC_SSL", @@ -984,26 +982,27 @@ def ecs_certificate_argument_spec() -> dict[str, dict[str, t.Any]]: "CDS_ENT_PRO", "SMIME_ENT", ], - ), - csr=dict(type="str"), - subject_alt_name=dict(type="list", elements="str"), - eku=dict( - type="str", choices=["SERVER_AUTH", "CLIENT_AUTH", "SERVER_AND_CLIENT_AUTH"] - ), - ct_log=dict(type="bool"), - client_id=dict(type="int", default=1), - org=dict(type="str"), - ou=dict(type="list", elements="str"), - end_user_key_storage_agreement=dict(type="bool"), - tracking_info=dict(type="str"), - requester_name=dict(type="str", required=True), - requester_email=dict(type="str", required=True), - requester_phone=dict(type="str", required=True), - additional_emails=dict(type="list", elements="str"), - custom_fields=dict(type="dict", default=None, options=custom_fields_spec()), - cert_expiry=dict(type="str"), - cert_lifetime=dict(type="str", choices=["P1Y", "P2Y", "P3Y"]), - ) + }, + "csr": {"type": "str"}, + "subject_alt_name": {"type": "list", "elements": "str"}, + "eku": { + "type": "str", + "choices": ["SERVER_AUTH", "CLIENT_AUTH", "SERVER_AND_CLIENT_AUTH"], + }, + "ct_log": {"type": "bool"}, + "client_id": {"type": "int", "default": 1}, + "org": {"type": "str"}, + "ou": {"type": "list", "elements": "str"}, + "end_user_key_storage_agreement": {"type": "bool"}, + "tracking_info": {"type": "str"}, + "requester_name": {"type": "str", "required": True}, + "requester_email": {"type": "str", "required": True}, + "requester_phone": {"type": "str", "required": True}, + "additional_emails": {"type": "list", "elements": "str"}, + "custom_fields": {"type": "dict", "options": custom_fields_spec()}, + "cert_expiry": {"type": "str"}, + "cert_lifetime": {"type": "str", "choices": ["P1Y", "P2Y", "P3Y"]}, + } def main() -> t.NoReturn: diff --git a/plugins/modules/ecs_domain.py b/plugins/modules/ecs_domain.py index 0a8aa175..513117da 100644 --- a/plugins/modules/ecs_domain.py +++ b/plugins/modules/ecs_domain.py @@ -268,6 +268,7 @@ class EcsDomain: # method of the domain, we'll use module.params when requesting a new # one, in case the verification method has changed. self.verification_method = None + self.client_id: str | None = None # Instantiate the ECS client and then try a no-op connection to verify credentials are valid try: @@ -321,18 +322,15 @@ class EcsDomain: clientId=module.params["client_id"], domain=module.params["domain_name"] ) self.set_domain_details(domain_details) - if ( - self.domain_status != "APPROVED" - and self.domain_status != "INITIAL_VERIFICATION" - and self.domain_status != "RE_VERIFICATION" + if self.domain_status not in ( + "APPROVED", + "INITIAL_VERIFICATION", + "RE_VERIFICATION", ): return False # If domain verification is in process, we want to return the random values and treat it as a valid. - if ( - self.domain_status == "INITIAL_VERIFICATION" - or self.domain_status == "RE_VERIFICATION" - ): + if self.domain_status in ("INITIAL_VERIFICATION", "RE_VERIFICATION"): # Unless the verification method has changed, in which case we need to do a reverify request. if self.verification_method != module.params["verification_method"]: return False @@ -383,7 +381,7 @@ class EcsDomain: module.params["verification_method"] == "dns" or module.params["verification_method"] == "web_server" ): - for i in range(4): + for _i in range(4): # Check both that random values are now available, and that they're different than were populated by previous 'check' if module.params["verification_method"] == "dns": if ( @@ -445,14 +443,16 @@ class EcsDomain: def ecs_domain_argument_spec() -> dict[str, dict[str, t.Any]]: - return dict( - client_id=dict(type="int", default=1), - domain_name=dict(type="str", required=True), - verification_method=dict( - type="str", required=True, choices=["dns", "email", "manual", "web_server"] - ), - verification_email=dict(type="str"), - ) + return { + "client_id": {"type": "int", "default": 1}, + "domain_name": {"type": "str", "required": True}, + "verification_method": { + "type": "str", + "required": True, + "choices": ["dns", "email", "manual", "web_server"], + }, + "verification_email": {"type": "str"}, + } def main() -> t.NoReturn: diff --git a/plugins/modules/get_certificate.py b/plugins/modules/get_certificate.py index f96778be..22d19ef8 100644 --- a/plugins/modules/get_certificate.py +++ b/plugins/modules/get_certificate.py @@ -324,23 +324,25 @@ def send_starttls_packet(sock: socket, server_type: t.Literal["mysql"]) -> None: def main() -> t.NoReturn: module = AnsibleModule( - argument_spec=dict( - ca_cert=dict(type="path"), - host=dict(type="str", required=True), - port=dict(type="int", required=True), - proxy_host=dict(type="str"), - proxy_port=dict(type="int", default=8080), - server_name=dict(type="str"), - timeout=dict(type="int", default=10), - select_crypto_backend=dict( - type="str", choices=["auto", "cryptography"], default="auto" - ), - starttls=dict(type="str", choices=["mysql"]), - ciphers=dict(type="list", elements="str"), - asn1_base64=dict(type="bool", default=True), - tls_ctx_options=dict(type="list", elements="raw"), - get_certificate_chain=dict(type="bool", default=False), - ), + argument_spec={ + "ca_cert": {"type": "path"}, + "host": {"type": "str", "required": True}, + "port": {"type": "int", "required": True}, + "proxy_host": {"type": "str"}, + "proxy_port": {"type": "int", "default": 8080}, + "server_name": {"type": "str"}, + "timeout": {"type": "int", "default": 10}, + "select_crypto_backend": { + "type": "str", + "default": "auto", + "choices": ["auto", "cryptography"], + }, + "starttls": {"type": "str", "choices": ["mysql"]}, + "ciphers": {"type": "list", "elements": "str"}, + "asn1_base64": {"type": "bool", "default": True}, + "tls_ctx_options": {"type": "list", "elements": "raw"}, + "get_certificate_chain": {"type": "bool", "default": False}, + }, ) ca_cert: str | None = module.params.get("ca_cert") @@ -444,7 +446,8 @@ def main() -> t.NoReturn: try: # Add the int value of the item to ctx options - ctx.options |= tls_ctx_option_int + # (pylint does not yet notice that module.fail_json cannot return) + ctx.options |= tls_ctx_option_int # pylint: disable=possibly-used-before-assignment except Exception: module.fail_json( msg=f"Failed to add {tls_ctx_option_str or tls_ctx_option_int} to CTX options" @@ -465,9 +468,16 @@ def main() -> t.NoReturn: def _convert_chain(chain): if not chain: return [] - return [c.public_bytes(ssl._ssl.ENCODING_DER) for c in chain] + return [ + c.public_bytes( + ssl._ssl.ENCODING_DER # pylint: disable=protected-access + ) + for c in chain + ] - ssl_obj = tls_sock._sslobj # This is of type ssl._ssl._SSLSocket + ssl_obj = ( + tls_sock._sslobj # pylint: disable=protected-access + ) # This is of type ssl._ssl._SSLSocket verified_der_chain = _convert_chain(ssl_obj.get_verified_chain()) unverified_der_chain = _convert_chain(ssl_obj.get_unverified_chain()) else: @@ -482,7 +492,9 @@ def main() -> t.NoReturn: ( c if isinstance(c, bytes) - else c.public_bytes(ssl._ssl.ENCODING_DER) + else c.public_bytes( + ssl._ssl.ENCODING_DER # pylint: disable=protected-access + ) ) for c in chain ] diff --git a/plugins/modules/luks_device.py b/plugins/modules/luks_device.py index f8dcb7f7..6b98ede1 100644 --- a/plugins/modules/luks_device.py +++ b/plugins/modules/luks_device.py @@ -500,21 +500,21 @@ class Handler: def get_device_by_uuid(self, uuid: str | None) -> str | None: """Returns the device that holds UUID passed by user""" - self._blkid_bin = self._module.get_bin_path("blkid", True) + blkid_bin = self._module.get_bin_path("blkid", True) if uuid is None: return None - rc, stdout, dummy = self._run_command([self._blkid_bin, "--uuid", uuid]) + rc, stdout, dummy = self._run_command([blkid_bin, "--uuid", uuid]) if rc != 0: return None return stdout.strip() def get_device_by_label(self, label: str) -> str | None: """Returns the device that holds label passed by user""" - self._blkid_bin = self._module.get_bin_path("blkid", True) + blkid_bin = self._module.get_bin_path("blkid", True) label = self._module.params["label"] if label is None: return None - rc, stdout, dummy = self._run_command([self._blkid_bin, "--label", label]) + rc, stdout, dummy = self._run_command([blkid_bin, "--label", label]) if rc != 0: return None return stdout.strip() @@ -536,7 +536,7 @@ class Handler: class CryptHandler(Handler): def __init__(self, module: AnsibleModule) -> None: - super(CryptHandler, self).__init__(module) + super().__init__(module) self._cryptsetup_bin = self._module.get_bin_path("cryptsetup", True) def get_container_name_by_device(self, device: str) -> str | None: @@ -722,7 +722,7 @@ class CryptHandler(Handler): except Exception as exc: raise ValueError( f"Error while wiping LUKS container signatures for {device}: {exc}" - ) + ) from exc def run_luks_add_key( self, @@ -869,7 +869,7 @@ class CryptHandler(Handler): class ConditionsHandler(Handler): def __init__(self, module: AnsibleModule, crypthandler: CryptHandler) -> None: - super(ConditionsHandler, self).__init__(module) + super().__init__(module) self._crypthandler = crypthandler self.device = self.get_device_name() @@ -953,6 +953,7 @@ class ConditionsHandler(Handler): ) or self._module.params["state"] != "closed": # conditions for close not fulfilled return False + luks_is_open = False if self.device is not None: name = self._crypthandler.get_container_name_by_device(self.device) @@ -1084,52 +1085,58 @@ class ConditionsHandler(Handler): def run_module() -> t.NoReturn: # available arguments/parameters that a user can pass - module_args = dict( - state=dict( - type="str", - default="present", - choices=["present", "absent", "opened", "closed"], - ), - device=dict(type="str"), - name=dict(type="str"), - keyfile=dict(type="path"), - new_keyfile=dict(type="path"), - remove_keyfile=dict(type="path"), - passphrase=dict(type="str", no_log=True), - new_passphrase=dict(type="str", no_log=True), - remove_passphrase=dict(type="str", no_log=True), - passphrase_encoding=dict( - type="str", default="text", choices=["text", "base64"], no_log=False - ), - keyslot=dict(type="int", no_log=False), - new_keyslot=dict(type="int", no_log=False), - remove_keyslot=dict(type="int", no_log=False), - force_remove_last_key=dict(type="bool", default=False), - keysize=dict(type="int"), - label=dict(type="str"), - uuid=dict(type="str"), - type=dict(type="str", choices=["luks1", "luks2"]), - cipher=dict(type="str"), - hash=dict(type="str"), - pbkdf=dict( - type="dict", - options=dict( - iteration_time=dict(type="float"), - iteration_count=dict(type="int"), - algorithm=dict(type="str", choices=["argon2i", "argon2id", "pbkdf2"]), - memory=dict(type="int"), - parallel=dict(type="int"), - ), - mutually_exclusive=[("iteration_time", "iteration_count")], - ), - sector_size=dict(type="int"), - perf_same_cpu_crypt=dict(type="bool", default=False), - perf_submit_from_crypt_cpus=dict(type="bool", default=False), - perf_no_read_workqueue=dict(type="bool", default=False), - perf_no_write_workqueue=dict(type="bool", default=False), - persistent=dict(type="bool", default=False), - allow_discards=dict(type="bool", default=False), - ) + module_args = { + "state": { + "type": "str", + "default": "present", + "choices": ["present", "absent", "opened", "closed"], + }, + "device": {"type": "str"}, + "name": {"type": "str"}, + "keyfile": {"type": "path"}, + "new_keyfile": {"type": "path"}, + "remove_keyfile": {"type": "path"}, + "passphrase": {"type": "str", "no_log": True}, + "new_passphrase": {"type": "str", "no_log": True}, + "remove_passphrase": {"type": "str", "no_log": True}, + "passphrase_encoding": { + "type": "str", + "default": "text", + "choices": ["text", "base64"], + "no_log": False, + }, + "keyslot": {"type": "int", "no_log": False}, + "new_keyslot": {"type": "int", "no_log": False}, + "remove_keyslot": {"type": "int", "no_log": False}, + "force_remove_last_key": {"type": "bool", "default": False}, + "keysize": {"type": "int"}, + "label": {"type": "str"}, + "uuid": {"type": "str"}, + "type": {"type": "str", "choices": ["luks1", "luks2"]}, + "cipher": {"type": "str"}, + "hash": {"type": "str"}, + "pbkdf": { + "type": "dict", + "options": { + "iteration_time": {"type": "float"}, + "iteration_count": {"type": "int"}, + "algorithm": { + "type": "str", + "choices": ["argon2i", "argon2id", "pbkdf2"], + }, + "memory": {"type": "int"}, + "parallel": {"type": "int"}, + }, + "mutually_exclusive": [("iteration_time", "iteration_count")], + }, + "sector_size": {"type": "int"}, + "perf_same_cpu_crypt": {"type": "bool", "default": False}, + "perf_submit_from_crypt_cpus": {"type": "bool", "default": False}, + "perf_no_read_workqueue": {"type": "bool", "default": False}, + "perf_no_write_workqueue": {"type": "bool", "default": False}, + "persistent": {"type": "bool", "default": False}, + "allow_discards": {"type": "bool", "default": False}, + } mutually_exclusive = [ ("keyfile", "passphrase"), @@ -1145,9 +1152,12 @@ def run_module() -> t.NoReturn: supports_check_mode=True, mutually_exclusive=mutually_exclusive, ) - module.run_command_environ_update = dict( - LANG="C", LC_ALL="C", LC_MESSAGES="C", LC_CTYPE="C" - ) + module.run_command_environ_update = { + "LANG": "C", + "LC_ALL": "C", + "LC_MESSAGES": "C", + "LC_CTYPE": "C", + } if module.params["device"] is not None: try: diff --git a/plugins/modules/openssh_cert.py b/plugins/modules/openssh_cert.py index 483b399b..19929a19 100644 --- a/plugins/modules/openssh_cert.py +++ b/plugins/modules/openssh_cert.py @@ -304,7 +304,7 @@ from ansible_collections.community.crypto.plugins.module_utils._version import ( class Certificate(OpensshModule): def __init__(self, module: AnsibleModule) -> None: - super(Certificate, self).__init__(module=module) + super().__init__(module=module) self.ssh_keygen = KeygenCommand(self.module) self.identifier: str = self.module.params["identifier"] or "" @@ -406,19 +406,18 @@ class Certificate(OpensshModule): def _should_generate(self) -> bool: if self.regenerate == "never": return self.original_data is None - elif self.regenerate == "fail": + if self.regenerate == "fail": if self.original_data and not self._is_fully_valid(): self.module.fail_json( msg="Certificate does not match the provided options.", cert=get_cert_dict(self.original_data), ) return self.original_data is None - elif self.regenerate == "partial_idempotence": + if self.regenerate == "partial_idempotence": return self.original_data is None or not self._is_partially_valid() - elif self.regenerate == "full_idempotence": + if self.regenerate == "full_idempotence": return self.original_data is None or not self._is_fully_valid() - else: - return True + return True def _is_fully_valid(self) -> bool: if self.original_data is None: @@ -542,10 +541,10 @@ class Certificate(OpensshModule): serial_number=self.serial_number, signature_algorithm=self.signature_algorithm, signing_key_path=self.signing_key, - type=self.type, + cert_type=self.type, time_parameters=self.time_parameters, use_agent=self.use_agent, - environ_update=dict(TZ="UTC"), + environ_update={"TZ": "UTC"}, check_rc=True, ) @@ -625,38 +624,43 @@ def get_cert_dict(data: OpensshCertificate | None) -> dict[str, t.Any]: def main() -> t.NoReturn: module = AnsibleModule( - argument_spec=dict( - force=dict(type="bool", default=False), - identifier=dict(type="str"), - options=dict(type="list", elements="str"), - path=dict(type="path", required=True), - pkcs11_provider=dict(type="str"), - principals=dict(type="list", elements="str"), - public_key=dict(type="path"), - regenerate=dict( - type="str", - default="partial_idempotence", - choices=[ + argument_spec={ + "force": {"type": "bool", "default": False}, + "identifier": {"type": "str"}, + "options": {"type": "list", "elements": "str"}, + "path": {"type": "path", "required": True}, + "pkcs11_provider": {"type": "str"}, + "principals": {"type": "list", "elements": "str"}, + "public_key": {"type": "path"}, + "regenerate": { + "type": "str", + "default": "partial_idempotence", + "choices": [ "never", "fail", "partial_idempotence", "full_idempotence", "always", ], - ), - signature_algorithm=dict( - type="str", choices=["ssh-rsa", "rsa-sha2-256", "rsa-sha2-512"] - ), - signing_key=dict(type="path"), - serial_number=dict(type="int"), - state=dict(type="str", default="present", choices=["absent", "present"]), - type=dict(type="str", choices=["host", "user"]), - use_agent=dict(type="bool", default=False), - valid_at=dict(type="str"), - valid_from=dict(type="str"), - valid_to=dict(type="str"), - ignore_timestamps=dict(type="bool", default=False), - ), + }, + "signature_algorithm": { + "type": "str", + "choices": ["ssh-rsa", "rsa-sha2-256", "rsa-sha2-512"], + }, + "signing_key": {"type": "path"}, + "serial_number": {"type": "int"}, + "state": { + "type": "str", + "default": "present", + "choices": ["absent", "present"], + }, + "type": {"type": "str", "choices": ["host", "user"]}, + "use_agent": {"type": "bool", "default": False}, + "valid_at": {"type": "str"}, + "valid_from": {"type": "str"}, + "valid_to": {"type": "str"}, + "ignore_timestamps": {"type": "bool", "default": False}, + }, supports_check_mode=True, add_file_common_args=True, required_if=[ diff --git a/plugins/modules/openssh_keypair.py b/plugins/modules/openssh_keypair.py index e5b53204..22a67759 100644 --- a/plugins/modules/openssh_keypair.py +++ b/plugins/modules/openssh_keypair.py @@ -209,41 +209,45 @@ from ansible_collections.community.crypto.plugins.module_utils._openssh.backends def main() -> t.NoReturn: module = AnsibleModule( - argument_spec=dict( - state=dict(type="str", default="present", choices=["present", "absent"]), - size=dict(type="int"), - type=dict( - type="str", - default="rsa", - choices=["rsa", "dsa", "rsa1", "ecdsa", "ed25519"], - ), - force=dict(type="bool", default=False), - path=dict(type="path", required=True), - comment=dict(type="str"), - regenerate=dict( - type="str", - default="partial_idempotence", - choices=[ + argument_spec={ + "state": { + "type": "str", + "default": "present", + "choices": ["present", "absent"], + }, + "size": {"type": "int"}, + "type": { + "type": "str", + "default": "rsa", + "choices": ["rsa", "dsa", "rsa1", "ecdsa", "ed25519"], + }, + "force": {"type": "bool", "default": False}, + "path": {"type": "path", "required": True}, + "comment": {"type": "str"}, + "regenerate": { + "type": "str", + "default": "partial_idempotence", + "choices": [ "never", "fail", "partial_idempotence", "full_idempotence", "always", ], - ), - passphrase=dict(type="str", no_log=True), - private_key_format=dict( - type="str", - default="auto", - no_log=False, - choices=["auto", "pkcs1", "pkcs8", "ssh"], - ), - backend=dict( - type="str", - default="auto", - choices=["auto", "cryptography", "opensshbin"], - ), - ), + }, + "passphrase": {"type": "str", "no_log": True}, + "private_key_format": { + "type": "str", + "default": "auto", + "no_log": False, + "choices": ["auto", "pkcs1", "pkcs8", "ssh"], + }, + "backend": { + "type": "str", + "default": "auto", + "choices": ["auto", "cryptography", "opensshbin"], + }, + }, supports_check_mode=True, add_file_common_args=True, ) diff --git a/plugins/modules/openssl_csr.py b/plugins/modules/openssl_csr.py index bade73b8..ef1c1498 100644 --- a/plugins/modules/openssl_csr.py +++ b/plugins/modules/openssl_csr.py @@ -269,7 +269,7 @@ class CertificateSigningRequestModule(OpenSSLObject): def __init__( self, module: AnsibleModule, module_backend: CertificateSigningRequestBackend ) -> None: - super(CertificateSigningRequestModule, self).__init__( + super().__init__( path=module.params["path"], state=module.params["state"], force=module.params["force"], @@ -308,7 +308,7 @@ class CertificateSigningRequestModule(OpenSSLObject): self.module_backend.set_existing(csr_bytes=None) if self.backup and not self.check_mode: self.backup_file = module.backup_local(self.path) - super(CertificateSigningRequestModule, self).remove(module) + super().remove(module) def dump(self) -> dict[str, t.Any]: """Serialize the object into a dictionary.""" @@ -327,13 +327,17 @@ class CertificateSigningRequestModule(OpenSSLObject): def main() -> t.NoReturn: argument_spec = get_csr_argument_spec() argument_spec.argument_spec.update( - dict( - state=dict(type="str", default="present", choices=["absent", "present"]), - force=dict(type="bool", default=False), - path=dict(type="path", required=True), - backup=dict(type="bool", default=False), - return_content=dict(type="bool", default=False), - ) + { + "state": { + "type": "str", + "default": "present", + "choices": ["absent", "present"], + }, + "force": {"type": "bool", "default": False}, + "path": {"type": "path", "required": True}, + "backup": {"type": "bool", "default": False}, + "return_content": {"type": "bool", "default": False}, + } ) argument_spec.required_if.extend( [("state", "present", rof, True) for rof in argument_spec.required_one_of] diff --git a/plugins/modules/openssl_csr_info.py b/plugins/modules/openssl_csr_info.py index 5ce6140a..f4c41456 100644 --- a/plugins/modules/openssl_csr_info.py +++ b/plugins/modules/openssl_csr_info.py @@ -321,16 +321,20 @@ from ansible_collections.community.crypto.plugins.module_utils._crypto.module_ba def main() -> t.NoReturn: module = AnsibleModule( - argument_spec=dict( - path=dict(type="path"), - content=dict(type="str"), - name_encoding=dict( - type="str", default="ignore", choices=["ignore", "idna", "unicode"] - ), - select_crypto_backend=dict( - type="str", default="auto", choices=["auto", "cryptography"] - ), - ), + argument_spec={ + "path": {"type": "path"}, + "content": {"type": "str"}, + "name_encoding": { + "type": "str", + "default": "ignore", + "choices": ["ignore", "idna", "unicode"], + }, + "select_crypto_backend": { + "type": "str", + "default": "auto", + "choices": ["auto", "cryptography"], + }, + }, required_one_of=(["path", "content"],), mutually_exclusive=(["path", "content"],), supports_check_mode=True, diff --git a/plugins/modules/openssl_csr_pipe.py b/plugins/modules/openssl_csr_pipe.py index d8619ca6..8ed7a441 100644 --- a/plugins/modules/openssl_csr_pipe.py +++ b/plugins/modules/openssl_csr_pipe.py @@ -178,9 +178,9 @@ class CertificateSigningRequestModule: def main() -> t.NoReturn: argument_spec = get_csr_argument_spec() argument_spec.argument_spec.update( - dict( - content=dict(type="str"), - ) + { + "content": {"type": "str"}, + } ) module = argument_spec.create_ansible_module( supports_check_mode=True, diff --git a/plugins/modules/openssl_dhparam.py b/plugins/modules/openssl_dhparam.py index 89846819..cbaf5b3b 100644 --- a/plugins/modules/openssl_dhparam.py +++ b/plugins/modules/openssl_dhparam.py @@ -252,7 +252,7 @@ class DHParameterBase: class DHParameterAbsent(DHParameterBase): def __init__(self, module: AnsibleModule) -> None: - super(DHParameterAbsent, self).__init__(module) + super().__init__(module) def _do_generate(self, module: AnsibleModule) -> None: """Actually generate the DH params.""" @@ -265,7 +265,7 @@ class DHParameterAbsent(DHParameterBase): class DHParameterOpenSSL(DHParameterBase): def __init__(self, module: AnsibleModule) -> None: - super(DHParameterOpenSSL, self).__init__(module) + super().__init__(module) self.openssl_bin = module.get_bin_path("openssl", True) def _do_generate(self, module: AnsibleModule) -> None: @@ -320,7 +320,7 @@ class DHParameterOpenSSL(DHParameterBase): class DHParameterCryptography(DHParameterBase): def __init__(self, module: AnsibleModule) -> None: - super(DHParameterCryptography, self).__init__(module) + super().__init__(module) def _do_generate(self, module: AnsibleModule) -> None: """Actually generate the DH params.""" @@ -359,17 +359,23 @@ def main() -> t.NoReturn: """Main function""" module = AnsibleModule( - argument_spec=dict( - state=dict(type="str", default="present", choices=["absent", "present"]), - size=dict(type="int", default=4096), - force=dict(type="bool", default=False), - path=dict(type="path", required=True), - backup=dict(type="bool", default=False), - select_crypto_backend=dict( - type="str", default="auto", choices=["auto", "cryptography", "openssl"] - ), - return_content=dict(type="bool", default=False), - ), + argument_spec={ + "state": { + "type": "str", + "default": "present", + "choices": ["absent", "present"], + }, + "size": {"type": "int", "default": 4096}, + "force": {"type": "bool", "default": False}, + "path": {"type": "path", "required": True}, + "backup": {"type": "bool", "default": False}, + "select_crypto_backend": { + "type": "str", + "default": "auto", + "choices": ["auto", "cryptography", "openssl"], + }, + "return_content": {"type": "bool", "default": False}, + }, supports_check_mode=True, add_file_common_args=True, ) diff --git a/plugins/modules/openssl_pkcs12.py b/plugins/modules/openssl_pkcs12.py index 3c257b30..20450258 100644 --- a/plugins/modules/openssl_pkcs12.py +++ b/plugins/modules/openssl_pkcs12.py @@ -368,7 +368,7 @@ class Pkcs(OpenSSLObject): path: str def __init__(self, module: AnsibleModule, iter_size_default: int = 2048) -> None: - super(Pkcs, self).__init__( + super().__init__( path=module.params["path"], state=module.params["state"], force=module.params["force"], @@ -413,7 +413,7 @@ class Pkcs(OpenSSLObject): with open(self.certificate_path, "rb") as fh: self.certificate_content = fh.read() except (IOError, OSError) as exc: - raise PkcsError(exc) + raise PkcsError(exc) from exc elif certificate_content is not None: self.certificate_content = to_bytes(certificate_content) @@ -423,7 +423,7 @@ class Pkcs(OpenSSLObject): with open(self.privatekey_path, "rb") as fh: self.privatekey_content = fh.read() except (IOError, OSError) as exc: - raise PkcsError(exc) + raise PkcsError(exc) from exc elif privatekey_content is not None: self.privatekey_content = to_bytes(privatekey_content) @@ -480,11 +480,9 @@ class Pkcs(OpenSSLObject): def _get_friendly_name(self, pkcs12: PKCS12) -> bytes | None: pass - def check(self, module: AnsibleModule, perms_required: bool = True) -> bool: + def check(self, module: AnsibleModule, *, perms_required: bool = True) -> bool: """Ensure the resource is in its desired state.""" - state_and_perms = super(Pkcs, self).check( - module=module, perms_required=perms_required - ) + state_and_perms = super().check(module=module, perms_required=perms_required) def _check_pkey_passphrase() -> bool: if self.privatekey_passphrase: @@ -599,7 +597,7 @@ class Pkcs(OpenSSLObject): def remove(self, module: AnsibleModule) -> None: if self.backup: self.backup_file = module.backup_local(self.path) - super(Pkcs, self).remove(module) + super().remove(module) def parse(self) -> tuple[ bytes | None, @@ -616,7 +614,7 @@ class Pkcs(OpenSSLObject): pkcs12_content = pkcs12_fh.read() return self.parse_bytes(pkcs12_content) except IOError as exc: - raise PkcsError(exc) + raise PkcsError(exc) from exc def generate(self, module: AnsibleModule) -> None: # Empty method because OpenSSLObject wants this @@ -635,7 +633,7 @@ class Pkcs(OpenSSLObject): class PkcsCryptography(Pkcs): def __init__(self, module: AnsibleModule) -> None: - super(PkcsCryptography, self).__init__(module, iter_size_default=50000) + super().__init__(module, iter_size_default=50000) if ( self.encryption_level == "compatibility2022" and not CRYPTOGRAPHY_HAS_COMPATIBILITY2022 @@ -656,7 +654,7 @@ class PkcsCryptography(Pkcs): passphrase=self.privatekey_passphrase, ) except OpenSSLBadPassphraseError as exc: - raise PkcsError(exc) + raise PkcsError(exc) from exc cert = None if self.certificate_content: @@ -725,7 +723,7 @@ class PkcsCryptography(Pkcs): return (pkey, crt, other_certs, friendly_name) except ValueError as exc: - raise PkcsError(exc) + raise PkcsError(exc) from exc def _dump_privatekey(self, pkcs12: PKCS12) -> bytes | None: return ( @@ -759,39 +757,49 @@ def select_backend(module: AnsibleModule) -> Pkcs: def main() -> t.NoReturn: - argument_spec = dict( - action=dict(type="str", default="export", choices=["export", "parse"]), - other_certificates=dict( - type="list", elements="path", aliases=["ca_certificates"] - ), - other_certificates_parse_all=dict(type="bool", default=False), - other_certificates_content=dict(type="list", elements="str"), - certificate_path=dict(type="path"), - certificate_content=dict(type="str"), - force=dict(type="bool", default=False), - friendly_name=dict(type="str", aliases=["name"]), - encryption_level=dict( - type="str", choices=["auto", "compatibility2022"], default="auto" - ), - iter_size=dict(type="int"), - maciter_size=dict( - type="int", - removed_in_version="4.0.0", - removed_from_collection="community.crypto", - ), - passphrase=dict(type="str", no_log=True), - path=dict(type="path", required=True), - privatekey_passphrase=dict(type="str", no_log=True), - privatekey_path=dict(type="path"), - privatekey_content=dict(type="str", no_log=True), - state=dict(type="str", default="present", choices=["absent", "present"]), - src=dict(type="path"), - backup=dict(type="bool", default=False), - return_content=dict(type="bool", default=False), - select_crypto_backend=dict( - type="str", default="auto", choices=["auto", "cryptography"] - ), - ) + argument_spec = { + "action": {"type": "str", "default": "export", "choices": ["export", "parse"]}, + "other_certificates": { + "type": "list", + "elements": "path", + "aliases": ["ca_certificates"], + }, + "other_certificates_parse_all": {"type": "bool", "default": False}, + "other_certificates_content": {"type": "list", "elements": "str"}, + "certificate_path": {"type": "path"}, + "certificate_content": {"type": "str"}, + "force": {"type": "bool", "default": False}, + "friendly_name": {"type": "str", "aliases": ["name"]}, + "encryption_level": { + "type": "str", + "choices": ["auto", "compatibility2022"], + "default": "auto", + }, + "iter_size": {"type": "int"}, + "maciter_size": { + "type": "int", + "removed_in_version": "4.0.0", + "removed_from_collection": "community.crypto", + }, + "passphrase": {"type": "str", "no_log": True}, + "path": {"type": "path", "required": True}, + "privatekey_passphrase": {"type": "str", "no_log": True}, + "privatekey_path": {"type": "path"}, + "privatekey_content": {"type": "str", "no_log": True}, + "state": { + "type": "str", + "default": "present", + "choices": ["absent", "present"], + }, + "src": {"type": "path"}, + "backup": {"type": "bool", "default": False}, + "return_content": {"type": "bool", "default": False}, + "select_crypto_backend": { + "type": "str", + "default": "auto", + "choices": ["auto", "cryptography"], + }, + } required_if = [ ["action", "parse", ["src"]], @@ -837,7 +845,7 @@ def main() -> t.NoReturn: pkcs12.write(module, pkcs12_content, 0o600) changed = True else: - pkey, cert, other_certs, friendly_name = pkcs12.parse() + pkey, cert, other_certs, _friendly_name = pkcs12.parse() dump_content = "".join( [ to_native(pem) diff --git a/plugins/modules/openssl_privatekey.py b/plugins/modules/openssl_privatekey.py index 671dd90e..0627042e 100644 --- a/plugins/modules/openssl_privatekey.py +++ b/plugins/modules/openssl_privatekey.py @@ -185,7 +185,7 @@ class PrivateKeyModule(OpenSSLObject): def __init__( self, module: AnsibleModule, module_backend: PrivateKeyBackend ) -> None: - super(PrivateKeyModule, self).__init__( + super().__init__( path=module.params["path"], state=module.params["state"], force=module.params["force"], @@ -216,8 +216,6 @@ class PrivateKeyModule(OpenSSLObject): self.backup_file = module.backup_local(self.path) self.module_backend.generate_private_key() privatekey_data = self.module_backend.get_private_key_data() - if self.return_content: - self.privatekey_bytes = privatekey_data write_file(module=module, content=privatekey_data, default_mode=0o600) self.changed = True elif self.module_backend.needs_conversion(): @@ -227,8 +225,6 @@ class PrivateKeyModule(OpenSSLObject): self.backup_file = module.backup_local(self.path) self.module_backend.convert_private_key() privatekey_data = self.module_backend.get_private_key_data() - if self.return_content: - self.privatekey_bytes = privatekey_data write_file(module=module, content=privatekey_data, default_mode=0o600) self.changed = True @@ -244,7 +240,7 @@ class PrivateKeyModule(OpenSSLObject): self.module_backend.set_existing(privatekey_bytes=None) if self.backup and not self.check_mode: self.backup_file = module.backup_local(self.path) - super(PrivateKeyModule, self).remove(module) + super().remove(module) def dump(self) -> dict[str, t.Any]: """Serialize the object into a dictionary.""" @@ -262,13 +258,17 @@ def main() -> t.NoReturn: argument_spec = get_privatekey_argument_spec() argument_spec.argument_spec.update( - dict( - state=dict(type="str", default="present", choices=["present", "absent"]), - force=dict(type="bool", default=False), - path=dict(type="path", required=True), - backup=dict(type="bool", default=False), - return_content=dict(type="bool", default=False), - ) + { + "state": { + "type": "str", + "default": "present", + "choices": ["present", "absent"], + }, + "force": {"type": "bool", "default": False}, + "path": {"type": "path", "required": True}, + "backup": {"type": "bool", "default": False}, + "return_content": {"type": "bool", "default": False}, + } ) module = argument_spec.create_ansible_module( supports_check_mode=True, diff --git a/plugins/modules/openssl_privatekey_convert.py b/plugins/modules/openssl_privatekey_convert.py index 02b70e60..327ffb7e 100644 --- a/plugins/modules/openssl_privatekey_convert.py +++ b/plugins/modules/openssl_privatekey_convert.py @@ -89,7 +89,7 @@ class PrivateKeyConvertModule(OpenSSLObject): def __init__( self, module: AnsibleModule, module_backend: PrivateKeyConvertBackend ) -> None: - super(PrivateKeyConvertModule, self).__init__( + super().__init__( path=module.params["dest_path"], state="present", force=False, @@ -145,10 +145,10 @@ def main() -> t.NoReturn: argument_spec = get_privatekey_argument_spec() argument_spec.argument_spec.update( - dict( - dest_path=dict(type="path", required=True), - backup=dict(type="bool", default=False), - ) + { + "dest_path": {"type": "path", "required": True}, + "backup": {"type": "bool", "default": False}, + } ) module = argument_spec.create_ansible_module( supports_check_mode=True, diff --git a/plugins/modules/openssl_privatekey_info.py b/plugins/modules/openssl_privatekey_info.py index d75590d3..2f9c7254 100644 --- a/plugins/modules/openssl_privatekey_info.py +++ b/plugins/modules/openssl_privatekey_info.py @@ -215,26 +215,28 @@ from ansible_collections.community.crypto.plugins.module_utils._crypto.module_ba def main() -> t.NoReturn: module = AnsibleModule( - argument_spec=dict( - path=dict(type="path"), - content=dict(type="str", no_log=True), - passphrase=dict(type="str", no_log=True), - return_private_key_data=dict(type="bool", default=False), - check_consistency=dict(type="bool", default=False), - select_crypto_backend=dict( - type="str", default="auto", choices=["auto", "cryptography"] - ), - ), + argument_spec={ + "path": {"type": "path"}, + "content": {"type": "str", "no_log": True}, + "passphrase": {"type": "str", "no_log": True}, + "return_private_key_data": {"type": "bool", "default": False}, + "check_consistency": {"type": "bool", "default": False}, + "select_crypto_backend": { + "type": "str", + "default": "auto", + "choices": ["auto", "cryptography"], + }, + }, required_one_of=(["path", "content"],), mutually_exclusive=(["path", "content"],), supports_check_mode=True, ) - result = dict( - can_load_key=False, - can_parse_key=False, - key_is_consistent=None, - ) + result = { + "can_load_key": False, + "can_parse_key": False, + "key_is_consistent": None, + } if module.params["content"] is not None: data = module.params["content"].encode("utf-8") diff --git a/plugins/modules/openssl_publickey.py b/plugins/modules/openssl_publickey.py index 195b663e..f6072468 100644 --- a/plugins/modules/openssl_publickey.py +++ b/plugins/modules/openssl_publickey.py @@ -233,7 +233,7 @@ class PublicKeyError(OpenSSLObjectError): class PublicKey(OpenSSLObject): def __init__(self, module: AnsibleModule) -> None: - super(PublicKey, self).__init__( + super().__init__( path=module.params["path"], state=module.params["state"], force=module.params["force"], @@ -287,11 +287,10 @@ class PublicKey(OpenSSLObject): crypto_serialization.Encoding.OpenSSH, crypto_serialization.PublicFormat.OpenSSH, ) - else: - return self.privatekey.public_key().public_bytes( - crypto_serialization.Encoding.PEM, - crypto_serialization.PublicFormat.SubjectPublicKeyInfo, - ) + return self.privatekey.public_key().public_bytes( + crypto_serialization.Encoding.PEM, + crypto_serialization.PublicFormat.SubjectPublicKeyInfo, + ) def generate(self, module: AnsibleModule) -> None: """Generate the public key.""" @@ -316,9 +315,9 @@ class PublicKey(OpenSSLObject): self.changed = True except OpenSSLBadPassphraseError as exc: - raise PublicKeyError(exc) + raise PublicKeyError(exc) from exc except (IOError, OSError) as exc: - raise PublicKeyError(exc) + raise PublicKeyError(exc) from exc self.fingerprint = get_fingerprint( path=self.privatekey_path, @@ -331,12 +330,10 @@ class PublicKey(OpenSSLObject): elif module.set_fs_attributes_if_different(file_args, False): self.changed = True - def check(self, module: AnsibleModule, perms_required: bool = True) -> bool: + def check(self, module: AnsibleModule, *, perms_required: bool = True) -> bool: """Ensure the resource is in its desired state.""" - state_and_perms = super(PublicKey, self).check( - module=module, perms_required=perms_required - ) + state_and_perms = super().check(module=module, perms_required=perms_required) def _check_privatekey() -> bool: if self.privatekey_path is not None and not os.path.exists( @@ -374,7 +371,7 @@ class PublicKey(OpenSSLObject): try: desired_publickey = self._create_publickey(module) except OpenSSLBadPassphraseError as exc: - raise PublicKeyError(exc) + raise PublicKeyError(exc) from exc return publickey_content == desired_publickey @@ -386,7 +383,7 @@ class PublicKey(OpenSSLObject): def remove(self, module: AnsibleModule) -> None: if self.backup: self.backup_file = module.backup_local(self.path) - super(PublicKey, self).remove(module) + super().remove(module) def dump(self) -> dict[str, t.Any]: """Serialize the object into a dictionary.""" @@ -409,10 +406,10 @@ class PublicKey(OpenSSLObject): self.publickey_bytes.decode("utf-8") if self.publickey_bytes else None ) - result["diff"] = dict( - before=self.diff_before, - after=self.diff_after, - ) + result["diff"] = { + "before": self.diff_before, + "after": self.diff_after, + } return result @@ -420,20 +417,26 @@ class PublicKey(OpenSSLObject): def main() -> t.NoReturn: module = AnsibleModule( - argument_spec=dict( - state=dict(type="str", default="present", choices=["present", "absent"]), - force=dict(type="bool", default=False), - path=dict(type="path", required=True), - privatekey_path=dict(type="path"), - privatekey_content=dict(type="str", no_log=True), - format=dict(type="str", default="PEM", choices=["OpenSSH", "PEM"]), - privatekey_passphrase=dict(type="str", no_log=True), - backup=dict(type="bool", default=False), - select_crypto_backend=dict( - type="str", choices=["auto", "cryptography"], default="auto" - ), - return_content=dict(type="bool", default=False), - ), + argument_spec={ + "state": { + "type": "str", + "default": "present", + "choices": ["present", "absent"], + }, + "force": {"type": "bool", "default": False}, + "path": {"type": "path", "required": True}, + "privatekey_path": {"type": "path"}, + "privatekey_content": {"type": "str", "no_log": True}, + "format": {"type": "str", "default": "PEM", "choices": ["OpenSSH", "PEM"]}, + "privatekey_passphrase": {"type": "str", "no_log": True}, + "backup": {"type": "bool", "default": False}, + "select_crypto_backend": { + "type": "str", + "choices": ["auto", "cryptography"], + "default": "auto", + }, + "return_content": {"type": "bool", "default": False}, + }, supports_check_mode=True, add_file_common_args=True, required_if=[ diff --git a/plugins/modules/openssl_publickey_info.py b/plugins/modules/openssl_publickey_info.py index 4a144e57..98dd4b5b 100644 --- a/plugins/modules/openssl_publickey_info.py +++ b/plugins/modules/openssl_publickey_info.py @@ -166,23 +166,25 @@ from ansible_collections.community.crypto.plugins.module_utils._crypto.module_ba def main() -> t.NoReturn: module = AnsibleModule( - argument_spec=dict( - path=dict(type="path"), - content=dict(type="str", no_log=True), - select_crypto_backend=dict( - type="str", default="auto", choices=["auto", "cryptography"] - ), - ), + argument_spec={ + "path": {"type": "path"}, + "content": {"type": "str", "no_log": True}, + "select_crypto_backend": { + "type": "str", + "default": "auto", + "choices": ["auto", "cryptography"], + }, + }, required_one_of=(["path", "content"],), mutually_exclusive=(["path", "content"],), supports_check_mode=True, ) - result = dict( - can_load_key=False, - can_parse_key=False, - key_is_consistent=None, - ) + result = { + "can_load_key": False, + "can_parse_key": False, + "key_is_consistent": None, + } if module.params["content"] is not None: data = module.params["content"].encode("utf-8") diff --git a/plugins/modules/openssl_signature.py b/plugins/modules/openssl_signature.py index f0a8b418..192bfe8b 100644 --- a/plugins/modules/openssl_signature.py +++ b/plugins/modules/openssl_signature.py @@ -134,7 +134,7 @@ from ansible_collections.community.crypto.plugins.module_utils._crypto.support i class SignatureBase(OpenSSLObject): def __init__(self, module: AnsibleModule) -> None: - super(SignatureBase, self).__init__( + super().__init__( path=module.params["path"], state="present", force=False, @@ -163,7 +163,7 @@ class SignatureBase(OpenSSLObject): class SignatureCryptography(SignatureBase): def __init__(self, module: AnsibleModule) -> None: - super(SignatureCryptography, self).__init__(module) + super().__init__(module) def run(self) -> dict[str, t.Any]: _padding = cryptography.hazmat.primitives.asymmetric.padding.PKCS1v15() @@ -224,20 +224,22 @@ class SignatureCryptography(SignatureBase): return result except Exception as e: - raise OpenSSLObjectError(e) + raise OpenSSLObjectError(e) from e def main() -> t.NoReturn: module = AnsibleModule( - argument_spec=dict( - privatekey_path=dict(type="path"), - privatekey_content=dict(type="str", no_log=True), - privatekey_passphrase=dict(type="str", no_log=True), - path=dict(type="path", required=True), - select_crypto_backend=dict( - type="str", choices=["auto", "cryptography"], default="auto" - ), - ), + argument_spec={ + "privatekey_path": {"type": "path"}, + "privatekey_content": {"type": "str", "no_log": True}, + "privatekey_passphrase": {"type": "str", "no_log": True}, + "path": {"type": "path", "required": True}, + "select_crypto_backend": { + "type": "str", + "choices": ["auto", "cryptography"], + "default": "auto", + }, + }, mutually_exclusive=(["privatekey_path", "privatekey_content"],), required_one_of=(["privatekey_path", "privatekey_content"],), supports_check_mode=True, diff --git a/plugins/modules/openssl_signature_info.py b/plugins/modules/openssl_signature_info.py index 8222e211..22cc2b94 100644 --- a/plugins/modules/openssl_signature_info.py +++ b/plugins/modules/openssl_signature_info.py @@ -123,7 +123,7 @@ from ansible_collections.community.crypto.plugins.module_utils._crypto.support i class SignatureInfoBase(OpenSSLObject): def __init__(self, module: AnsibleModule) -> None: - super(SignatureInfoBase, self).__init__( + super().__init__( path=module.params["path"], state="present", force=False, @@ -152,7 +152,7 @@ class SignatureInfoBase(OpenSSLObject): class SignatureInfoCryptography(SignatureInfoBase): def __init__(self, module: AnsibleModule) -> None: - super(SignatureInfoCryptography, self).__init__(module) + super().__init__(module) def run(self) -> dict[str, t.Any]: _padding = cryptography.hazmat.primitives.asymmetric.padding.PKCS1v15() @@ -229,20 +229,22 @@ class SignatureInfoCryptography(SignatureInfoBase): return result except Exception as e: - raise OpenSSLObjectError(e) + raise OpenSSLObjectError(e) from e def main() -> t.NoReturn: module = AnsibleModule( - argument_spec=dict( - certificate_path=dict(type="path"), - certificate_content=dict(type="str"), - path=dict(type="path", required=True), - signature=dict(type="str", required=True), - select_crypto_backend=dict( - type="str", choices=["auto", "cryptography"], default="auto" - ), - ), + argument_spec={ + "certificate_path": {"type": "path"}, + "certificate_content": {"type": "str"}, + "path": {"type": "path", "required": True}, + "signature": {"type": "str", "required": True}, + "select_crypto_backend": { + "type": "str", + "choices": ["auto", "cryptography"], + "default": "auto", + }, + }, mutually_exclusive=(["certificate_path", "certificate_content"],), required_one_of=(["certificate_path", "certificate_content"],), supports_check_mode=True, diff --git a/plugins/modules/x509_certificate.py b/plugins/modules/x509_certificate.py index 7c5acaa8..c051fb08 100644 --- a/plugins/modules/x509_certificate.py +++ b/plugins/modules/x509_certificate.py @@ -267,7 +267,7 @@ if t.TYPE_CHECKING: class CertificateAbsent(OpenSSLObject): def __init__(self, module: AnsibleModule) -> None: - super(CertificateAbsent, self).__init__( + super().__init__( path=module.params["path"], state=module.params["state"], force=module.params["force"], @@ -284,7 +284,7 @@ class CertificateAbsent(OpenSSLObject): def remove(self, module: AnsibleModule) -> None: if self.backup: self.backup_file = module.backup_local(self.path) - super(CertificateAbsent, self).remove(module) + super().remove(module) def dump(self, check_mode: bool = False) -> dict[str, t.Any]: result = { @@ -305,7 +305,7 @@ class GenericCertificate(OpenSSLObject): """Retrieve a certificate using the given module backend.""" def __init__(self, module: AnsibleModule, module_backend: CertificateBackend): - super(GenericCertificate, self).__init__( + super().__init__( path=module.params["path"], state=module.params["state"], force=module.params["force"], @@ -339,12 +339,10 @@ class GenericCertificate(OpenSSLObject): file_args, self.changed ) - def check(self, module: AnsibleModule, perms_required: bool = True) -> bool: + def check(self, module: AnsibleModule, *, perms_required: bool = True) -> bool: """Ensure the resource is in its desired state.""" return ( - super(GenericCertificate, self).check( - module=module, perms_required=perms_required - ) + super().check(module=module, perms_required=perms_required) and not self.module_backend.needs_regeneration() ) @@ -368,12 +366,16 @@ def main() -> t.NoReturn: add_ownca_provider_to_argument_spec(argument_spec) add_selfsigned_provider_to_argument_spec(argument_spec) argument_spec.argument_spec.update( - dict( - state=dict(type="str", default="present", choices=["present", "absent"]), - path=dict(type="path", required=True), - backup=dict(type="bool", default=False), - return_content=dict(type="bool", default=False), - ) + { + "state": { + "type": "str", + "default": "present", + "choices": ["present", "absent"], + }, + "path": {"type": "path", "required": True}, + "backup": {"type": "bool", "default": False}, + "return_content": {"type": "bool", "default": False}, + } ) argument_spec.required_if.append(("state", "present", ["provider"])) module = argument_spec.create_ansible_module( diff --git a/plugins/modules/x509_certificate_convert.py b/plugins/modules/x509_certificate_convert.py index 2a1f478e..60e6688e 100644 --- a/plugins/modules/x509_certificate_convert.py +++ b/plugins/modules/x509_certificate_convert.py @@ -143,13 +143,13 @@ except ImportError: def parse_certificate( - input: bytes, strict: bool = False + certificate_content: bytes, strict: bool = False ) -> tuple[bytes, t.Literal["pem", "der"], str | None]: input_format: t.Literal["pem", "der"] = ( - "pem" if identify_pem_format(input) else "der" + "pem" if identify_pem_format(certificate_content) else "der" ) if input_format == "pem": - pems = split_pem_list(to_text(input)) + pems = split_pem_list(to_text(certificate_content)) if len(pems) > 1 and strict: raise ValueError( f"The input contains {len(pems)} PEM objects, expecting only one since strict=true" @@ -159,15 +159,15 @@ def parse_certificate( raise ValueError( f"type is {pem_header_type!r}, expecting CERTIFICATE or X509 CERTIFICATE" ) - input = base64.b64decode(content) + certificate_content = base64.b64decode(content) else: pem_header_type = None - return input, input_format, pem_header_type + return certificate_content, input_format, pem_header_type class X509CertificateConvertModule(OpenSSLObject): def __init__(self, module: AnsibleModule) -> None: - super(X509CertificateConvertModule, self).__init__( + super().__init__( path=module.params["dest_path"], state="present", force=False, @@ -287,16 +287,16 @@ class X509CertificateConvertModule(OpenSSLObject): def main() -> t.NoReturn: - argument_spec = dict( - src_path=dict(type="path"), - src_content=dict(type="str"), - src_content_base64=dict(type="bool", default=False), - format=dict(type="str", required=True, choices=["pem", "der"]), - strict=dict(type="bool", default=False), - dest_path=dict(type="path", required=True), - backup=dict(type="bool", default=False), - verify_cert_parsable=dict(type="bool", default=False), - ) + argument_spec = { + "src_path": {"type": "path"}, + "src_content": {"type": "str"}, + "src_content_base64": {"type": "bool", "default": False}, + "format": {"type": "str", "required": True, "choices": ["pem", "der"]}, + "strict": {"type": "bool", "default": False}, + "dest_path": {"type": "path", "required": True}, + "backup": {"type": "bool", "default": False}, + "verify_cert_parsable": {"type": "bool", "default": False}, + } module = AnsibleModule( argument_spec, supports_check_mode=True, diff --git a/plugins/modules/x509_certificate_info.py b/plugins/modules/x509_certificate_info.py index 075e0d73..ae655419 100644 --- a/plugins/modules/x509_certificate_info.py +++ b/plugins/modules/x509_certificate_info.py @@ -410,17 +410,21 @@ from ansible_collections.community.crypto.plugins.module_utils._time import ( def main() -> t.NoReturn: module = AnsibleModule( - argument_spec=dict( - path=dict(type="path"), - content=dict(type="str"), - valid_at=dict(type="dict"), - name_encoding=dict( - type="str", default="ignore", choices=["ignore", "idna", "unicode"] - ), - select_crypto_backend=dict( - type="str", default="auto", choices=["auto", "cryptography"] - ), - ), + argument_spec={ + "path": {"type": "path"}, + "content": {"type": "str"}, + "valid_at": {"type": "dict"}, + "name_encoding": { + "type": "str", + "default": "ignore", + "choices": ["ignore", "idna", "unicode"], + }, + "select_crypto_backend": { + "type": "str", + "default": "auto", + "choices": ["auto", "cryptography"], + }, + }, required_one_of=(["path", "content"],), mutually_exclusive=(["path", "content"],), supports_check_mode=True, @@ -460,7 +464,7 @@ def main() -> t.NoReturn: not_before = module_backend.get_not_before() not_after = module_backend.get_not_after() - result["valid_at"] = dict() + result["valid_at"] = {} if valid_at: for k, v in valid_at.items(): result["valid_at"][k] = not_before <= v <= not_after diff --git a/plugins/modules/x509_certificate_pipe.py b/plugins/modules/x509_certificate_pipe.py index 98a76d12..4254ba55 100644 --- a/plugins/modules/x509_certificate_pipe.py +++ b/plugins/modules/x509_certificate_pipe.py @@ -182,9 +182,9 @@ def main() -> t.NoReturn: add_ownca_provider_to_argument_spec(argument_spec) add_selfsigned_provider_to_argument_spec(argument_spec) argument_spec.argument_spec.update( - dict( - content=dict(type="str"), - ) + { + "content": {"type": "str"}, + } ) module = argument_spec.create_ansible_module( supports_check_mode=True, diff --git a/plugins/modules/x509_crl.py b/plugins/modules/x509_crl.py index 6823564c..0c62aed9 100644 --- a/plugins/modules/x509_crl.py +++ b/plugins/modules/x509_crl.py @@ -507,7 +507,7 @@ class CRLError(OpenSSLObjectError): class CRL(OpenSSLObject): def __init__(self, module: AnsibleModule) -> None: - super(CRL, self).__init__( + super().__init__( path=module.params["path"], state=module.params["state"], force=module.params["force"], @@ -650,7 +650,7 @@ class CRL(OpenSSLObject): passphrase=self.privatekey_passphrase, ) except OpenSSLBadPassphraseError as exc: - raise CRLError(exc) + raise CRLError(exc) from exc self.crl = None try: @@ -704,7 +704,7 @@ class CRL(OpenSSLObject): def remove(self, module: AnsibleModule) -> None: if self.backup: self.backup_file = self.module.backup_local(self.path) - super(CRL, self).remove(self.module) + super().remove(self.module) def _compress_entry(self, entry: dict[str, t.Any]) -> ( tuple[ @@ -750,27 +750,27 @@ class CRL(OpenSSLObject): entry["invalidity_date"], entry["invalidity_date_critical"], ) - else: - return ( - entry["serial_number"], - entry["revocation_date"], - issuer, - entry["issuer_critical"], - entry["reason"], - entry["reason_critical"], - entry["invalidity_date"], - entry["invalidity_date_critical"], - ) + return ( + entry["serial_number"], + entry["revocation_date"], + issuer, + entry["issuer_critical"], + entry["reason"], + entry["reason_critical"], + entry["invalidity_date"], + entry["invalidity_date_critical"], + ) def check( self, module: AnsibleModule, + *, perms_required: bool = True, ignore_conversion: bool = True, ) -> bool: """Ensure the resource is in its desired state.""" - state_and_perms = super(CRL, self).check( + state_and_perms = super().check( module=self.module, perms_required=perms_required ) @@ -843,16 +843,16 @@ class CRL(OpenSSLObject): ) ) except ValueError as e: - raise CRLError(e) + raise CRLError(e) from e crl = set_last_update(crl, value=self.last_update) if self.next_update is not None: crl = set_next_update(crl, value=self.next_update) if self.update and self.crl: - new_entries = set( - [self._compress_entry(entry) for entry in self.revoked_certificates] - ) + new_entries = { + self._compress_entry(entry) for entry in self.revoked_certificates + } for entry in self.crl: decoded_entry = self._compress_entry( cryptography_decode_revoked_certificate(entry) @@ -888,8 +888,7 @@ class CRL(OpenSSLObject): self.crl = crl.sign(self.privatekey, digest) if self.format == "pem": return self.crl.public_bytes(Encoding.PEM) - else: - return self.crl.public_bytes(Encoding.DER) + return self.crl.public_bytes(Encoding.DER) def generate(self, module: AnsibleModule) -> None: result = None @@ -996,49 +995,53 @@ class CRL(OpenSSLObject): if self.return_content: result["crl"] = self.crl_content - result["diff"] = dict( - before=self.diff_before, - after=self.diff_after, - ) + result["diff"] = { + "before": self.diff_before, + "after": self.diff_after, + } return result def main() -> t.NoReturn: module = AnsibleModule( - argument_spec=dict( - state=dict(type="str", default="present", choices=["present", "absent"]), - crl_mode=dict( - type="str", - default="generate", - choices=["generate", "update"], - ), - force=dict(type="bool", default=False), - backup=dict(type="bool", default=False), - path=dict(type="path", required=True), - format=dict(type="str", default="pem", choices=["pem", "der"]), - privatekey_path=dict(type="path"), - privatekey_content=dict(type="str", no_log=True), - privatekey_passphrase=dict(type="str", no_log=True), - issuer=dict(type="dict"), - issuer_ordered=dict(type="list", elements="dict"), - last_update=dict(type="str", default="+0s"), - next_update=dict(type="str"), - digest=dict(type="str", default="sha256"), - ignore_timestamps=dict(type="bool", default=False), - return_content=dict(type="bool", default=False), - revoked_certificates=dict( - type="list", - elements="dict", - options=dict( - path=dict(type="path"), - content=dict(type="str"), - serial_number=dict(type="raw"), - revocation_date=dict(type="str", default="+0s"), - issuer=dict(type="list", elements="str"), - issuer_critical=dict(type="bool", default=False), - reason=dict( - type="str", - choices=[ + argument_spec={ + "state": { + "type": "str", + "default": "present", + "choices": ["present", "absent"], + }, + "crl_mode": { + "type": "str", + "default": "generate", + "choices": ["generate", "update"], + }, + "force": {"type": "bool", "default": False}, + "backup": {"type": "bool", "default": False}, + "path": {"type": "path", "required": True}, + "format": {"type": "str", "default": "pem", "choices": ["pem", "der"]}, + "privatekey_path": {"type": "path"}, + "privatekey_content": {"type": "str", "no_log": True}, + "privatekey_passphrase": {"type": "str", "no_log": True}, + "issuer": {"type": "dict"}, + "issuer_ordered": {"type": "list", "elements": "dict"}, + "last_update": {"type": "str", "default": "+0s"}, + "next_update": {"type": "str"}, + "digest": {"type": "str", "default": "sha256"}, + "ignore_timestamps": {"type": "bool", "default": False}, + "return_content": {"type": "bool", "default": False}, + "revoked_certificates": { + "type": "list", + "elements": "dict", + "options": { + "path": {"type": "path"}, + "content": {"type": "str"}, + "serial_number": {"type": "raw"}, + "revocation_date": {"type": "str", "default": "+0s"}, + "issuer": {"type": "list", "elements": "str"}, + "issuer_critical": {"type": "bool", "default": False}, + "reason": { + "type": "str", + "choices": [ "unspecified", "key_compromise", "ca_compromise", @@ -1050,21 +1053,25 @@ def main() -> t.NoReturn: "aa_compromise", "remove_from_crl", ], - ), - reason_critical=dict(type="bool", default=False), - invalidity_date=dict(type="str"), - invalidity_date_critical=dict(type="bool", default=False), - ), - required_one_of=[["path", "content", "serial_number"]], - mutually_exclusive=[["path", "content", "serial_number"]], - ), - name_encoding=dict( - type="str", default="ignore", choices=["ignore", "idna", "unicode"] - ), - serial_numbers=dict( - type="str", default="integer", choices=["integer", "hex-octets"] - ), - ), + }, + "reason_critical": {"type": "bool", "default": False}, + "invalidity_date": {"type": "str"}, + "invalidity_date_critical": {"type": "bool", "default": False}, + }, + "required_one_of": [["path", "content", "serial_number"]], + "mutually_exclusive": [["path", "content", "serial_number"]], + }, + "name_encoding": { + "type": "str", + "default": "ignore", + "choices": ["ignore", "idna", "unicode"], + }, + "serial_numbers": { + "type": "str", + "default": "integer", + "choices": ["integer", "hex-octets"], + }, + }, required_if=[ ("state", "present", ["privatekey_path", "privatekey_content"], True), ("state", "present", ["issuer", "issuer_ordered"], True), diff --git a/plugins/modules/x509_crl_info.py b/plugins/modules/x509_crl_info.py index 4d5a562c..54a45ada 100644 --- a/plugins/modules/x509_crl_info.py +++ b/plugins/modules/x509_crl_info.py @@ -190,14 +190,16 @@ from ansible_collections.community.crypto.plugins.module_utils._crypto.pem impor def main() -> t.NoReturn: module = AnsibleModule( - argument_spec=dict( - path=dict(type="path"), - content=dict(type="str"), - list_revoked_certificates=dict(type="bool", default=True), - name_encoding=dict( - type="str", default="ignore", choices=["ignore", "idna", "unicode"] - ), - ), + argument_spec={ + "path": {"type": "path"}, + "content": {"type": "str"}, + "list_revoked_certificates": {"type": "bool", "default": True}, + "name_encoding": { + "type": "str", + "default": "ignore", + "choices": ["ignore", "idna", "unicode"], + }, + }, required_one_of=(["path", "content"],), mutually_exclusive=(["path", "content"],), supports_check_mode=True, diff --git a/plugins/plugin_utils/_action_module.py b/plugins/plugin_utils/_action_module.py index 46e309db..7586c1cb 100644 --- a/plugins/plugin_utils/_action_module.py +++ b/plugins/plugin_utils/_action_module.py @@ -36,7 +36,7 @@ if t.TYPE_CHECKING: class _ModuleExitException(Exception): def __init__(self, result: dict[str, t.Any]) -> None: - super(_ModuleExitException, self).__init__() + super().__init__() self.result = result @@ -187,10 +187,12 @@ class AnsibleActionModule: collection_name=d.get("collection_name"), ) else: + # pylint: disable-next=unknown-option-value self.deprecate( # pylint: disable=ansible-deprecated-no-version d ) else: + # pylint: disable-next=unknown-option-value self.deprecate( # pylint: disable=ansible-deprecated-no-version kwargs["deprecations"] ) @@ -226,9 +228,9 @@ class ActionModuleBase(ActionBase, metaclass=abc.ABCMeta): def run(self, tmp=None, task_vars=None) -> dict[str, t.Any]: if task_vars is None: - task_vars = dict() + task_vars = {} - result = super(ActionModuleBase, self).run(tmp, task_vars) + result = super().run(tmp, task_vars) del tmp # tmp no longer has any effect try: diff --git a/plugins/plugin_utils/_gnupg.py b/plugins/plugin_utils/_gnupg.py index ad8770eb..08a0ba6c 100644 --- a/plugins/plugin_utils/_gnupg.py +++ b/plugins/plugin_utils/_gnupg.py @@ -25,8 +25,10 @@ class PluginGPGRunner(GPGRunner): if executable is None: try: executable = get_bin_path("gpg") - except ValueError: - raise GPGError("Cannot find the `gpg` executable on the controller") + except ValueError as exc: + raise GPGError( + "Cannot find the `gpg` executable on the controller" + ) from exc self.executable = executable self.cwd = cwd @@ -45,17 +47,17 @@ class PluginGPGRunner(GPGRunner): Raises a ``GPGError`` in case of errors. """ command = [self.executable] + command - p = Popen( + with Popen( command, shell=False, cwd=self.cwd, stdin=PIPE, stdout=PIPE, stderr=PIPE - ) - stdout, stderr = p.communicate(input=data) - stdout_n = to_native(stdout, errors="surrogate_or_replace") - stderr_n = to_native(stderr, errors="surrogate_or_replace") - if check_rc and p.returncode != 0: - raise GPGError( - f'Running {" ".join(command)} yielded return code {p.returncode} with stdout: "{stdout_n}" and stderr: "{stderr_n}")' - ) - return t.cast(int, p.returncode), stdout_n, stderr_n + ) as p: + stdout, stderr = p.communicate(input=data) + stdout_n = to_native(stdout, errors="surrogate_or_replace") + stderr_n = to_native(stderr, errors="surrogate_or_replace") + if check_rc and p.returncode != 0: + raise GPGError( + f'Running {" ".join(command)} yielded return code {p.returncode} with stdout: "{stdout_n}" and stderr: "{stderr_n}")' + ) + return t.cast(int, p.returncode), stdout_n, stderr_n __all__ = ("PluginGPGRunner",) diff --git a/tests/sanity/ignore-2.17.txt b/tests/sanity/ignore-2.17.txt index 7027e87d..0a4408d2 100644 --- a/tests/sanity/ignore-2.17.txt +++ b/tests/sanity/ignore-2.17.txt @@ -23,6 +23,7 @@ plugins/modules/acme_certificate_revoke.py pylint:unpacking-non-sequence plugins/modules/acme_inspect.py pylint:unpacking-non-sequence plugins/modules/ecs_certificate.py no-assert plugins/modules/ecs_domain.py pep8:E704 +plugins/modules/get_certificate.py pylint:unknown-option-value plugins/modules/luks_device.py no-assert plugins/modules/openssl_pkcs12.py no-assert tests/ee/roles/smoke/library/smoke_ipaddress.py shebang diff --git a/tests/unit/plugins/module_utils/_acme/backend_data.py b/tests/unit/plugins/module_utils/_acme/backend_data.py index e8c20285..d1a147e8 100644 --- a/tests/unit/plugins/module_utils/_acme/backend_data.py +++ b/tests/unit/plugins/module_utils/_acme/backend_data.py @@ -139,47 +139,61 @@ TEST_PARSE_ACME_TIMESTAMP: list[tuple[datetime.timedelta, str, dict[str, int]]] [ ( "2024-01-01T00:11:22Z", - dict(year=2024, month=1, day=1, hour=0, minute=11, second=22), + { + "year": 2024, + "month": 1, + "day": 1, + "hour": 0, + "minute": 11, + "second": 22, + }, ), ( "2024-01-01T00:11:22.123Z", - dict( - year=2024, - month=1, - day=1, - hour=0, - minute=11, - second=22, - microsecond=123000, - ), + { + "year": 2024, + "month": 1, + "day": 1, + "hour": 0, + "minute": 11, + "second": 22, + "microsecond": 123000, + }, ), ( "2024-04-17T06:54:13.333333334Z", - dict( - year=2024, - month=4, - day=17, - hour=6, - minute=54, - second=13, - microsecond=333333, - ), + { + "year": 2024, + "month": 4, + "day": 17, + "hour": 6, + "minute": 54, + "second": 13, + "microsecond": 333333, + }, ), ( "2024-01-01T00:11:22+0100", - dict(year=2023, month=12, day=31, hour=23, minute=11, second=22), + { + "year": 2023, + "month": 12, + "day": 31, + "hour": 23, + "minute": 11, + "second": 22, + }, ), ( "2024-01-01T00:11:22.123+0100", - dict( - year=2023, - month=12, - day=31, - hour=23, - minute=11, - second=22, - microsecond=123000, - ), + { + "year": 2023, + "month": 12, + "day": 31, + "hour": 23, + "minute": 11, + "second": 22, + "microsecond": 123000, + }, ), ], ) @@ -192,22 +206,22 @@ TEST_INTERPOLATE_TIMESTAMP: list[ TIMEZONES, [ ( - dict(year=2024, month=1, day=1, hour=0, minute=0, second=0), - dict(year=2024, month=1, day=1, hour=1, minute=0, second=0), + {"year": 2024, "month": 1, "day": 1, "hour": 0, "minute": 0, "second": 0}, + {"year": 2024, "month": 1, "day": 1, "hour": 1, "minute": 0, "second": 0}, 0.0, - dict(year=2024, month=1, day=1, hour=0, minute=0, second=0), + {"year": 2024, "month": 1, "day": 1, "hour": 0, "minute": 0, "second": 0}, ), ( - dict(year=2024, month=1, day=1, hour=0, minute=0, second=0), - dict(year=2024, month=1, day=1, hour=1, minute=0, second=0), + {"year": 2024, "month": 1, "day": 1, "hour": 0, "minute": 0, "second": 0}, + {"year": 2024, "month": 1, "day": 1, "hour": 1, "minute": 0, "second": 0}, 0.5, - dict(year=2024, month=1, day=1, hour=0, minute=30, second=0), + {"year": 2024, "month": 1, "day": 1, "hour": 0, "minute": 30, "second": 0}, ), ( - dict(year=2024, month=1, day=1, hour=0, minute=0, second=0), - dict(year=2024, month=1, day=1, hour=1, minute=0, second=0), + {"year": 2024, "month": 1, "day": 1, "hour": 0, "minute": 0, "second": 0}, + {"year": 2024, "month": 1, "day": 1, "hour": 1, "minute": 0, "second": 0}, 1.0, - dict(year=2024, month=1, day=1, hour=1, minute=0, second=0), + {"year": 2024, "month": 1, "day": 1, "hour": 1, "minute": 0, "second": 0}, ), ], ) @@ -216,6 +230,7 @@ TEST_INTERPOLATE_TIMESTAMP: list[ class FakeBackend(CryptoBackend): def parse_key( self, + *, key_file: str | os.PathLike | None = None, key_content: str | None = None, passphrase=None, @@ -223,15 +238,16 @@ class FakeBackend(CryptoBackend): raise BackendException("Not implemented in fake backend") def sign( - self, payload64: str, protected64: str, key_data: dict[str, t.Any] | None + self, *, payload64: str, protected64: str, key_data: dict[str, t.Any] | None ) -> t.NoReturn: raise BackendException("Not implemented in fake backend") - def create_mac_key(self, alg: str, key: str) -> t.NoReturn: + def create_mac_key(self, *, alg: str, key: str) -> t.NoReturn: raise BackendException("Not implemented in fake backend") def get_ordered_csr_identifiers( self, + *, csr_filename: str | os.PathLike | None = None, csr_content: str | bytes | None = None, ) -> t.NoReturn: @@ -239,6 +255,7 @@ class FakeBackend(CryptoBackend): def get_csr_identifiers( self, + *, csr_filename: str | os.PathLike | None = None, csr_content: str | bytes | None = None, ) -> t.NoReturn: @@ -246,17 +263,19 @@ class FakeBackend(CryptoBackend): def get_cert_days( self, + *, cert_filename: str | os.PathLike | None = None, cert_content: str | bytes | None = None, now: datetime.datetime | None = None, ) -> t.NoReturn: raise BackendException("Not implemented in fake backend") - def create_chain_matcher(self, criterium: Criterium) -> t.NoReturn: + def create_chain_matcher(self, *, criterium: Criterium) -> t.NoReturn: raise BackendException("Not implemented in fake backend") def get_cert_information( self, + *, cert_filename: str | os.PathLike | None = None, cert_content: str | bytes | None = None, ) -> t.NoReturn: diff --git a/tests/unit/plugins/module_utils/_acme/test_backend_cryptography.py b/tests/unit/plugins/module_utils/_acme/test_backend_cryptography.py index ec7767aa..acd67390 100644 --- a/tests/unit/plugins/module_utils/_acme/test_backend_cryptography.py +++ b/tests/unit/plugins/module_utils/_acme/test_backend_cryptography.py @@ -136,15 +136,15 @@ def test_now(timezone: datetime.timedelta) -> None: assert now == datetime.datetime(2024, 2, 3, 4, 5, 6) -@pytest.mark.parametrize("timezone, input, expected", TEST_PARSE_ACME_TIMESTAMP) +@pytest.mark.parametrize("timezone, timestamp_str, expected", TEST_PARSE_ACME_TIMESTAMP) def test_parse_acme_timestamp( - timezone: datetime.timedelta, input: str, expected: dict[str, int] + timezone: datetime.timedelta, timestamp_str: str, expected: dict[str, int] ) -> None: with freeze_time("2024-02-03 04:05:06 +00:00", tz_offset=timezone): module = MagicMock() backend = CryptographyBackend(module=module) ts_expected = backend.get_utc_datetime(**expected) - timestamp = backend.parse_acme_timestamp(input) + timestamp = backend.parse_acme_timestamp(timestamp_str) assert ts_expected == timestamp diff --git a/tests/unit/plugins/module_utils/_acme/test_backend_openssl_cli.py b/tests/unit/plugins/module_utils/_acme/test_backend_openssl_cli.py index 6e5feef1..fab56af5 100644 --- a/tests/unit/plugins/module_utils/_acme/test_backend_openssl_cli.py +++ b/tests/unit/plugins/module_utils/_acme/test_backend_openssl_cli.py @@ -84,7 +84,7 @@ def test_csridentifiers_openssl( def test_normalize_ip(ip: str, result: str) -> None: module = MagicMock() backend = OpenSSLCLIBackend(module=module, openssl_binary="openssl") - assert backend._normalize_ip(ip) == result + assert backend._normalize_ip(ip) == result # pylint: disable=protected-access @pytest.mark.parametrize("timezone, now, expected_days", TEST_CERT_DAYS) @@ -142,15 +142,15 @@ def test_now(timezone: datetime.timedelta) -> None: assert now == datetime.datetime(2024, 2, 3, 4, 5, 6, tzinfo=UTC) -@pytest.mark.parametrize("timezone, input, expected", TEST_PARSE_ACME_TIMESTAMP) +@pytest.mark.parametrize("timezone, timestamp_str, expected", TEST_PARSE_ACME_TIMESTAMP) def test_parse_acme_timestamp( - timezone: datetime.timedelta, input: str, expected: dict[str, int] + timezone: datetime.timedelta, timestamp_str: str, expected: dict[str, int] ) -> None: with freeze_time("2024-02-03 04:05:06", tz_offset=timezone): module = MagicMock() backend = OpenSSLCLIBackend(module=module, openssl_binary="openssl") ts_expected = backend.get_utc_datetime(**expected) - timestamp = backend.parse_acme_timestamp(input) + timestamp = backend.parse_acme_timestamp(timestamp_str) assert ts_expected == timestamp diff --git a/tests/unit/plugins/module_utils/_acme/test_errors.py b/tests/unit/plugins/module_utils/_acme/test_errors.py index 54c6c780..435ca026 100644 --- a/tests/unit/plugins/module_utils/_acme/test_errors.py +++ b/tests/unit/plugins/module_utils/_acme/test_errors.py @@ -193,7 +193,7 @@ TEST_ACME_PROTOCOL_EXCEPTION: list[ }, "response": create_regular_response("xxx"), }, - lambda content: dict(foo="bar"), + lambda content: {"foo": "bar"}, "ACME request failed for https://ca.example.com/foo with HTTP status 201 Created. The JSON error result: {'foo': 'bar'}", { "http_url": "https://ca.example.com/foo", @@ -224,7 +224,7 @@ TEST_ACME_PROTOCOL_EXCEPTION: list[ }, "response": create_error_response(), }, - lambda content: dict(foo="bar"), + lambda content: {"foo": "bar"}, "ACME request failed for https://ca.example.com/foo with HTTP status 201 Created. The JSON error result: {'foo': 'bar'}", { "http_url": "https://ca.example.com/foo", @@ -345,9 +345,11 @@ TEST_ACME_PROTOCOL_EXCEPTION: list[ ] -@pytest.mark.parametrize("input, from_json, msg, args", TEST_ACME_PROTOCOL_EXCEPTION) +@pytest.mark.parametrize( + "parameters, from_json, msg, args", TEST_ACME_PROTOCOL_EXCEPTION +) def test_acme_protocol_exception( - input: dict[str, t.Any], + parameters: dict[str, t.Any], from_json: t.Callable[[t.Any], t.NoReturn] | None, msg: str, args: dict[str, t.Any], @@ -358,7 +360,7 @@ def test_acme_protocol_exception( module = MagicMock() module.from_json = from_json with pytest.raises(ACMEProtocolException) as exc: - raise ACMEProtocolException(module=module, **input) # type: ignore + raise ACMEProtocolException(module=module, **parameters) # type: ignore print(exc.value.msg) print(exc.value.module_fail_args) diff --git a/tests/unit/plugins/module_utils/test__time.py b/tests/unit/plugins/module_utils/test__time.py index 163cbc6a..5b432725 100644 --- a/tests/unit/plugins/module_utils/test__time.py +++ b/tests/unit/plugins/module_utils/test__time.py @@ -94,39 +94,51 @@ TEST_EPOCH_SECONDS: list[tuple[datetime.timedelta, float, dict[str, int]]] = ( [ ( 0, - dict( - year=1970, day=1, month=1, hour=0, minute=0, second=0, microsecond=0 - ), + { + "year": 1970, + "month": 1, + "day": 1, + "hour": 0, + "minute": 0, + "second": 0, + "microsecond": 0, + }, ), ( 1e-6, - dict( - year=1970, day=1, month=1, hour=0, minute=0, second=0, microsecond=1 - ), + { + "year": 1970, + "month": 1, + "day": 1, + "hour": 0, + "minute": 0, + "second": 0, + "microsecond": 1, + }, ), ( 1e-3, - dict( - year=1970, - day=1, - month=1, - hour=0, - minute=0, - second=0, - microsecond=1000, - ), + { + "year": 1970, + "month": 1, + "day": 1, + "hour": 0, + "minute": 0, + "second": 0, + "microsecond": 1000, + }, ), ( 3691.2, - dict( - year=1970, - day=1, - month=1, - hour=1, - minute=1, - second=31, - microsecond=200000, - ), + { + "year": 1970, + "month": 1, + "day": 1, + "hour": 1, + "minute": 1, + "second": 31, + "microsecond": 200000, + }, ), ], ) @@ -283,25 +295,29 @@ TEST_GET_RELATIVE_TIME_OPTION: list[ ) -@pytest.mark.parametrize("timezone, input, expected", TEST_REMOVE_TIMEZONE) +@pytest.mark.parametrize("timezone, input_timestamp, expected", TEST_REMOVE_TIMEZONE) def test_remove_timezone( - timezone: datetime.timedelta, input: datetime.datetime, expected: datetime.datetime + timezone: datetime.timedelta, + input_timestamp: datetime.datetime, + expected: datetime.datetime, ) -> None: with freeze_time("2024-02-03 04:05:06", tz_offset=timezone): - output_1 = remove_timezone(input) + output_1 = remove_timezone(input_timestamp) assert expected == output_1 - output_2 = add_or_remove_timezone(input, with_timezone=False) + output_2 = add_or_remove_timezone(input_timestamp, with_timezone=False) assert expected == output_2 -@pytest.mark.parametrize("timezone, input, expected", TEST_UTC_TIMEZONE) +@pytest.mark.parametrize("timezone, input_timestamp, expected", TEST_UTC_TIMEZONE) def test_utc_timezone( - timezone: datetime.timedelta, input: datetime.datetime, expected: datetime.datetime + timezone: datetime.timedelta, + input_timestamp: datetime.datetime, + expected: datetime.datetime, ) -> None: with freeze_time("2024-02-03 04:05:06", tz_offset=timezone): - output_1 = ensure_utc_timezone(input) + output_1 = ensure_utc_timezone(input_timestamp) assert expected == output_1 - output_2 = add_or_remove_timezone(input, with_timezone=True) + output_2 = add_or_remove_timezone(input_timestamp, with_timezone=True) assert expected == output_2 diff --git a/tests/unit/plugins/modules/test_luks_device.py b/tests/unit/plugins/modules/test_luks_device.py index 7c92d15e..45d22a64 100644 --- a/tests/unit/plugins/modules/test_luks_device.py +++ b/tests/unit/plugins/modules/test_luks_device.py @@ -13,7 +13,7 @@ from ansible_collections.community.crypto.plugins.modules import luks_device class DummyModule: # module to mock AnsibleModule class def __init__(self): - self.params = dict() + self.params = {} def fail_json(self, msg=""): raise ValueError(msg)