mirror of
https://github.com/certbot/certbot.git
synced 2026-05-28 04:34:11 -04:00
acme.ClientNetwork: JWK becomes optional (#10275)
This results in a ClientNetwork that can .get() but not .post(). Useful for fetching ARI, which does not require authentication.
This commit is contained in:
parent
2cf6cda1fa
commit
0075104805
4 changed files with 25 additions and 10 deletions
|
|
@ -693,12 +693,11 @@ class ClientNetworkTest(unittest.TestCase):
|
|||
except requests.exceptions.ConnectionError as z: #pragma: no cover
|
||||
assert "'Connection aborted.'" in str(z) or "[WinError 10061]" in str(z)
|
||||
|
||||
|
||||
class ClientNetworkWithMockedResponseTest(unittest.TestCase):
|
||||
"""Tests for acme.client.ClientNetwork which mock out response."""
|
||||
|
||||
def setUp(self):
|
||||
self.net = ClientNetwork(key=None, alg=None)
|
||||
self.net = ClientNetwork(key='fake', alg=None)
|
||||
|
||||
self.response = mock.MagicMock(ok=True, status_code=http_client.OK)
|
||||
self.response.headers = {}
|
||||
|
|
@ -851,6 +850,16 @@ class ClientNetworkWithMockedResponseTest(unittest.TestCase):
|
|||
self.content_type = None
|
||||
self.net.post('uri', self.obj, content_type=None, new_nonce_url='new_nonce_uri')
|
||||
|
||||
def test_no_key_error(self):
|
||||
"A ClientNetwork with no key should error on POST but succeed on GET"
|
||||
self.net = ClientNetwork()
|
||||
self.net._send_request = mock.MagicMock()
|
||||
self.net._send_request.return_value = self.response
|
||||
with pytest.raises(errors.Error):
|
||||
self.net.post('uri', "body")
|
||||
assert self.response == self.net.get(
|
||||
'uri', content_type=self.content_type, bar='baz')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(pytest.main(sys.argv[1:] + [__file__])) # pragma: no cover
|
||||
|
|
|
|||
|
|
@ -526,7 +526,7 @@ class ClientNetwork:
|
|||
|
||||
"""Initialize.
|
||||
|
||||
:param josepy.JWK key: Account private key
|
||||
:param josepy.JWK key: Account private key. Required to use .post().
|
||||
:param messages.RegistrationResource account: Account object. Required if you are
|
||||
planning to use .post() for anything other than creating a new account;
|
||||
may be set later after registering.
|
||||
|
|
@ -535,7 +535,8 @@ class ClientNetwork:
|
|||
:param str user_agent: String to send as User-Agent header.
|
||||
:param int timeout: Timeout for requests.
|
||||
"""
|
||||
def __init__(self, key: jose.JWK, account: Optional[messages.RegistrationResource] = None,
|
||||
def __init__(self, key: Optional[jose.JWK] = None,
|
||||
account: Optional[messages.RegistrationResource] = None,
|
||||
alg: jose.JWASignature = jose.RS256, verify_ssl: bool = True,
|
||||
user_agent: str = 'acme-python', timeout: int = DEFAULT_NETWORK_TIMEOUT) -> None:
|
||||
self.key = key
|
||||
|
|
@ -572,16 +573,17 @@ class ClientNetwork:
|
|||
"""
|
||||
jobj = obj.json_dumps(indent=2).encode() if obj else b''
|
||||
logger.debug('JWS payload:\n%s', jobj)
|
||||
assert self.key
|
||||
kwargs = {
|
||||
"alg": self.alg,
|
||||
"nonce": nonce,
|
||||
"url": url
|
||||
"url": url,
|
||||
"key": self.key
|
||||
}
|
||||
# newAccount and revokeCert work without the kid
|
||||
# newAccount must not have kid
|
||||
if self.account is not None:
|
||||
kwargs["kid"] = self.account["uri"]
|
||||
kwargs["key"] = self.key
|
||||
return jws.JWS.sign(jobj, **cast(Mapping[str, Any], kwargs)).json_dumps(indent=2)
|
||||
|
||||
@classmethod
|
||||
|
|
@ -771,6 +773,8 @@ class ClientNetwork:
|
|||
def _post_once(self, url: str, obj: jose.JSONDeSerializable,
|
||||
content_type: str = JOSE_CONTENT_TYPE, **kwargs: Any) -> requests.Response:
|
||||
new_nonce_url = kwargs.pop('new_nonce_url', None)
|
||||
if not self.key:
|
||||
raise errors.Error("acme.ClientNetwork with no private key can't POST.")
|
||||
data = self._wrap_in_jws(obj, self._get_nonce(url, new_nonce_url), url)
|
||||
kwargs.setdefault('headers', {'Content-Type': content_type})
|
||||
response = self._send_request('POST', url, data=data, **kwargs)
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ Certbot adheres to [Semantic Versioning](https://semver.org/).
|
|||
### Changed
|
||||
|
||||
* Switched to src-layout from flat-layout to accommodate PEP 517 pip editable installs
|
||||
* acme.client.ClientNetwork now makes the "key" parameter optional.
|
||||
* Deprecated `acme.challenges.TLSALPN01Response`
|
||||
* Deprecated `acme.challenges.TLSALPN01`
|
||||
* Deprecated ivar `alpn_selection` from `acme.crypto_util.SSLSocket`
|
||||
|
|
|
|||
|
|
@ -49,11 +49,12 @@ logger = logging.getLogger(__name__)
|
|||
|
||||
|
||||
def acme_from_config_key(config: configuration.NamespaceConfig,
|
||||
key: jose.JWK,
|
||||
key: Optional[jose.JWK] = None,
|
||||
regr: Optional[messages.RegistrationResource] = None,
|
||||
) -> acme_client.ClientV2:
|
||||
"""Wrangle ACME client construction"""
|
||||
if key.typ == 'EC':
|
||||
alg = RS256
|
||||
if key and key.typ == 'EC':
|
||||
public_key = key.key
|
||||
if public_key.key_size == 256:
|
||||
alg = ES256
|
||||
|
|
@ -65,8 +66,6 @@ def acme_from_config_key(config: configuration.NamespaceConfig,
|
|||
raise errors.NotSupportedError(
|
||||
"No matching signing algorithm can be found for the key"
|
||||
)
|
||||
else:
|
||||
alg = RS256
|
||||
net = acme_client.ClientNetwork(key, alg=alg, account=regr,
|
||||
verify_ssl=(not config.no_verify_ssl),
|
||||
user_agent=determine_user_agent(config))
|
||||
|
|
@ -225,6 +224,8 @@ def perform_registration(acme: acme_client.ClientV2, config: configuration.Names
|
|||
:returns: Registration Resource.
|
||||
:rtype: `acme.messages.RegistrationResource`
|
||||
"""
|
||||
if not acme.net.key:
|
||||
raise errors.Error("acme client with no private key cannot register account.")
|
||||
|
||||
eab_credentials_supplied = config.eab_kid and config.eab_hmac_key
|
||||
eab: Optional[Dict[str, Any]]
|
||||
|
|
|
|||
Loading…
Reference in a new issue