1from __future__ import absolute_import, division, print_function 2__metaclass__ = type 3 4 5import pytest 6 7from mock import MagicMock 8 9 10from ansible_collections.community.crypto.plugins.module_utils.acme.challenges import ( 11 combine_identifier, 12 split_identifier, 13 Challenge, 14 Authorization, 15) 16 17from ansible_collections.community.crypto.plugins.module_utils.acme.errors import ( 18 ACMEProtocolException, 19 ModuleFailException, 20) 21 22 23def test_combine_identifier(): 24 assert combine_identifier('', '') == ':' 25 assert combine_identifier('a', 'b') == 'a:b' 26 27 28def test_split_identifier(): 29 assert split_identifier(':') == ['', ''] 30 assert split_identifier('a:b') == ['a', 'b'] 31 assert split_identifier('a:b:c') == ['a', 'b:c'] 32 with pytest.raises(ModuleFailException) as exc: 33 split_identifier('a') 34 assert exc.value.msg == 'Identifier "a" is not of the form <type>:<identifier>' 35 36 37def test_challenge_from_to_json(): 38 client = MagicMock() 39 40 data = { 41 'url': 'xxx', 42 'type': 'type', 43 'status': 'valid', 44 } 45 client.version = 2 46 challenge = Challenge.from_json(client, data) 47 assert challenge.data == data 48 assert challenge.type == 'type' 49 assert challenge.url == 'xxx' 50 assert challenge.status == 'valid' 51 assert challenge.token is None 52 assert challenge.to_json() == data 53 54 data = { 55 'type': 'type', 56 'status': 'valid', 57 'token': 'foo', 58 } 59 challenge = Challenge.from_json(None, data, url='xxx') 60 assert challenge.data == data 61 assert challenge.type == 'type' 62 assert challenge.url == 'xxx' 63 assert challenge.status == 'valid' 64 assert challenge.token == 'foo' 65 assert challenge.to_json() == data 66 67 data = { 68 'uri': 'xxx', 69 'type': 'type', 70 'status': 'valid', 71 } 72 client.version = 1 73 challenge = Challenge.from_json(client, data) 74 assert challenge.data == data 75 assert challenge.type == 'type' 76 assert challenge.url == 'xxx' 77 assert challenge.status == 'valid' 78 assert challenge.token is None 79 assert challenge.to_json() == data 80 81 82def test_authorization_from_to_json(): 83 client = MagicMock() 84 client.version = 2 85 86 data = { 87 'challenges': [], 88 'status': 'valid', 89 'identifier': { 90 'type': 'dns', 91 'value': 'example.com', 92 }, 93 } 94 authz = Authorization.from_json(client, data, 'xxx') 95 assert authz.url == 'xxx' 96 assert authz.status == 'valid' 97 assert authz.identifier == 'example.com' 98 assert authz.identifier_type == 'dns' 99 assert authz.challenges == [] 100 assert authz.to_json() == { 101 'uri': 'xxx', 102 'challenges': [], 103 'status': 'valid', 104 'identifier': { 105 'type': 'dns', 106 'value': 'example.com', 107 }, 108 } 109 110 data = { 111 'challenges': [ 112 { 113 'url': 'xxxyyy', 114 'type': 'type', 115 'status': 'valid', 116 } 117 ], 118 'status': 'valid', 119 'identifier': { 120 'type': 'dns', 121 'value': 'example.com', 122 }, 123 'wildcard': True, 124 } 125 authz = Authorization.from_json(client, data, 'xxx') 126 assert authz.url == 'xxx' 127 assert authz.status == 'valid' 128 assert authz.identifier == '*.example.com' 129 assert authz.identifier_type == 'dns' 130 assert len(authz.challenges) == 1 131 assert authz.challenges[0].data == { 132 'url': 'xxxyyy', 133 'type': 'type', 134 'status': 'valid', 135 } 136 assert authz.to_json() == { 137 'uri': 'xxx', 138 'challenges': [ 139 { 140 'url': 'xxxyyy', 141 'type': 'type', 142 'status': 'valid', 143 } 144 ], 145 'status': 'valid', 146 'identifier': { 147 'type': 'dns', 148 'value': 'example.com', 149 }, 150 'wildcard': True, 151 } 152 153 client.version = 1 154 155 data = { 156 'challenges': [], 157 'identifier': { 158 'type': 'dns', 159 'value': 'example.com', 160 }, 161 } 162 authz = Authorization.from_json(client, data, 'xxx') 163 assert authz.url == 'xxx' 164 assert authz.status == 'pending' 165 assert authz.identifier == 'example.com' 166 assert authz.identifier_type == 'dns' 167 assert authz.challenges == [] 168 assert authz.to_json() == { 169 'uri': 'xxx', 170 'challenges': [], 171 'identifier': { 172 'type': 'dns', 173 'value': 'example.com', 174 }, 175 } 176 177 178def test_authorization_create_error(): 179 client = MagicMock() 180 client.version = 2 181 client.directory.directory = {} 182 with pytest.raises(ACMEProtocolException) as exc: 183 Authorization.create(client, 'dns', 'example.com') 184 185 assert exc.value.msg == 'ACME endpoint does not support pre-authorization.' 186 187 188def test_wait_for_validation_error(): 189 client = MagicMock() 190 client.version = 2 191 data = { 192 'challenges': [ 193 { 194 'url': 'xxxyyy1', 195 'type': 'dns-01', 196 'status': 'invalid', 197 'error': { 198 'type': 'dns-failed', 199 'subproblems': [ 200 { 201 'type': 'subproblem', 202 'detail': 'example.com DNS-01 validation failed', 203 }, 204 ] 205 }, 206 }, 207 { 208 'url': 'xxxyyy2', 209 'type': 'http-01', 210 'status': 'invalid', 211 'error': { 212 'type': 'http-failed', 213 'subproblems': [ 214 { 215 'type': 'subproblem', 216 'detail': 'example.com HTTP-01 validation failed', 217 }, 218 ] 219 }, 220 }, 221 { 222 'url': 'xxxyyy3', 223 'type': 'something-else', 224 'status': 'valid', 225 }, 226 ], 227 'status': 'invalid', 228 'identifier': { 229 'type': 'dns', 230 'value': 'example.com', 231 }, 232 } 233 client.get_request = MagicMock(return_value=(data, {})) 234 authz = Authorization.from_json(client, data, 'xxx') 235 with pytest.raises(ACMEProtocolException) as exc: 236 authz.wait_for_validation(client, 'dns') 237 238 assert exc.value.msg == ( 239 'Failed to validate challenge for dns:example.com: Status is "invalid". Challenge dns-01: Error dns-failed Subproblems:\n' 240 '(dns-01.0) Error subproblem: "example.com DNS-01 validation failed"; Challenge http-01: Error http-failed Subproblems:\n' 241 '(http-01.0) Error subproblem: "example.com HTTP-01 validation failed".' 242 ) 243 data = data.copy() 244 data['uri'] = 'xxx' 245 assert exc.value.module_fail_args == { 246 'identifier': 'dns:example.com', 247 'authorization': data, 248 } 249