diff --git a/LICENSES/BSD-2-Clause.txt b/LICENSES/BSD-2-Clause.txt deleted file mode 100644 index 6810e04e..00000000 --- a/LICENSES/BSD-2-Clause.txt +++ /dev/null @@ -1,8 +0,0 @@ -Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - -1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - -2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - diff --git a/README.md b/README.md index a605c4cd..65b25407 100644 --- a/README.md +++ b/README.md @@ -106,6 +106,6 @@ This collection is primarily licensed and distributed as a whole under the GNU G See [LICENSES/GPL-3.0-or-later.txt](https://github.com/ansible-collections/community.crypto/blob/main/COPYING) for the full text. -Parts of the collection are licensed under the [Apache 2.0 license](https://github.com/ansible-collections/community.crypto/blob/main/LICENSES/Apache-2.0.txt) (`plugins/module_utils/_crypto/_obj2txt.py` and `plugins/module_utils/_crypto/_objects_data.py`), the [BSD 2-Clause license](https://github.com/ansible-collections/community.crypto/blob/main/LICENSES/BSD-2-Clause.txt) (`plugins/module_utils/_ecs/api.py`), the [BSD 3-Clause license](https://github.com/ansible-collections/community.crypto/blob/main/LICENSES/BSD-3-Clause.txt) (`plugins/module_utils/_crypto/_obj2txt.py`). This only applies to vendored files in ``plugins/module_utils/`` and to the ECS module utils. +Parts of the collection are licensed under the [Apache 2.0 license](https://github.com/ansible-collections/community.crypto/blob/main/LICENSES/Apache-2.0.txt) (`plugins/module_utils/_crypto/_obj2txt.py` and `plugins/module_utils/_crypto/_objects_data.py`) and the [BSD 3-Clause license](https://github.com/ansible-collections/community.crypto/blob/main/LICENSES/BSD-3-Clause.txt) (`plugins/module_utils/_crypto/_obj2txt.py`). This only applies to vendored files in ``plugins/module_utils/``. All files have a machine readable `SDPX-License-Identifier:` comment denoting its respective license(s) or an equivalent entry in an accompanying `.license` file. Only changelog fragments (which will not be part of a release) are covered by a blanket statement in `REUSE.toml`. This conforms to the [REUSE specification](https://reuse.software/spec/). diff --git a/changelogs/fragments/900-remove-entrust.yml b/changelogs/fragments/900-remove-entrust.yml new file mode 100644 index 00000000..ff91ddb6 --- /dev/null +++ b/changelogs/fragments/900-remove-entrust.yml @@ -0,0 +1,6 @@ +removed_features: + - "All Entrust content is being removed since the Entrust service in currently being sunsetted after the sale of Entrust's Public Certificates Business to Sectigo; see `the announcement with key dates `__ and `the migration brief for customers `__ for details. Since this process will be completed in 2025, we decided to remove all Entrust content from community.general 3.0.0 (https://github.com/ansible-collections/community.crypto/issues/895, https://github.com/ansible-collections/community.crypto/pull/901)." + - "ecs_certificate - the module has been removed. Please use community.crypto 2.x.y if you need this module (https://github.com/ansible-collections/community.crypto/pull/900)." + - "ecs_domain - the module has been removed. Please use community.crypto 2.x.y if you need this module (https://github.com/ansible-collections/community.crypto/pull/900)." + - "x509_certificate - the ``entrust`` provider has been removed. Please use community.crypto 2.x.y if you need this provider (https://github.com/ansible-collections/community.crypto/pull/900)." + - "x509_certificate_pipe - the ``entrust`` provider has been removed. Please use community.crypto 2.x.y if you need this provider (https://github.com/ansible-collections/community.crypto/pull/900)." diff --git a/meta/runtime.yml b/meta/runtime.yml index 11be8b48..4a877d90 100644 --- a/meta/runtime.yml +++ b/meta/runtime.yml @@ -20,6 +20,14 @@ action_groups: plugin_routing: modules: + ecs_certificate: + tombstone: + removal_version: 3.0.0 + warning_text: The 'community.crypto.ecs_certificate' module has been removed due to the upcoming sunsetting of the ECS service. Please use community.crypto 2.x.y to continue using this module + ecs_domain: + tombstone: + removal_version: 3.0.0 + warning_text: The 'community.crypto.ecs_domain' module has been removed due to the upcoming sunsetting of the ECS service. Please use community.crypto 2.x.y to continue using this module. acme_account_facts: tombstone: removal_version: 2.0.0 diff --git a/plugins/doc_fragments/_ecs_credential.py b/plugins/doc_fragments/_ecs_credential.py deleted file mode 100644 index 1df538cf..00000000 --- a/plugins/doc_fragments/_ecs_credential.py +++ /dev/null @@ -1,44 +0,0 @@ -# Copyright (c), Entrust Datacard Corporation, 2019 -# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) -# SPDX-License-Identifier: GPL-3.0-or-later - -# Note that this doc fragment is **PRIVATE** to the collection. It can have breaking changes at any time. -# Do not use this from other collections or standalone plugins/modules! - -from __future__ import annotations - - -class ModuleDocFragment: - - # Plugin options for Entrust Certificate Services (ECS) credentials - DOCUMENTATION = r""" -options: - entrust_api_user: - description: - - The username for authentication to the Entrust Certificate Services (ECS) API. - type: str - required: true - entrust_api_key: - description: - - The key (password) for authentication to the Entrust Certificate Services (ECS) API. - type: str - required: true - entrust_api_client_cert_path: - description: - - The path to the client certificate used to authenticate to the Entrust Certificate Services (ECS) API. - type: path - required: true - entrust_api_client_cert_key_path: - description: - - The path to the key for the client certificate used to authenticate to the Entrust Certificate Services (ECS) API. - type: path - required: true - entrust_api_specification_path: - description: - - The path to the specification file defining the Entrust Certificate Services (ECS) API configuration. - - You can use this to keep a local copy of the specification to avoid downloading it every time the module is used. - type: path - default: https://cloud.entrust.net/EntrustCloud/documentation/cms-api-2.1.0.yaml -requirements: - - "PyYAML >= 3.11" -""" diff --git a/plugins/doc_fragments/_module_certificate.py b/plugins/doc_fragments/_module_certificate.py index f9348117..c21e6533 100644 --- a/plugins/doc_fragments/_module_certificate.py +++ b/plugins/doc_fragments/_module_certificate.py @@ -132,91 +132,6 @@ options: default: https://acme-v02.api.letsencrypt.org/directory """ - BACKEND_ENTRUST_DOCUMENTATION = r""" -options: - entrust_cert_type: - description: - - Specify the type of certificate requested. - - This is only used by the V(entrust) provider. - type: str - default: STANDARD_SSL - choices: [STANDARD_SSL, ADVANTAGE_SSL, UC_SSL, EV_SSL, WILDCARD_SSL, PRIVATE_SSL, PD_SSL, CDS_ENT_LITE, CDS_ENT_PRO, SMIME_ENT] - - entrust_requester_email: - description: - - The email of the requester of the certificate (for tracking purposes). - - This is only used by the V(entrust) provider. - - This is required if the provider is V(entrust). - type: str - - entrust_requester_name: - description: - - The name of the requester of the certificate (for tracking purposes). - - This is only used by the V(entrust) provider. - - This is required if the provider is V(entrust). - type: str - - entrust_requester_phone: - description: - - The phone number of the requester of the certificate (for tracking purposes). - - This is only used by the V(entrust) provider. - - This is required if the provider is V(entrust). - type: str - - entrust_api_user: - description: - - The username for authentication to the Entrust Certificate Services (ECS) API. - - This is only used by the V(entrust) provider. - - This is required if the provider is V(entrust). - type: str - - entrust_api_key: - description: - - The key (password) for authentication to the Entrust Certificate Services (ECS) API. - - This is only used by the V(entrust) provider. - - This is required if the provider is V(entrust). - type: str - - entrust_api_client_cert_path: - description: - - The path to the client certificate used to authenticate to the Entrust Certificate Services (ECS) API. - - This is only used by the V(entrust) provider. - - This is required if the provider is V(entrust). - type: path - - entrust_api_client_cert_key_path: - description: - - The path to the private key of the client certificate used to authenticate to the Entrust Certificate Services (ECS) API. - - This is only used by the V(entrust) provider. - - This is required if the provider is V(entrust). - type: path - - entrust_not_after: - description: - - The point in time at which the certificate stops being valid. - - Time can be specified either as relative time or as an absolute timestamp. - - A valid absolute time format is C(ASN.1 TIME) such as V(2019-06-18). - - A valid relative time format is V([+-]timespec) where timespec can be an integer + C([w | d | h | m | s]), such as V(+365d) or V(+32w1d2h)). - - Time will always be interpreted as UTC. - - Note that only the date (day, month, year) is supported for specifying the expiry date of the issued certificate. - - The full date-time is adjusted to EST (GMT -5:00) before issuance, which may result in a certificate with an expiration date one day - earlier than expected if a relative time is used. - - The minimum certificate lifetime is 90 days, and maximum is three years. - - If this value is not specified, the certificate will stop being valid 365 days the date of issue. - - This is only used by the V(entrust) provider. - - Please note that this value is B(not) covered by the O(ignore_timestamps) option. - type: str - default: +365d - - entrust_api_specification_path: - description: - - The path to the specification file defining the Entrust Certificate Services (ECS) API configuration. - - You can use this to keep a local copy of the specification to avoid downloading it every time the module is used. - - This is only used by the V(entrust) provider. - type: path - default: https://cloud.entrust.net/EntrustCloud/documentation/cms-api-2.1.0.yaml -""" - BACKEND_OWNCA_DOCUMENTATION = r""" description: - The V(ownca) provider is intended for generating an OpenSSL certificate signed with your own diff --git a/plugins/module_utils/_crypto/module_backends/certificate_entrust.py b/plugins/module_utils/_crypto/module_backends/certificate_entrust.py deleted file mode 100644 index 92d44e74..00000000 --- a/plugins/module_utils/_crypto/module_backends/certificate_entrust.py +++ /dev/null @@ -1,297 +0,0 @@ -# Copyright (c) 2016-2017, Yanis Guenane -# Copyright (c) 2017, Markus Teufelberger -# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) -# SPDX-License-Identifier: GPL-3.0-or-later - -# Note that this module util is **PRIVATE** to the collection. It can have breaking changes at any time. -# Do not use this from other collections or standalone plugins/modules! - -from __future__ import annotations - -import datetime -import os -import typing as t - -from ansible.module_utils.common.text.converters import to_bytes, to_text -from ansible_collections.community.crypto.plugins.module_utils._crypto.cryptography_support import ( - CRYPTOGRAPHY_TIMEZONE, - get_not_valid_after, -) -from ansible_collections.community.crypto.plugins.module_utils._crypto.module_backends.certificate import ( - CertificateBackend, - CertificateError, - CertificateProvider, -) -from ansible_collections.community.crypto.plugins.module_utils._crypto.support import ( - load_certificate, -) -from ansible_collections.community.crypto.plugins.module_utils._ecs.api import ( - ECSClient, - RestOperationException, - SessionConfigurationException, -) -from ansible_collections.community.crypto.plugins.module_utils._time import ( - get_now_datetime, - get_relative_time_option, -) - - -if t.TYPE_CHECKING: - from ansible.module_utils.basic import AnsibleModule - from ansible_collections.community.crypto.plugins.module_utils._argspec import ( - ArgumentSpec, - ) - - -try: - from cryptography.x509.oid import NameOID -except ImportError: - pass - - -class EntrustCertificateBackend(CertificateBackend): - def __init__(self, *, module: AnsibleModule) -> None: - 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: - raise CertificateError( - "csr_path or csr_content is required for entrust provider" - ) - if not os.path.exists(self.csr_path): - raise CertificateError( - f"The certificate signing request file {self.csr_path} does not exist" - ) - - self._ensure_csr_loaded() - if self.csr is None: - raise CertificateError("CSR not provided") - - # ECS API defaults to using the validated organization tied to the account. - # We want to always force behavior of trying to use the organization provided in the CSR. - # To that end we need to parse out the organization from the CSR. - self.csr_org = None - csr_subject_orgs = self.csr.subject.get_attributes_for_oid( - NameOID.ORGANIZATION_NAME - ) - if len(csr_subject_orgs) == 1: - self.csr_org = csr_subject_orgs[0].value - elif len(csr_subject_orgs) > 1: - self.module.fail_json( - msg=( - "Entrust provider does not currently support multiple validated organizations. Multiple organizations found in " - f"Subject DN: '{self.csr.subject}'. " - ) - ) - # If no organization in the CSR, explicitly tell ECS that it should be blank in issued cert, not defaulted to - # organization tied to the account. - if self.csr_org is None: - self.csr_org = "" - - try: - self.ecs_client = ECSClient( - entrust_api_user=self.module.params["entrust_api_user"], - entrust_api_key=self.module.params["entrust_api_key"], - entrust_api_cert=self.module.params["entrust_api_client_cert_path"], - entrust_api_cert_key=self.module.params[ - "entrust_api_client_cert_key_path" - ], - entrust_api_specification_path=self.module.params[ - "entrust_api_specification_path" - ], - ) - except SessionConfigurationException as e: - module.fail_json(msg=f"Failed to initialize Entrust Provider: {e}") - - def generate_certificate(self) -> None: - """(Re-)Generate certificate.""" - body = {} - - # Read the CSR that was generated for us - if self.csr_content is not None: - # csr_content contains bytes - body["csr"] = to_text(self.csr_content) - else: - assert self.csr_path is not None - 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"] - - # Handle expiration (30 days if not specified) - expiry = self.notAfter - if not expiry: - gmt_now = get_now_datetime(with_timezone=CRYPTOGRAPHY_TIMEZONE) - expiry = gmt_now + datetime.timedelta(days=365) - - expiry_iso3339 = expiry.strftime("%Y-%m-%dT%H:%M:%S.00Z") - body["certExpiryDate"] = expiry_iso3339 - body["org"] = self.csr_org - body["tracking"] = { - "requesterName": self.module.params["entrust_requester_name"], - "requesterEmail": self.module.params["entrust_requester_email"], - "requesterPhone": self.module.params["entrust_requester_phone"], - } - - try: - result = self.ecs_client.NewCertRequest( # type: ignore[attr-defined] # pylint: disable=no-member - Body=body - ) - self.trackingId = result.get("trackingId") - except RestOperationException as e: - self.module.fail_json( - msg=f"Failed to request new certificate from Entrust Certificate Services (ECS): {e.message}" - ) - - self.cert_bytes = to_bytes(result.get("endEntityCert")) - self.cert = load_certificate( - path=None, - content=self.cert_bytes, - ) - - 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( - self, - *, - not_before: datetime.datetime | None = None, - not_after: datetime.datetime | None = None, - ) -> bool: - parent_check = super().needs_regeneration() - - try: - cert_details = self._get_cert_details() - except RestOperationException as e: - self.module.fail_json( - msg=f"Failed to get status of existing certificate from Entrust Certificate Services (ECS): {e.message}." - ) - - # Always issue a new certificate if the certificate is expired, suspended or revoked - status = cert_details.get("status", False) - 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 - if ( - self.module.params["entrust_cert_type"] - and cert_details.get("certType") - and self.module.params["entrust_cert_type"] != cert_details.get("certType") - ): - return True - - return parent_check - - def _get_cert_details(self) -> dict[str, t.Any]: - cert_details: dict[str, t.Any] = {} - try: - self._ensure_existing_certificate_loaded() - except Exception: - return cert_details - if self.existing_certificate: - serial_number = f"{self.existing_certificate.serial_number:X}" - expiry = get_not_valid_after(self.existing_certificate) - - # get some information about the expiry of this certificate - expiry_iso3339 = expiry.strftime("%Y-%m-%dT%H:%M:%S.00Z") - cert_details["expiresAfter"] = expiry_iso3339 - - # If a trackingId is not already defined (from the result of a generate) - # use the serial number to identify the tracking Id - if self.trackingId is None and serial_number is not None: - cert_results = self.ecs_client.GetCertificates( # type: ignore[attr-defined] # pylint: disable=no-member - serialNumber=serial_number - ).get( - "certificates", {} - ) - - # Finding 0 or more than 1 result is a very unlikely use case, it simply means we cannot perform additional checks - # on the 'state' as returned by Entrust Certificate Services (ECS). The general certificate validity is - # still checked as it is in the rest of the module. - if len(cert_results) == 1: - self.trackingId = cert_results[0].get("trackingId") - - if self.trackingId is not None: - cert_details.update( - self.ecs_client.GetCertificate( # pylint: disable=no-member - trackingId=self.trackingId - ) - ) - - return cert_details - - -class EntrustCertificateProvider(CertificateProvider): - def validate_module_args(self, module: AnsibleModule) -> None: - pass - - def create_backend(self, module: AnsibleModule) -> EntrustCertificateBackend: - return EntrustCertificateBackend(module=module) - - -def add_entrust_provider_to_argument_spec(argument_spec: ArgumentSpec) -> None: - argument_spec.argument_spec["provider"]["choices"].append("entrust") - argument_spec.argument_spec.update( - { - "entrust_cert_type": { - "type": "str", - "default": "STANDARD_SSL", - "choices": [ - "STANDARD_SSL", - "ADVANTAGE_SSL", - "UC_SSL", - "EV_SSL", - "WILDCARD_SSL", - "PRIVATE_SSL", - "PD_SSL", - "CDS_ENT_LITE", - "CDS_ENT_PRO", - "SMIME_ENT", - ], - }, - "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( - ( - "provider", - "entrust", - [ - "entrust_requester_email", - "entrust_requester_name", - "entrust_requester_phone", - "entrust_api_user", - "entrust_api_key", - "entrust_api_client_cert_path", - "entrust_api_client_cert_key_path", - ], - ) - ) - - -__all__ = ( - "EntrustCertificateBackend", - "EntrustCertificateProvider", - "add_entrust_provider_to_argument_spec", -) diff --git a/plugins/module_utils/_ecs/api.py b/plugins/module_utils/_ecs/api.py deleted file mode 100644 index 87f70a73..00000000 --- a/plugins/module_utils/_ecs/api.py +++ /dev/null @@ -1,421 +0,0 @@ -# This code is part of Ansible, but is an independent component. -# This particular file snippet, and this file snippet only, is licensed under the -# Modified BSD License. Modules you write using this snippet, which is embedded -# dynamically by Ansible, still belong to the author of the module, and may assign -# their own license to the complete work. -# -# Copyright (c), Entrust Datacard Corporation, 2019 -# Simplified BSD License (see LICENSES/BSD-2-Clause.txt or https://opensource.org/licenses/BSD-2-Clause) -# SPDX-License-Identifier: BSD-2-Clause - -# Note that this module util is **PRIVATE** to the collection. It can have breaking changes at any time. -# Do not use this from other collections or standalone plugins/modules! - -from __future__ import annotations - -import json -import os -import re -import traceback -import typing as t -from urllib.error import HTTPError -from urllib.parse import urlencode - -from ansible.module_utils.basic import missing_required_lib -from ansible.module_utils.common.text.converters import to_text -from ansible.module_utils.urls import Request - - -if t.TYPE_CHECKING: - _P = t.ParamSpec("_P") - - -YAML_IMP_ERR = None -try: - import yaml -except ImportError: - YAML_FOUND = False - YAML_IMP_ERR = traceback.format_exc() -else: - YAML_FOUND = True - -valid_file_format = re.compile(r".*(\.)(yml|yaml|json)$") - - -def ecs_client_argument_spec() -> dict[str, t.Any]: - 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): - """Raised if we cannot configure a session with the API""" - - -class RestOperationException(Exception): - """Encapsulate a REST API error""" - - def __init__(self, error: dict[str, t.Any]) -> None: - self.status = to_text(error.get("status", None)) - self.errors = [to_text(err.get("message")) for err in error.get("errors", {})] - self.message = " ".join(self.errors) - - -def generate_docstring(operation_spec: dict[str, t.Any]) -> str: - """Generate a docstring for an operation defined in operation_spec (swagger)""" - # Description of the operation - docs = operation_spec.get("description", "No Description") - docs += "\n\n" - - # Parameters of the operation - parameters = operation_spec.get("parameters", []) - if len(parameters) != 0: - docs += "\tArguments:\n\n" - for parameter in parameters: - req = "Required" if parameter.get("required", False) else "Not Required" - docs += f"{parameter.get('name')} ({parameter.get('type', 'No Type')}:{req}): {parameter.get('description')}\n" - - return docs - - -_T = t.TypeVar("_T") -_R = t.TypeVar("_R") - - -def bind( - instance: _T, - method: t.Callable[t.Concatenate[_T, _P], _R], - operation_spec: dict[str, str], -) -> t.Callable[_P, _R]: - def binding_scope_fn(*args, **kwargs) -> _R: - return method(instance, *args, **kwargs) - - # Make sure we do not confuse users; add the proper name and documentation to the function. - # Users can use !help() to get help on the function from interactive python or pdb - operation_name = operation_spec["operationId"].split("Using")[0] - binding_scope_fn.__name__ = str(operation_name) - binding_scope_fn.__doc__ = generate_docstring(operation_spec) - - return binding_scope_fn - - -class RestOperation: - def __init__( - self, - session: "ECSSession", - uri: str, - method: str, - parameters: dict | None = None, - ) -> None: - self.session = session - self.method = method - if parameters is None: - self.parameters = {} - else: - self.parameters = parameters - self.url = ( - f"https://{session._spec.get('host')}{session._spec.get('basePath')}{uri}" - ) - - def restmethod(self, *args, **kwargs) -> t.Any: - """Do the hard work of making the request here""" - - # gather named path parameters and do substitution on the URL - body_parameters: dict[str, t.Any] | None - if self.parameters: - path_parameters = {} - body_parameters = {} - query_parameters = {} - for x in self.parameters: - expected_location = x.get("in") - key_name = x.get("name", None) - key_value = kwargs.get(key_name, None) - if expected_location == "path" and key_name and key_value: - path_parameters.update({key_name: key_value}) - elif expected_location == "body" and key_name and key_value: - body_parameters.update({key_name: key_value}) - elif expected_location == "query" and key_name and key_value: - query_parameters.update({key_name: key_value}) - - if len(body_parameters.keys()) >= 1: - body_parameters = body_parameters.get(list(body_parameters.keys())[0]) - else: - body_parameters = None - else: - path_parameters = {} - query_parameters = {} - body_parameters = None - - # This will fail if we have not set path parameters with a KeyError - url = self.url.format(**path_parameters) - if query_parameters: - # modify the URL to add path parameters - url = url + "?" + urlencode(query_parameters) - - try: - if body_parameters: - body_parameters_json = json.dumps(body_parameters) - response = self.session.request.open( - method=self.method, url=url, data=body_parameters_json - ) - else: - response = self.session.request.open(method=self.method, url=url) - except HTTPError as e: - # An HTTPError has the same methods available as a valid response from request.open - response = e - - # Return the result if JSON and success ({} for empty responses) - # Raise an exception if there was a failure. - try: - result_code = response.getcode() - result = json.loads(response.read()) - except ValueError: - result = {} - - if result or result == {}: - if result_code and result_code < 400: - return result - raise RestOperationException(result) - - # Raise a generic RestOperationException if this fails - raise RestOperationException( - {"status": result_code, "errors": [{"message": "REST Operation Failed"}]} - ) - - -class Resource: - """Implement basic CRUD operations against a path.""" - - def __init__(self, session: "ECSSession") -> None: - self.session = session - self.parameters: dict[str, t.Any] = {} - - for url in session._spec.get("paths").keys(): - methods = session._spec.get("paths").get(url) - for method in methods.keys(): - operation_spec = methods.get(method) - operation_name = operation_spec.get("operationId", None) - parameters = operation_spec.get("parameters") - - if not operation_name: - if method.lower() == "post": - operation_name = "Create" - elif method.lower() == "get": - operation_name = "Get" - elif method.lower() == "put": - operation_name = "Update" - elif method.lower() == "delete": - operation_name = "Delete" - elif method.lower() == "patch": - operation_name = "Patch" - else: - raise SessionConfigurationException( - f"Invalid REST method type {method}" - ) - - # Get the non-parameter parts of the URL and append to the operation name - # e.g /application/version -> GetApplicationVersion - # e.g. /application/{id} -> GetApplication - # This may lead to duplicates, which we must prevent. - operation_name += ( - re.sub(r"{(.*)}", "", url) - .replace("/", " ") - .title() - .replace(" ", "") - ) - operation_spec["operationId"] = operation_name - - op = RestOperation(session, url, method, parameters) - setattr(self, operation_name, bind(self, op.restmethod, operation_spec)) - - -# Session to encapsulate the connection parameters of the module_utils Request object, the api spec, etc -class ECSSession: - def __init__(self, name: str, **kwargs) -> None: - """ - Initialize our session - """ - - self._set_config(name, **kwargs) - - def client(self) -> Resource: - resource = Resource(self) - return resource - - def _set_config(self, name: str, **kwargs) -> None: - headers = { - "Content-Type": "application/json", - "Connection": "keep-alive", - } - self.request = Request(headers=headers, timeout=60) - - configurators = [self._read_config_vars] - for configurator in configurators: - self._config = configurator(name, **kwargs) - if self._config: - break - if self._config is None: - raise SessionConfigurationException("No Configuration Found.") - - # set up auth if passed - entrust_api_user: str | None = self.get_config("entrust_api_user") - entrust_api_key: str | None = self.get_config("entrust_api_key") - if entrust_api_user and entrust_api_key: - self.request.url_username = entrust_api_user - self.request.url_password = entrust_api_key - else: - raise SessionConfigurationException("User and key must be provided.") - - # set up client certificate if passed (support all-in one or cert + key) - entrust_api_cert: str | None = self.get_config("entrust_api_cert") - entrust_api_cert_key: str | None = self.get_config("entrust_api_cert_key") - if entrust_api_cert: - self.request.client_cert = entrust_api_cert - if entrust_api_cert_key: - self.request.client_key = entrust_api_cert_key - else: - raise SessionConfigurationException( - "Client certificate for authentication to the API must be provided." - ) - - # set up the spec - entrust_api_specification_path = self.get_config( - "entrust_api_specification_path" - ) - if not isinstance(entrust_api_specification_path, str): - raise SessionConfigurationException( - "entrust_api_specification_path must be a string." - ) - - if not entrust_api_specification_path.startswith("http") and not os.path.isfile( - entrust_api_specification_path - ): - raise SessionConfigurationException( - f"OpenAPI specification was not found at location {entrust_api_specification_path}." - ) - if not valid_file_format.match(entrust_api_specification_path): - raise SessionConfigurationException( - "OpenAPI specification filename must end in .json, .yml or .yaml" - ) - - self.verify = True - - if entrust_api_specification_path.startswith("http"): - try: - http_response = Request().open( - method="GET", url=entrust_api_specification_path - ) - http_response_contents = http_response.read() - if entrust_api_specification_path.endswith(".json"): - self._spec = json.load(http_response_contents) - elif entrust_api_specification_path.endswith( - ".yml" - ) or entrust_api_specification_path.endswith(".yaml"): - self._spec = yaml.safe_load(http_response_contents) - 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, "rb") as f: - if ".json" in entrust_api_specification_path: - self._spec = json.load(f) - elif ( - ".yml" in entrust_api_specification_path - or ".yaml" in entrust_api_specification_path - ): - self._spec = yaml.safe_load(f) - - def get_config(self, item: str) -> t.Any | None: - return self._config.get(item, None) - - def _read_config_vars(self, name: str, **kwargs) -> dict[str, t.Any]: - """Read configuration from variables passed to the module.""" - config = {} - - entrust_api_specification_path = kwargs.get("entrust_api_specification_path") - if not entrust_api_specification_path or ( - not entrust_api_specification_path.startswith("http") - and not os.path.isfile(entrust_api_specification_path) - ): - raise SessionConfigurationException( - f"Parameter provided for entrust_api_specification_path of value '{entrust_api_specification_path}'" - " was not a valid file path or HTTPS address." - ) - - for required_file in ["entrust_api_cert", "entrust_api_cert_key"]: - file_path = kwargs.get(required_file) - if not file_path or not os.path.isfile(file_path): - raise SessionConfigurationException( - f"Parameter provided for {required_file} of value '{file_path}' was not a valid file path." - ) - - for required_var in ["entrust_api_user", "entrust_api_key"]: - if not kwargs.get(required_var): - raise SessionConfigurationException( - f"Parameter provided for {required_var} was missing." - ) - - config["entrust_api_cert"] = kwargs.get("entrust_api_cert") - config["entrust_api_cert_key"] = kwargs.get("entrust_api_cert_key") - config["entrust_api_specification_path"] = kwargs.get( - "entrust_api_specification_path" - ) - config["entrust_api_user"] = kwargs.get("entrust_api_user") - config["entrust_api_key"] = kwargs.get("entrust_api_key") - - return config - - -def ECSClient( - entrust_api_user: str | None = None, - entrust_api_key: str | None = None, - entrust_api_cert: str | None = None, - entrust_api_cert_key: str | None = None, - entrust_api_specification_path: str | None = None, -) -> Resource: - """Create an ECS client""" - - if not YAML_FOUND: - raise SessionConfigurationException( - missing_required_lib("PyYAML") # TODO: pass `exception=YAML_IMP_ERR` - ) - - if entrust_api_specification_path is None: - entrust_api_specification_path = ( - "https://cloud.entrust.net/EntrustCloud/documentation/cms-api-2.1.0.yaml" - ) - - # Not functionally necessary with current uses of this module_util, but better to be explicit for future use cases - entrust_api_user = to_text(entrust_api_user) - entrust_api_key = to_text(entrust_api_key) - entrust_api_cert_key = to_text(entrust_api_cert_key) - entrust_api_specification_path = to_text(entrust_api_specification_path) - - return ECSSession( - "ecs", - entrust_api_user=entrust_api_user, - entrust_api_key=entrust_api_key, - entrust_api_cert=entrust_api_cert, - entrust_api_cert_key=entrust_api_cert_key, - entrust_api_specification_path=entrust_api_specification_path, - ).client() - - -__all__ = ( - "ecs_client_argument_spec", - "SessionConfigurationException", - "RestOperationException", - "ECSClient", -) diff --git a/plugins/modules/ecs_certificate.py b/plugins/modules/ecs_certificate.py deleted file mode 100644 index 3ec43a9d..00000000 --- a/plugins/modules/ecs_certificate.py +++ /dev/null @@ -1,1094 +0,0 @@ -#!/usr/bin/python -# Copyright (c), Entrust Datacard Corporation, 2019 -# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) -# SPDX-License-Identifier: GPL-3.0-or-later - -from __future__ import annotations - - -DOCUMENTATION = r""" -module: ecs_certificate -author: - - Chris Trufan (@ctrufan) -short_description: Request SSL/TLS certificates with the Entrust Certificate Services (ECS) API -description: - - Create, reissue, and renew certificates with the Entrust Certificate Services (ECS) API. - - Requires credentials for the L(Entrust Certificate Services,https://www.entrustdatacard.com/products/categories/ssl-certificates) - (ECS) API. - - In order to request a certificate, the domain and organization used in the certificate signing request must be already - validated in the ECS system. It is I(not) the responsibility of this module to perform those steps. -notes: - - O(path) must be specified as the output location of the certificate. -extends_documentation_fragment: - - community.crypto._attributes - - community.crypto._attributes.files - - community.crypto._cryptography_dep.minimum - - community.crypto._ecs_credential -attributes: - check_mode: - support: partial - details: - - Check mode is only supported if O(request_type=new). - diff_mode: - support: none - safe_file_operations: - support: full - idempotent: - support: partial - details: - - The module is not idempotent if O(force=true). - - Under which conditions the module is idempotent still needs to be determined. - If you are using this module and have more information, please contribute to - the documentation! -options: - backup: - description: - - Whether a backup should be made for the certificate in O(path). - type: bool - default: false - force: - description: - - If force is used, a certificate is requested regardless of whether O(path) points to an existing valid certificate. - - If O(request_type=renew), a forced renew will fail if the certificate being renewed has been issued within the past - 30 days, regardless of the value of O(remaining_days) or the return value of RV(cert_days) - the ECS API does not - support the "renew" operation for certificates that are not at least 30 days old. - type: bool - default: false - path: - description: - - The destination path for the generated certificate as a PEM encoded cert. - - If the certificate at this location is not an Entrust issued certificate, a new certificate will always be requested - even if the current certificate is technically valid. - - If there is already an Entrust certificate at this location, whether it is replaced is depends on the O(remaining_days) - calculation. - - If an existing certificate is being replaced (see O(remaining_days), O(force), and O(tracking_id)), whether a new - certificate is requested or the existing certificate is renewed or reissued is based on O(request_type). - type: path - required: true - full_chain_path: - description: - - The destination path for the full certificate chain of the certificate, intermediates, and roots. - type: path - csr: - description: - - Base-64 encoded Certificate Signing Request (CSR). O(csr) is accepted with or without PEM formatting around the Base-64 - string. - - If no O(csr) is provided when O(request_type=reissue) or O(request_type=renew), the certificate will be generated - with the same public key as the certificate being renewed or reissued. - - If O(subject_alt_name) is specified, it will override the subject alternate names in the CSR. - - If O(eku) is specified, it will override the extended key usage in the CSR. - - If O(ou) is specified, it will override the organizational units "ou=" present in the subject distinguished name of - the CSR, if any. - - The organization "O=" field from the CSR will not be used. It will be replaced in the issued certificate by O(org) - if present, and if not present, the organization tied to O(client_id). - type: str - tracking_id: - description: - - The tracking ID of the certificate to reissue or renew. - - O(tracking_id) is invalid if O(request_type=new) or O(request_type=validate_only). - - If there is a certificate present in O(path) and it is an ECS certificate, O(tracking_id) will be ignored. - - If there is no certificate present in O(path) or there is but it is from another provider, the certificate represented - by O(tracking_id) will be renewed or reissued and saved to O(path). - - If there is no certificate present in O(path) and the O(force) and O(remaining_days) parameters do not indicate a - new certificate is needed, the certificate referenced by O(tracking_id) certificate will be saved to O(path). - - This can be used when a known certificate is not currently present on a server, but you want to renew or reissue it - to be managed by an ansible playbook. For example, if you specify O(request_type=renew), O(tracking_id) of an issued - certificate, and O(path) to a file that does not exist, the first run of a task will download the certificate specified - by O(tracking_id) (assuming it is still valid). Future runs of the task will (if applicable - see O(force) and O(remaining_days)) - renew the certificate now present in O(path). - type: int - remaining_days: - description: - - The number of days the certificate must have left being valid. If RV(cert_days) < O(remaining_days) then a new certificate - will be obtained using O(request_type). - - If O(request_type=renew), a renewal will fail if the certificate being renewed has been issued within the past 30 - days, so do not set a O(remaining_days) value that is within 30 days of the full lifetime of the certificate being - acted upon. - - For example, if you are requesting Certificates with a 90 day lifetime, do not set O(remaining_days) to a value V(60) - or higher). - - The O(force) option may be used to ensure that a new certificate is always obtained. - type: int - default: 30 - request_type: - description: - - The operation performed if O(tracking_id) references a valid certificate to reissue, or there is already a certificate - present in O(path) but either O(force) is specified or RV(cert_days) < O(remaining_days). - - Specifying O(request_type=validate_only) means the request will be validated against the ECS API, but no certificate - will be issued. - - Specifying O(request_type=new) means a certificate request will always be submitted and a new certificate issued. - - Specifying O(request_type=renew) means that an existing certificate (specified by O(tracking_id) if present, otherwise - O(path)) will be renewed. If there is no certificate to renew, a new certificate is requested. - - Specifying O(request_type=reissue) means that an existing certificate (specified by O(tracking_id) if present, otherwise - O(path)) will be reissued. If there is no certificate to reissue, a new certificate is requested. - - If a certificate was issued within the past 30 days, the V(renew) operation is not a valid operation and will fail. - - Note that V(reissue) is an operation that will result in the revocation of the certificate that is reissued, be cautious - with its use. - - I(check_mode) is only supported if O(request_type=new). - - For example, setting O(request_type=renew) and O(remaining_days=30) and pointing to the same certificate on multiple - playbook runs means that on the first run new certificate will be requested. It will then be left along on future - runs until it is within 30 days of expiry, then the ECS "renew" operation will be performed. - type: str - choices: ['new', 'renew', 'reissue', 'validate_only'] - default: new - cert_type: - description: - - Specify the type of certificate requested. - - If a certificate is being reissued or renewed, this parameter is ignored, and the O(cert_type) of the initial certificate - is used. - type: str - choices: - - STANDARD_SSL - - ADVANTAGE_SSL - - UC_SSL - - EV_SSL - - WILDCARD_SSL - - PRIVATE_SSL - - PD_SSL - - CODE_SIGNING - - EV_CODE_SIGNING - - CDS_INDIVIDUAL - - CDS_GROUP - - CDS_ENT_LITE - - CDS_ENT_PRO - - SMIME_ENT - subject_alt_name: - description: - - The subject alternative name identifiers, as an array of values (applies to O(cert_type) with a value of V(STANDARD_SSL), - V(ADVANTAGE_SSL), V(UC_SSL), V(EV_SSL), V(WILDCARD_SSL), V(PRIVATE_SSL), and V(PD_SSL)). - - If you are requesting a new SSL certificate, and you pass a O(subject_alt_name) parameter, any SAN names in the CSR - are ignored. If no subjectAltName parameter is passed, the SAN names in the CSR are used. - - See O(request_type) to understand more about SANs during reissues and renewals. - - In the case of certificates of type V(STANDARD_SSL) certificates, if the CN of the certificate is . only - the www.. value is accepted. If the CN of the certificate is www.. only the . - value is accepted. - type: list - elements: str - eku: - description: - - If specified, overrides the key usage in the O(csr). - type: str - choices: [SERVER_AUTH, CLIENT_AUTH, SERVER_AND_CLIENT_AUTH] - ct_log: - description: - - In compliance with browser requirements, this certificate may be posted to the Certificate Transparency (CT) logs. - This is a best practice technique that helps domain owners monitor certificates issued to their domains. Note that - not all certificates are eligible for CT logging. - - If O(ct_log) is not specified, the certificate uses the account default. - - If O(ct_log) is specified and the account settings allow it, O(ct_log) overrides the account default. - - If O(ct_log) is set to V(false), but the account settings are set to "always log", the certificate generation will - fail. - type: bool - client_id: - description: - - The client ID to submit the Certificate Signing Request under. - - If no client ID is specified, the certificate will be submitted under the primary client with ID of 1. - - When using a client other than the primary client, the O(org) parameter cannot be specified. - - The issued certificate will have an organization value in the subject distinguished name represented by the client. - type: int - default: 1 - org: - description: - - Organization "O=" to include in the certificate. - - If O(org) is not specified, the organization from the client represented by O(client_id) is used. - - Unless the O(cert_type) is V(PD_SSL), this field may not be specified if the value of O(client_id) is not "1" (the - primary client). non-primary clients, certificates may only be issued with the organization of that client. - type: str - ou: - description: - - Organizational unit "OU=" to include in the certificate. - - O(ou) behavior is dependent on whether organizational units are enabled for your account. If organizational unit support - is disabled for your account, organizational units from the O(csr) and the O(ou) parameter are ignored. - - If both O(csr) and O(ou) are specified, the value in O(ou) will override the OU fields present in the subject distinguished - name in the O(csr). - - If neither O(csr) nor O(ou) are specified for a renew or reissue operation, the OU fields in the initial certificate - are reused. - - An invalid OU from O(csr) is ignored, but any invalid organizational units in O(ou) will result in an error indicating - "Unapproved OU". The O(ou) parameter can be used to force failure if an unapproved organizational unit is provided. - - A maximum of one OU may be specified for current products. Multiple OUs are reserved for future products. - type: list - elements: str - end_user_key_storage_agreement: - description: - - The end user of the Code Signing certificate must generate and store the private key for this request on cryptographically - secure hardware to be compliant with the Entrust CSP and Subscription agreement. If requesting a certificate of type - V(CODE_SIGNING) or V(EV_CODE_SIGNING), you must set O(end_user_key_storage_agreement) to true if and only if you acknowledge - that you will inform the user of this requirement. - - Applicable only to O(cert_type) of values V(CODE_SIGNING) and V(EV_CODE_SIGNING). - type: bool - tracking_info: - description: Free form tracking information to attach to the record for the certificate. - type: str - requester_name: - description: The requester name to associate with certificate tracking information. - type: str - required: true - requester_email: - description: The requester email to associate with certificate tracking information and receive delivery and expiry notices - for the certificate. - type: str - required: true - requester_phone: - description: The requester phone number to associate with certificate tracking information. - type: str - required: true - additional_emails: - description: A list of additional email addresses to receive the delivery notice and expiry notification for the certificate. - type: list - elements: str - custom_fields: - description: - - Mapping of custom fields to associate with the certificate request and certificate. - - Only supported if custom fields are enabled for your account. - - Each custom field specified must be a custom field you have defined for your account. - type: dict - suboptions: - text1: - description: Custom text field (maximum 500 characters). - type: str - text2: - description: Custom text field (maximum 500 characters). - type: str - text3: - description: Custom text field (maximum 500 characters). - type: str - text4: - description: Custom text field (maximum 500 characters). - type: str - text5: - description: Custom text field (maximum 500 characters). - type: str - text6: - description: Custom text field (maximum 500 characters). - type: str - text7: - description: Custom text field (maximum 500 characters). - type: str - text8: - description: Custom text field (maximum 500 characters). - type: str - text9: - description: Custom text field (maximum 500 characters). - type: str - text10: - description: Custom text field (maximum 500 characters). - type: str - text11: - description: Custom text field (maximum 500 characters). - type: str - text12: - description: Custom text field (maximum 500 characters). - type: str - text13: - description: Custom text field (maximum 500 characters). - type: str - text14: - description: Custom text field (maximum 500 characters). - type: str - text15: - description: Custom text field (maximum 500 characters). - type: str - number1: - description: Custom number field. - type: float - number2: - description: Custom number field. - type: float - number3: - description: Custom number field. - type: float - number4: - description: Custom number field. - type: float - number5: - description: Custom number field. - type: float - date1: - description: Custom date field. - type: str - date2: - description: Custom date field. - type: str - date3: - description: Custom date field. - type: str - date4: - description: Custom date field. - type: str - date5: - description: Custom date field. - type: str - email1: - description: Custom email field. - type: str - email2: - description: Custom email field. - type: str - email3: - description: Custom email field. - type: str - email4: - description: Custom email field. - type: str - email5: - description: Custom email field. - type: str - dropdown1: - description: Custom dropdown field. - type: str - dropdown2: - description: Custom dropdown field. - type: str - dropdown3: - description: Custom dropdown field. - type: str - dropdown4: - description: Custom dropdown field. - type: str - dropdown5: - description: Custom dropdown field. - type: str - cert_expiry: - description: - - The date the certificate should be set to expire, in RFC3339 compliant date or date-time format. For example, V(2020-02-23), - V(2020-02-23T15:00:00.05Z). - - O(cert_expiry) is only supported for requests of O(request_type=new) or O(request_type=renew). If O(request_type=reissue), - O(cert_expiry) will be used for the first certificate issuance, but subsequent issuances will have the same expiry - as the initial certificate. - - A reissued certificate will always have the same expiry as the original certificate. - - Note that only the date (day, month, year) is supported for specifying the expiry date. If you choose to specify an - expiry time with the expiry date, the time will be adjusted to Eastern Standard Time (EST). This could have the unintended - effect of moving your expiry date to the previous day. - - Applies only to accounts with a pooling inventory model. - - Only one of O(cert_expiry) or O(cert_lifetime) may be specified. - type: str - cert_lifetime: - description: - - The lifetime of the certificate. - - Applies to all certificates for accounts with a non-pooling inventory model. - - O(cert_lifetime) is only supported for requests of O(request_type=new) or O(request_type=renew). If O(request_type=reissue), - O(cert_lifetime) will be used for the first certificate issuance, but subsequent issuances will have the same expiry - as the initial certificate. - - Applies to certificates of O(cert_type=CDS_INDIVIDUAL), V(CDS_GROUP), V(CDS_ENT_LITE), V(CDS_ENT_PRO), or V(SMIME_ENT) - for accounts with a pooling inventory model. - - V(P1Y) is a certificate with a 1 year lifetime. - - V(P2Y) is a certificate with a 2 year lifetime. - - V(P3Y) is a certificate with a 3 year lifetime. - - Only one of O(cert_expiry) or O(cert_lifetime) may be specified. - type: str - choices: [P1Y, P2Y, P3Y] -seealso: - - module: community.crypto.openssl_privatekey - description: Can be used to create private keys (both for certificates and accounts). - - module: community.crypto.openssl_csr - description: Can be used to create a Certificate Signing Request (CSR). - - plugin: community.crypto.to_serial - plugin_type: filter -""" - -EXAMPLES = r""" ---- -- name: Request a new certificate from Entrust with bare minimum parameters. Will request a new certificate if current one - is valid but within 30 days of expiry. If replacing an existing file in path, will back it up. - community.crypto.ecs_certificate: - backup: true - path: /etc/ssl/crt/ansible.com.crt - full_chain_path: /etc/ssl/crt/ansible.com.chain.crt - csr: /etc/ssl/csr/ansible.com.csr - cert_type: EV_SSL - requester_name: Jo Doe - requester_email: jdoe@ansible.com - requester_phone: 555-555-5555 - entrust_api_user: apiusername - entrust_api_key: a^lv*32!cd9LnT - entrust_api_client_cert_path: /etc/ssl/entrust/ecs-client.crt - entrust_api_client_cert_key_path: /etc/ssl/entrust/ecs-client.key - -- name: If there is no certificate present in path, request a new certificate of type EV_SSL. Otherwise, if there is an - Entrust managed certificate in path and it is within 63 days of expiration, request a renew of that certificate. - community.crypto.ecs_certificate: - path: /etc/ssl/crt/ansible.com.crt - csr: /etc/ssl/csr/ansible.com.csr - cert_type: EV_SSL - cert_expiry: '2020-08-20' - request_type: renew - remaining_days: 63 - requester_name: Jo Doe - requester_email: jdoe@ansible.com - requester_phone: 555-555-5555 - entrust_api_user: apiusername - entrust_api_key: a^lv*32!cd9LnT - entrust_api_client_cert_path: /etc/ssl/entrust/ecs-client.crt - entrust_api_client_cert_key_path: /etc/ssl/entrust/ecs-client.key - -- name: If there is no certificate present in path, download certificate specified by tracking_id if it is still valid. - Otherwise, if the certificate is within 79 days of expiration, request a renew of that certificate and save it in path. - This can be used to "migrate" a certificate to be Ansible managed. - community.crypto.ecs_certificate: - path: /etc/ssl/crt/ansible.com.crt - csr: /etc/ssl/csr/ansible.com.csr - tracking_id: 2378915 - request_type: renew - remaining_days: 79 - entrust_api_user: apiusername - entrust_api_key: a^lv*32!cd9LnT - entrust_api_client_cert_path: /etc/ssl/entrust/ecs-client.crt - entrust_api_client_cert_key_path: /etc/ssl/entrust/ecs-client.key - -- name: Force a reissue of the certificate specified by tracking_id. - community.crypto.ecs_certificate: - path: /etc/ssl/crt/ansible.com.crt - force: true - tracking_id: 2378915 - request_type: reissue - entrust_api_user: apiusername - entrust_api_key: a^lv*32!cd9LnT - entrust_api_client_cert_path: /etc/ssl/entrust/ecs-client.crt - entrust_api_client_cert_key_path: /etc/ssl/entrust/ecs-client.key - -- name: Request a new certificate with an alternative client. Note that the issued certificate will have its Subject Distinguished - Name use the organization details associated with that client, rather than what is in the CSR. - community.crypto.ecs_certificate: - path: /etc/ssl/crt/ansible.com.crt - csr: /etc/ssl/csr/ansible.com.csr - client_id: 2 - requester_name: Jo Doe - requester_email: jdoe@ansible.com - requester_phone: 555-555-5555 - entrust_api_user: apiusername - entrust_api_key: a^lv*32!cd9LnT - entrust_api_client_cert_path: /etc/ssl/entrust/ecs-client.crt - entrust_api_client_cert_key_path: /etc/ssl/entrust/ecs-client.key - -- name: Request a new certificate with a number of CSR parameters overridden and tracking information - community.crypto.ecs_certificate: - path: /etc/ssl/crt/ansible.com.crt - full_chain_path: /etc/ssl/crt/ansible.com.chain.crt - csr: /etc/ssl/csr/ansible.com.csr - subject_alt_name: - - ansible.testcertificates.com - - www.testcertificates.com - eku: SERVER_AND_CLIENT_AUTH - ct_log: true - org: Test Organization Inc. - ou: - - Administration - tracking_info: "Submitted via Ansible" - additional_emails: - - itsupport@testcertificates.com - - jsmith@ansible.com - custom_fields: - text1: Admin - text2: Invoice 25 - number1: 342 - date1: '2018-01-01' - email1: sales@ansible.testcertificates.com - dropdown1: red - cert_expiry: '2020-08-15' - requester_name: Jo Doe - requester_email: jdoe@ansible.com - requester_phone: 555-555-5555 - entrust_api_user: apiusername - entrust_api_key: a^lv*32!cd9LnT - entrust_api_client_cert_path: /etc/ssl/entrust/ecs-client.crt - entrust_api_client_cert_key_path: /etc/ssl/entrust/ecs-client.key -""" - -RETURN = r""" -filename: - description: The destination path for the generated certificate. - returned: changed or success - type: str - sample: /etc/ssl/crt/www.ansible.com.crt -backup_file: - description: Name of backup file created for the certificate. - returned: changed and if O(backup) is V(true) - type: str - sample: /path/to/www.ansible.com.crt.2019-03-09@11:22~ -backup_full_chain_file: - description: Name of the backup file created for the certificate chain. - returned: changed and if O(backup) is V(true) and O(full_chain_path) is set. - type: str - sample: /path/to/ca.chain.crt.2019-03-09@11:22~ -tracking_id: - description: The tracking ID to reference and track the certificate in ECS. - returned: success - type: int - sample: 380079 -serial_number: - description: - - The serial number of the issued certificate. - - This return value is an B(integer). If you need the serial numbers as a colon-separated hex string, such as C(11:22:33), - you need to convert it to that form with P(community.crypto.to_serial#filter). - returned: success - type: int - sample: 1235262234164342 -cert_days: - description: The number of days the certificate remains valid. - returned: success - type: int - sample: 253 -cert_status: - description: - - The certificate status in ECS. - - 'Current possible values (which may be expanded in the future) are: V(ACTIVE), V(APPROVED), V(DEACTIVATED), V(DECLINED), - V(EXPIRED), V(NA), V(PENDING), V(PENDING_QUORUM), V(READY), V(REISSUED), V(REISSUING), V(RENEWED), V(RENEWING), V(REVOKED), - V(SUSPENDED).' - returned: success - type: str - sample: ACTIVE -cert_details: - description: - - The full response JSON from the Get Certificate call of the ECS API. - - While the response contents are guaranteed to be forwards compatible with new ECS API releases, Entrust recommends that - you do not make any playbooks take actions based on the content of this field. However it may be useful for debugging, - logging, or auditing purposes. - returned: success - type: dict -""" - -import datetime -import os -import re -import time -import typing as t - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.common.text.converters import to_bytes -from ansible_collections.community.crypto.plugins.module_utils._crypto.support import ( - load_certificate, -) -from ansible_collections.community.crypto.plugins.module_utils._cryptography_dep import ( - COLLECTION_MINIMUM_CRYPTOGRAPHY_VERSION, - assert_required_cryptography_version, -) -from ansible_collections.community.crypto.plugins.module_utils._ecs.api import ( - ECSClient, - RestOperationException, - SessionConfigurationException, - ecs_client_argument_spec, -) -from ansible_collections.community.crypto.plugins.module_utils._io import write_file - - -MINIMAL_CRYPTOGRAPHY_VERSION = COLLECTION_MINIMUM_CRYPTOGRAPHY_VERSION - - -def validate_cert_expiry(cert_expiry: str) -> bool: - search_string_partial = re.compile( - r"^([0-9]+)-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])\Z" - ) - search_string_full = re.compile( - r"^([0-9]+)-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])[Tt]([01][0-9]|2[0-3]):([0-5][0-9]):" - r"([0-5][0-9]|60)(.[0-9]+)?(([Zz])|([+|-]([01][0-9]|2[0-3]):[0-5][0-9]))\Z" - ) - if search_string_partial.match(cert_expiry) or search_string_full.match( - cert_expiry - ): - return True - return False - - -def calculate_cert_days(expires_after: str | None) -> int: - cert_days = 0 - if expires_after is not None: - expires_after_datetime = datetime.datetime.strptime( - expires_after, "%Y-%m-%dT%H:%M:%SZ" - ) - cert_days = (expires_after_datetime - datetime.datetime.now()).days - return cert_days - - -# Populate the value of body[dict_param_name] with the JSON equivalent of -# module parameter of param_name if that parameter is present, otherwise leave field -# out of resulting dict -def convert_module_param_to_json_bool( - module: AnsibleModule, dict_param_name: str, param_name: str -) -> dict[str, str]: - body = {} - if module.params[param_name] is not None: - if module.params[param_name]: - body[dict_param_name] = "true" - else: - body[dict_param_name] = "false" - return body - - -class EcsCertificate: - """ - Entrust Certificate Services certificate class. - """ - - def __init__(self, module: AnsibleModule) -> None: - self.path: str = module.params["path"] - self.full_chain_path: str | None = module.params["full_chain_path"] - self.force: bool = module.params["force"] - self.backup: bool = module.params["backup"] - self.request_type: t.Literal["new", "renew", "reissue", "validate_only"] = ( - module.params["request_type"] - ) - self.csr: str | None = module.params["csr"] - - # All return values - self.changed = False - self.filename: str | None = None - self.tracking_id: int | None = None - self.cert_status: str | None = None - self.serial_number: int | None = None - self.cert_days: int | None = None - self.cert_details: dict[str, t.Any] | None = None - self.backup_file: str | None = None - self.backup_full_chain_file: str | None = None - - self.cert = None - if self.path and os.path.exists(self.path): - try: - self.cert = load_certificate(path=self.path) - except Exception: - pass - # Instantiate the ECS client and then try a no-op connection to verify credentials are valid - try: - self.ecs_client = ECSClient( - entrust_api_user=module.params["entrust_api_user"], - entrust_api_key=module.params["entrust_api_key"], - entrust_api_cert=module.params["entrust_api_client_cert_path"], - entrust_api_cert_key=module.params["entrust_api_client_cert_key_path"], - entrust_api_specification_path=module.params[ - "entrust_api_specification_path" - ], - ) - except SessionConfigurationException as e: - module.fail_json(msg=f"Failed to initialize Entrust Provider: {e}") - try: - self.ecs_client.GetAppVersion() # type: ignore[attr-defined] # pylint: disable=no-member - except RestOperationException as e: - module.fail_json( - msg=f"Please verify credential information. Received exception when testing ECS connection: {e.message}" - ) - - # Conversion of the fields that go into the 'tracking' parameter of the request object - def convert_tracking_params(self, module: AnsibleModule) -> dict[str, t.Any]: - body = {} - tracking = {} - if module.params["requester_name"]: - tracking["requesterName"] = module.params["requester_name"] - if module.params["requester_email"]: - tracking["requesterEmail"] = module.params["requester_email"] - if module.params["requester_phone"]: - tracking["requesterPhone"] = module.params["requester_phone"] - if module.params["tracking_info"]: - tracking["trackingInfo"] = module.params["tracking_info"] - if module.params["custom_fields"]: - # Omit custom fields from submitted dict if not present, instead of submitting them with value of 'null' - # The ECS API does technically accept null without error, but it complicates debugging user escalations and is unnecessary bandwidth. - custom_fields = {} - for k, v in module.params["custom_fields"].items(): - if v is not None: - custom_fields[k] = v - tracking["customFields"] = custom_fields - if module.params["additional_emails"]: - tracking["additionalEmails"] = module.params["additional_emails"] - body["tracking"] = tracking - return body - - def convert_cert_subject_params(self, module: AnsibleModule) -> dict[str, t.Any]: - body = {} - if module.params["subject_alt_name"]: - body["subjectAltName"] = module.params["subject_alt_name"] - if module.params["org"]: - body["org"] = module.params["org"] - if module.params["ou"]: - body["ou"] = module.params["ou"] - return body - - def convert_general_params(self, module: AnsibleModule) -> dict[str, t.Any]: - body = {} - if module.params["eku"]: - body["eku"] = module.params["eku"] - if self.request_type == "new": - body["certType"] = module.params["cert_type"] - body["clientId"] = module.params["client_id"] - body.update(convert_module_param_to_json_bool(module, "ctLog", "ct_log")) - body.update( - convert_module_param_to_json_bool( - module, "endUserKeyStorageAgreement", "end_user_key_storage_agreement" - ) - ) - return body - - def convert_expiry_params(self, module: AnsibleModule) -> dict[str, t.Any]: - body = {} - if module.params["cert_lifetime"]: - body["certLifetime"] = module.params["cert_lifetime"] - elif module.params["cert_expiry"]: - body["certExpiryDate"] = module.params["cert_expiry"] - # If neither cerTLifetime or certExpiryDate was specified and the request type is new, default to 365 days - elif self.request_type != "reissue": - gmt_now = datetime.datetime.fromtimestamp(time.mktime(time.gmtime())) - expiry = gmt_now + datetime.timedelta(days=365) - body["certExpiryDate"] = expiry.strftime("%Y-%m-%dT%H:%M:%S.00Z") - return body - - def set_tracking_id_by_serial_number(self, module: AnsibleModule) -> None: - assert self.cert is not None - try: - # Use serial_number to identify if certificate is an Entrust Certificate - # with an associated tracking ID - serial_number = f"{self.cert.serial_number:X}" - cert_results = self.ecs_client.GetCertificates( # type: ignore[attr-defined] # pylint: disable=no-member - serialNumber=serial_number - ).get( - "certificates", {} - ) - if len(cert_results) == 1: - self.tracking_id = cert_results[0].get("trackingId") - except RestOperationException: - # If we fail to find a cert by serial number, that's fine, we just do not set self.tracking_id - pass - - def set_cert_details(self, module: AnsibleModule) -> None: - try: - self.cert_details = self.ecs_client.GetCertificate( # type: ignore[attr-defined] # pylint: disable=no-member - trackingId=self.tracking_id - ) - assert self.cert_details is not None - self.cert_status = self.cert_details.get("status") - self.serial_number = self.cert_details.get("serialNumber") - self.cert_days = calculate_cert_days(self.cert_details.get("expiresAfter")) - except RestOperationException as e: - module.fail_json( - msg=f'Failed to get details of certificate with tracking_id="{self.tracking_id}", Error: {e.message}' - ) - - def check(self, module: AnsibleModule) -> bool: - if self.cert: - # We will only treat a certificate as valid if it is found as a managed entrust cert. - # We will only set updated tracking ID based on certificate in "path" if it is managed by entrust. - self.set_tracking_id_by_serial_number(module) - - if ( - module.params["tracking_id"] - and self.tracking_id - and module.params["tracking_id"] != self.tracking_id - ): - module.warn( - f'tracking_id parameter of "{module.params["tracking_id"]}" provided, but will be ignored.' - f' Valid certificate was present in path "{self.path}" with ' - f'tracking_id of "{self.tracking_id}".' - ) - - # If we did not end up setting tracking_id based on existing cert, get from module params - if not self.tracking_id: - self.tracking_id = module.params["tracking_id"] - - if not self.tracking_id: - return False - - self.set_cert_details(module) - assert self.cert_details is not None - - if self.cert_status in ("EXPIRED", "SUSPENDED", "REVOKED"): - return False - if self.cert_days < module.params["remaining_days"]: - return False - - return True - - def request_cert(self, module: AnsibleModule) -> None: - if not self.check(module) or self.force: - body = {} - - # Read the CSR contents - if self.csr and os.path.exists(self.csr): - with open(self.csr, "r", encoding="utf-8") as csr_file: - body["csr"] = csr_file.read() - - # Check if the path is already a cert - # tracking_id may be set as a parameter or by get_cert_details if an entrust cert is in 'path'. If tracking ID is null - # We will be performing a reissue operation. - if self.request_type != "new" and not self.tracking_id: - module.warn( - f"No existing Entrust certificate found in path={self.path}" - ' and no tracking_id was provided, setting request_type to "new" for this task' - "run. Future playbook runs that point to the pathination file" - f" in {self.path} will use request_type={self.request_type}" - ) - self.request_type = "new" - elif self.request_type == "new" and self.tracking_id: - module.warn( - 'Existing certificate being acted upon, but request_type is "new", so will be a new certificate issuance rather than a' - "reissue or renew" - ) - # Use cases where request type is new and no existing certificate, or where request type is reissue/renew and a valid - # existing certificate is found, do not need warnings. - - body.update(self.convert_tracking_params(module)) - body.update(self.convert_cert_subject_params(module)) - body.update(self.convert_general_params(module)) - body.update(self.convert_expiry_params(module)) - - if not module.check_mode: - try: - if self.request_type == "validate_only": - body["validateOnly"] = "true" - result = self.ecs_client.NewCertRequest( # type: ignore[attr-defined] # pylint: disable=no-member - Body=body - ) - if self.request_type == "new": - result = self.ecs_client.NewCertRequest( # type: ignore[attr-defined] # pylint: disable=no-member - Body=body - ) - elif self.request_type == "renew": - result = self.ecs_client.RenewCertRequest( # type: ignore[attr-defined] # pylint: disable=no-member - trackingId=self.tracking_id, Body=body - ) - elif self.request_type == "reissue": - 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 - except RestOperationException as e: - module.fail_json( - msg=f"Failed to request new certificate from Entrust (ECS) {e.message}" - ) - - if self.request_type != "validate_only": - if self.backup: - self.backup_file = module.backup_local(self.path) - write_file( - module=module, - content=to_bytes(self.cert_details.get("endEntityCert")), - ) - chain_certs = self.cert_details.get("chainCerts") - if self.full_chain_path and chain_certs: - assert isinstance(chain_certs, list) - if self.backup: - self.backup_full_chain_file = module.backup_local( - self.full_chain_path - ) - chain_string = "\n".join(chain_certs) + "\n" - write_file( - module=module, - content=to_bytes(chain_string), - path=self.full_chain_path, - ) - self.changed = True - # If there is no certificate present in path but a tracking ID was specified, save it to disk - elif not os.path.exists(self.path) and self.tracking_id: - if not module.check_mode: - assert self.cert_details is not None - write_file( - module=module, - content=to_bytes(self.cert_details.get("endEntityCert")), - ) - chain_certs = self.cert_details.get("chainCerts") - if self.full_chain_path and chain_certs: - assert isinstance(chain_certs, list) - chain_string = "\n".join(chain_certs) + "\n" - write_file( - module=module, - content=to_bytes(chain_string), - path=self.full_chain_path, - ) - self.changed = True - - def dump(self) -> dict[str, t.Any]: - result = { - "changed": self.changed, - "filename": self.path, - "tracking_id": self.tracking_id, - "cert_status": self.cert_status, - "serial_number": self.serial_number, - "cert_days": self.cert_days, - "cert_details": self.cert_details, - } - if self.backup_file: - result["backup_file"] = self.backup_file - result["backup_full_chain_file"] = self.backup_full_chain_file - return result - - -def custom_fields_spec() -> dict[str, dict[str, 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 { - "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", - "EV_SSL", - "WILDCARD_SSL", - "PRIVATE_SSL", - "PD_SSL", - "CODE_SIGNING", - "EV_CODE_SIGNING", - "CDS_INDIVIDUAL", - "CDS_GROUP", - "CDS_ENT_LITE", - "CDS_ENT_PRO", - "SMIME_ENT", - ], - }, - "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: - ecs_argument_spec = ecs_client_argument_spec() - ecs_argument_spec.update(ecs_certificate_argument_spec()) - module = AnsibleModule( - argument_spec=ecs_argument_spec, - required_if=( - ["request_type", "new", ["cert_type"]], - ["request_type", "validate_only", ["cert_type"]], - ["cert_type", "CODE_SIGNING", ["end_user_key_storage_agreement"]], - ["cert_type", "EV_CODE_SIGNING", ["end_user_key_storage_agreement"]], - ), - mutually_exclusive=(["cert_expiry", "cert_lifetime"],), - supports_check_mode=True, - ) - - assert_required_cryptography_version( - module, minimum_cryptography_version=MINIMAL_CRYPTOGRAPHY_VERSION - ) - - # If validate_only is used, pointing to an existing tracking_id is an invalid operation - if module.params["tracking_id"]: - if ( - module.params["request_type"] == "new" - or module.params["request_type"] == "validate_only" - ): - module.fail_json( - msg=f'The tracking_id field is invalid when request_type="{module.params["request_type"]}".' - ) - - # A reissued request can not specify an expiration date or lifetime - if module.params["request_type"] == "reissue": - if module.params["cert_expiry"]: - module.fail_json( - msg='The cert_expiry field is invalid when request_type="reissue".' - ) - elif module.params["cert_lifetime"]: - module.fail_json( - msg='The cert_lifetime field is invalid when request_type="reissue".' - ) - # Reissued or renew request can omit the CSR - elif module.params["request_type"] != "renew": - module_params_csr = module.params["csr"] - if module_params_csr is None: - module.fail_json( - msg=f"The csr field is required when request_type={module.params['request_type']}" - ) - elif not os.path.exists(module_params_csr): - module.fail_json( - msg=f"The csr field of {module_params_csr} was not a valid path." - f" csr is required when request_type={module.params['request_type']}" - ) - - if module.params["ou"] and len(module.params["ou"]) > 1: - module.fail_json(msg='Multiple "ou" values are not currently supported.') - - if module.params["end_user_key_storage_agreement"]: - if ( - module.params["cert_type"] != "CODE_SIGNING" - and module.params["cert_type"] != "EV_CODE_SIGNING" - ): - module.fail_json( - msg='Parameter "end_user_key_storage_agreement" is valid only for cert_types "CODE_SIGNING" and "EV_CODE_SIGNING"' - ) - - if ( - module.params["org"] - and module.params["client_id"] != 1 - and module.params["cert_type"] != "PD_SSL" - ): - module.fail_json( - msg='The "org" parameter is not supported when client_id parameter is set to a value other than 1, unless cert_type is "PD_SSL".' - ) - - if module.params["cert_expiry"]: - if not validate_cert_expiry(module.params["cert_expiry"]): - module.fail_json( - msg=f'The "cert_expiry" parameter of "{module.params["cert_expiry"]}" is not a valid date or date-time' - ) - - certificate = EcsCertificate(module) - certificate.request_cert(module) - result = certificate.dump() - module.exit_json(**result) - - -if __name__ == "__main__": - main() diff --git a/plugins/modules/ecs_domain.py b/plugins/modules/ecs_domain.py deleted file mode 100644 index 1372d3c1..00000000 --- a/plugins/modules/ecs_domain.py +++ /dev/null @@ -1,481 +0,0 @@ -#!/usr/bin/python -# Copyright 2019 Entrust Datacard Corporation. -# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) -# SPDX-License-Identifier: GPL-3.0-or-later - -from __future__ import annotations - - -DOCUMENTATION = r""" -module: ecs_domain -author: - - Chris Trufan (@ctrufan) -version_added: '1.0.0' -short_description: Request validation of a domain with the Entrust Certificate Services (ECS) API -description: - - Request validation or re-validation of a domain with the Entrust Certificate Services (ECS) API. - - Requires credentials for the L(Entrust Certificate Services,https://www.entrustdatacard.com/products/categories/ssl-certificates) - (ECS) API. - - If the domain is already in the validation process, no new validation will be requested, but the validation data (if applicable) - will be returned. - - If the domain is already in the validation process but the O(verification_method) specified is different than the current - O(verification_method), the O(verification_method) will be updated and validation data (if applicable) will be returned. - - If the domain is an active, validated domain, the return value of C(changed) will be false, unless RV(domain_status=EXPIRED), - in which case a re-validation will be performed. - - If O(verification_method=dns), details about the required DNS entry will be specified in the return parameters RV(dns_contents), - RV(dns_location), and RV(dns_resource_type). - - If O(verification_method=web_server), details about the required file details will be specified in the return parameters - RV(file_contents) and RV(file_location). - - If O(verification_method=email), the email address(es) that the validation email(s) were sent to will be in the return - parameter RV(emails). This is purely informational. For domains requested using this module, this will always be a list - of size 1. -notes: - - There is a small delay (typically about 5 seconds, but can be as long as 60 seconds) before obtaining the random values - when requesting a validation while O(verification_method=dns) or O(verification_method=web_server). Be aware of that if - doing many domain validation requests. -extends_documentation_fragment: - - community.crypto._attributes - - community.crypto._ecs_credential -attributes: - check_mode: - support: none - diff_mode: - support: none - idempotent: - support: partial - details: - - Under which conditions the module is idempotent still needs to be determined. - If you are using this module and have more information, please contribute to - the documentation! -options: - client_id: - description: - - The client ID to request the domain be associated with. - - If no client ID is specified, the domain will be added under the primary client with ID of 1. - type: int - default: 1 - domain_name: - description: - - The domain name to be verified or reverified. - type: str - required: true - verification_method: - description: - - The verification method to be used to prove control of the domain. - - If O(verification_method=email) and the value O(verification_email) is specified, that value is used for the email - validation. If O(verification_email) is not provided, the first value present in WHOIS data will be used. An email - will be sent to the address in O(verification_email) with instructions on how to verify control of the domain. - - If O(verification_method=dns), the value RV(dns_contents) must be stored in location RV(dns_location), with a DNS - record type of RV(dns_resource_type). To prove domain ownership, update your DNS records so the text string returned - by RV(dns_contents) is available at RV(dns_location). - - If O(verification_method=web_server), the contents of return value RV(file_contents) must be made available on a web - server accessible at location RV(file_location). - - If O(verification_method=manual), the domain will be validated with a manual process. This is not recommended. - type: str - choices: ['dns', 'email', 'manual', 'web_server'] - required: true - verification_email: - description: - - Email address to be used to verify domain ownership. - - 'Email address must be either an email address present in the WHOIS data for O(domain_name), or one of the following - constructed emails: admin@O(domain_name), administrator@O(domain_name), webmaster@O(domain_name), hostmaster@O(domain_name), - postmaster@O(domain_name).' - - Note that if O(domain_name) includes subdomains, the top level domain should be used. For example, if requesting validation - of example1.ansible.com, or test.example2.ansible.com, and you want to use the "admin" preconstructed name, the email - address should be admin@ansible.com. - - If using the email values from the WHOIS data for the domain or its top level namespace, they must be exact matches. - - If O(verification_method=email) but O(verification_email) is not provided, the first email address found in WHOIS - data for the domain will be used. - - To verify domain ownership, domain owner must follow the instructions in the email they receive. - - Only allowed if O(verification_method=email). - type: str -seealso: - - module: community.crypto.x509_certificate - description: Can be used to request certificates from ECS, with O(community.crypto.x509_certificate#module:provider=entrust). - - module: community.crypto.ecs_certificate - description: Can be used to request a Certificate from ECS using a verified domain. -""" - -EXAMPLES = r""" ---- -- name: Request domain validation using email validation for client ID of 2. - community.crypto.ecs_domain: - domain_name: ansible.com - client_id: 2 - verification_method: email - verification_email: admin@ansible.com - entrust_api_user: apiusername - entrust_api_key: a^lv*32!cd9LnT - entrust_api_client_cert_path: /etc/ssl/entrust/ecs-client.crt - entrust_api_client_cert_key_path: /etc/ssl/entrust/ecs-client.key - -- name: Request domain validation using DNS. If domain is already valid, request revalidation if expires within 90 days - community.crypto.ecs_domain: - domain_name: ansible.com - verification_method: dns - entrust_api_user: apiusername - entrust_api_key: a^lv*32!cd9LnT - entrust_api_client_cert_path: /etc/ssl/entrust/ecs-client.crt - entrust_api_client_cert_key_path: /etc/ssl/entrust/ecs-client.key - -- name: Request domain validation using web server validation, and revalidate if fewer than 60 days remaining of EV eligibility. - community.crypto.ecs_domain: - domain_name: ansible.com - verification_method: web_server - entrust_api_user: apiusername - entrust_api_key: a^lv*32!cd9LnT - entrust_api_client_cert_path: /etc/ssl/entrust/ecs-client.crt - entrust_api_client_cert_key_path: /etc/ssl/entrust/ecs-client.key - -- name: Request domain validation using manual validation. - community.crypto.ecs_domain: - domain_name: ansible.com - verification_method: manual - entrust_api_user: apiusername - entrust_api_key: a^lv*32!cd9LnT - entrust_api_client_cert_path: /etc/ssl/entrust/ecs-client.crt - entrust_api_client_cert_key_path: /etc/ssl/entrust/ecs-client.key -""" - -RETURN = r""" -domain_status: - description: Status of the current domain. Will be one of V(APPROVED), V(DECLINED), V(CANCELLED), V(INITIAL_VERIFICATION), - V(DECLINED), V(CANCELLED), V(RE_VERIFICATION), V(EXPIRED), V(EXPIRING). - returned: changed or success - type: str - sample: APPROVED -verification_method: - description: Verification method used to request the domain validation. If C(changed) will be the same as O(verification_method) - input parameter. - returned: changed or success - type: str - sample: dns -file_location: - description: The location that ECS will be expecting to be able to find the file for domain verification, containing the - contents of RV(file_contents). - returned: O(verification_method) is V(web_server) - type: str - sample: http://ansible.com/.well-known/pki-validation/abcd.txt -file_contents: - description: The contents of the file that ECS will be expecting to find at RV(file_location). - returned: O(verification_method) is V(web_server) - type: str - sample: AB23CD41432522FF2526920393982FAB -emails: - description: - - The list of emails used to request validation of this domain. - - Domains requested using this module will only have a list of size 1. - returned: O(verification_method) is V(email) - type: list - sample: [admin@ansible.com, administrator@ansible.com] -dns_location: - description: The location that ECS will be expecting to be able to find the DNS entry for domain verification, containing - the contents of RV(dns_contents). - returned: changed and if O(verification_method) is V(dns) - type: str - sample: _pki-validation.ansible.com -dns_contents: - description: The value that ECS will be expecting to find in the DNS record located at RV(dns_location). - returned: changed and if O(verification_method) is V(dns) - type: str - sample: AB23CD41432522FF2526920393982FAB -dns_resource_type: - description: The type of resource record that ECS will be expecting for the DNS record located at RV(dns_location). - returned: changed and if O(verification_method) is V(dns) - type: str - sample: TXT -client_id: - description: Client ID that the domain belongs to. If the input value O(client_id) is specified, this will always be the - same as O(client_id). - returned: changed or success - type: int - sample: 1 -ov_eligible: - description: Whether the domain is eligible for submission of "OV" certificates. Will never be V(false) if RV(ev_eligible) - is V(true). - returned: success and RV(domain_status) is V(APPROVED), V(RE_VERIFICATION), V(EXPIRING), or V(EXPIRED). - type: bool - sample: true -ov_days_remaining: - description: The number of days the domain remains eligible for submission of "OV" certificates. Will never be less than - the value of RV(ev_days_remaining). - returned: success and RV(ov_eligible) is V(true) and RV(domain_status) is V(APPROVED), V(RE_VERIFICATION) or V(EXPIRING). - type: int - sample: 129 -ev_eligible: - description: Whether the domain is eligible for submission of "EV" certificates. Will never be V(true) if RV(ov_eligible) - is V(false). - returned: success and RV(domain_status) is V(APPROVED), V(RE_VERIFICATION) or V(EXPIRING), or V(EXPIRED). - type: bool - sample: true -ev_days_remaining: - description: The number of days the domain remains eligible for submission of "EV" certificates. Will never be greater than - the value of RV(ov_days_remaining). - returned: success and RV(ev_eligible) is V(true) and RV(domain_status) is V(APPROVED), V(RE_VERIFICATION) or V(EXPIRING). - type: int - sample: 94 -""" - -import datetime -import time -import typing as t - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.crypto.plugins.module_utils._ecs.api import ( - ECSClient, - RestOperationException, - SessionConfigurationException, - ecs_client_argument_spec, -) - - -@t.overload -def calculate_days_remaining(expiry_date: str) -> int: ... - - -@t.overload -def calculate_days_remaining(expiry_date: str | None) -> int | None: ... - - -def calculate_days_remaining(expiry_date: str | None) -> int | None: - days_remaining = None - if expiry_date is not None: - expiry_datetime = datetime.datetime.strptime(expiry_date, "%Y-%m-%dT%H:%M:%SZ") - days_remaining = (expiry_datetime - datetime.datetime.now()).days - return days_remaining - - -class EcsDomain: - """ - Entrust Certificate Services domain class. - """ - - def __init__(self, module: AnsibleModule) -> None: - self.changed = False - self.domain_status = None - self.verification_method = None - self.file_location = None - self.file_contents = None - self.dns_location = None - self.dns_contents = None - self.dns_resource_type = None - self.emails = None - self.ov_eligible: bool | None = None - self.ov_days_remaining: int | None = None - self.ev_eligible: bool | None = None - self.ev_days_remaining: int | None = None - # Note that verification_method is the 'current' verification - # 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: - self.ecs_client = ECSClient( - entrust_api_user=module.params["entrust_api_user"], - entrust_api_key=module.params["entrust_api_key"], - entrust_api_cert=module.params["entrust_api_client_cert_path"], - entrust_api_cert_key=module.params["entrust_api_client_cert_key_path"], - entrust_api_specification_path=module.params[ - "entrust_api_specification_path" - ], - ) - except SessionConfigurationException as e: - module.fail_json(msg=f"Failed to initialize Entrust Provider: {e}") - try: - self.ecs_client.GetAppVersion() # type: ignore[attr-defined] # pylint: disable=no-member - except RestOperationException as e: - module.fail_json( - msg=f"Please verify credential information. Received exception when testing ECS connection: {e.message}" - ) - - def set_domain_details(self, domain_details: dict[str, t.Any]) -> None: - if domain_details.get("verificationMethod"): - self.verification_method = domain_details["verificationMethod"].lower() - self.domain_status = domain_details["verificationStatus"] - self.ov_eligible = domain_details.get("ovEligible") - self.ov_days_remaining = calculate_days_remaining( - domain_details.get("ovExpiry") - ) - self.ev_eligible = domain_details.get("evEligible") - self.ev_days_remaining = calculate_days_remaining( - domain_details.get("evExpiry") - ) - self.client_id = domain_details["clientId"] - - if self.verification_method == "dns" and domain_details.get("dnsMethod"): - self.dns_location = domain_details["dnsMethod"]["recordDomain"] - self.dns_resource_type = domain_details["dnsMethod"]["recordType"] - self.dns_contents = domain_details["dnsMethod"]["recordValue"] - elif self.verification_method == "web_server" and domain_details.get( - "webServerMethod" - ): - self.file_location = domain_details["webServerMethod"]["fileLocation"] - self.file_contents = domain_details["webServerMethod"]["fileContents"] - elif self.verification_method == "email" and domain_details.get("emailMethod"): - self.emails = domain_details["emailMethod"] - - def check(self, module: AnsibleModule) -> bool: - try: - domain_details = self.ecs_client.GetDomain( # type: ignore[attr-defined] # pylint: disable=no-member - clientId=module.params["client_id"], domain=module.params["domain_name"] - ) - self.set_domain_details(domain_details) - 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 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 - - if self.domain_status == "EXPIRING": - return False - - return True - except RestOperationException: - return False - - def request_domain(self, module: AnsibleModule) -> None: - if not self.check(module): - body = {} - - body["verificationMethod"] = module.params["verification_method"].upper() - if module.params["verification_method"] == "email": - emailMethod = {} - if module.params["verification_email"]: - emailMethod["emailSource"] = "SPECIFIED" - emailMethod["email"] = module.params["verification_email"] - else: - emailMethod["emailSource"] = "INCLUDE_WHOIS" - body["emailMethod"] = emailMethod - # Only populate domain name in body if it is not an existing domain - if not self.domain_status: - body["domainName"] = module.params["domain_name"] - try: - if not self.domain_status: - self.ecs_client.AddDomain( # type: ignore[attr-defined] # pylint: disable=no-member - clientId=module.params["client_id"], Body=body - ) - else: - self.ecs_client.ReverifyDomain( # type: ignore[attr-defined] # pylint: disable=no-member - clientId=module.params["client_id"], - domain=module.params["domain_name"], - Body=body, - ) - - time.sleep(5) - result = self.ecs_client.GetDomain( # type: ignore[attr-defined] # pylint: disable=no-member - clientId=module.params["client_id"], - domain=module.params["domain_name"], - ) - - # It takes a bit of time before the random values are available - if ( - module.params["verification_method"] == "dns" - or module.params["verification_method"] == "web_server" - ): - 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 ( - result.get("dnsMethod") - and result["dnsMethod"]["recordValue"] - != self.dns_contents - ): - break - elif module.params["verification_method"] == "web_server": - if ( - result.get("webServerMethod") - and result["webServerMethod"]["fileContents"] - != self.file_contents - ): - break - time.sleep(10) - result = self.ecs_client.GetDomain( # type: ignore[attr-defined] # pylint: disable=no-member - clientId=module.params["client_id"], - domain=module.params["domain_name"], - ) - self.changed = True - self.set_domain_details(result) - except RestOperationException as e: - module.fail_json( - msg=f"Failed to request domain validation from Entrust (ECS) {e.message}" - ) - - def dump(self) -> dict[str, t.Any]: - result: dict[str, t.Any] = { - "changed": self.changed, - "client_id": self.client_id, - "domain_status": self.domain_status, - } - - if self.verification_method: - result["verification_method"] = self.verification_method - if self.ov_eligible is not None: - result["ov_eligible"] = self.ov_eligible - if self.ov_days_remaining: - result["ov_days_remaining"] = self.ov_days_remaining - if self.ev_eligible is not None: - result["ev_eligible"] = self.ev_eligible - if self.ev_days_remaining: - result["ev_days_remaining"] = self.ev_days_remaining - if self.emails: - result["emails"] = self.emails - - if self.verification_method == "dns": - result["dns_location"] = self.dns_location - result["dns_contents"] = self.dns_contents - result["dns_resource_type"] = self.dns_resource_type - elif self.verification_method == "web_server": - result["file_location"] = self.file_location - result["file_contents"] = self.file_contents - elif self.verification_method == "email": - result["emails"] = self.emails - - return result - - -def ecs_domain_argument_spec() -> dict[str, dict[str, t.Any]]: - 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: - ecs_argument_spec = ecs_client_argument_spec() - ecs_argument_spec.update(ecs_domain_argument_spec()) - module = AnsibleModule( - argument_spec=ecs_argument_spec, - supports_check_mode=False, - ) - - if ( - module.params["verification_email"] - and module.params["verification_method"] != "email" - ): - module.fail_json( - msg=f'The verification_email field is invalid when verification_method="{module.params["verification_method"]}".' - ) - - domain = EcsDomain(module) - domain.request_domain(module) - result = domain.dump() - module.exit_json(**result) - - -if __name__ == "__main__": - main() diff --git a/plugins/modules/x509_certificate.py b/plugins/modules/x509_certificate.py index a5c3f788..1edfa9b1 100644 --- a/plugins/modules/x509_certificate.py +++ b/plugins/modules/x509_certificate.py @@ -11,7 +11,7 @@ DOCUMENTATION = r""" module: x509_certificate short_description: Generate and/or check OpenSSL certificates description: - - It implements a notion of provider (one of V(selfsigned), V(ownca), V(acme), and V(entrust)) for your certificate. + - It implements a notion of provider (one of V(selfsigned), V(ownca), and V(acme)) for your certificate. - Please note that the module regenerates existing certificate if it does not match the module's options, or if it seems to be corrupt. If you are concerned that this could overwrite your existing certificate, consider using the O(backup) option. @@ -29,7 +29,6 @@ extends_documentation_fragment: - community.crypto._attributes.files - community.crypto._module_certificate - community.crypto._module_certificate.backend_acme_documentation - - community.crypto._module_certificate.backend_entrust_documentation - community.crypto._module_certificate.backend_ownca_documentation - community.crypto._module_certificate.backend_selfsigned_documentation attributes: @@ -56,11 +55,10 @@ options: - Name of the provider to use to generate/retrieve the OpenSSL certificate. Please see the examples on how to emulate it with M(community.crypto.x509_certificate_info), M(community.crypto.openssl_csr_info), M(community.crypto.openssl_privatekey_info) and M(ansible.builtin.assert). - - The V(entrust) provider was added for Ansible 2.9 and requires credentials for the - L(Entrust Certificate Services,https://www.entrustdatacard.com/products/categories/ssl-certificates) (ECS) API. - Required if O(state) is V(present). + - The V(entrust) provider has been removed from community.crypto 3.0.0 due to sunsetting of the ECS API. type: str - choices: [acme, entrust, ownca, selfsigned] + choices: [acme, ownca, selfsigned] return_content: description: @@ -125,21 +123,6 @@ EXAMPLES = r""" acme_challenge_path: /etc/ssl/challenges/ansible.com/ force: true -- name: Generate an Entrust certificate via the Entrust Certificate Services (ECS) API - community.crypto.x509_certificate: - path: /etc/ssl/crt/ansible.com.crt - csr_path: /etc/ssl/csr/ansible.com.csr - provider: entrust - entrust_requester_name: Jo Doe - entrust_requester_email: jdoe@ansible.com - entrust_requester_phone: 555-555-5555 - entrust_cert_type: STANDARD_SSL - entrust_api_user: apiusername - entrust_api_key: a^lv*32!cd9LnT - entrust_api_client_cert_path: /etc/ssl/entrust/ecs-client.crt - entrust_api_client_cert_key_path: /etc/ssl/entrust/ecs-key.crt - entrust_api_specification_path: /etc/ssl/entrust/api-docs/cms-api-2.1.0.yaml - # The following example shows how to emulate the behavior of the removed # "assertonly" provider with the x509_certificate_info, openssl_csr_info, # openssl_privatekey_info and assert modules: @@ -237,10 +220,6 @@ from ansible_collections.community.crypto.plugins.module_utils._crypto.module_ba AcmeCertificateProvider, add_acme_provider_to_argument_spec, ) -from ansible_collections.community.crypto.plugins.module_utils._crypto.module_backends.certificate_entrust import ( - EntrustCertificateProvider, - add_entrust_provider_to_argument_spec, -) from ansible_collections.community.crypto.plugins.module_utils._crypto.module_backends.certificate_ownca import ( OwnCACertificateProvider, add_ownca_provider_to_argument_spec, @@ -362,7 +341,6 @@ class GenericCertificate(OpenSSLObject): def main() -> t.NoReturn: argument_spec = get_certificate_argument_spec() add_acme_provider_to_argument_spec(argument_spec) - add_entrust_provider_to_argument_spec(argument_spec) add_ownca_provider_to_argument_spec(argument_spec) add_selfsigned_provider_to_argument_spec(argument_spec) argument_spec.argument_spec.update( @@ -407,12 +385,10 @@ def main() -> t.NoReturn: provider_map: dict[ str, type[AcmeCertificateProvider] - | type[EntrustCertificateProvider] | type[OwnCACertificateProvider] | type[SelfSignedCertificateProvider], ] = { "acme": AcmeCertificateProvider, - "entrust": EntrustCertificateProvider, "ownca": OwnCACertificateProvider, "selfsigned": SelfSignedCertificateProvider, } diff --git a/plugins/modules/x509_certificate_pipe.py b/plugins/modules/x509_certificate_pipe.py index a4f1d784..32946173 100644 --- a/plugins/modules/x509_certificate_pipe.py +++ b/plugins/modules/x509_certificate_pipe.py @@ -13,7 +13,7 @@ module: x509_certificate_pipe short_description: Generate and/or check OpenSSL certificates version_added: 1.3.0 description: - - It implements a notion of provider (one of V(selfsigned), V(ownca), V(entrust)) for your certificate. + - It implements a notion of provider (one of V(selfsigned) and V(ownca)) for your certificate. author: - Yanis Guenane (@Spredzy) - Markus Teufelberger (@MarkusTeufelberger) @@ -21,7 +21,6 @@ author: extends_documentation_fragment: - community.crypto._attributes - community.crypto._module_certificate - - community.crypto._module_certificate.backend_entrust_documentation - community.crypto._module_certificate.backend_ownca_documentation - community.crypto._module_certificate.backend_selfsigned_documentation attributes: @@ -33,10 +32,9 @@ options: provider: description: - Name of the provider to use to generate/retrieve the OpenSSL certificate. - - The V(entrust) provider requires credentials for the L(Entrust Certificate Services, - https://www.entrustdatacard.com/products/categories/ssl-certificates) (ECS) API. + - The V(entrust) provider has been removed from community.crypto 3.0.0 due to sunsetting of the ECS API. type: str - choices: [entrust, ownca, selfsigned] + choices: [ownca, selfsigned] required: true content: @@ -127,10 +125,6 @@ from ansible_collections.community.crypto.plugins.module_utils._crypto.module_ba get_certificate_argument_spec, select_backend, ) -from ansible_collections.community.crypto.plugins.module_utils._crypto.module_backends.certificate_entrust import ( - EntrustCertificateProvider, - add_entrust_provider_to_argument_spec, -) from ansible_collections.community.crypto.plugins.module_utils._crypto.module_backends.certificate_ownca import ( OwnCACertificateProvider, add_ownca_provider_to_argument_spec, @@ -178,7 +172,6 @@ class GenericCertificate: def main() -> t.NoReturn: argument_spec = get_certificate_argument_spec() argument_spec.argument_spec["provider"]["required"] = True - add_entrust_provider_to_argument_spec(argument_spec) add_ownca_provider_to_argument_spec(argument_spec) add_selfsigned_provider_to_argument_spec(argument_spec) argument_spec.argument_spec.update( @@ -194,11 +187,8 @@ def main() -> t.NoReturn: provider = module.params["provider"] provider_map: dict[ str, - type[EntrustCertificateProvider] - | type[OwnCACertificateProvider] - | type[SelfSignedCertificateProvider], + type[OwnCACertificateProvider] | type[SelfSignedCertificateProvider], ] = { - "entrust": EntrustCertificateProvider, "ownca": OwnCACertificateProvider, "selfsigned": SelfSignedCertificateProvider, } diff --git a/tests/integration/targets/ecs_certificate/aliases b/tests/integration/targets/ecs_certificate/aliases deleted file mode 100644 index 12273caf..00000000 --- a/tests/integration/targets/ecs_certificate/aliases +++ /dev/null @@ -1,19 +0,0 @@ -# Copyright (c) Ansible Project -# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) -# SPDX-License-Identifier: GPL-3.0-or-later - -# Not enabled due to lack of access to test environments. May be enabled using custom integration_config.yml -# Example integation_config.yml -# --- -# entrust_api_user: -# entrust_api_key: -# entrust_api_client_cert_path: /var/integration-testing/publicCert.pem -# entrust_api_client_cert_key_path: /var/integration-testing/privateKey.pem -# entrust_api_ip_address: 127.0.0.1 -# entrust_cloud_ip_address: 127.0.0.1 -# # Used for certificate path validation of QA environments - we chose not to support disabling path validation ever. -# cacerts_bundle_path_local: /var/integration-testing/cacerts - -### WARNING: This test will update HOSTS file and CERTIFICATE STORE of target host, in order to be able to validate -# to a QA environment. ### -unsupported diff --git a/tests/integration/targets/ecs_certificate/defaults/main.yml b/tests/integration/targets/ecs_certificate/defaults/main.yml deleted file mode 100644 index d42aab01..00000000 --- a/tests/integration/targets/ecs_certificate/defaults/main.yml +++ /dev/null @@ -1,6 +0,0 @@ ---- -# Copyright (c) Ansible Project -# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) -# SPDX-License-Identifier: GPL-3.0-or-later - -# defaults file for test_ecs_certificate diff --git a/tests/integration/targets/ecs_certificate/meta/main.yml b/tests/integration/targets/ecs_certificate/meta/main.yml deleted file mode 100644 index b7fbb90f..00000000 --- a/tests/integration/targets/ecs_certificate/meta/main.yml +++ /dev/null @@ -1,8 +0,0 @@ ---- -# Copyright (c) Ansible Project -# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) -# SPDX-License-Identifier: GPL-3.0-or-later - -dependencies: - - prepare_tests - - setup_openssl diff --git a/tests/integration/targets/ecs_certificate/tasks/main.yml b/tests/integration/targets/ecs_certificate/tasks/main.yml deleted file mode 100644 index 54cd3f62..00000000 --- a/tests/integration/targets/ecs_certificate/tasks/main.yml +++ /dev/null @@ -1,222 +0,0 @@ ---- -# Copyright (c) Ansible Project -# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) -# SPDX-License-Identifier: GPL-3.0-or-later - -#################################################################### -# WARNING: These are designed specifically for Ansible tests # -# and should not be used as examples of how to write Ansible roles # -#################################################################### - -## Verify that integration_config was specified -- assert: - that: - - entrust_api_user is defined - - entrust_api_key is defined - - entrust_api_ip_address is defined - - entrust_cloud_ip_address is defined - - entrust_api_client_cert_path is defined or entrust_api_client_cert_contents is defined - - entrust_api_client_cert_key_path is defined or entrust_api_client_cert_key_contents - - cacerts_bundle_path_local is defined - -## SET UP TEST ENVIRONMENT ######################################################################## -- name: copy the files needed for verifying test server certificate to the host - copy: - src: '{{ cacerts_bundle_path_local }}/' - dest: '{{ cacerts_bundle_path }}' - -- name: Update the CA certificates for our QA certs (collection may need updating if new QA environments used) - command: c_rehash {{ cacerts_bundle_path }} - -- name: Update hosts file - lineinfile: - path: /etc/hosts - state: present - regexp: 'api.entrust.net$' - line: '{{ entrust_api_ip_address }} api.entrust.net' - -- name: Update hosts file - lineinfile: - path: /etc/hosts - state: present - regexp: 'cloud.entrust.net$' - line: '{{ entrust_cloud_ip_address }} cloud.entrust.net' - -- name: Clear out the temporary directory for storing the API connection information - file: - path: '{{ tmpdir_path }}' - state: absent - -- name: Create a directory for storing the API connection Information - file: - path: '{{ tmpdir_path }}' - state: directory - -- name: Copy the files needed for the connection to entrust API to the host - copy: - src: '{{ entrust_api_client_cert_path }}' - dest: '{{ entrust_api_cert }}' - -- name: Copy the files needed for the connection to entrust API to the host - copy: - src: '{{ entrust_api_client_cert_key_path }}' - dest: '{{ entrust_api_cert_key }}' - -## SETUP CSR TO REQUEST -- name: Generate a 2048 bit RSA private key - openssl_privatekey: - path: '{{ privatekey_path }}' - passphrase: '{{ privatekey_passphrase }}' - type: RSA - size: 2048 - -- name: Generate a certificate signing request using the generated key - openssl_csr: - path: '{{ csr_path }}' - privatekey_path: '{{ privatekey_path }}' - privatekey_passphrase: '{{ privatekey_passphrase }}' - common_name: '{{ common_name }}' - organization_name: '{{ organization_name | default(omit) }}' - organizational_unit_name: '{{ organizational_unit_name | default(omit) }}' - country_name: '{{ country_name | default(omit) }}' - state_or_province_name: '{{ state_or_province_name | default(omit) }}' - digest: sha256 - -- block: - - name: Have ECS generate a signed certificate - ecs_certificate: - backup: true - path: '{{ example1_cert_path }}' - full_chain_path: '{{ example1_chain_path }}' - csr: '{{ csr_path }}' - cert_type: '{{ example1_cert_type }}' - requester_name: '{{ entrust_requester_name }}' - requester_email: '{{ entrust_requester_email }}' - requester_phone: '{{ entrust_requester_phone }}' - entrust_api_user: '{{ entrust_api_user }}' - entrust_api_key: '{{ entrust_api_key }}' - entrust_api_client_cert_path: '{{ entrust_api_cert }}' - entrust_api_client_cert_key_path: '{{ entrust_api_cert_key }}' - register: example1_result - - - assert: - that: - - example1_result is not failed - - example1_result.changed - - example1_result.tracking_id > 0 - - example1_result.serial_number is string - - # Internal CA refuses to issue certificates with the same DN in a short time frame - - name: Sleep for 5 seconds so we don't run into duplicate-request errors - pause: - seconds: 5 - - - name: Attempt to have ECS generate a signed certificate, but existing one is valid - ecs_certificate: - backup: true - path: '{{ example1_cert_path }}' - full_chain_path: '{{ example1_chain_path }}' - csr: '{{ csr_path }}' - cert_type: '{{ example1_cert_type }}' - requester_name: '{{ entrust_requester_name }}' - requester_email: '{{ entrust_requester_email }}' - requester_phone: '{{ entrust_requester_phone }}' - entrust_api_user: '{{ entrust_api_user }}' - entrust_api_key: '{{ entrust_api_key }}' - entrust_api_client_cert_path: '{{ entrust_api_cert }}' - entrust_api_client_cert_key_path: '{{ entrust_api_cert_key }}' - register: example2_result - - - assert: - that: - - example2_result is not failed - - not example2_result.changed - - example2_result.backup_file is undefined - - example2_result.backup_full_chain_file is undefined - - example2_result.serial_number == example1_result.serial_number - - example2_result.tracking_id == example1_result.tracking_id - - # Internal CA refuses to issue certificates with the same DN in a short time frame - - name: Sleep for 5 seconds so we don't run into duplicate-request errors - pause: - seconds: 5 - - - name: Force a reissue with no CSR, verify that contents changed - ecs_certificate: - backup: true - force: true - path: '{{ example1_cert_path }}' - full_chain_path: '{{ example1_chain_path }}' - cert_type: '{{ example1_cert_type }}' - request_type: reissue - requester_name: '{{ entrust_requester_name }}' - requester_email: '{{ entrust_requester_email }}' - requester_phone: '{{ entrust_requester_phone }}' - entrust_api_user: '{{ entrust_api_user }}' - entrust_api_key: '{{ entrust_api_key }}' - entrust_api_client_cert_path: '{{ entrust_api_cert }}' - entrust_api_client_cert_key_path: '{{ entrust_api_cert_key }}' - register: example3_result - - - assert: - that: - - example3_result is not failed - - example3_result.changed - - example3_result.backup_file is string - - example3_result.backup_full_chain_file is string - - example3_result.tracking_id > 0 - - example3_result.tracking_id != example1_result.tracking_id - - example3_result.serial_number != example1_result.serial_number - - # Internal CA refuses to issue certificates with the same DN in a short time frame - - name: Sleep for 5 seconds so we don't run into duplicate-request errors - pause: - seconds: 5 - - - name: Test a request with all of the various optional possible fields populated - ecs_certificate: - path: '{{ example4_cert_path }}' - full_chain_path: '{{ example4_full_chain_path }}' - csr: '{{ csr_path }}' - subject_alt_name: '{{ example4_subject_alt_name }}' - eku: '{{ example4_eku }}' - ct_log: true - cert_type: '{{ example4_cert_type }}' - org: '{{ example4_org }}' - ou: '{{ example4_ou }}' - tracking_info: '{{ example4_tracking_info }}' - additional_emails: '{{ example4_additional_emails }}' - custom_fields: '{{ example4_custom_fields }}' - cert_expiry: '{{ example4_cert_expiry }}' - requester_name: '{{ entrust_requester_name }}' - requester_email: '{{ entrust_requester_email }}' - requester_phone: '{{ entrust_requester_phone }}' - entrust_api_user: '{{ entrust_api_user }}' - entrust_api_key: '{{ entrust_api_key }}' - entrust_api_client_cert_path: '{{ entrust_api_cert }}' - entrust_api_client_cert_key_path: '{{ entrust_api_cert_key }}' - register: example4_result - - - assert: - that: - - example4_result is not failed - - example4_result.changed - - example4_result.backup_file is undefined - - example4_result.backup_full_chain_file is undefined - - example4_result.tracking_id > 0 - - example4_result.serial_number is string - - # For bug 61738, verify that the full chain is valid - - name: Verify that the full chain path can be successfully imported - command: '{{ openssl_binary }} verify "{{ example4_full_chain_path }}"' - register: openssl_result - - - assert: - that: - - "' OK' in openssl_result.stdout_lines[0]" - - always: - - name: clean-up temporary folder - file: - path: '{{ tmpdir_path }}' - state: absent diff --git a/tests/integration/targets/ecs_certificate/vars/main.yml b/tests/integration/targets/ecs_certificate/vars/main.yml deleted file mode 100644 index ae9eeb5d..00000000 --- a/tests/integration/targets/ecs_certificate/vars/main.yml +++ /dev/null @@ -1,56 +0,0 @@ ---- -# Copyright (c) Ansible Project -# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) -# SPDX-License-Identifier: GPL-3.0-or-later - -# vars file for test_ecs_certificate - -# Path on various hosts that cacerts need to be put as a prerequisite to API server cert validation. -# May need to be customized for some environments based on SSL implementations -# that ansible "urls" module utility is using as a backing. -cacerts_bundle_path: /etc/pki/tls/certs - -common_name: '{{ ansible_date_time.epoch }}.ansint.testcertificates.com' -organization_name: CMS API, Inc. -organizational_unit_name: RSA -country_name: US -state_or_province_name: MA -privatekey_passphrase: Passphrase452! -tmpdir_path: /tmp/ecs_cert_test/{{ ansible_date_time.epoch }} -privatekey_path: '{{ tmpdir_path }}/testcertificates.key' -entrust_api_cert: '{{ tmpdir_path }}/authcert.cer' -entrust_api_cert_key: '{{ tmpdir_path }}/authkey.cer' -csr_path: '{{ tmpdir_path }}/request.csr' - -entrust_requester_name: C Trufan -entrust_requester_email: CTIntegrationTests@entrustdatacard.com -entrust_requester_phone: 1-555-555-5555 # e.g. 15555555555 - -# TEST 1 -example1_cert_path: '{{ tmpdir_path }}/issuedcert_1.pem' -example1_chain_path: '{{ tmpdir_path }}/issuedcert_1_chain.pem' -example1_cert_type: EV_SSL - -example4_cert_path: '{{ tmpdir_path }}/issuedcert_2.pem' -example4_subject_alt_name: - - ansible.testcertificates.com - - www.testcertificates.com -example4_eku: SERVER_AND_CLIENT_AUTH -example4_cert_type: UC_SSL -# Test a secondary org and special characters -example4_org: CaƱon City, Inc. -example4_ou: - - StringrsaString -example4_tracking_info: Submitted via Ansible Integration -example4_additional_emails: - - itsupport@testcertificates.com - - jsmith@ansible.com -example4_custom_fields: - text1: Admin - text2: Invoice 25 - number1: 342 - date3: '2018-01-01' - email2: sales@ansible.testcertificates.com - dropdown2: Dropdown 2 Value 1 -example4_cert_expiry: 2020-08-15 -example4_full_chain_path: '{{ tmpdir_path }}/issuedcert_2_chain.pem' diff --git a/tests/integration/targets/ecs_domain/aliases b/tests/integration/targets/ecs_domain/aliases deleted file mode 100644 index 12273caf..00000000 --- a/tests/integration/targets/ecs_domain/aliases +++ /dev/null @@ -1,19 +0,0 @@ -# Copyright (c) Ansible Project -# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) -# SPDX-License-Identifier: GPL-3.0-or-later - -# Not enabled due to lack of access to test environments. May be enabled using custom integration_config.yml -# Example integation_config.yml -# --- -# entrust_api_user: -# entrust_api_key: -# entrust_api_client_cert_path: /var/integration-testing/publicCert.pem -# entrust_api_client_cert_key_path: /var/integration-testing/privateKey.pem -# entrust_api_ip_address: 127.0.0.1 -# entrust_cloud_ip_address: 127.0.0.1 -# # Used for certificate path validation of QA environments - we chose not to support disabling path validation ever. -# cacerts_bundle_path_local: /var/integration-testing/cacerts - -### WARNING: This test will update HOSTS file and CERTIFICATE STORE of target host, in order to be able to validate -# to a QA environment. ### -unsupported diff --git a/tests/integration/targets/ecs_domain/defaults/main.yml b/tests/integration/targets/ecs_domain/defaults/main.yml deleted file mode 100644 index 13656110..00000000 --- a/tests/integration/targets/ecs_domain/defaults/main.yml +++ /dev/null @@ -1,6 +0,0 @@ ---- -# Copyright (c) Ansible Project -# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) -# SPDX-License-Identifier: GPL-3.0-or-later - -# defaults file for test_ecs_domain diff --git a/tests/integration/targets/ecs_domain/meta/main.yml b/tests/integration/targets/ecs_domain/meta/main.yml deleted file mode 100644 index 368ea207..00000000 --- a/tests/integration/targets/ecs_domain/meta/main.yml +++ /dev/null @@ -1,7 +0,0 @@ ---- -# Copyright (c) Ansible Project -# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) -# SPDX-License-Identifier: GPL-3.0-or-later - -dependencies: - - prepare_tests diff --git a/tests/integration/targets/ecs_domain/tasks/main.yml b/tests/integration/targets/ecs_domain/tasks/main.yml deleted file mode 100644 index 5ff84189..00000000 --- a/tests/integration/targets/ecs_domain/tasks/main.yml +++ /dev/null @@ -1,277 +0,0 @@ ---- -# Copyright (c) Ansible Project -# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) -# SPDX-License-Identifier: GPL-3.0-or-later - -#################################################################### -# WARNING: These are designed specifically for Ansible tests # -# and should not be used as examples of how to write Ansible roles # -#################################################################### - -## Verify that integration_config was specified -- assert: - that: - - entrust_api_user is defined - - entrust_api_key is defined - - entrust_api_ip_address is defined - - entrust_cloud_ip_address is defined - - entrust_api_client_cert_path is defined or entrust_api_client_cert_contents is defined - - entrust_api_client_cert_key_path is defined or entrust_api_client_cert_key_contents - - cacerts_bundle_path_local is defined - -## SET UP TEST ENVIRONMENT ######################################################################## -- name: copy the files needed for verifying test server certificate to the host - copy: - src: '{{ cacerts_bundle_path_local }}/' - dest: '{{ cacerts_bundle_path }}' - -- name: Update the CA certificates for our QA certs (collection may need updating if new QA environments used) - command: c_rehash {{ cacerts_bundle_path }} - -- name: Update hosts file - lineinfile: - path: /etc/hosts - state: present - regexp: 'api.entrust.net$' - line: '{{ entrust_api_ip_address }} api.entrust.net' - -- name: Update hosts file - lineinfile: - path: /etc/hosts - state: present - regexp: 'cloud.entrust.net$' - line: '{{ entrust_cloud_ip_address }} cloud.entrust.net' - -- name: Clear out the temporary directory for storing the API connection information - file: - path: '{{ tmpdir_path }}' - state: absent - -- name: Create a directory for storing the API connection Information - file: - path: '{{ tmpdir_path }}' - state: directory - -- name: Copy the files needed for the connection to entrust API to the host - copy: - src: '{{ entrust_api_client_cert_path }}' - dest: '{{ entrust_api_cert }}' - -- name: Copy the files needed for the connection to entrust API to the host - copy: - src: '{{ entrust_api_client_cert_key_path }}' - dest: '{{ entrust_api_cert_key }}' - -- block: - - name: Have ECS request a domain validation via dns - ecs_domain: - domain_name: dns.{{ common_name }} - verification_method: dns - entrust_api_user: '{{ entrust_api_user }}' - entrust_api_key: '{{ entrust_api_key }}' - entrust_api_client_cert_path: '{{ entrust_api_cert }}' - entrust_api_client_cert_key_path: '{{ entrust_api_cert_key }}' - register: dns_result - - - assert: - that: - - dns_result is not failed - - dns_result.changed - - dns_result.domain_status == 'INITIAL_VERIFICATION' - - dns_result.verification_method == 'dns' - - dns_result.dns_location is string - - dns_result.dns_contents is string - - dns_result.dns_resource_type is string - - dns_result.file_location is undefined - - dns_result.file_contents is undefined - - dns_result.emails is undefined - - - name: Have ECS request a domain validation via web_server - ecs_domain: - domain_name: FILE.{{ common_name }} - verification_method: web_server - entrust_api_user: '{{ entrust_api_user }}' - entrust_api_key: '{{ entrust_api_key }}' - entrust_api_client_cert_path: '{{ entrust_api_cert }}' - entrust_api_client_cert_key_path: '{{ entrust_api_cert_key }}' - register: file_result - - - assert: - that: - - file_result is not failed - - file_result.changed - - file_result.domain_status == 'INITIAL_VERIFICATION' - - file_result.verification_method == 'web_server' - - file_result.dns_location is undefined - - file_result.dns_contents is undefined - - file_result.dns_resource_type is undefined - - file_result.file_location is string - - file_result.file_contents is string - - file_result.emails is undefined - - - name: Have ECS request a domain validation via email - ecs_domain: - domain_name: email.{{ common_name }} - verification_method: email - verification_email: admin@testcertificates.com - entrust_api_user: '{{ entrust_api_user }}' - entrust_api_key: '{{ entrust_api_key }}' - entrust_api_client_cert_path: '{{ entrust_api_cert }}' - entrust_api_client_cert_key_path: '{{ entrust_api_cert_key }}' - register: email_result - - - assert: - that: - - email_result is not failed - - email_result.changed - - email_result.domain_status == 'INITIAL_VERIFICATION' - - email_result.verification_method == 'email' - - email_result.dns_location is undefined - - email_result.dns_contents is undefined - - email_result.dns_resource_type is undefined - - email_result.file_location is undefined - - email_result.file_contents is undefined - - email_result.emails[0] == 'admin@testcertificates.com' - - - name: Have ECS request a domain validation via email with no address provided - ecs_domain: - domain_name: email2.{{ common_name }} - verification_method: email - entrust_api_user: '{{ entrust_api_user }}' - entrust_api_key: '{{ entrust_api_key }}' - entrust_api_client_cert_path: '{{ entrust_api_cert }}' - entrust_api_client_cert_key_path: '{{ entrust_api_cert_key }}' - register: email_result2 - - - assert: - that: - - email_result2 is not failed - - email_result2.changed - - email_result2.domain_status == 'INITIAL_VERIFICATION' - - email_result2.verification_method == 'email' - - email_result2.dns_location is undefined - - email_result2.dns_contents is undefined - - email_result2.dns_resource_type is undefined - - email_result2.file_location is undefined - - email_result2.file_contents is undefined - - email_result2.emails is defined - - - name: Have ECS request a domain validation via manual - ecs_domain: - domain_name: manual.{{ common_name }} - verification_method: manual - entrust_api_user: '{{ entrust_api_user }}' - entrust_api_key: '{{ entrust_api_key }}' - entrust_api_client_cert_path: '{{ entrust_api_cert }}' - entrust_api_client_cert_key_path: '{{ entrust_api_cert_key }}' - register: manual_result - - - assert: - that: - - manual_result is not failed - - manual_result.changed - - manual_result.domain_status == 'INITIAL_VERIFICATION' - - manual_result.verification_method == 'manual' - - manual_result.dns_location is undefined - - manual_result.dns_contents is undefined - - manual_result.dns_resource_type is undefined - - manual_result.file_location is undefined - - manual_result.file_contents is undefined - - manual_result.emails is undefined - - - name: Have ECS request a domain validation via dns that remains unchanged - ecs_domain: - domain_name: dns.{{ common_name }} - verification_method: dns - entrust_api_user: '{{ entrust_api_user }}' - entrust_api_key: '{{ entrust_api_key }}' - entrust_api_client_cert_path: '{{ entrust_api_cert }}' - entrust_api_client_cert_key_path: '{{ entrust_api_cert_key }}' - register: dns_result2 - - - assert: - that: - - dns_result2 is not failed - - not dns_result2.changed - - dns_result2.domain_status == 'INITIAL_VERIFICATION' - - dns_result2.verification_method == 'dns' - - dns_result2.dns_location is string - - dns_result2.dns_contents is string - - dns_result2.dns_resource_type is string - - dns_result2.file_location is undefined - - dns_result2.file_contents is undefined - - dns_result2.emails is undefined - - - name: Have ECS request a domain validation via FILE for dns, to change verification method - ecs_domain: - domain_name: dns.{{ common_name }} - verification_method: web_server - entrust_api_user: '{{ entrust_api_user }}' - entrust_api_key: '{{ entrust_api_key }}' - entrust_api_client_cert_path: '{{ entrust_api_cert }}' - entrust_api_client_cert_key_path: '{{ entrust_api_cert_key }}' - register: dns_result_now_file - - - assert: - that: - - dns_result_now_file is not failed - - dns_result_now_file.changed - - dns_result_now_file.domain_status == 'INITIAL_VERIFICATION' - - dns_result_now_file.verification_method == 'web_server' - - dns_result_now_file.dns_location is undefined - - dns_result_now_file.dns_contents is undefined - - dns_result_now_file.dns_resource_type is undefined - - dns_result_now_file.file_location is string - - dns_result_now_file.file_contents is string - - dns_result_now_file.emails is undefined - - - name: Request revalidation of an approved domain - ecs_domain: - domain_name: '{{ existing_domain_common_name }}' - verification_method: manual - entrust_api_user: '{{ entrust_api_user }}' - entrust_api_key: '{{ entrust_api_key }}' - entrust_api_client_cert_path: '{{ entrust_api_cert }}' - entrust_api_client_cert_key_path: '{{ entrust_api_cert_key }}' - register: manual_existing_domain - - - assert: - that: - - manual_existing_domain is not failed - - not manual_existing_domain.changed - - manual_existing_domain.domain_status == 'RE_VERIFICATION' - - manual_existing_domain.dns_location is undefined - - manual_existing_domain.dns_contents is undefined - - manual_existing_domain.dns_resource_type is undefined - - manual_existing_domain.file_location is undefined - - manual_existing_domain.file_contents is undefined - - manual_existing_domain.emails is undefined - - - name: Request revalidation of an approved domain - ecs_domain: - domain_name: '{{ existing_domain_common_name }}' - verification_method: web_server - entrust_api_user: '{{ entrust_api_user }}' - entrust_api_key: '{{ entrust_api_key }}' - entrust_api_client_cert_path: '{{ entrust_api_cert }}' - entrust_api_client_cert_key_path: '{{ entrust_api_cert_key }}' - register: file_existing_domain_revalidate - - - assert: - that: - - file_existing_domain_revalidate is not failed - - file_existing_domain_revalidate.changed - - file_existing_domain_revalidate.domain_status == 'RE_VERIFICATION' - - file_existing_domain_revalidate.verification_method == 'web_server' - - file_existing_domain_revalidate.dns_location is undefined - - file_existing_domain_revalidate.dns_contents is undefined - - file_existing_domain_revalidate.dns_resource_type is undefined - - file_existing_domain_revalidate.file_location is string - - file_existing_domain_revalidate.file_contents is string - - file_existing_domain_revalidate.emails is undefined - - always: - - name: clean-up temporary folder - file: - path: '{{ tmpdir_path }}' - state: absent diff --git a/tests/integration/targets/ecs_domain/vars/main.yml b/tests/integration/targets/ecs_domain/vars/main.yml deleted file mode 100644 index 71bf2703..00000000 --- a/tests/integration/targets/ecs_domain/vars/main.yml +++ /dev/null @@ -1,19 +0,0 @@ ---- -# Copyright (c) Ansible Project -# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) -# SPDX-License-Identifier: GPL-3.0-or-later - -# vars file for test_ecs_certificate - -# Path on various hosts that cacerts need to be put as a prerequisite to API server cert validation. -# May need to be customized for some environments based on SSL implementations -# that ansible "urls" module utility is using as a backing. -cacerts_bundle_path: /etc/pki/tls/certs - -common_name: '{{ ansible_date_time.epoch }}.testcertificates.com' -existing_domain_common_name: 'testcertificates.com' - -tmpdir_path: /tmp/ecs_cert_test/{{ ansible_date_time.epoch }} - -entrust_api_cert: '{{ tmpdir_path }}/authcert.cer' -entrust_api_cert_key: '{{ tmpdir_path }}/authkey.cer' diff --git a/tests/sanity/ignore-2.17.txt b/tests/sanity/ignore-2.17.txt index 0a4408d2..93d9a8be 100644 --- a/tests/sanity/ignore-2.17.txt +++ b/tests/sanity/ignore-2.17.txt @@ -1,3 +1,4 @@ +meta/runtime.yml runtime-metadata # Bug in ansible-test: https://github.com/ansible/ansible/pull/85198 plugins/module_utils/_acme/account.py pep8:E704 plugins/module_utils/_acme/acme.py pep8:E704 plugins/module_utils/_acme/acme.py pylint:unpacking-non-sequence @@ -5,7 +6,6 @@ plugins/module_utils/_acme/backend_openssl_cli.py pep8:E704 plugins/module_utils/_acme/certificate.py pep8:E704 plugins/module_utils/_crypto/cryptography_support.py pep8:E704 plugins/module_utils/_crypto/module_backends/certificate.py no-assert -plugins/module_utils/_crypto/module_backends/certificate_entrust.py no-assert plugins/module_utils/_crypto/module_backends/certificate_ownca.py no-assert plugins/module_utils/_crypto/module_backends/certificate_selfsigned.py no-assert plugins/module_utils/_crypto/module_backends/csr.py no-assert @@ -21,8 +21,6 @@ plugins/modules/acme_certificate_deactivate_authz.py pylint:unpacking-non-sequen plugins/modules/acme_certificate_order_finalize.py pylint:unpacking-non-sequence 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 diff --git a/tests/sanity/ignore-2.18.txt b/tests/sanity/ignore-2.18.txt index 47306ca1..67340837 100644 --- a/tests/sanity/ignore-2.18.txt +++ b/tests/sanity/ignore-2.18.txt @@ -1,10 +1,10 @@ +meta/runtime.yml runtime-metadata # Bug in ansible-test: https://github.com/ansible/ansible/pull/85198 plugins/module_utils/_acme/account.py pep8:E704 plugins/module_utils/_acme/acme.py pep8:E704 plugins/module_utils/_acme/backend_openssl_cli.py pep8:E704 plugins/module_utils/_acme/certificate.py pep8:E704 plugins/module_utils/_crypto/cryptography_support.py pep8:E704 plugins/module_utils/_crypto/module_backends/certificate.py no-assert -plugins/module_utils/_crypto/module_backends/certificate_entrust.py no-assert plugins/module_utils/_crypto/module_backends/certificate_ownca.py no-assert plugins/module_utils/_crypto/module_backends/certificate_selfsigned.py no-assert plugins/module_utils/_crypto/module_backends/csr.py no-assert @@ -13,8 +13,6 @@ plugins/module_utils/_crypto/support.py pep8:E704 plugins/module_utils/_openssh/backends/keypair_backend.py no-assert plugins/module_utils/_openssh/certificate.py pep8:E704 plugins/modules/acme_certificate.py no-assert -plugins/modules/ecs_certificate.py no-assert -plugins/modules/ecs_domain.py pep8:E704 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/sanity/ignore-2.19.txt b/tests/sanity/ignore-2.19.txt index 28295a47..b796d021 100644 --- a/tests/sanity/ignore-2.19.txt +++ b/tests/sanity/ignore-2.19.txt @@ -1,12 +1,11 @@ +meta/runtime.yml runtime-metadata # Bug in ansible-test: https://github.com/ansible/ansible/pull/85198 plugins/module_utils/_crypto/module_backends/certificate.py no-assert -plugins/module_utils/_crypto/module_backends/certificate_entrust.py no-assert plugins/module_utils/_crypto/module_backends/certificate_ownca.py no-assert plugins/module_utils/_crypto/module_backends/certificate_selfsigned.py no-assert plugins/module_utils/_crypto/module_backends/csr.py no-assert plugins/module_utils/_crypto/module_backends/privatekey_convert.py no-assert plugins/module_utils/_openssh/backends/keypair_backend.py no-assert plugins/modules/acme_certificate.py no-assert -plugins/modules/ecs_certificate.py no-assert plugins/modules/luks_device.py no-assert plugins/modules/openssl_pkcs12.py no-assert tests/ee/roles/smoke/library/smoke_ipaddress.py shebang