# 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 from __future__ import annotations import typing as t from unittest.mock import ( MagicMock, ) import pytest from ansible_collections.community.crypto.plugins.module_utils._acme.challenges import ( Authorization, Challenge, combine_identifier, split_identifier, ) from ansible_collections.community.crypto.plugins.module_utils._acme.errors import ( ACMEProtocolException, ModuleFailException, ) def test_combine_identifier() -> None: assert combine_identifier(identifier_type="", identifier="") == ":" assert combine_identifier(identifier_type="a", identifier="b") == "a:b" def test_split_identifier() -> None: assert split_identifier(":") == ("", "") assert split_identifier("a:b") == ("a", "b") assert split_identifier("a:b:c") == ("a", "b:c") with pytest.raises(ModuleFailException) as exc: split_identifier("a") assert exc.value.msg == 'Identifier "a" is not of the form :' def test_challenge_from_to_json() -> None: client = MagicMock() data = { "url": "xxx", "type": "type", "status": "valid", } client.version = 2 challenge = Challenge.from_json(client=client, data=data) assert challenge.data == data assert challenge.type == "type" assert challenge.url == "xxx" assert challenge.status == "valid" assert challenge.token is None assert challenge.to_json() == data data = { "type": "type", "status": "valid", "token": "foo", } challenge = Challenge.from_json(client=None, data=data, url="xxx") # type: ignore assert challenge.data == data assert challenge.type == "type" assert challenge.url == "xxx" assert challenge.status == "valid" assert challenge.token == "foo" assert challenge.to_json() == data def test_authorization_from_to_json() -> None: client = MagicMock() client.version = 2 data: dict[str, t.Any] data = { "challenges": [], "status": "valid", "identifier": { "type": "dns", "value": "example.com", }, } authz = Authorization.from_json(client=client, data=data, url="xxx") assert authz.url == "xxx" assert authz.status == "valid" assert authz.identifier == "example.com" assert authz.identifier_type == "dns" assert authz.challenges == [] assert authz.to_json() == { "uri": "xxx", "challenges": [], "status": "valid", "identifier": { "type": "dns", "value": "example.com", }, } data = { "challenges": [ { "url": "xxxyyy", "type": "type", "status": "valid", } ], "status": "valid", "identifier": { "type": "dns", "value": "example.com", }, "wildcard": True, } authz = Authorization.from_json(client=client, data=data, url="xxx") assert authz.url == "xxx" assert authz.status == "valid" assert authz.identifier == "*.example.com" assert authz.identifier_type == "dns" assert len(authz.challenges) == 1 assert authz.challenges[0].data == { "url": "xxxyyy", "type": "type", "status": "valid", } assert authz.to_json() == { "uri": "xxx", "challenges": [ { "url": "xxxyyy", "type": "type", "status": "valid", } ], "status": "valid", "identifier": { "type": "dns", "value": "example.com", }, "wildcard": True, } def test_authorization_create_error() -> None: client = MagicMock() client.version = 2 client.directory.directory = {} with pytest.raises(ACMEProtocolException) as exc: Authorization.create( client=client, identifier_type="dns", identifier="example.com" ) assert exc.value.msg == "ACME endpoint does not support pre-authorization." def test_wait_for_validation_error() -> None: client = MagicMock() client.version = 2 data = { "challenges": [ { "url": "xxxyyy1", "type": "dns-01", "status": "invalid", "error": { "type": "dns-failed", "subproblems": [ { "type": "subproblem", "detail": "example.com DNS-01 validation failed", }, ], }, }, { "url": "xxxyyy2", "type": "http-01", "status": "invalid", "error": { "type": "http-failed", "subproblems": [ { "type": "subproblem", "detail": "example.com HTTP-01 validation failed", }, ], }, }, { "url": "xxxyyy3", "type": "something-else", "status": "valid", }, ], "status": "invalid", "identifier": { "type": "dns", "value": "example.com", }, } client.get_request = MagicMock(return_value=(data, {})) authz = Authorization.from_json(client=client, data=data, url="xxx") with pytest.raises(ACMEProtocolException) as exc: authz.wait_for_validation(client=client) assert exc.value.msg == ( 'Failed to validate challenge for dns:example.com: Status is "invalid". Challenge dns-01: Error dns-failed Subproblems:\n' '(dns-01.0) Error subproblem: "example.com DNS-01 validation failed"; Challenge http-01: Error http-failed Subproblems:\n' '(http-01.0) Error subproblem: "example.com HTTP-01 validation failed".' ) data = data.copy() data["uri"] = "xxx" assert exc.value.module_fail_args == { "identifier": "dns:example.com", "authorization": data, }