mirror of
https://github.com/certbot/certbot.git
synced 2026-01-26 07:41:33 +03:00
Typed jose fields (#9073)
* Add generic methods to save some casts, and fix lint * Update current and oldest pinning * Fix classes * Remove some todos thanks to josepy 1.11.0 * Cleanup some useless pylint disable * Finish complete typing * Better TypeVar names * Upgrade pinning and fix some typing errors * Use protocol * Fix types in apache Co-authored-by: Brad Warren <bmw@users.noreply.github.com>
This commit is contained in:
@@ -12,6 +12,8 @@ from typing import Mapping
|
|||||||
from typing import Optional
|
from typing import Optional
|
||||||
from typing import Tuple
|
from typing import Tuple
|
||||||
from typing import Type
|
from typing import Type
|
||||||
|
from typing import TypeVar
|
||||||
|
from typing import Union
|
||||||
|
|
||||||
from cryptography.hazmat.primitives import hashes
|
from cryptography.hazmat.primitives import hashes
|
||||||
import josepy as jose
|
import josepy as jose
|
||||||
@@ -27,6 +29,8 @@ from acme.mixins import TypeMixin
|
|||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
GenericChallenge = TypeVar('GenericChallenge', bound='Challenge')
|
||||||
|
|
||||||
|
|
||||||
class Challenge(jose.TypedJSONObjectWithFields):
|
class Challenge(jose.TypedJSONObjectWithFields):
|
||||||
# _fields_to_partial_json
|
# _fields_to_partial_json
|
||||||
@@ -34,9 +38,10 @@ class Challenge(jose.TypedJSONObjectWithFields):
|
|||||||
TYPES: Dict[str, Type['Challenge']] = {}
|
TYPES: Dict[str, Type['Challenge']] = {}
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_json(cls, jobj: Mapping[str, Any]) -> 'Challenge':
|
def from_json(cls: Type[GenericChallenge],
|
||||||
|
jobj: Mapping[str, Any]) -> Union[GenericChallenge, 'UnrecognizedChallenge']:
|
||||||
try:
|
try:
|
||||||
return super().from_json(jobj)
|
return cast(GenericChallenge, super().from_json(jobj))
|
||||||
except jose.UnrecognizedTypeError as error:
|
except jose.UnrecognizedTypeError as error:
|
||||||
logger.debug(error)
|
logger.debug(error)
|
||||||
return UnrecognizedChallenge.from_json(jobj)
|
return UnrecognizedChallenge.from_json(jobj)
|
||||||
@@ -47,7 +52,7 @@ class ChallengeResponse(ResourceMixin, TypeMixin, jose.TypedJSONObjectWithFields
|
|||||||
"""ACME challenge response."""
|
"""ACME challenge response."""
|
||||||
TYPES: Dict[str, Type['ChallengeResponse']] = {}
|
TYPES: Dict[str, Type['ChallengeResponse']] = {}
|
||||||
resource_type = 'challenge'
|
resource_type = 'challenge'
|
||||||
resource = fields.Resource(resource_type)
|
resource: str = fields.resource(resource_type)
|
||||||
|
|
||||||
|
|
||||||
class UnrecognizedChallenge(Challenge):
|
class UnrecognizedChallenge(Challenge):
|
||||||
@@ -62,6 +67,7 @@ class UnrecognizedChallenge(Challenge):
|
|||||||
:ivar jobj: Original JSON decoded object.
|
:ivar jobj: Original JSON decoded object.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
jobj: Dict[str, Any]
|
||||||
|
|
||||||
def __init__(self, jobj: Mapping[str, Any]) -> None:
|
def __init__(self, jobj: Mapping[str, Any]) -> None:
|
||||||
super().__init__()
|
super().__init__()
|
||||||
@@ -85,7 +91,7 @@ class _TokenChallenge(Challenge):
|
|||||||
"""Minimum size of the :attr:`token` in bytes."""
|
"""Minimum size of the :attr:`token` in bytes."""
|
||||||
|
|
||||||
# TODO: acme-spec doesn't specify token as base64-encoded value
|
# TODO: acme-spec doesn't specify token as base64-encoded value
|
||||||
token: bytes = jose.Field(
|
token: bytes = jose.field(
|
||||||
"token", encoder=jose.encode_b64jose, decoder=functools.partial(
|
"token", encoder=jose.encode_b64jose, decoder=functools.partial(
|
||||||
jose.decode_b64jose, size=TOKEN_SIZE, minimum=True))
|
jose.decode_b64jose, size=TOKEN_SIZE, minimum=True))
|
||||||
|
|
||||||
@@ -108,10 +114,10 @@ class _TokenChallenge(Challenge):
|
|||||||
class KeyAuthorizationChallengeResponse(ChallengeResponse):
|
class KeyAuthorizationChallengeResponse(ChallengeResponse):
|
||||||
"""Response to Challenges based on Key Authorization.
|
"""Response to Challenges based on Key Authorization.
|
||||||
|
|
||||||
:param unicode key_authorization:
|
:param str key_authorization:
|
||||||
|
|
||||||
"""
|
"""
|
||||||
key_authorization = jose.Field("keyAuthorization")
|
key_authorization: str = jose.field("keyAuthorization")
|
||||||
thumbprint_hash_function = hashes.SHA256
|
thumbprint_hash_function = hashes.SHA256
|
||||||
|
|
||||||
def verify(self, chall: 'KeyAuthorizationChallenge', account_public_key: jose.JWK) -> bool:
|
def verify(self, chall: 'KeyAuthorizationChallenge', account_public_key: jose.JWK) -> bool:
|
||||||
@@ -126,7 +132,7 @@ class KeyAuthorizationChallengeResponse(ChallengeResponse):
|
|||||||
:rtype: bool
|
:rtype: bool
|
||||||
|
|
||||||
"""
|
"""
|
||||||
parts = self.key_authorization.split('.')
|
parts = self.key_authorization.split('.') # pylint: disable=no-member
|
||||||
if len(parts) != 2:
|
if len(parts) != 2:
|
||||||
logger.debug("Key authorization (%r) is not well formed",
|
logger.debug("Key authorization (%r) is not well formed",
|
||||||
self.key_authorization)
|
self.key_authorization)
|
||||||
@@ -152,6 +158,9 @@ class KeyAuthorizationChallengeResponse(ChallengeResponse):
|
|||||||
return jobj
|
return jobj
|
||||||
|
|
||||||
|
|
||||||
|
# TODO: Make this method a generic of K (bound=KeyAuthorizationChallenge), response_cls of type
|
||||||
|
# Type[K] and use it in response/response_and_validation return types once Python 3.6 support is
|
||||||
|
# dropped (do not support generic ABC classes, see https://github.com/python/typing/issues/449).
|
||||||
class KeyAuthorizationChallenge(_TokenChallenge, metaclass=abc.ABCMeta):
|
class KeyAuthorizationChallenge(_TokenChallenge, metaclass=abc.ABCMeta):
|
||||||
"""Challenge based on Key Authorization.
|
"""Challenge based on Key Authorization.
|
||||||
|
|
||||||
@@ -168,7 +177,7 @@ class KeyAuthorizationChallenge(_TokenChallenge, metaclass=abc.ABCMeta):
|
|||||||
"""Generate Key Authorization.
|
"""Generate Key Authorization.
|
||||||
|
|
||||||
:param JWK account_key:
|
:param JWK account_key:
|
||||||
:rtype unicode:
|
:rtype str:
|
||||||
|
|
||||||
"""
|
"""
|
||||||
return self.encode("token") + "." + jose.b64encode(
|
return self.encode("token") + "." + jose.b64encode(
|
||||||
@@ -229,7 +238,7 @@ class DNS01Response(KeyAuthorizationChallengeResponse):
|
|||||||
around `KeyAuthorizationChallengeResponse.verify`.
|
around `KeyAuthorizationChallengeResponse.verify`.
|
||||||
|
|
||||||
:param challenges.DNS01 chall: Corresponding challenge.
|
:param challenges.DNS01 chall: Corresponding challenge.
|
||||||
:param unicode domain: Domain name being verified.
|
:param str domain: Domain name being verified.
|
||||||
:param JWK account_public_key: Public key for the key pair
|
:param JWK account_public_key: Public key for the key pair
|
||||||
being authorized.
|
being authorized.
|
||||||
|
|
||||||
@@ -257,7 +266,7 @@ class DNS01(KeyAuthorizationChallenge):
|
|||||||
"""Generate validation.
|
"""Generate validation.
|
||||||
|
|
||||||
:param JWK account_key:
|
:param JWK account_key:
|
||||||
:rtype: unicode
|
:rtype: str
|
||||||
|
|
||||||
"""
|
"""
|
||||||
return jose.b64encode(hashlib.sha256(self.key_authorization(
|
return jose.b64encode(hashlib.sha256(self.key_authorization(
|
||||||
@@ -266,7 +275,8 @@ class DNS01(KeyAuthorizationChallenge):
|
|||||||
def validation_domain_name(self, name: str) -> str:
|
def validation_domain_name(self, name: str) -> str:
|
||||||
"""Domain name for TXT validation record.
|
"""Domain name for TXT validation record.
|
||||||
|
|
||||||
:param unicode name: Domain name being validated.
|
:param str name: Domain name being validated.
|
||||||
|
:rtype: str
|
||||||
|
|
||||||
"""
|
"""
|
||||||
return "{0}.{1}".format(self.LABEL, name)
|
return "{0}.{1}".format(self.LABEL, name)
|
||||||
@@ -293,7 +303,7 @@ class HTTP01Response(KeyAuthorizationChallengeResponse):
|
|||||||
"""Simple verify.
|
"""Simple verify.
|
||||||
|
|
||||||
:param challenges.SimpleHTTP chall: Corresponding challenge.
|
:param challenges.SimpleHTTP chall: Corresponding challenge.
|
||||||
:param unicode domain: Domain name being verified.
|
:param str domain: Domain name being verified.
|
||||||
:param JWK account_public_key: Public key for the key pair
|
:param JWK account_public_key: Public key for the key pair
|
||||||
being authorized.
|
being authorized.
|
||||||
:param int port: Port used in the validation.
|
:param int port: Port used in the validation.
|
||||||
@@ -357,7 +367,7 @@ class HTTP01(KeyAuthorizationChallenge):
|
|||||||
def path(self) -> str:
|
def path(self) -> str:
|
||||||
"""Path (starting with '/') for provisioned resource.
|
"""Path (starting with '/') for provisioned resource.
|
||||||
|
|
||||||
:rtype: string
|
:rtype: str
|
||||||
|
|
||||||
"""
|
"""
|
||||||
return '/' + self.URI_ROOT_PATH + '/' + self.encode('token')
|
return '/' + self.URI_ROOT_PATH + '/' + self.encode('token')
|
||||||
@@ -368,8 +378,8 @@ class HTTP01(KeyAuthorizationChallenge):
|
|||||||
Forms an URI to the HTTPS server provisioned resource
|
Forms an URI to the HTTPS server provisioned resource
|
||||||
(containing :attr:`~SimpleHTTP.token`).
|
(containing :attr:`~SimpleHTTP.token`).
|
||||||
|
|
||||||
:param unicode domain: Domain name being verified.
|
:param str domain: Domain name being verified.
|
||||||
:rtype: string
|
:rtype: str
|
||||||
|
|
||||||
"""
|
"""
|
||||||
return "http://" + domain + self.path
|
return "http://" + domain + self.path
|
||||||
@@ -378,7 +388,7 @@ class HTTP01(KeyAuthorizationChallenge):
|
|||||||
"""Generate validation.
|
"""Generate validation.
|
||||||
|
|
||||||
:param JWK account_key:
|
:param JWK account_key:
|
||||||
:rtype: unicode
|
:rtype: str
|
||||||
|
|
||||||
"""
|
"""
|
||||||
return self.key_authorization(account_key)
|
return self.key_authorization(account_key)
|
||||||
@@ -409,7 +419,7 @@ class TLSALPN01Response(KeyAuthorizationChallengeResponse):
|
|||||||
) -> Tuple[crypto.X509, crypto.PKey]:
|
) -> Tuple[crypto.X509, crypto.PKey]:
|
||||||
"""Generate tls-alpn-01 certificate.
|
"""Generate tls-alpn-01 certificate.
|
||||||
|
|
||||||
:param unicode domain: Domain verified by the challenge.
|
:param str domain: Domain verified by the challenge.
|
||||||
:param OpenSSL.crypto.PKey key: Optional private key used in
|
:param OpenSSL.crypto.PKey key: Optional private key used in
|
||||||
certificate generation. If not provided (``None``), then
|
certificate generation. If not provided (``None``), then
|
||||||
fresh key will be generated.
|
fresh key will be generated.
|
||||||
@@ -433,8 +443,8 @@ class TLSALPN01Response(KeyAuthorizationChallengeResponse):
|
|||||||
port: Optional[int] = None) -> crypto.X509:
|
port: Optional[int] = None) -> crypto.X509:
|
||||||
"""Probe tls-alpn-01 challenge certificate.
|
"""Probe tls-alpn-01 challenge certificate.
|
||||||
|
|
||||||
:param unicode domain: domain being validated, required.
|
:param str domain: domain being validated, required.
|
||||||
:param string host: IP address used to probe the certificate.
|
:param str host: IP address used to probe the certificate.
|
||||||
:param int port: Port used to probe the certificate.
|
:param int port: Port used to probe the certificate.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
@@ -450,7 +460,7 @@ class TLSALPN01Response(KeyAuthorizationChallengeResponse):
|
|||||||
def verify_cert(self, domain: str, cert: crypto.X509) -> bool:
|
def verify_cert(self, domain: str, cert: crypto.X509) -> bool:
|
||||||
"""Verify tls-alpn-01 challenge certificate.
|
"""Verify tls-alpn-01 challenge certificate.
|
||||||
|
|
||||||
:param unicode domain: Domain name being validated.
|
:param str domain: Domain name being validated.
|
||||||
:param OpensSSL.crypto.X509 cert: Challenge certificate.
|
:param OpensSSL.crypto.X509 cert: Challenge certificate.
|
||||||
|
|
||||||
:returns: Whether the certificate was successfully verified.
|
:returns: Whether the certificate was successfully verified.
|
||||||
@@ -523,7 +533,7 @@ class TLSALPN01(KeyAuthorizationChallenge):
|
|||||||
"""Generate validation.
|
"""Generate validation.
|
||||||
|
|
||||||
:param JWK account_key:
|
:param JWK account_key:
|
||||||
:param unicode domain: Domain verified by the challenge.
|
:param str domain: Domain verified by the challenge.
|
||||||
:param OpenSSL.crypto.PKey cert_key: Optional private key used
|
:param OpenSSL.crypto.PKey cert_key: Optional private key used
|
||||||
in certificate generation. If not provided (``None``), then
|
in certificate generation. If not provided (``None``), then
|
||||||
fresh key will be generated.
|
fresh key will be generated.
|
||||||
@@ -531,9 +541,10 @@ class TLSALPN01(KeyAuthorizationChallenge):
|
|||||||
:rtype: `tuple` of `OpenSSL.crypto.X509` and `OpenSSL.crypto.PKey`
|
:rtype: `tuple` of `OpenSSL.crypto.X509` and `OpenSSL.crypto.PKey`
|
||||||
|
|
||||||
"""
|
"""
|
||||||
return self.response(account_key).gen_cert(
|
# TODO: Remove cast when response() is generic.
|
||||||
|
return cast(TLSALPN01Response, self.response(account_key)).gen_cert(
|
||||||
key=kwargs.get('cert_key'),
|
key=kwargs.get('cert_key'),
|
||||||
domain=kwargs.get('domain'))
|
domain=cast(str, kwargs.get('domain')))
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def is_supported() -> bool:
|
def is_supported() -> bool:
|
||||||
@@ -599,13 +610,12 @@ class DNS(_TokenChallenge):
|
|||||||
:rtype: DNSResponse
|
:rtype: DNSResponse
|
||||||
|
|
||||||
"""
|
"""
|
||||||
return DNSResponse(validation=self.gen_validation(
|
return DNSResponse(validation=self.gen_validation(account_key, **kwargs))
|
||||||
account_key, **kwargs))
|
|
||||||
|
|
||||||
def validation_domain_name(self, name: str) -> str:
|
def validation_domain_name(self, name: str) -> str:
|
||||||
"""Domain name for TXT validation record.
|
"""Domain name for TXT validation record.
|
||||||
|
|
||||||
:param unicode name: Domain name being validated.
|
:param str name: Domain name being validated.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
return "{0}.{1}".format(self.LABEL, name)
|
return "{0}.{1}".format(self.LABEL, name)
|
||||||
@@ -620,7 +630,7 @@ class DNSResponse(ChallengeResponse):
|
|||||||
"""
|
"""
|
||||||
typ = "dns"
|
typ = "dns"
|
||||||
|
|
||||||
validation = jose.Field("validation", decoder=jose.JWS.from_json)
|
validation: jose.JWS = jose.field("validation", decoder=jose.JWS.from_json)
|
||||||
|
|
||||||
def check_validation(self, chall: 'DNS', account_public_key: jose.JWK) -> bool:
|
def check_validation(self, chall: 'DNS', account_public_key: jose.JWK) -> bool:
|
||||||
"""Check validation.
|
"""Check validation.
|
||||||
@@ -631,4 +641,4 @@ class DNSResponse(ChallengeResponse):
|
|||||||
:rtype: bool
|
:rtype: bool
|
||||||
|
|
||||||
"""
|
"""
|
||||||
return chall.check_validation(cast(jose.JWS, self.validation), account_public_key)
|
return chall.check_validation(self.validation, account_public_key)
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ from typing import cast
|
|||||||
from typing import Dict
|
from typing import Dict
|
||||||
from typing import Iterable
|
from typing import Iterable
|
||||||
from typing import List
|
from typing import List
|
||||||
|
from typing import Mapping
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
from typing import Set
|
from typing import Set
|
||||||
from typing import Text
|
from typing import Text
|
||||||
@@ -33,6 +34,7 @@ from requests.adapters import HTTPAdapter
|
|||||||
from requests.utils import parse_header_links
|
from requests.utils import parse_header_links
|
||||||
from requests_toolbelt.adapters.source import SourceAddressAdapter
|
from requests_toolbelt.adapters.source import SourceAddressAdapter
|
||||||
|
|
||||||
|
from acme import challenges
|
||||||
from acme import crypto_util
|
from acme import crypto_util
|
||||||
from acme import errors
|
from acme import errors
|
||||||
from acme import jws
|
from acme import jws
|
||||||
@@ -156,12 +158,12 @@ class ClientBase:
|
|||||||
authzr = messages.AuthorizationResource(
|
authzr = messages.AuthorizationResource(
|
||||||
body=messages.Authorization.from_json(response.json()),
|
body=messages.Authorization.from_json(response.json()),
|
||||||
uri=response.headers.get('Location', uri))
|
uri=response.headers.get('Location', uri))
|
||||||
if identifier is not None and authzr.body.identifier != identifier:
|
if identifier is not None and authzr.body.identifier != identifier: # pylint: disable=no-member
|
||||||
raise errors.UnexpectedUpdate(authzr)
|
raise errors.UnexpectedUpdate(authzr)
|
||||||
return authzr
|
return authzr
|
||||||
|
|
||||||
def answer_challenge(self, challb: messages.ChallengeBody, response: requests.Response
|
def answer_challenge(self, challb: messages.ChallengeBody,
|
||||||
) -> messages.ChallengeResource:
|
response: challenges.ChallengeResponse) -> messages.ChallengeResource:
|
||||||
"""Answer challenge.
|
"""Answer challenge.
|
||||||
|
|
||||||
:param challb: Challenge Resource body.
|
:param challb: Challenge Resource body.
|
||||||
@@ -176,15 +178,15 @@ class ClientBase:
|
|||||||
:raises .UnexpectedUpdate:
|
:raises .UnexpectedUpdate:
|
||||||
|
|
||||||
"""
|
"""
|
||||||
response = self._post(challb.uri, response)
|
resp = self._post(challb.uri, response)
|
||||||
try:
|
try:
|
||||||
authzr_uri = response.links['up']['url']
|
authzr_uri = resp.links['up']['url']
|
||||||
except KeyError:
|
except KeyError:
|
||||||
raise errors.ClientError('"up" Link header missing')
|
raise errors.ClientError('"up" Link header missing')
|
||||||
challr = messages.ChallengeResource(
|
challr = messages.ChallengeResource(
|
||||||
authzr_uri=authzr_uri,
|
authzr_uri=authzr_uri,
|
||||||
body=messages.ChallengeBody.from_json(response.json()))
|
body=messages.ChallengeBody.from_json(resp.json()))
|
||||||
# TODO: check that challr.uri == response.headers['Location']?
|
# TODO: check that challr.uri == resp.headers['Location']?
|
||||||
if challr.uri != challb.uri:
|
if challr.uri != challb.uri:
|
||||||
raise errors.UnexpectedUpdate(challr.uri)
|
raise errors.UnexpectedUpdate(challr.uri)
|
||||||
return challr
|
return challr
|
||||||
@@ -492,7 +494,7 @@ class Client(ClientBase):
|
|||||||
updated[authzr] = updated_authzr
|
updated[authzr] = updated_authzr
|
||||||
|
|
||||||
attempts[authzr] += 1
|
attempts[authzr] += 1
|
||||||
if updated_authzr.body.status not in (
|
if updated_authzr.body.status not in ( # pylint: disable=no-member
|
||||||
messages.STATUS_VALID, messages.STATUS_INVALID):
|
messages.STATUS_VALID, messages.STATUS_INVALID):
|
||||||
if attempts[authzr] < max_attempts:
|
if attempts[authzr] < max_attempts:
|
||||||
# push back to the priority queue, with updated retry_after
|
# push back to the priority queue, with updated retry_after
|
||||||
@@ -599,7 +601,7 @@ class Client(ClientBase):
|
|||||||
:raises .ClientError: If revocation is unsuccessful.
|
:raises .ClientError: If revocation is unsuccessful.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
self._revoke(cert, rsn, self.directory[cast(str, messages.Revocation)])
|
self._revoke(cert, rsn, self.directory[messages.Revocation])
|
||||||
|
|
||||||
|
|
||||||
class ClientV2(ClientBase):
|
class ClientV2(ClientBase):
|
||||||
@@ -756,7 +758,7 @@ class ClientV2(ClientBase):
|
|||||||
for url in orderr.body.authorizations:
|
for url in orderr.body.authorizations:
|
||||||
while datetime.datetime.now() < deadline:
|
while datetime.datetime.now() < deadline:
|
||||||
authzr = self._authzr_from_response(self._post_as_get(url), uri=url)
|
authzr = self._authzr_from_response(self._post_as_get(url), uri=url)
|
||||||
if authzr.body.status != messages.STATUS_PENDING:
|
if authzr.body.status != messages.STATUS_PENDING: # pylint: disable=no-member
|
||||||
responses.append(authzr)
|
responses.append(authzr)
|
||||||
break
|
break
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
@@ -897,11 +899,11 @@ class BackwardsCompatibleClientV2:
|
|||||||
check_tos_cb(tos)
|
check_tos_cb(tos)
|
||||||
if self.acme_version == 1:
|
if self.acme_version == 1:
|
||||||
client_v1 = cast(Client, self.client)
|
client_v1 = cast(Client, self.client)
|
||||||
regr = client_v1.register(regr)
|
regr_res = client_v1.register(regr)
|
||||||
if regr.terms_of_service is not None:
|
if regr_res.terms_of_service is not None:
|
||||||
_assess_tos(regr.terms_of_service)
|
_assess_tos(regr_res.terms_of_service)
|
||||||
return client_v1.agree_to_tos(regr)
|
return client_v1.agree_to_tos(regr_res)
|
||||||
return regr
|
return regr_res
|
||||||
else:
|
else:
|
||||||
client_v2 = cast(ClientV2, self.client)
|
client_v2 = cast(ClientV2, self.client)
|
||||||
if "terms_of_service" in client_v2.directory.meta:
|
if "terms_of_service" in client_v2.directory.meta:
|
||||||
@@ -970,7 +972,8 @@ class BackwardsCompatibleClientV2:
|
|||||||
'certificate, please rerun the command for a new one.')
|
'certificate, please rerun the command for a new one.')
|
||||||
|
|
||||||
cert = OpenSSL.crypto.dump_certificate(
|
cert = OpenSSL.crypto.dump_certificate(
|
||||||
OpenSSL.crypto.FILETYPE_PEM, certr.body.wrapped).decode()
|
OpenSSL.crypto.FILETYPE_PEM,
|
||||||
|
cast(OpenSSL.crypto.X509, cast(jose.ComparableX509, certr.body).wrapped)).decode()
|
||||||
chain_str = crypto_util.dump_pyopenssl_chain(chain).decode()
|
chain_str = crypto_util.dump_pyopenssl_chain(chain).decode()
|
||||||
|
|
||||||
return orderr.update(fullchain_pem=(cert + chain_str))
|
return orderr.update(fullchain_pem=(cert + chain_str))
|
||||||
@@ -1056,7 +1059,7 @@ class ClientNetwork:
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
def _wrap_in_jws(self, obj: jose.JSONDeSerializable, nonce: str, url: str,
|
def _wrap_in_jws(self, obj: jose.JSONDeSerializable, nonce: str, url: str,
|
||||||
acme_version: int) -> jose.JWS:
|
acme_version: int) -> str:
|
||||||
"""Wrap `JSONDeSerializable` object in JWS.
|
"""Wrap `JSONDeSerializable` object in JWS.
|
||||||
|
|
||||||
.. todo:: Implement ``acmePath``.
|
.. todo:: Implement ``acmePath``.
|
||||||
@@ -1064,7 +1067,7 @@ class ClientNetwork:
|
|||||||
:param josepy.JSONDeSerializable obj:
|
:param josepy.JSONDeSerializable obj:
|
||||||
:param str url: The URL to which this object will be POSTed
|
:param str url: The URL to which this object will be POSTed
|
||||||
:param str nonce:
|
:param str nonce:
|
||||||
:rtype: `josepy.JWS`
|
:rtype: str
|
||||||
|
|
||||||
"""
|
"""
|
||||||
if isinstance(obj, VersionedLEACMEMixin):
|
if isinstance(obj, VersionedLEACMEMixin):
|
||||||
@@ -1082,7 +1085,7 @@ class ClientNetwork:
|
|||||||
if self.account is not None:
|
if self.account is not None:
|
||||||
kwargs["kid"] = self.account["uri"]
|
kwargs["kid"] = self.account["uri"]
|
||||||
kwargs["key"] = self.key
|
kwargs["key"] = self.key
|
||||||
return jws.JWS.sign(jobj, **kwargs).json_dumps(indent=2)
|
return jws.JWS.sign(jobj, **cast(Mapping[str, Any], kwargs)).json_dumps(indent=2)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _check_response(cls, response: requests.Response,
|
def _check_response(cls, response: requests.Response,
|
||||||
|
|||||||
@@ -278,7 +278,7 @@ def _pyopenssl_cert_or_req_san(cert_or_req: Union[crypto.X509, crypto.X509Req])
|
|||||||
:type cert_or_req: `OpenSSL.crypto.X509` or `OpenSSL.crypto.X509Req`.
|
:type cert_or_req: `OpenSSL.crypto.X509` or `OpenSSL.crypto.X509Req`.
|
||||||
|
|
||||||
:returns: A list of Subject Alternative Names that is DNS.
|
:returns: A list of Subject Alternative Names that is DNS.
|
||||||
:rtype: `list` of `unicode`
|
:rtype: `list` of `str`
|
||||||
|
|
||||||
"""
|
"""
|
||||||
# This function finds SANs with dns name
|
# This function finds SANs with dns name
|
||||||
@@ -300,7 +300,7 @@ def _pyopenssl_cert_or_req_san_ip(cert_or_req: Union[crypto.X509, crypto.X509Req
|
|||||||
:type cert_or_req: `OpenSSL.crypto.X509` or `OpenSSL.crypto.X509Req`.
|
:type cert_or_req: `OpenSSL.crypto.X509` or `OpenSSL.crypto.X509Req`.
|
||||||
|
|
||||||
:returns: A list of Subject Alternative Names that are IP Addresses.
|
:returns: A list of Subject Alternative Names that are IP Addresses.
|
||||||
:rtype: `list` of `unicode`. note that this returns as string, not IPaddress object
|
:rtype: `list` of `str`. note that this returns as string, not IPaddress object
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@@ -320,7 +320,7 @@ def _pyopenssl_extract_san_list_raw(cert_or_req: Union[crypto.X509, crypto.X509R
|
|||||||
:type cert_or_req: `OpenSSL.crypto.X509` or `OpenSSL.crypto.X509Req`.
|
:type cert_or_req: `OpenSSL.crypto.X509` or `OpenSSL.crypto.X509Req`.
|
||||||
|
|
||||||
:returns: raw san strings, parsed byte as utf-8
|
:returns: raw san strings, parsed byte as utf-8
|
||||||
:rtype: `list` of `unicode`
|
:rtype: `list` of `str`
|
||||||
|
|
||||||
"""
|
"""
|
||||||
# This function finds SANs by dumping the certificate/CSR to text and
|
# This function finds SANs by dumping the certificate/CSR to text and
|
||||||
@@ -352,7 +352,7 @@ def gen_ss_cert(key: crypto.PKey, domains: Optional[List[str]] = None,
|
|||||||
) -> crypto.X509:
|
) -> crypto.X509:
|
||||||
"""Generate new self-signed certificate.
|
"""Generate new self-signed certificate.
|
||||||
|
|
||||||
:type domains: `list` of `unicode`
|
:type domains: `list` of `str`
|
||||||
:param OpenSSL.crypto.PKey key:
|
:param OpenSSL.crypto.PKey key:
|
||||||
:param bool force_san:
|
:param bool force_san:
|
||||||
:param extensions: List of additional extensions to include in the cert.
|
:param extensions: List of additional extensions to include in the cert.
|
||||||
@@ -410,7 +410,8 @@ def gen_ss_cert(key: crypto.PKey, domains: Optional[List[str]] = None,
|
|||||||
return cert
|
return cert
|
||||||
|
|
||||||
|
|
||||||
def dump_pyopenssl_chain(chain: List[crypto.X509], filetype: int = crypto.FILETYPE_PEM) -> bytes:
|
def dump_pyopenssl_chain(chain: Union[List[jose.ComparableX509], List[crypto.X509]],
|
||||||
|
filetype: int = crypto.FILETYPE_PEM) -> bytes:
|
||||||
"""Dump certificate chain into a bundle.
|
"""Dump certificate chain into a bundle.
|
||||||
|
|
||||||
:param list chain: List of `OpenSSL.crypto.X509` (or wrapped in
|
:param list chain: List of `OpenSSL.crypto.X509` (or wrapped in
|
||||||
@@ -425,6 +426,8 @@ def dump_pyopenssl_chain(chain: List[crypto.X509], filetype: int = crypto.FILETY
|
|||||||
|
|
||||||
def _dump_cert(cert: Union[jose.ComparableX509, crypto.X509]) -> bytes:
|
def _dump_cert(cert: Union[jose.ComparableX509, crypto.X509]) -> bytes:
|
||||||
if isinstance(cert, jose.ComparableX509):
|
if isinstance(cert, jose.ComparableX509):
|
||||||
|
if isinstance(cert.wrapped, crypto.X509Req):
|
||||||
|
raise errors.Error("Unexpected CSR provided.") # pragma: no cover
|
||||||
cert = cert.wrapped
|
cert = cert.wrapped
|
||||||
return crypto.dump_certificate(filetype, cert)
|
return crypto.dump_certificate(filetype, cert)
|
||||||
|
|
||||||
|
|||||||
@@ -56,8 +56,8 @@ class Resource(jose.Field):
|
|||||||
|
|
||||||
def __init__(self, resource_type: str, *args: Any, **kwargs: Any) -> None:
|
def __init__(self, resource_type: str, *args: Any, **kwargs: Any) -> None:
|
||||||
self.resource_type = resource_type
|
self.resource_type = resource_type
|
||||||
super().__init__(
|
kwargs['default'] = resource_type
|
||||||
'resource', default=resource_type, *args, **kwargs)
|
super().__init__('resource', *args, **kwargs)
|
||||||
|
|
||||||
def decode(self, value: Any) -> Any:
|
def decode(self, value: Any) -> Any:
|
||||||
if value != self.resource_type:
|
if value != self.resource_type:
|
||||||
@@ -65,3 +65,18 @@ class Resource(jose.Field):
|
|||||||
'Wrong resource type: {0} instead of {1}'.format(
|
'Wrong resource type: {0} instead of {1}'.format(
|
||||||
value, self.resource_type))
|
value, self.resource_type))
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
def fixed(json_name: str, value: Any) -> Any:
|
||||||
|
"""Generates a type-friendly Fixed field."""
|
||||||
|
return Fixed(json_name, value)
|
||||||
|
|
||||||
|
|
||||||
|
def rfc3339(json_name: str, omitempty: bool = False) -> Any:
|
||||||
|
"""Generates a type-friendly RFC3339 field."""
|
||||||
|
return RFC3339Field(json_name, omitempty=omitempty)
|
||||||
|
|
||||||
|
|
||||||
|
def resource(resource_type: str) -> Any:
|
||||||
|
"""Generates a type-friendly Resource field."""
|
||||||
|
return Resource(resource_type)
|
||||||
|
|||||||
@@ -12,14 +12,14 @@ import josepy as jose
|
|||||||
class Header(jose.Header):
|
class Header(jose.Header):
|
||||||
"""ACME-specific JOSE Header. Implements nonce, kid, and url.
|
"""ACME-specific JOSE Header. Implements nonce, kid, and url.
|
||||||
"""
|
"""
|
||||||
nonce = jose.Field('nonce', omitempty=True, encoder=jose.encode_b64jose)
|
nonce: Optional[bytes] = jose.field('nonce', omitempty=True, encoder=jose.encode_b64jose)
|
||||||
kid = jose.Field('kid', omitempty=True)
|
kid: Optional[str] = jose.field('kid', omitempty=True)
|
||||||
url = jose.Field('url', omitempty=True)
|
url: Optional[str] = jose.field('url', omitempty=True)
|
||||||
|
|
||||||
# Mypy does not understand the josepy magic happening here, and falsely claims
|
# Mypy does not understand the josepy magic happening here, and falsely claims
|
||||||
# that nonce is redefined. Let's ignore the type check here.
|
# that nonce is redefined. Let's ignore the type check here.
|
||||||
@nonce.decoder # type: ignore
|
@nonce.decoder # type: ignore[no-redef,union-attr]
|
||||||
def nonce(value: str) -> bytes: # pylint: disable=no-self-argument,missing-function-docstring
|
def nonce(value: str) -> bytes: # type: ignore[misc] # pylint: disable=no-self-argument,missing-function-docstring
|
||||||
try:
|
try:
|
||||||
return jose.decode_b64jose(value)
|
return jose.decode_b64jose(value)
|
||||||
except jose.DeserializationError as error:
|
except jose.DeserializationError as error:
|
||||||
@@ -29,12 +29,12 @@ class Header(jose.Header):
|
|||||||
|
|
||||||
class Signature(jose.Signature):
|
class Signature(jose.Signature):
|
||||||
"""ACME-specific Signature. Uses ACME-specific Header for customer fields."""
|
"""ACME-specific Signature. Uses ACME-specific Header for customer fields."""
|
||||||
__slots__ = jose.Signature._orig_slots # pylint: disable=no-member
|
__slots__ = jose.Signature._orig_slots # type: ignore[attr-defined] # pylint: disable=protected-access,no-member
|
||||||
|
|
||||||
# TODO: decoder/encoder should accept cls? Otherwise, subclassing
|
# TODO: decoder/encoder should accept cls? Otherwise, subclassing
|
||||||
# JSONObjectWithFields is tricky...
|
# JSONObjectWithFields is tricky...
|
||||||
header_cls = Header
|
header_cls = Header
|
||||||
header = jose.Field(
|
header: Header = jose.field(
|
||||||
'header', omitempty=True, default=header_cls(),
|
'header', omitempty=True, default=header_cls(),
|
||||||
decoder=header_cls.from_json)
|
decoder=header_cls.from_json)
|
||||||
|
|
||||||
@@ -44,10 +44,10 @@ class Signature(jose.Signature):
|
|||||||
class JWS(jose.JWS):
|
class JWS(jose.JWS):
|
||||||
"""ACME-specific JWS. Includes none, url, and kid in protected header."""
|
"""ACME-specific JWS. Includes none, url, and kid in protected header."""
|
||||||
signature_cls = Signature
|
signature_cls = Signature
|
||||||
__slots__ = jose.JWS._orig_slots
|
__slots__ = jose.JWS._orig_slots # type: ignore[attr-defined] # pylint: disable=protected-access
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
# pylint: disable=arguments-differ
|
# type: ignore[override] # pylint: disable=arguments-differ
|
||||||
def sign(cls, payload: bytes, key: jose.JWK, alg: jose.JWASignature, nonce: Optional[bytes],
|
def sign(cls, payload: bytes, key: jose.JWK, alg: jose.JWASignature, nonce: Optional[bytes],
|
||||||
url: Optional[str] = None, kid: Optional[str] = None) -> jose.JWS:
|
url: Optional[str] = None, kid: Optional[str] = None) -> jose.JWS:
|
||||||
# Per ACME spec, jwk and kid are mutually exclusive, so only include a
|
# Per ACME spec, jwk and kid are mutually exclusive, so only include a
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
"""ACME protocol messages."""
|
"""ACME protocol messages."""
|
||||||
|
import datetime
|
||||||
from collections.abc import Hashable
|
from collections.abc import Hashable
|
||||||
import json
|
import json
|
||||||
from typing import Any
|
from typing import Any
|
||||||
@@ -7,9 +8,12 @@ from typing import Iterator
|
|||||||
from typing import List
|
from typing import List
|
||||||
from typing import Mapping
|
from typing import Mapping
|
||||||
from typing import MutableMapping
|
from typing import MutableMapping
|
||||||
|
from typing import Optional
|
||||||
from typing import Tuple
|
from typing import Tuple
|
||||||
from typing import Type
|
from typing import Type
|
||||||
from typing import Optional
|
from typing import TYPE_CHECKING
|
||||||
|
from typing import TypeVar
|
||||||
|
from typing import Union
|
||||||
|
|
||||||
import josepy as jose
|
import josepy as jose
|
||||||
|
|
||||||
@@ -20,6 +24,11 @@ from acme import jws
|
|||||||
from acme import util
|
from acme import util
|
||||||
from acme.mixins import ResourceMixin
|
from acme.mixins import ResourceMixin
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from typing_extensions import Protocol # pragma: no cover
|
||||||
|
else:
|
||||||
|
Protocol = object
|
||||||
|
|
||||||
OLD_ERROR_PREFIX = "urn:acme:error:"
|
OLD_ERROR_PREFIX = "urn:acme:error:"
|
||||||
ERROR_PREFIX = "urn:ietf:params:acme:error:"
|
ERROR_PREFIX = "urn:ietf:params:acme:error:"
|
||||||
|
|
||||||
@@ -75,20 +84,20 @@ class Error(jose.JSONObjectWithFields, errors.Error):
|
|||||||
|
|
||||||
https://tools.ietf.org/html/draft-ietf-appsawg-http-problem-00
|
https://tools.ietf.org/html/draft-ietf-appsawg-http-problem-00
|
||||||
|
|
||||||
:ivar unicode typ:
|
:ivar str typ:
|
||||||
:ivar unicode title:
|
:ivar str title:
|
||||||
:ivar unicode detail:
|
:ivar str detail:
|
||||||
|
|
||||||
"""
|
"""
|
||||||
typ = jose.Field('type', omitempty=True, default='about:blank')
|
typ: str = jose.field('type', omitempty=True, default='about:blank')
|
||||||
title = jose.Field('title', omitempty=True)
|
title: str = jose.field('title', omitempty=True)
|
||||||
detail = jose.Field('detail', omitempty=True)
|
detail: str = jose.field('detail', omitempty=True)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def with_code(cls, code: str, **kwargs: Any) -> 'Error':
|
def with_code(cls, code: str, **kwargs: Any) -> 'Error':
|
||||||
"""Create an Error instance with an ACME Error code.
|
"""Create an Error instance with an ACME Error code.
|
||||||
|
|
||||||
:unicode code: An ACME error code, like 'dnssec'.
|
:str code: An ACME error code, like 'dnssec'.
|
||||||
:kwargs: kwargs to pass to Error.
|
:kwargs: kwargs to pass to Error.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
@@ -98,14 +107,14 @@ class Error(jose.JSONObjectWithFields, errors.Error):
|
|||||||
typ = ERROR_PREFIX + code
|
typ = ERROR_PREFIX + code
|
||||||
# Mypy will not understand that the Error constructor accepts a named argument
|
# Mypy will not understand that the Error constructor accepts a named argument
|
||||||
# "typ" because of josepy magic. Let's ignore the type check here.
|
# "typ" because of josepy magic. Let's ignore the type check here.
|
||||||
return cls(typ=typ, **kwargs) # type: ignore
|
return cls(typ=typ, **kwargs)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def description(self) -> Optional[str]:
|
def description(self) -> Optional[str]:
|
||||||
"""Hardcoded error description based on its type.
|
"""Hardcoded error description based on its type.
|
||||||
|
|
||||||
:returns: Description if standard ACME error or ``None``.
|
:returns: Description if standard ACME error or ``None``.
|
||||||
:rtype: unicode
|
:rtype: str
|
||||||
|
|
||||||
"""
|
"""
|
||||||
return ERROR_TYPE_DESCRIPTIONS.get(self.typ)
|
return ERROR_TYPE_DESCRIPTIONS.get(self.typ)
|
||||||
@@ -117,7 +126,7 @@ class Error(jose.JSONObjectWithFields, errors.Error):
|
|||||||
Basically self.typ without the ERROR_PREFIX.
|
Basically self.typ without the ERROR_PREFIX.
|
||||||
|
|
||||||
:returns: error code if standard ACME code or ``None``.
|
:returns: error code if standard ACME code or ``None``.
|
||||||
:rtype: unicode
|
:rtype: str
|
||||||
|
|
||||||
"""
|
"""
|
||||||
code = str(self.typ).rsplit(':', maxsplit=1)[-1]
|
code = str(self.typ).rsplit(':', maxsplit=1)[-1]
|
||||||
@@ -164,7 +173,7 @@ class _Constant(jose.JSONDeSerializable, Hashable):
|
|||||||
|
|
||||||
class Status(_Constant):
|
class Status(_Constant):
|
||||||
"""ACME "status" field."""
|
"""ACME "status" field."""
|
||||||
POSSIBLE_NAMES: Dict[str, 'Status'] = {}
|
POSSIBLE_NAMES: Dict[str, _Constant] = {}
|
||||||
|
|
||||||
|
|
||||||
STATUS_UNKNOWN = Status('unknown')
|
STATUS_UNKNOWN = Status('unknown')
|
||||||
@@ -179,7 +188,7 @@ STATUS_DEACTIVATED = Status('deactivated')
|
|||||||
|
|
||||||
class IdentifierType(_Constant):
|
class IdentifierType(_Constant):
|
||||||
"""ACME identifier type."""
|
"""ACME identifier type."""
|
||||||
POSSIBLE_NAMES: Dict[str, 'IdentifierType'] = {}
|
POSSIBLE_NAMES: Dict[str, _Constant] = {}
|
||||||
|
|
||||||
|
|
||||||
IDENTIFIER_FQDN = IdentifierType('dns') # IdentifierDNS in Boulder
|
IDENTIFIER_FQDN = IdentifierType('dns') # IdentifierDNS in Boulder
|
||||||
@@ -190,25 +199,35 @@ class Identifier(jose.JSONObjectWithFields):
|
|||||||
"""ACME identifier.
|
"""ACME identifier.
|
||||||
|
|
||||||
:ivar IdentifierType typ:
|
:ivar IdentifierType typ:
|
||||||
:ivar unicode value:
|
:ivar str value:
|
||||||
|
|
||||||
"""
|
"""
|
||||||
typ = jose.Field('type', decoder=IdentifierType.from_json)
|
typ: IdentifierType = jose.field('type', decoder=IdentifierType.from_json)
|
||||||
value = jose.Field('value')
|
value: str = jose.field('value')
|
||||||
|
|
||||||
|
|
||||||
|
class HasResourceType(Protocol):
|
||||||
|
"""
|
||||||
|
Represents a class with a resource_type class parameter of type string.
|
||||||
|
"""
|
||||||
|
resource_type: str = NotImplemented
|
||||||
|
|
||||||
|
|
||||||
|
GenericHasResourceType = TypeVar("GenericHasResourceType", bound=HasResourceType)
|
||||||
|
|
||||||
|
|
||||||
class Directory(jose.JSONDeSerializable):
|
class Directory(jose.JSONDeSerializable):
|
||||||
"""Directory."""
|
"""Directory."""
|
||||||
|
|
||||||
_REGISTERED_TYPES: Dict[str, Type['Directory']] = {}
|
_REGISTERED_TYPES: Dict[str, Type[HasResourceType]] = {}
|
||||||
|
|
||||||
class Meta(jose.JSONObjectWithFields):
|
class Meta(jose.JSONObjectWithFields):
|
||||||
"""Directory Meta."""
|
"""Directory Meta."""
|
||||||
_terms_of_service = jose.Field('terms-of-service', omitempty=True)
|
_terms_of_service: str = jose.field('terms-of-service', omitempty=True)
|
||||||
_terms_of_service_v2 = jose.Field('termsOfService', omitempty=True)
|
_terms_of_service_v2: str = jose.field('termsOfService', omitempty=True)
|
||||||
website = jose.Field('website', omitempty=True)
|
website: str = jose.field('website', omitempty=True)
|
||||||
caa_identities = jose.Field('caaIdentities', omitempty=True)
|
caa_identities: List[str] = jose.field('caaIdentities', omitempty=True)
|
||||||
external_account_required = jose.Field('externalAccountRequired', omitempty=True)
|
external_account_required: bool = jose.field('externalAccountRequired', omitempty=True)
|
||||||
|
|
||||||
def __init__(self, **kwargs: Any) -> None:
|
def __init__(self, **kwargs: Any) -> None:
|
||||||
kwargs = {self._internal_name(k): v for k, v in kwargs.items()}
|
kwargs = {self._internal_name(k): v for k, v in kwargs.items()}
|
||||||
@@ -229,11 +248,14 @@ class Directory(jose.JSONDeSerializable):
|
|||||||
return '_' + name if name == 'terms_of_service' else name
|
return '_' + name if name == 'terms_of_service' else name
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _canon_key(cls, key: str) -> str:
|
def _canon_key(cls, key: Union[str, HasResourceType, Type[HasResourceType]]) -> str:
|
||||||
return getattr(key, 'resource_type', key)
|
if isinstance(key, str):
|
||||||
|
return key
|
||||||
|
return key.resource_type
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def register(cls, resource_body_cls: Type['Directory']) -> Type['Directory']:
|
def register(cls,
|
||||||
|
resource_body_cls: Type[GenericHasResourceType]) -> Type[GenericHasResourceType]:
|
||||||
"""Register resource."""
|
"""Register resource."""
|
||||||
resource_type = resource_body_cls.resource_type
|
resource_type = resource_body_cls.resource_type
|
||||||
assert resource_type not in cls._REGISTERED_TYPES
|
assert resource_type not in cls._REGISTERED_TYPES
|
||||||
@@ -252,7 +274,7 @@ class Directory(jose.JSONDeSerializable):
|
|||||||
except KeyError as error:
|
except KeyError as error:
|
||||||
raise AttributeError(str(error))
|
raise AttributeError(str(error))
|
||||||
|
|
||||||
def __getitem__(self, name: str) -> Any:
|
def __getitem__(self, name: Union[str, HasResourceType, Type[HasResourceType]]) -> Any:
|
||||||
try:
|
try:
|
||||||
return self._jobj[self._canon_key(name)]
|
return self._jobj[self._canon_key(name)]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
@@ -273,16 +295,16 @@ class Resource(jose.JSONObjectWithFields):
|
|||||||
:ivar acme.messages.ResourceBody body: Resource body.
|
:ivar acme.messages.ResourceBody body: Resource body.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
body = jose.Field('body')
|
body: "ResourceBody" = jose.field('body')
|
||||||
|
|
||||||
|
|
||||||
class ResourceWithURI(Resource):
|
class ResourceWithURI(Resource):
|
||||||
"""ACME Resource with URI.
|
"""ACME Resource with URI.
|
||||||
|
|
||||||
:ivar unicode ~.uri: Location of the resource.
|
:ivar str uri: Location of the resource.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
uri = jose.Field('uri') # no ChallengeResource.uri
|
uri: str = jose.field('uri') # no ChallengeResource.uri
|
||||||
|
|
||||||
|
|
||||||
class ResourceBody(jose.JSONObjectWithFields):
|
class ResourceBody(jose.JSONObjectWithFields):
|
||||||
@@ -308,35 +330,40 @@ class ExternalAccountBinding:
|
|||||||
return eab.to_partial_json()
|
return eab.to_partial_json()
|
||||||
|
|
||||||
|
|
||||||
|
GenericRegistration = TypeVar('GenericRegistration', bound='Registration')
|
||||||
|
|
||||||
|
|
||||||
class Registration(ResourceBody):
|
class Registration(ResourceBody):
|
||||||
"""Registration Resource Body.
|
"""Registration Resource Body.
|
||||||
|
|
||||||
:ivar josepy.jwk.JWK key: Public key.
|
:ivar jose.JWK key: Public key.
|
||||||
:ivar tuple contact: Contact information following ACME spec,
|
:ivar tuple contact: Contact information following ACME spec,
|
||||||
`tuple` of `unicode`.
|
`tuple` of `str`.
|
||||||
:ivar unicode agreement:
|
:ivar str agreement:
|
||||||
|
|
||||||
"""
|
"""
|
||||||
# on new-reg key server ignores 'key' and populates it based on
|
# on new-reg key server ignores 'key' and populates it based on
|
||||||
# JWS.signature.combined.jwk
|
# JWS.signature.combined.jwk
|
||||||
key = jose.Field('key', omitempty=True, decoder=jose.JWK.from_json)
|
key: jose.JWK = jose.field('key', omitempty=True, decoder=jose.JWK.from_json)
|
||||||
# Contact field implements special behavior to allow messages that clear existing
|
# Contact field implements special behavior to allow messages that clear existing
|
||||||
# contacts while not expecting the `contact` field when loading from json.
|
# contacts while not expecting the `contact` field when loading from json.
|
||||||
# This is implemented in the constructor and *_json methods.
|
# This is implemented in the constructor and *_json methods.
|
||||||
contact = jose.Field('contact', omitempty=True, default=())
|
contact: Tuple[str, ...] = jose.field('contact', omitempty=True, default=())
|
||||||
agreement = jose.Field('agreement', omitempty=True)
|
agreement: str = jose.field('agreement', omitempty=True)
|
||||||
status = jose.Field('status', omitempty=True)
|
status: Status = jose.field('status', omitempty=True)
|
||||||
terms_of_service_agreed = jose.Field('termsOfServiceAgreed', omitempty=True)
|
terms_of_service_agreed: bool = jose.field('termsOfServiceAgreed', omitempty=True)
|
||||||
only_return_existing = jose.Field('onlyReturnExisting', omitempty=True)
|
only_return_existing: bool = jose.field('onlyReturnExisting', omitempty=True)
|
||||||
external_account_binding = jose.Field('externalAccountBinding', omitempty=True)
|
external_account_binding: Dict[str, Any] = jose.field('externalAccountBinding',
|
||||||
|
omitempty=True)
|
||||||
|
|
||||||
phone_prefix = 'tel:'
|
phone_prefix = 'tel:'
|
||||||
email_prefix = 'mailto:'
|
email_prefix = 'mailto:'
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_data(cls, phone: Optional[str] = None, email: Optional[str] = None,
|
def from_data(cls: Type[GenericRegistration], phone: Optional[str] = None,
|
||||||
|
email: Optional[str] = None,
|
||||||
external_account_binding: Optional[Dict[str, Any]] = None,
|
external_account_binding: Optional[Dict[str, Any]] = None,
|
||||||
**kwargs: Any) -> 'Registration':
|
**kwargs: Any) -> GenericRegistration:
|
||||||
"""
|
"""
|
||||||
Create registration resource from contact details.
|
Create registration resource from contact details.
|
||||||
|
|
||||||
@@ -419,26 +446,26 @@ class Registration(ResourceBody):
|
|||||||
class NewRegistration(ResourceMixin, Registration):
|
class NewRegistration(ResourceMixin, Registration):
|
||||||
"""New registration."""
|
"""New registration."""
|
||||||
resource_type = 'new-reg'
|
resource_type = 'new-reg'
|
||||||
resource = fields.Resource(resource_type)
|
resource: str = fields.resource(resource_type)
|
||||||
|
|
||||||
|
|
||||||
class UpdateRegistration(ResourceMixin, Registration):
|
class UpdateRegistration(ResourceMixin, Registration):
|
||||||
"""Update registration."""
|
"""Update registration."""
|
||||||
resource_type = 'reg'
|
resource_type = 'reg'
|
||||||
resource = fields.Resource(resource_type)
|
resource: str = fields.resource(resource_type)
|
||||||
|
|
||||||
|
|
||||||
class RegistrationResource(ResourceWithURI):
|
class RegistrationResource(ResourceWithURI):
|
||||||
"""Registration Resource.
|
"""Registration Resource.
|
||||||
|
|
||||||
:ivar acme.messages.Registration body:
|
:ivar acme.messages.Registration body:
|
||||||
:ivar unicode new_authzr_uri: Deprecated. Do not use.
|
:ivar str new_authzr_uri: Deprecated. Do not use.
|
||||||
:ivar unicode terms_of_service: URL for the CA TOS.
|
:ivar str terms_of_service: URL for the CA TOS.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
body = jose.Field('body', decoder=Registration.from_json)
|
body: Registration = jose.field('body', decoder=Registration.from_json)
|
||||||
new_authzr_uri = jose.Field('new_authzr_uri', omitempty=True)
|
new_authzr_uri: str = jose.field('new_authzr_uri', omitempty=True)
|
||||||
terms_of_service = jose.Field('terms_of_service', omitempty=True)
|
terms_of_service: str = jose.field('terms_of_service', omitempty=True)
|
||||||
|
|
||||||
|
|
||||||
class ChallengeBody(ResourceBody):
|
class ChallengeBody(ResourceBody):
|
||||||
@@ -463,12 +490,12 @@ class ChallengeBody(ResourceBody):
|
|||||||
# challenge object supports either one, but should be accessed through the
|
# challenge object supports either one, but should be accessed through the
|
||||||
# name "uri". In Client.answer_challenge, whichever one is set will be
|
# name "uri". In Client.answer_challenge, whichever one is set will be
|
||||||
# used.
|
# used.
|
||||||
_uri = jose.Field('uri', omitempty=True, default=None)
|
_uri: str = jose.field('uri', omitempty=True, default=None)
|
||||||
_url = jose.Field('url', omitempty=True, default=None)
|
_url: str = jose.field('url', omitempty=True, default=None)
|
||||||
status = jose.Field('status', decoder=Status.from_json,
|
status: Status = jose.field('status', decoder=Status.from_json,
|
||||||
omitempty=True, default=STATUS_PENDING)
|
omitempty=True, default=STATUS_PENDING)
|
||||||
validated = fields.RFC3339Field('validated', omitempty=True)
|
validated: datetime.datetime = fields.rfc3339('validated', omitempty=True)
|
||||||
error = jose.Field('error', decoder=Error.from_json,
|
error: Error = jose.field('error', decoder=Error.from_json,
|
||||||
omitempty=True, default=None)
|
omitempty=True, default=None)
|
||||||
|
|
||||||
def __init__(self, **kwargs: Any) -> None:
|
def __init__(self, **kwargs: Any) -> None:
|
||||||
@@ -511,16 +538,16 @@ class ChallengeResource(Resource):
|
|||||||
"""Challenge Resource.
|
"""Challenge Resource.
|
||||||
|
|
||||||
:ivar acme.messages.ChallengeBody body:
|
:ivar acme.messages.ChallengeBody body:
|
||||||
:ivar unicode authzr_uri: URI found in the 'up' ``Link`` header.
|
:ivar str authzr_uri: URI found in the 'up' ``Link`` header.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
body = jose.Field('body', decoder=ChallengeBody.from_json)
|
body: ChallengeBody = jose.field('body', decoder=ChallengeBody.from_json)
|
||||||
authzr_uri = jose.Field('authzr_uri')
|
authzr_uri: str = jose.field('authzr_uri')
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def uri(self) -> str:
|
def uri(self) -> str:
|
||||||
"""The URL of the challenge body."""
|
"""The URL of the challenge body."""
|
||||||
return self.body.uri
|
return self.body.uri # pylint: disable=no-member
|
||||||
|
|
||||||
|
|
||||||
class Authorization(ResourceBody):
|
class Authorization(ResourceBody):
|
||||||
@@ -534,26 +561,26 @@ class Authorization(ResourceBody):
|
|||||||
:ivar datetime.datetime expires:
|
:ivar datetime.datetime expires:
|
||||||
|
|
||||||
"""
|
"""
|
||||||
identifier = jose.Field('identifier', decoder=Identifier.from_json, omitempty=True)
|
identifier: Identifier = jose.field('identifier', decoder=Identifier.from_json, omitempty=True)
|
||||||
challenges = jose.Field('challenges', omitempty=True)
|
challenges: List[ChallengeBody] = jose.field('challenges', omitempty=True)
|
||||||
combinations = jose.Field('combinations', omitempty=True)
|
combinations: Tuple[Tuple[int, ...], ...] = jose.field('combinations', omitempty=True)
|
||||||
|
|
||||||
status = jose.Field('status', omitempty=True, decoder=Status.from_json)
|
status: Status = jose.field('status', omitempty=True, decoder=Status.from_json)
|
||||||
# TODO: 'expires' is allowed for Authorization Resources in
|
# TODO: 'expires' is allowed for Authorization Resources in
|
||||||
# general, but for Key Authorization '[t]he "expires" field MUST
|
# general, but for Key Authorization '[t]he "expires" field MUST
|
||||||
# be absent'... then acme-spec gives example with 'expires'
|
# be absent'... then acme-spec gives example with 'expires'
|
||||||
# present... That's confusing!
|
# present... That's confusing!
|
||||||
expires = fields.RFC3339Field('expires', omitempty=True)
|
expires: datetime.datetime = fields.rfc3339('expires', omitempty=True)
|
||||||
wildcard = jose.Field('wildcard', omitempty=True)
|
wildcard: bool = jose.field('wildcard', omitempty=True)
|
||||||
|
|
||||||
# Mypy does not understand the josepy magic happening here, and falsely claims
|
# Mypy does not understand the josepy magic happening here, and falsely claims
|
||||||
# that challenge is redefined. Let's ignore the type check here.
|
# that challenge is redefined. Let's ignore the type check here.
|
||||||
@challenges.decoder # type: ignore
|
@challenges.decoder # type: ignore
|
||||||
def challenges(value: List[Mapping[str, Any]]) -> Tuple[ChallengeBody, ...]: # pylint: disable=no-self-argument,missing-function-docstring
|
def challenges(value: List[Dict[str, Any]]) -> Tuple[ChallengeBody, ...]: # type: ignore[misc] # pylint: disable=no-self-argument,missing-function-docstring
|
||||||
return tuple(ChallengeBody.from_json(chall) for chall in value)
|
return tuple(ChallengeBody.from_json(chall) for chall in value)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def resolved_combinations(self) -> Tuple[Tuple[Dict[str, Any], ...], ...]:
|
def resolved_combinations(self) -> Tuple[Tuple[ChallengeBody, ...], ...]:
|
||||||
"""Combinations with challenges instead of indices."""
|
"""Combinations with challenges instead of indices."""
|
||||||
return tuple(tuple(self.challenges[idx] for idx in combo)
|
return tuple(tuple(self.challenges[idx] for idx in combo)
|
||||||
for combo in self.combinations) # pylint: disable=not-an-iterable
|
for combo in self.combinations) # pylint: disable=not-an-iterable
|
||||||
@@ -563,37 +590,37 @@ class Authorization(ResourceBody):
|
|||||||
class NewAuthorization(ResourceMixin, Authorization):
|
class NewAuthorization(ResourceMixin, Authorization):
|
||||||
"""New authorization."""
|
"""New authorization."""
|
||||||
resource_type = 'new-authz'
|
resource_type = 'new-authz'
|
||||||
resource = fields.Resource(resource_type)
|
resource: str = fields.resource(resource_type)
|
||||||
|
|
||||||
|
|
||||||
class UpdateAuthorization(ResourceMixin, Authorization):
|
class UpdateAuthorization(ResourceMixin, Authorization):
|
||||||
"""Update authorization."""
|
"""Update authorization."""
|
||||||
resource_type = 'authz'
|
resource_type = 'authz'
|
||||||
resource = fields.Resource(resource_type)
|
resource: str = fields.resource(resource_type)
|
||||||
|
|
||||||
|
|
||||||
class AuthorizationResource(ResourceWithURI):
|
class AuthorizationResource(ResourceWithURI):
|
||||||
"""Authorization Resource.
|
"""Authorization Resource.
|
||||||
|
|
||||||
:ivar acme.messages.Authorization body:
|
:ivar acme.messages.Authorization body:
|
||||||
:ivar unicode new_cert_uri: Deprecated. Do not use.
|
:ivar str new_cert_uri: Deprecated. Do not use.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
body = jose.Field('body', decoder=Authorization.from_json)
|
body: Authorization = jose.field('body', decoder=Authorization.from_json)
|
||||||
new_cert_uri = jose.Field('new_cert_uri', omitempty=True)
|
new_cert_uri: str = jose.field('new_cert_uri', omitempty=True)
|
||||||
|
|
||||||
|
|
||||||
@Directory.register
|
@Directory.register
|
||||||
class CertificateRequest(ResourceMixin, jose.JSONObjectWithFields):
|
class CertificateRequest(ResourceMixin, jose.JSONObjectWithFields):
|
||||||
"""ACME new-cert request.
|
"""ACME new-cert request.
|
||||||
|
|
||||||
:ivar josepy.util.ComparableX509 csr:
|
:ivar jose.ComparableX509 csr:
|
||||||
`OpenSSL.crypto.X509Req` wrapped in `.ComparableX509`
|
`OpenSSL.crypto.X509Req` wrapped in `.ComparableX509`
|
||||||
|
|
||||||
"""
|
"""
|
||||||
resource_type = 'new-cert'
|
resource_type = 'new-cert'
|
||||||
resource = fields.Resource(resource_type)
|
resource: str = fields.resource(resource_type)
|
||||||
csr = jose.Field('csr', decoder=jose.decode_csr, encoder=jose.encode_csr)
|
csr: jose.ComparableX509 = jose.field('csr', decoder=jose.decode_csr, encoder=jose.encode_csr)
|
||||||
|
|
||||||
|
|
||||||
class CertificateResource(ResourceWithURI):
|
class CertificateResource(ResourceWithURI):
|
||||||
@@ -601,27 +628,27 @@ class CertificateResource(ResourceWithURI):
|
|||||||
|
|
||||||
:ivar josepy.util.ComparableX509 body:
|
:ivar josepy.util.ComparableX509 body:
|
||||||
`OpenSSL.crypto.X509` wrapped in `.ComparableX509`
|
`OpenSSL.crypto.X509` wrapped in `.ComparableX509`
|
||||||
:ivar unicode cert_chain_uri: URI found in the 'up' ``Link`` header
|
:ivar str cert_chain_uri: URI found in the 'up' ``Link`` header
|
||||||
:ivar tuple authzrs: `tuple` of `AuthorizationResource`.
|
:ivar tuple authzrs: `tuple` of `AuthorizationResource`.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
cert_chain_uri = jose.Field('cert_chain_uri')
|
cert_chain_uri: str = jose.field('cert_chain_uri')
|
||||||
authzrs = jose.Field('authzrs')
|
authzrs: Tuple[AuthorizationResource, ...] = jose.field('authzrs')
|
||||||
|
|
||||||
|
|
||||||
@Directory.register
|
@Directory.register
|
||||||
class Revocation(ResourceMixin, jose.JSONObjectWithFields):
|
class Revocation(ResourceMixin, jose.JSONObjectWithFields):
|
||||||
"""Revocation message.
|
"""Revocation message.
|
||||||
|
|
||||||
:ivar .ComparableX509 certificate: `OpenSSL.crypto.X509` wrapped in
|
:ivar jose.ComparableX509 certificate: `OpenSSL.crypto.X509` wrapped in
|
||||||
`.ComparableX509`
|
`jose.ComparableX509`
|
||||||
|
|
||||||
"""
|
"""
|
||||||
resource_type = 'revoke-cert'
|
resource_type = 'revoke-cert'
|
||||||
resource = fields.Resource(resource_type)
|
resource: str = fields.resource(resource_type)
|
||||||
certificate = jose.Field(
|
certificate: jose.ComparableX509 = jose.field(
|
||||||
'certificate', decoder=jose.decode_cert, encoder=jose.encode_cert)
|
'certificate', decoder=jose.decode_cert, encoder=jose.encode_cert)
|
||||||
reason = jose.Field('reason')
|
reason: int = jose.field('reason')
|
||||||
|
|
||||||
|
|
||||||
class Order(ResourceBody):
|
class Order(ResourceBody):
|
||||||
@@ -638,19 +665,18 @@ class Order(ResourceBody):
|
|||||||
:ivar datetime.datetime expires: When the order expires.
|
:ivar datetime.datetime expires: When the order expires.
|
||||||
:ivar ~.Error error: Any error that occurred during finalization, if applicable.
|
:ivar ~.Error error: Any error that occurred during finalization, if applicable.
|
||||||
"""
|
"""
|
||||||
identifiers = jose.Field('identifiers', omitempty=True)
|
identifiers: List[Identifier] = jose.field('identifiers', omitempty=True)
|
||||||
status = jose.Field('status', decoder=Status.from_json,
|
status: Status = jose.field('status', decoder=Status.from_json, omitempty=True)
|
||||||
omitempty=True)
|
authorizations: List[str] = jose.field('authorizations', omitempty=True)
|
||||||
authorizations = jose.Field('authorizations', omitempty=True)
|
certificate: str = jose.field('certificate', omitempty=True)
|
||||||
certificate = jose.Field('certificate', omitempty=True)
|
finalize: str = jose.field('finalize', omitempty=True)
|
||||||
finalize = jose.Field('finalize', omitempty=True)
|
expires: datetime.datetime = fields.rfc3339('expires', omitempty=True)
|
||||||
expires = fields.RFC3339Field('expires', omitempty=True)
|
error: Error = jose.field('error', omitempty=True, decoder=Error.from_json)
|
||||||
error = jose.Field('error', omitempty=True, decoder=Error.from_json)
|
|
||||||
|
|
||||||
# Mypy does not understand the josepy magic happening here, and falsely claims
|
# Mypy does not understand the josepy magic happening here, and falsely claims
|
||||||
# that identifiers is redefined. Let's ignore the type check here.
|
# that identifiers is redefined. Let's ignore the type check here.
|
||||||
@identifiers.decoder # type: ignore
|
@identifiers.decoder # type: ignore
|
||||||
def identifiers(value: List[Mapping[str, Any]]) -> Tuple[Identifier, ...]: # pylint: disable=no-self-argument,missing-function-docstring
|
def identifiers(value: List[Dict[str, Any]]) -> Tuple[Identifier, ...]: # type: ignore[misc] # pylint: disable=no-self-argument,missing-function-docstring
|
||||||
return tuple(Identifier.from_json(identifier) for identifier in value)
|
return tuple(Identifier.from_json(identifier) for identifier in value)
|
||||||
|
|
||||||
|
|
||||||
@@ -658,7 +684,7 @@ class OrderResource(ResourceWithURI):
|
|||||||
"""Order Resource.
|
"""Order Resource.
|
||||||
|
|
||||||
:ivar acme.messages.Order body:
|
:ivar acme.messages.Order body:
|
||||||
:ivar str csr_pem: The CSR this Order will be finalized with.
|
:ivar bytes csr_pem: The CSR this Order will be finalized with.
|
||||||
:ivar authorizations: Fully-fetched AuthorizationResource objects.
|
:ivar authorizations: Fully-fetched AuthorizationResource objects.
|
||||||
:vartype authorizations: `list` of `acme.messages.AuthorizationResource`
|
:vartype authorizations: `list` of `acme.messages.AuthorizationResource`
|
||||||
:ivar str fullchain_pem: The fetched contents of the certificate URL
|
:ivar str fullchain_pem: The fetched contents of the certificate URL
|
||||||
@@ -668,11 +694,13 @@ class OrderResource(ResourceWithURI):
|
|||||||
finalization.
|
finalization.
|
||||||
:vartype alternative_fullchains_pem: `list` of `str`
|
:vartype alternative_fullchains_pem: `list` of `str`
|
||||||
"""
|
"""
|
||||||
body = jose.Field('body', decoder=Order.from_json)
|
body: Order = jose.field('body', decoder=Order.from_json)
|
||||||
csr_pem = jose.Field('csr_pem', omitempty=True)
|
csr_pem: bytes = jose.field('csr_pem', omitempty=True)
|
||||||
authorizations = jose.Field('authorizations')
|
authorizations: List[AuthorizationResource] = jose.field('authorizations')
|
||||||
fullchain_pem = jose.Field('fullchain_pem', omitempty=True)
|
fullchain_pem: str = jose.field('fullchain_pem', omitempty=True)
|
||||||
alternative_fullchains_pem = jose.Field('alternative_fullchains_pem', omitempty=True)
|
alternative_fullchains_pem: List[str] = jose.field('alternative_fullchains_pem',
|
||||||
|
omitempty=True)
|
||||||
|
|
||||||
|
|
||||||
@Directory.register
|
@Directory.register
|
||||||
class NewOrder(Order):
|
class NewOrder(Order):
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import socket
|
|||||||
import socketserver
|
import socketserver
|
||||||
import threading
|
import threading
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
from typing import cast
|
||||||
from typing import List
|
from typing import List
|
||||||
from typing import Mapping
|
from typing import Mapping
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
@@ -39,10 +40,10 @@ class TLSServer(socketserver.TCPServer):
|
|||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
def _wrap_sock(self) -> None:
|
def _wrap_sock(self) -> None:
|
||||||
self.socket = crypto_util.SSLSocket(
|
self.socket = cast(socket.socket, crypto_util.SSLSocket(
|
||||||
self.socket, cert_selection=self._cert_selection,
|
self.socket, cert_selection=self._cert_selection,
|
||||||
alpn_selection=getattr(self, '_alpn_selection', None),
|
alpn_selection=getattr(self, '_alpn_selection', None),
|
||||||
method=self.method)
|
method=self.method))
|
||||||
|
|
||||||
def _cert_selection(self, connection: SSL.Connection
|
def _cert_selection(self, connection: SSL.Connection
|
||||||
) -> Tuple[crypto.PKey, crypto.X509]: # pragma: no cover
|
) -> Tuple[crypto.PKey, crypto.X509]: # pragma: no cover
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ version = '1.23.0.dev0'
|
|||||||
|
|
||||||
install_requires = [
|
install_requires = [
|
||||||
'cryptography>=2.5.0',
|
'cryptography>=2.5.0',
|
||||||
'josepy>=1.9.0',
|
'josepy>=1.10.0',
|
||||||
'PyOpenSSL>=17.3.0',
|
'PyOpenSSL>=17.3.0',
|
||||||
'pyrfc3339',
|
'pyrfc3339',
|
||||||
'pytz',
|
'pytz',
|
||||||
@@ -24,6 +24,7 @@ docs_extras = [
|
|||||||
test_extras = [
|
test_extras = [
|
||||||
'pytest',
|
'pytest',
|
||||||
'pytest-xdist',
|
'pytest-xdist',
|
||||||
|
'typing-extensions',
|
||||||
]
|
]
|
||||||
|
|
||||||
setup(
|
setup(
|
||||||
|
|||||||
@@ -10,8 +10,8 @@ class FixedTest(unittest.TestCase):
|
|||||||
"""Tests for acme.fields.Fixed."""
|
"""Tests for acme.fields.Fixed."""
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
from acme.fields import Fixed
|
from acme.fields import fixed
|
||||||
self.field = Fixed('name', 'x')
|
self.field = fixed('name', 'x')
|
||||||
|
|
||||||
def test_decode(self):
|
def test_decode(self):
|
||||||
self.assertEqual('x', self.field.decode('x'))
|
self.assertEqual('x', self.field.decode('x'))
|
||||||
|
|||||||
@@ -22,11 +22,9 @@ from typing import Type
|
|||||||
from typing import Union
|
from typing import Union
|
||||||
|
|
||||||
from acme import challenges
|
from acme import challenges
|
||||||
from acme.challenges import Challenge
|
|
||||||
from certbot import achallenges
|
from certbot import achallenges
|
||||||
from certbot import errors
|
from certbot import errors
|
||||||
from certbot import util
|
from certbot import util
|
||||||
from certbot.achallenges import KeyAuthorizationAnnotatedChallenge
|
|
||||||
from certbot.compat import filesystem
|
from certbot.compat import filesystem
|
||||||
from certbot.compat import os
|
from certbot.compat import os
|
||||||
from certbot.display import util as display_util
|
from certbot.display import util as display_util
|
||||||
@@ -240,7 +238,7 @@ class ApacheConfigurator(common.Configurator):
|
|||||||
# Add name_server association dict
|
# Add name_server association dict
|
||||||
self.assoc: Dict[str, obj.VirtualHost] = {}
|
self.assoc: Dict[str, obj.VirtualHost] = {}
|
||||||
# Outstanding challenges
|
# Outstanding challenges
|
||||||
self._chall_out: Set[KeyAuthorizationAnnotatedChallenge] = set()
|
self._chall_out: Set[achallenges.AnnotatedChallenge] = set()
|
||||||
# List of vhosts configured per wildcard domain on this run.
|
# List of vhosts configured per wildcard domain on this run.
|
||||||
# used by deploy_cert() and enhance()
|
# used by deploy_cert() and enhance()
|
||||||
self._wildcard_vhosts: Dict[str, List[obj.VirtualHost]] = {}
|
self._wildcard_vhosts: Dict[str, List[obj.VirtualHost]] = {}
|
||||||
@@ -2532,9 +2530,8 @@ class ApacheConfigurator(common.Configurator):
|
|||||||
"""Return list of challenge preferences."""
|
"""Return list of challenge preferences."""
|
||||||
return [challenges.HTTP01]
|
return [challenges.HTTP01]
|
||||||
|
|
||||||
def perform(
|
def perform(self, achalls: List[achallenges.AnnotatedChallenge]
|
||||||
self, achalls: List[KeyAuthorizationAnnotatedChallenge]
|
) -> List[challenges.ChallengeResponse]:
|
||||||
) -> List[Challenge]:
|
|
||||||
"""Perform the configuration related challenge.
|
"""Perform the configuration related challenge.
|
||||||
|
|
||||||
This function currently assumes all challenges will be fulfilled.
|
This function currently assumes all challenges will be fulfilled.
|
||||||
@@ -2543,10 +2540,13 @@ class ApacheConfigurator(common.Configurator):
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
self._chall_out.update(achalls)
|
self._chall_out.update(achalls)
|
||||||
responses: List[Optional[Challenge]] = [None] * len(achalls)
|
responses: List[Optional[challenges.ChallengeResponse]] = [None] * len(achalls)
|
||||||
http_doer = http_01.ApacheHttp01(self)
|
http_doer = http_01.ApacheHttp01(self)
|
||||||
|
|
||||||
for i, achall in enumerate(achalls):
|
for i, achall in enumerate(achalls):
|
||||||
|
if not isinstance(achall, achallenges.KeyAuthorizationAnnotatedChallenge):
|
||||||
|
raise errors.Error("Challenge should be an instance " # pragma: no cover
|
||||||
|
"of KeyAuthorizationAnnotatedChallenge")
|
||||||
# Currently also have chall_doer hold associated index of the
|
# Currently also have chall_doer hold associated index of the
|
||||||
# challenge. This helps to put all of the responses back together
|
# challenge. This helps to put all of the responses back together
|
||||||
# when they are all complete.
|
# when they are all complete.
|
||||||
@@ -2560,18 +2560,17 @@ class ApacheConfigurator(common.Configurator):
|
|||||||
self.restart()
|
self.restart()
|
||||||
|
|
||||||
# TODO: Remove this dirty hack. We need to determine a reliable way
|
# TODO: Remove this dirty hack. We need to determine a reliable way
|
||||||
# of identifying when the new configuration is being used.
|
# of identifying when the new configuration is being used.
|
||||||
time.sleep(3)
|
time.sleep(3)
|
||||||
|
|
||||||
self._update_responses(responses, http_response, http_doer)
|
self._update_responses(responses, http_response, http_doer)
|
||||||
|
|
||||||
# We assume all challenges has been fulfilled as described in the function documentation.
|
return [response for response in responses if response]
|
||||||
return cast(List[Challenge], responses)
|
|
||||||
|
|
||||||
def _update_responses(
|
def _update_responses(
|
||||||
self,
|
self,
|
||||||
responses: List[Optional[challenges.HTTP01Response]],
|
responses: List[Optional[challenges.ChallengeResponse]],
|
||||||
chall_response: List[Challenge],
|
chall_response: List[challenges.KeyAuthorizationChallengeResponse],
|
||||||
chall_doer: http_01.ApacheHttp01
|
chall_doer: http_01.ApacheHttp01
|
||||||
) -> None:
|
) -> None:
|
||||||
# Go through all of the challenges and assign them to the proper
|
# Go through all of the challenges and assign them to the proper
|
||||||
@@ -2580,7 +2579,7 @@ class ApacheConfigurator(common.Configurator):
|
|||||||
for i, resp in enumerate(chall_response):
|
for i, resp in enumerate(chall_response):
|
||||||
responses[chall_doer.indices[i]] = resp
|
responses[chall_doer.indices[i]] = resp
|
||||||
|
|
||||||
def cleanup(self, achalls: List[KeyAuthorizationAnnotatedChallenge]) -> None:
|
def cleanup(self, achalls: List[achallenges.AnnotatedChallenge]) -> None:
|
||||||
"""Revert all challenges."""
|
"""Revert all challenges."""
|
||||||
self._chall_out.difference_update(achalls)
|
self._chall_out.difference_update(achalls)
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,11 @@
|
|||||||
"""A class that performs HTTP-01 challenges for Apache"""
|
"""A class that performs HTTP-01 challenges for Apache"""
|
||||||
import errno
|
import errno
|
||||||
import logging
|
import logging
|
||||||
from typing import Any
|
|
||||||
from typing import List
|
from typing import List
|
||||||
from typing import Set
|
from typing import Set
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
from acme.challenges import HTTP01Response
|
from acme.challenges import KeyAuthorizationChallengeResponse
|
||||||
from certbot import errors
|
from certbot import errors
|
||||||
from certbot.achallenges import KeyAuthorizationAnnotatedChallenge
|
from certbot.achallenges import KeyAuthorizationAnnotatedChallenge
|
||||||
from certbot.compat import filesystem
|
from certbot.compat import filesystem
|
||||||
@@ -67,7 +66,7 @@ class ApacheHttp01(common.ChallengePerformer):
|
|||||||
"http_challenges")
|
"http_challenges")
|
||||||
self.moded_vhosts: Set[VirtualHost] = set()
|
self.moded_vhosts: Set[VirtualHost] = set()
|
||||||
|
|
||||||
def perform(self) -> List[KeyAuthorizationAnnotatedChallenge]:
|
def perform(self) -> List[KeyAuthorizationChallengeResponse]:
|
||||||
"""Perform all HTTP-01 challenges."""
|
"""Perform all HTTP-01 challenges."""
|
||||||
if not self.achalls:
|
if not self.achalls:
|
||||||
return []
|
return []
|
||||||
@@ -182,7 +181,7 @@ class ApacheHttp01(common.ChallengePerformer):
|
|||||||
"""Return all VirtualHost objects with no ServerName"""
|
"""Return all VirtualHost objects with no ServerName"""
|
||||||
return [vh for vh in self.configurator.vhosts if vh.name is None]
|
return [vh for vh in self.configurator.vhosts if vh.name is None]
|
||||||
|
|
||||||
def _set_up_challenges(self) -> List[HTTP01Response]:
|
def _set_up_challenges(self) -> List[KeyAuthorizationChallengeResponse]:
|
||||||
if not os.path.isdir(self.challenge_dir):
|
if not os.path.isdir(self.challenge_dir):
|
||||||
old_umask = filesystem.umask(0o022)
|
old_umask = filesystem.umask(0o022)
|
||||||
try:
|
try:
|
||||||
@@ -200,9 +199,8 @@ class ApacheHttp01(common.ChallengePerformer):
|
|||||||
|
|
||||||
return responses
|
return responses
|
||||||
|
|
||||||
def _set_up_challenge(self, achall: KeyAuthorizationAnnotatedChallenge) -> HTTP01Response:
|
def _set_up_challenge(self, achall: KeyAuthorizationAnnotatedChallenge
|
||||||
response: HTTP01Response
|
) -> KeyAuthorizationChallengeResponse:
|
||||||
validation: Any
|
|
||||||
response, validation = achall.response_and_validation()
|
response, validation = achall.response_and_validation()
|
||||||
|
|
||||||
name: str = os.path.join(self.challenge_dir, achall.chall.encode("token"))
|
name: str = os.path.join(self.challenge_dir, achall.chall.encode("token"))
|
||||||
|
|||||||
@@ -48,7 +48,8 @@ def _suppress_x509_verification_warnings() -> None:
|
|||||||
# Handle old versions of request with vendorized urllib3
|
# Handle old versions of request with vendorized urllib3
|
||||||
# pylint: disable=no-member
|
# pylint: disable=no-member
|
||||||
from requests.packages.urllib3.exceptions import InsecureRequestWarning
|
from requests.packages.urllib3.exceptions import InsecureRequestWarning
|
||||||
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
|
requests.packages.urllib3.disable_warnings( # type: ignore[attr-defined]
|
||||||
|
InsecureRequestWarning)
|
||||||
|
|
||||||
|
|
||||||
def check_until_timeout(url: str, attempts: int = 30) -> None:
|
def check_until_timeout(url: str, attempts: int = 30) -> None:
|
||||||
|
|||||||
@@ -106,7 +106,7 @@ def test_authenticator(plugin: common.Proxy, config: str, temp_dir: str) -> bool
|
|||||||
|
|
||||||
def _create_achalls(plugin: common.Proxy) -> List[achallenges.AnnotatedChallenge]:
|
def _create_achalls(plugin: common.Proxy) -> List[achallenges.AnnotatedChallenge]:
|
||||||
"""Returns a list of annotated challenges to test on plugin"""
|
"""Returns a list of annotated challenges to test on plugin"""
|
||||||
achalls = []
|
achalls: List[achallenges.AnnotatedChallenge] = []
|
||||||
names = plugin.get_testable_domain_names()
|
names = plugin.get_testable_domain_names()
|
||||||
for domain in names:
|
for domain in names:
|
||||||
prefs = plugin.get_chall_pref(domain)
|
prefs = plugin.get_chall_pref(domain)
|
||||||
|
|||||||
@@ -138,7 +138,7 @@ class _RFC2136Client:
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise errors.PluginError('Encountered error adding TXT record: {0}'
|
raise errors.PluginError('Encountered error adding TXT record: {0}'
|
||||||
.format(e))
|
.format(e))
|
||||||
rcode = response.rcode()
|
rcode = response.rcode() # type: ignore[attr-defined]
|
||||||
|
|
||||||
if rcode == dns.rcode.NOERROR:
|
if rcode == dns.rcode.NOERROR:
|
||||||
logger.debug('Successfully added TXT record %s', record_name)
|
logger.debug('Successfully added TXT record %s', record_name)
|
||||||
@@ -173,7 +173,7 @@ class _RFC2136Client:
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise errors.PluginError('Encountered error deleting TXT record: {0}'
|
raise errors.PluginError('Encountered error deleting TXT record: {0}'
|
||||||
.format(e))
|
.format(e))
|
||||||
rcode = response.rcode()
|
rcode = response.rcode() # type: ignore[attr-defined]
|
||||||
|
|
||||||
if rcode == dns.rcode.NOERROR:
|
if rcode == dns.rcode.NOERROR:
|
||||||
logger.debug('Successfully deleted TXT record %s', record_name)
|
logger.debug('Successfully deleted TXT record %s', record_name)
|
||||||
@@ -223,11 +223,13 @@ class _RFC2136Client:
|
|||||||
except (OSError, dns.exception.Timeout) as e:
|
except (OSError, dns.exception.Timeout) as e:
|
||||||
logger.debug('TCP query failed, fallback to UDP: %s', e)
|
logger.debug('TCP query failed, fallback to UDP: %s', e)
|
||||||
response = dns.query.udp(request, self.server, self._default_timeout, self.port)
|
response = dns.query.udp(request, self.server, self._default_timeout, self.port)
|
||||||
rcode = response.rcode()
|
rcode = response.rcode() # type: ignore[attr-defined]
|
||||||
|
|
||||||
# Authoritative Answer bit should be set
|
# Authoritative Answer bit should be set
|
||||||
if (rcode == dns.rcode.NOERROR and response.get_rrset(response.answer,
|
if (rcode == dns.rcode.NOERROR
|
||||||
domain, dns.rdataclass.IN, dns.rdatatype.SOA) and response.flags & dns.flags.AA):
|
and response.get_rrset(response.answer, # type: ignore[attr-defined]
|
||||||
|
domain, dns.rdataclass.IN, dns.rdatatype.SOA)
|
||||||
|
and response.flags & dns.flags.AA):
|
||||||
logger.debug('Received authoritative SOA response for %s', domain_name)
|
logger.debug('Received authoritative SOA response for %s', domain_name)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|||||||
@@ -1180,7 +1180,7 @@ class NginxConfigurator(common.Configurator):
|
|||||||
|
|
||||||
# Entry point in main.py for performing challenges
|
# Entry point in main.py for performing challenges
|
||||||
def perform(self, achalls: List[achallenges.AnnotatedChallenge]
|
def perform(self, achalls: List[achallenges.AnnotatedChallenge]
|
||||||
) -> List[challenges.HTTP01Response]:
|
) -> List[challenges.ChallengeResponse]:
|
||||||
"""Perform the configuration related challenge.
|
"""Perform the configuration related challenge.
|
||||||
|
|
||||||
This function currently assumes all challenges will be fulfilled.
|
This function currently assumes all challenges will be fulfilled.
|
||||||
@@ -1189,13 +1189,16 @@ class NginxConfigurator(common.Configurator):
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
self._chall_out += len(achalls)
|
self._chall_out += len(achalls)
|
||||||
responses: List[Optional[challenges.HTTP01Response]] = [None] * len(achalls)
|
responses: List[Optional[challenges.ChallengeResponse]] = [None] * len(achalls)
|
||||||
http_doer = http_01.NginxHttp01(self)
|
http_doer = http_01.NginxHttp01(self)
|
||||||
|
|
||||||
for i, achall in enumerate(achalls):
|
for i, achall in enumerate(achalls):
|
||||||
# Currently also have chall_doer hold associated index of the
|
# Currently also have chall_doer hold associated index of the
|
||||||
# challenge. This helps to put all of the responses back together
|
# challenge. This helps to put all of the responses back together
|
||||||
# when they are all complete.
|
# when they are all complete.
|
||||||
|
if not isinstance(achall, achallenges.KeyAuthorizationAnnotatedChallenge):
|
||||||
|
raise errors.Error("Challenge should be an instance "
|
||||||
|
"of KeyAuthorizationAnnotatedChallenge")
|
||||||
http_doer.add_chall(achall, i)
|
http_doer.add_chall(achall, i)
|
||||||
|
|
||||||
http_response = http_doer.perform()
|
http_response = http_doer.perform()
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ from certbot_nginx._internal import nginxparser
|
|||||||
from certbot_nginx._internal.obj import Addr
|
from certbot_nginx._internal.obj import Addr
|
||||||
|
|
||||||
from acme import challenges
|
from acme import challenges
|
||||||
from acme.challenges import HTTP01Response
|
from acme.challenges import KeyAuthorizationChallengeResponse
|
||||||
from certbot import errors
|
from certbot import errors
|
||||||
from certbot.achallenges import KeyAuthorizationAnnotatedChallenge
|
from certbot.achallenges import KeyAuthorizationAnnotatedChallenge
|
||||||
from certbot.compat import os
|
from certbot.compat import os
|
||||||
@@ -49,10 +49,10 @@ class NginxHttp01(common.ChallengePerformer):
|
|||||||
self.challenge_conf = os.path.join(
|
self.challenge_conf = os.path.join(
|
||||||
configurator.config.config_dir, "le_http_01_cert_challenge.conf")
|
configurator.config.config_dir, "le_http_01_cert_challenge.conf")
|
||||||
|
|
||||||
def perform(self) -> List[HTTP01Response]:
|
def perform(self) -> List[KeyAuthorizationChallengeResponse]:
|
||||||
"""Perform a challenge on Nginx.
|
"""Perform a challenge on Nginx.
|
||||||
|
|
||||||
:returns: list of :class:`certbot.acme.challenges.HTTP01Response`
|
:returns: list of :class:`acme.challenges.KeyAuthorizationChallengeResponse`
|
||||||
:rtype: list
|
:rtype: list
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
# Forked from https://github.com/fatiherikli/nginxparser (MIT Licensed)
|
# Forked from https://github.com/fatiherikli/nginxparser (MIT Licensed)
|
||||||
import copy
|
import copy
|
||||||
import logging
|
import logging
|
||||||
|
import operator
|
||||||
import typing
|
import typing
|
||||||
from typing import Any
|
from typing import Any
|
||||||
from typing import IO
|
from typing import IO
|
||||||
@@ -167,13 +168,14 @@ class UnspacedList(List[Any]):
|
|||||||
inbound = UnspacedList(inbound)
|
inbound = UnspacedList(inbound)
|
||||||
return inbound, inbound.spaced
|
return inbound, inbound.spaced
|
||||||
|
|
||||||
def insert(self, i: int, x: Any) -> None:
|
def insert(self, i: "SupportsIndex", x: Any) -> None:
|
||||||
"""Insert object before index."""
|
"""Insert object before index."""
|
||||||
|
idx = operator.index(i)
|
||||||
item, spaced_item = self._coerce(x)
|
item, spaced_item = self._coerce(x)
|
||||||
slicepos = self._spaced_position(i) if i < len(self) else len(self.spaced)
|
slicepos = self._spaced_position(idx) if idx < len(self) else len(self.spaced)
|
||||||
self.spaced.insert(slicepos, spaced_item)
|
self.spaced.insert(slicepos, spaced_item)
|
||||||
if not spacey(item):
|
if not spacey(item):
|
||||||
super().insert(i, item)
|
super().insert(idx, item)
|
||||||
self.dirty = True
|
self.dirty = True
|
||||||
|
|
||||||
def append(self, x: Any) -> None:
|
def append(self, x: Any) -> None:
|
||||||
@@ -246,7 +248,7 @@ class UnspacedList(List[Any]):
|
|||||||
|
|
||||||
def _spaced_position(self, idx: "SupportsIndex") -> int:
|
def _spaced_position(self, idx: "SupportsIndex") -> int:
|
||||||
"""Convert from indexes in the unspaced list to positions in the spaced one"""
|
"""Convert from indexes in the unspaced list to positions in the spaced one"""
|
||||||
int_idx = idx.__index__()
|
int_idx = operator.index(idx)
|
||||||
pos = spaces = 0
|
pos = spaces = 0
|
||||||
# Normalize indexes like list[-1] etc, and save the result
|
# Normalize indexes like list[-1] etc, and save the result
|
||||||
if int_idx < 0:
|
if int_idx < 0:
|
||||||
|
|||||||
@@ -54,9 +54,9 @@ class Account:
|
|||||||
cross-machine migration scenarios.
|
cross-machine migration scenarios.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
creation_dt = acme_fields.RFC3339Field("creation_dt")
|
creation_dt: datetime.datetime = acme_fields.rfc3339("creation_dt")
|
||||||
creation_host = jose.Field("creation_host")
|
creation_host: str = jose.field("creation_host")
|
||||||
register_to_eff = jose.Field("register_to_eff", omitempty=True)
|
register_to_eff: str = jose.field("register_to_eff", omitempty=True)
|
||||||
|
|
||||||
def __init__(self, regr: messages.RegistrationResource, key: jose.JWK,
|
def __init__(self, regr: messages.RegistrationResource, key: jose.JWK,
|
||||||
meta: Optional['Meta'] = None) -> None:
|
meta: Optional['Meta'] = None) -> None:
|
||||||
@@ -135,7 +135,7 @@ class RegistrationResourceWithNewAuthzrURI(messages.RegistrationResource):
|
|||||||
continue to write out this field for some time so older
|
continue to write out this field for some time so older
|
||||||
clients don't crash in that scenario.
|
clients don't crash in that scenario.
|
||||||
"""
|
"""
|
||||||
new_authzr_uri = jose.Field('new_authzr_uri')
|
new_authzr_uri: str = jose.field('new_authzr_uri')
|
||||||
|
|
||||||
|
|
||||||
class AccountFileStorage(interfaces.AccountStorage):
|
class AccountFileStorage(interfaces.AccountStorage):
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ from typing import Dict
|
|||||||
from typing import Iterable
|
from typing import Iterable
|
||||||
from typing import List
|
from typing import List
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
from typing import Sequence
|
||||||
from typing import Tuple
|
from typing import Tuple
|
||||||
from typing import Type
|
from typing import Type
|
||||||
|
|
||||||
@@ -272,7 +273,7 @@ class AuthHandler:
|
|||||||
self.auth.cleanup(achalls)
|
self.auth.cleanup(achalls)
|
||||||
|
|
||||||
def _challenge_factory(self, authzr: messages.AuthorizationResource,
|
def _challenge_factory(self, authzr: messages.AuthorizationResource,
|
||||||
path: List[int]) -> List[achallenges.AnnotatedChallenge]:
|
path: Sequence[int]) -> List[achallenges.AnnotatedChallenge]:
|
||||||
"""Construct Namedtuple Challenges
|
"""Construct Namedtuple Challenges
|
||||||
|
|
||||||
:param messages.AuthorizationResource authzr: authorization
|
:param messages.AuthorizationResource authzr: authorization
|
||||||
@@ -350,7 +351,7 @@ def challb_to_achall(challb: messages.ChallengeBody, account_key: josepy.JWK,
|
|||||||
|
|
||||||
def gen_challenge_path(challbs: List[messages.ChallengeBody],
|
def gen_challenge_path(challbs: List[messages.ChallengeBody],
|
||||||
preferences: List[Type[challenges.Challenge]],
|
preferences: List[Type[challenges.Challenge]],
|
||||||
combinations: Tuple[List[int], ...]) -> List[int]:
|
combinations: Tuple[Tuple[int, ...], ...]) -> Tuple[int, ...]:
|
||||||
"""Generate a plan to get authority over the identity.
|
"""Generate a plan to get authority over the identity.
|
||||||
|
|
||||||
.. todo:: This can be possibly be rewritten to use resolved_combinations.
|
.. todo:: This can be possibly be rewritten to use resolved_combinations.
|
||||||
@@ -383,8 +384,8 @@ def gen_challenge_path(challbs: List[messages.ChallengeBody],
|
|||||||
|
|
||||||
def _find_smart_path(challbs: List[messages.ChallengeBody],
|
def _find_smart_path(challbs: List[messages.ChallengeBody],
|
||||||
preferences: List[Type[challenges.Challenge]],
|
preferences: List[Type[challenges.Challenge]],
|
||||||
combinations: Tuple[List[int], ...]
|
combinations: Tuple[Tuple[int, ...], ...]
|
||||||
) -> List[int]:
|
) -> Tuple[int, ...]:
|
||||||
"""Find challenge path with server hints.
|
"""Find challenge path with server hints.
|
||||||
|
|
||||||
Can be called if combinations is included. Function uses a simple
|
Can be called if combinations is included. Function uses a simple
|
||||||
@@ -399,7 +400,7 @@ def _find_smart_path(challbs: List[messages.ChallengeBody],
|
|||||||
|
|
||||||
# max_cost is now equal to sum(indices) + 1
|
# max_cost is now equal to sum(indices) + 1
|
||||||
|
|
||||||
best_combo: Optional[List[int]] = None
|
best_combo: Optional[Tuple[int, ...]] = None
|
||||||
# Set above completing all of the available challenges
|
# Set above completing all of the available challenges
|
||||||
best_combo_cost = max_cost
|
best_combo_cost = max_cost
|
||||||
|
|
||||||
@@ -422,7 +423,7 @@ def _find_smart_path(challbs: List[messages.ChallengeBody],
|
|||||||
|
|
||||||
|
|
||||||
def _find_dumb_path(challbs: List[messages.ChallengeBody],
|
def _find_dumb_path(challbs: List[messages.ChallengeBody],
|
||||||
preferences: List[Type[challenges.Challenge]]) -> List[int]:
|
preferences: List[Type[challenges.Challenge]]) -> Tuple[int, ...]:
|
||||||
"""Find challenge path without server hints.
|
"""Find challenge path without server hints.
|
||||||
|
|
||||||
Should be called if the combinations hint is not included by the
|
Should be called if the combinations hint is not included by the
|
||||||
@@ -440,7 +441,7 @@ def _find_dumb_path(challbs: List[messages.ChallengeBody],
|
|||||||
else:
|
else:
|
||||||
raise _report_no_chall_path(challbs)
|
raise _report_no_chall_path(challbs)
|
||||||
|
|
||||||
return path
|
return tuple(path)
|
||||||
|
|
||||||
|
|
||||||
def _report_no_chall_path(challbs: List[messages.ChallengeBody]) -> errors.AuthorizationError:
|
def _report_no_chall_path(challbs: List[messages.ChallengeBody]) -> errors.AuthorizationError:
|
||||||
|
|||||||
@@ -251,7 +251,7 @@ def perform_registration(acme: acme_client.ClientV2, config: configuration.Names
|
|||||||
raise errors.Error("The ACME client must be an instance of "
|
raise errors.Error("The ACME client must be an instance of "
|
||||||
"acme.client.BackwardsCompatibleClientV2")
|
"acme.client.BackwardsCompatibleClientV2")
|
||||||
except messages.Error as e:
|
except messages.Error as e:
|
||||||
if e.code in ('invalidEmail', 'invalidContact'):
|
if e.code in ("invalidEmail", "invalidContact"):
|
||||||
if config.noninteractive_mode:
|
if config.noninteractive_mode:
|
||||||
msg = ("The ACME server believes %s is an invalid email address. "
|
msg = ("The ACME server believes %s is an invalid email address. "
|
||||||
"Please ensure it is a valid email and attempt "
|
"Please ensure it is a valid email and attempt "
|
||||||
|
|||||||
@@ -98,7 +98,7 @@ permitted by DNS standards.)
|
|||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
self.reverter = reverter.Reverter(self.config)
|
self.reverter = reverter.Reverter(self.config)
|
||||||
self.reverter.recovery_routine()
|
self.reverter.recovery_routine()
|
||||||
self.env: Dict[achallenges.KeyAuthorizationAnnotatedChallenge, Dict[str, str]] = {}
|
self.env: Dict[achallenges.AnnotatedChallenge, Dict[str, str]] = {}
|
||||||
self.subsequent_dns_challenge = False
|
self.subsequent_dns_challenge = False
|
||||||
self.subsequent_any_challenge = False
|
self.subsequent_any_challenge = False
|
||||||
|
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ logger = logging.getLogger(__name__)
|
|||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
ServedType = DefaultDict[
|
ServedType = DefaultDict[
|
||||||
acme_standalone.BaseDualNetworkedServers,
|
acme_standalone.BaseDualNetworkedServers,
|
||||||
Set[achallenges.KeyAuthorizationAnnotatedChallenge]
|
Set[achallenges.AnnotatedChallenge]
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ from certbot import crypto_util
|
|||||||
from certbot import errors
|
from certbot import errors
|
||||||
from certbot import interfaces
|
from certbot import interfaces
|
||||||
from certbot._internal import cli
|
from certbot._internal import cli
|
||||||
from certbot.achallenges import KeyAuthorizationAnnotatedChallenge as AnnotatedChallenge
|
from certbot.achallenges import AnnotatedChallenge
|
||||||
from certbot.compat import filesystem
|
from certbot.compat import filesystem
|
||||||
from certbot.compat import os
|
from certbot.compat import os
|
||||||
from certbot.display import ops
|
from certbot.display import ops
|
||||||
@@ -85,7 +85,7 @@ to serve all files under specified web root ({0})."""
|
|||||||
"file, it needs to be on a single line, like: webroot-map = "
|
"file, it needs to be on a single line, like: webroot-map = "
|
||||||
'{"example.com":"/var/www"}.')
|
'{"example.com":"/var/www"}.')
|
||||||
|
|
||||||
def auth_hint(self, failed_achalls: Iterable[AnnotatedChallenge]) -> str: # pragma: no cover
|
def auth_hint(self, failed_achalls: List[AnnotatedChallenge]) -> str: # pragma: no cover
|
||||||
return ("The Certificate Authority failed to download the temporary challenge files "
|
return ("The Certificate Authority failed to download the temporary challenge files "
|
||||||
"created by Certbot. Ensure that the listed domains serve their content from "
|
"created by Certbot. Ensure that the listed domains serve their content from "
|
||||||
"the provided --webroot-path/-w and that files created there can be downloaded "
|
"the provided --webroot-path/-w and that files created there can be downloaded "
|
||||||
@@ -105,7 +105,7 @@ to serve all files under specified web root ({0})."""
|
|||||||
def prepare(self) -> None: # pylint: disable=missing-function-docstring
|
def prepare(self) -> None: # pylint: disable=missing-function-docstring
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def perform(self, achalls: Iterable[AnnotatedChallenge]) -> List[challenges.ChallengeResponse]: # pylint: disable=missing-function-docstring
|
def perform(self, achalls: List[AnnotatedChallenge]) -> List[challenges.ChallengeResponse]: # pylint: disable=missing-function-docstring
|
||||||
self._set_webroots(achalls)
|
self._set_webroots(achalls)
|
||||||
|
|
||||||
self._create_challenge_dirs()
|
self._create_challenge_dirs()
|
||||||
@@ -257,7 +257,7 @@ to serve all files under specified web root ({0})."""
|
|||||||
self.performed[root_path].add(achall)
|
self.performed[root_path].add(achall)
|
||||||
return response
|
return response
|
||||||
|
|
||||||
def cleanup(self, achalls: Iterable[AnnotatedChallenge]) -> None: # pylint: disable=missing-function-docstring
|
def cleanup(self, achalls: List[AnnotatedChallenge]) -> None: # pylint: disable=missing-function-docstring
|
||||||
for achall in achalls:
|
for achall in achalls:
|
||||||
root_path = self.full_roots.get(achall.domain, None)
|
root_path = self.full_roots.get(achall.domain, None)
|
||||||
if root_path is not None:
|
if root_path is not None:
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ class Reporter:
|
|||||||
_msg_type = collections.namedtuple('_msg_type', 'priority text on_crash')
|
_msg_type = collections.namedtuple('_msg_type', 'priority text on_crash')
|
||||||
|
|
||||||
def __init__(self, config: configuration.NamespaceConfig) -> None:
|
def __init__(self, config: configuration.NamespaceConfig) -> None:
|
||||||
self.messages: queue.PriorityQueue[Reporter._msg_type] = queue.PriorityQueue()
|
self.messages: "queue.PriorityQueue[Reporter._msg_type]" = queue.PriorityQueue()
|
||||||
self.config = config
|
self.config = config
|
||||||
|
|
||||||
def add_message(self, msg: str, priority: int, on_crash: bool = True) -> None:
|
def add_message(self, msg: str, priority: int, on_crash: bool = True) -> None:
|
||||||
|
|||||||
@@ -18,8 +18,8 @@ try:
|
|||||||
from urllib3.connectionpool import HTTPConnectionPool
|
from urllib3.connectionpool import HTTPConnectionPool
|
||||||
except ImportError:
|
except ImportError:
|
||||||
# Stub imports for oldest requirements, that will never be used in snaps.
|
# Stub imports for oldest requirements, that will never be used in snaps.
|
||||||
HTTPConnection = object
|
HTTPConnection = object # type: ignore[misc,assignment]
|
||||||
HTTPConnectionPool = object
|
HTTPConnectionPool = object # type: ignore[misc,assignment]
|
||||||
|
|
||||||
|
|
||||||
_ARCH_TRIPLET_MAP = {
|
_ARCH_TRIPLET_MAP = {
|
||||||
|
|||||||
@@ -290,8 +290,11 @@ def make_key(bits: int = 1024, key_type: str = "rsa",
|
|||||||
try:
|
try:
|
||||||
name = elliptic_curve.upper()
|
name = elliptic_curve.upper()
|
||||||
if name in ('SECP256R1', 'SECP384R1', 'SECP521R1'):
|
if name in ('SECP256R1', 'SECP384R1', 'SECP521R1'):
|
||||||
|
curve = getattr(ec, elliptic_curve.upper())
|
||||||
|
if not curve:
|
||||||
|
raise errors.Error(f"Invalid curve type: {elliptic_curve}")
|
||||||
_key = ec.generate_private_key(
|
_key = ec.generate_private_key(
|
||||||
curve=getattr(ec, elliptic_curve.upper(), None)(),
|
curve=curve(),
|
||||||
backend=default_backend()
|
backend=default_backend()
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -18,6 +18,8 @@ from typing import Tuple
|
|||||||
|
|
||||||
import pkg_resources
|
import pkg_resources
|
||||||
|
|
||||||
|
from acme import challenges
|
||||||
|
|
||||||
from certbot import achallenges
|
from certbot import achallenges
|
||||||
from certbot import configuration
|
from certbot import configuration
|
||||||
from certbot import crypto_util
|
from certbot import crypto_util
|
||||||
@@ -377,7 +379,7 @@ class ChallengePerformer:
|
|||||||
if idx is not None:
|
if idx is not None:
|
||||||
self.indices.append(idx)
|
self.indices.append(idx)
|
||||||
|
|
||||||
def perform(self) -> List[achallenges.KeyAuthorizationAnnotatedChallenge]:
|
def perform(self) -> List[challenges.KeyAuthorizationChallengeResponse]:
|
||||||
"""Perform all added challenges.
|
"""Perform all added challenges.
|
||||||
|
|
||||||
:returns: challenge responses
|
:returns: challenge responses
|
||||||
|
|||||||
@@ -7,8 +7,8 @@ import josepy as jose
|
|||||||
from requests.exceptions import HTTPError
|
from requests.exceptions import HTTPError
|
||||||
from requests.exceptions import RequestException
|
from requests.exceptions import RequestException
|
||||||
|
|
||||||
from acme.challenges import Challenge
|
|
||||||
from certbot import errors
|
from certbot import errors
|
||||||
|
from certbot.achallenges import AnnotatedChallenge
|
||||||
from certbot.plugins import dns_test_common
|
from certbot.plugins import dns_test_common
|
||||||
from certbot.plugins.dns_common_lexicon import LexiconClient
|
from certbot.plugins.dns_common_lexicon import LexiconClient
|
||||||
from certbot.plugins.dns_test_common import _AuthenticatorCallableTestCase
|
from certbot.plugins.dns_test_common import _AuthenticatorCallableTestCase
|
||||||
@@ -33,7 +33,7 @@ class _AuthenticatorCallableLexiconTestCase(_AuthenticatorCallableTestCase, Prot
|
|||||||
a mocked LexiconClient instance.
|
a mocked LexiconClient instance.
|
||||||
"""
|
"""
|
||||||
mock_client: MagicMock
|
mock_client: MagicMock
|
||||||
achall: Challenge
|
achall: AnnotatedChallenge
|
||||||
|
|
||||||
|
|
||||||
class _LexiconAwareTestCase(Protocol):
|
class _LexiconAwareTestCase(Protocol):
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
"""ACME utilities for testing."""
|
"""ACME utilities for testing."""
|
||||||
import datetime
|
import datetime
|
||||||
|
from typing import Any
|
||||||
|
from typing import Dict
|
||||||
from typing import Iterable
|
from typing import Iterable
|
||||||
from typing import Tuple
|
from typing import Tuple
|
||||||
|
|
||||||
@@ -74,7 +76,7 @@ def gen_authzr(authz_status: messages.Status, domain: str, challs: Iterable[chal
|
|||||||
chall_to_challb(chall, status)
|
chall_to_challb(chall, status)
|
||||||
for chall, status in zip(challs, statuses)
|
for chall, status in zip(challs, statuses)
|
||||||
)
|
)
|
||||||
authz_kwargs = {
|
authz_kwargs: Dict[str, Any] = {
|
||||||
"identifier": messages.Identifier(
|
"identifier": messages.Identifier(
|
||||||
typ=messages.IDENTIFIER_FQDN, value=domain),
|
typ=messages.IDENTIFIER_FQDN, value=domain),
|
||||||
"challenges": challbs,
|
"challenges": challbs,
|
||||||
|
|||||||
@@ -2,10 +2,9 @@
|
|||||||
# that script.
|
# that script.
|
||||||
apacheconfig==0.3.2
|
apacheconfig==0.3.2
|
||||||
asn1crypto==0.24.0
|
asn1crypto==0.24.0
|
||||||
astroid==2.8.6; python_version >= "3.6" and python_version < "4.0"
|
astroid==2.9.0; python_version >= "3.6" and python_version < "4.0"
|
||||||
atomicwrites==1.4.0; python_version >= "3.6" and python_full_version < "3.0.0" and sys_platform == "win32" or sys_platform == "win32" and python_version >= "3.6" and python_full_version >= "3.4.0"
|
atomicwrites==1.4.0; python_version >= "3.6" and python_full_version < "3.0.0" and sys_platform == "win32" or sys_platform == "win32" and python_version >= "3.6" and python_full_version >= "3.4.0"
|
||||||
attrs==21.2.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6"
|
attrs==21.4.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6"
|
||||||
backports.entry-points-selectable==1.1.1; python_version >= "3.6" and python_full_version < "3.0.0" or python_version >= "3.6" and python_full_version >= "3.5.0"
|
|
||||||
bcrypt==3.2.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6"
|
bcrypt==3.2.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6"
|
||||||
boto3==1.4.7
|
boto3==1.4.7
|
||||||
botocore==1.7.41
|
botocore==1.7.41
|
||||||
@@ -16,10 +15,10 @@ cloudflare==1.5.1
|
|||||||
colorama==0.4.4; python_version >= "3.6" and python_full_version < "3.0.0" and sys_platform == "win32" or python_full_version >= "3.5.0" and python_version >= "3.6" and sys_platform == "win32" or python_version >= "3.6" and python_full_version < "3.0.0" and python_version < "4.0" and sys_platform == "win32" or python_version >= "3.6" and python_version < "4.0" and sys_platform == "win32" and python_full_version >= "3.5.0"
|
colorama==0.4.4; python_version >= "3.6" and python_full_version < "3.0.0" and sys_platform == "win32" or python_full_version >= "3.5.0" and python_version >= "3.6" and sys_platform == "win32" or python_version >= "3.6" and python_full_version < "3.0.0" and python_version < "4.0" and sys_platform == "win32" or python_version >= "3.6" and python_version < "4.0" and sys_platform == "win32" and python_full_version >= "3.5.0"
|
||||||
configargparse==0.10.0
|
configargparse==0.10.0
|
||||||
configobj==5.0.6
|
configobj==5.0.6
|
||||||
coverage==6.1.2; python_version >= "3.6" or python_version >= "3.6"
|
coverage==6.2; python_version >= "3.6" or python_version >= "3.6"
|
||||||
cryptography==3.2.1; (python_version >= "2.7" and python_full_version < "3.0.0") or (python_full_version >= "3.5.0")
|
cryptography==3.2.1; (python_version >= "2.7" and python_full_version < "3.0.0") or (python_full_version >= "3.5.0")
|
||||||
cython==0.29.24; (python_version >= "2.6" and python_full_version < "3.0.0") or (python_full_version >= "3.3.0")
|
cython==0.29.26; (python_version >= "2.6" and python_full_version < "3.0.0") or (python_full_version >= "3.3.0")
|
||||||
distlib==0.3.3; python_version >= "3.6" and python_full_version < "3.0.0" or python_version >= "3.6" and python_full_version >= "3.5.0"
|
distlib==0.3.4; python_version >= "3.6" and python_full_version < "3.0.0" or python_version >= "3.6" and python_full_version >= "3.5.0"
|
||||||
distro==1.0.1
|
distro==1.0.1
|
||||||
dns-lexicon==3.2.1
|
dns-lexicon==3.2.1
|
||||||
dnspython==1.15.0
|
dnspython==1.15.0
|
||||||
@@ -30,30 +29,30 @@ dockerpty==0.4.1; python_version >= "3.6" and python_full_version < "3.0.0" or p
|
|||||||
docopt==0.6.2; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6"
|
docopt==0.6.2; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6"
|
||||||
docutils==0.18.1; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6"
|
docutils==0.18.1; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6"
|
||||||
execnet==1.9.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6"
|
execnet==1.9.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6"
|
||||||
filelock==3.4.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_version >= "3.6" and python_full_version >= "3.5.0" or python_version >= "3.6"
|
filelock==3.4.1; python_version >= "3.6" and python_full_version < "3.0.0" or python_version >= "3.6" and python_full_version >= "3.5.0" or python_version >= "3.6"
|
||||||
funcsigs==0.4
|
funcsigs==0.4
|
||||||
future==0.18.2; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.3.0" and python_version >= "3.6"
|
future==0.18.2; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.3.0" and python_version >= "3.6"
|
||||||
google-api-python-client==1.5.5
|
google-api-python-client==1.5.5
|
||||||
httplib2==0.9.2
|
httplib2==0.9.2
|
||||||
idna==2.6
|
idna==2.6
|
||||||
importlib-metadata==4.8.2; python_version >= "3.6" and python_full_version < "3.0.0" and python_version < "3.8" or python_version >= "3.6" and python_full_version >= "3.5.0" and python_version < "3.8" or python_version < "3.8" and python_version >= "3.6"
|
importlib-metadata==4.8.3; python_version >= "3.6" and python_full_version < "3.0.0" and python_version < "3.8" or python_version >= "3.6" and python_full_version >= "3.5.0" and python_version < "3.8" or python_version < "3.8" and python_version >= "3.6"
|
||||||
importlib-resources==5.4.0; python_version >= "3.6" and python_full_version < "3.0.0" and python_version < "3.7" or python_version >= "3.6" and python_full_version >= "3.5.0" and python_version < "3.7"
|
importlib-resources==5.4.0; python_version >= "3.6" and python_full_version < "3.0.0" and python_version < "3.7" or python_version >= "3.6" and python_full_version >= "3.5.0" and python_version < "3.7"
|
||||||
iniconfig==1.1.1; python_version >= "3.6"
|
iniconfig==1.1.1; python_version >= "3.6"
|
||||||
ipaddress==1.0.16
|
ipaddress==1.0.16
|
||||||
isort==5.8.0; python_version >= "3.6" and python_version < "4.0"
|
isort==5.8.0; python_version >= "3.6" and python_version < "4.0"
|
||||||
jmespath==0.10.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.3.0" and python_version >= "3.6"
|
jmespath==0.10.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.3.0" and python_version >= "3.6"
|
||||||
josepy==1.11.0; python_version >= "3.6"
|
josepy==1.12.0; python_version >= "3.6"
|
||||||
jsonschema==2.6.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6"
|
jsonschema==2.6.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6"
|
||||||
lazy-object-proxy==1.6.0; python_version >= "3.6" and python_full_version < "3.0.0" and python_version < "4.0" or python_version >= "3.6" and python_version < "4.0" and python_full_version >= "3.6.0"
|
lazy-object-proxy==1.7.1; python_version >= "3.6" and python_version < "4.0"
|
||||||
logger==1.4; python_version >= "3.6"
|
logger==1.4; python_version >= "3.6"
|
||||||
mccabe==0.6.1; python_version >= "3.6" and python_version < "4.0"
|
mccabe==0.6.1; python_version >= "3.6" and python_version < "4.0"
|
||||||
mock==1.0.1
|
mock==1.0.1
|
||||||
mypy-extensions==0.4.3; python_version >= "3.6"
|
mypy-extensions==0.4.3; python_version >= "3.6"
|
||||||
mypy==0.910; python_version >= "3.6"
|
mypy==0.931; python_version >= "3.6"
|
||||||
ndg-httpsclient==0.3.2
|
ndg-httpsclient==0.3.2
|
||||||
oauth2client==4.0.0
|
oauth2client==4.0.0
|
||||||
packaging==21.3; python_version >= "3.6"
|
packaging==21.3; python_version >= "3.6"
|
||||||
paramiko==2.8.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6"
|
paramiko==2.9.2; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6"
|
||||||
parsedatetime==2.4
|
parsedatetime==2.4
|
||||||
pbr==1.8.0
|
pbr==1.8.0
|
||||||
pip==21.3.1; python_version >= "3.6"
|
pip==21.3.1; python_version >= "3.6"
|
||||||
@@ -64,21 +63,21 @@ py==1.11.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_
|
|||||||
pyasn1-modules==0.0.10; python_version >= "3.6"
|
pyasn1-modules==0.0.10; python_version >= "3.6"
|
||||||
pyasn1==0.1.9
|
pyasn1==0.1.9
|
||||||
pycparser==2.14
|
pycparser==2.14
|
||||||
pylint==2.11.1; python_version >= "3.6" and python_version < "4.0"
|
pylint==2.12.0; python_version >= "3.6" and python_version < "4.0"
|
||||||
pynacl==1.4.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6"
|
pynacl==1.5.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6"
|
||||||
pyopenssl==17.3.0
|
pyopenssl==17.3.0
|
||||||
pyparsing==2.2.0
|
pyparsing==2.2.0
|
||||||
pypiwin32==223; sys_platform == "win32" and python_version >= "3.6" and (python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6")
|
pypiwin32==223; sys_platform == "win32" and python_version >= "3.6" and (python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6")
|
||||||
pyrfc3339==1.0
|
pyrfc3339==1.0
|
||||||
pytest-cov==3.0.0; python_version >= "3.6" or python_version >= "3.6"
|
pytest-cov==3.0.0; python_version >= "3.6" or python_version >= "3.6"
|
||||||
pytest-forked==1.3.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6"
|
pytest-forked==1.4.0; python_version >= "3.6"
|
||||||
pytest-xdist==2.4.0; python_version >= "3.6" or python_version >= "3.6"
|
pytest-xdist==2.5.0; python_version >= "3.6" or python_version >= "3.6"
|
||||||
pytest==6.2.5; python_version >= "3.6" or python_version >= "3.6" or python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6"
|
pytest==6.2.5; python_version >= "3.6" or python_version >= "3.6"
|
||||||
python-augeas==0.5.0
|
python-augeas==0.5.0
|
||||||
python-dateutil==2.8.2; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.3.0" and python_version >= "3.6"
|
python-dateutil==2.8.2; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.3.0" and python_version >= "3.6"
|
||||||
python-digitalocean==1.11
|
python-digitalocean==1.11
|
||||||
pytz==2012c
|
pytz==2012c
|
||||||
pywin32==302; sys_platform == "win32" and python_version >= "3.6" or sys_platform == "win32" and python_version >= "3.6" and (python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6")
|
pywin32==303; sys_platform == "win32" and python_version >= "3.6" or sys_platform == "win32" and python_version >= "3.6" and (python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6")
|
||||||
pyyaml==3.13; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6" or python_version >= "3.6"
|
pyyaml==3.13; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6" or python_version >= "3.6"
|
||||||
requests-file==1.5.1; python_version >= "3.6"
|
requests-file==1.5.1; python_version >= "3.6"
|
||||||
requests-toolbelt==0.9.1; python_version >= "3.6"
|
requests-toolbelt==0.9.1; python_version >= "3.6"
|
||||||
@@ -90,24 +89,25 @@ six==1.11.0
|
|||||||
texttable==0.9.1; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6"
|
texttable==0.9.1; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6"
|
||||||
tldextract==3.1.2; python_version >= "3.6"
|
tldextract==3.1.2; python_version >= "3.6"
|
||||||
toml==0.10.2; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.3.0" and python_version >= "3.6" or python_version >= "3.6" and python_full_version < "3.0.0" and python_version < "4.0" or python_version >= "3.6" and python_version < "4.0" and python_full_version >= "3.3.0"
|
toml==0.10.2; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.3.0" and python_version >= "3.6" or python_version >= "3.6" and python_full_version < "3.0.0" and python_version < "4.0" or python_version >= "3.6" and python_version < "4.0" and python_full_version >= "3.3.0"
|
||||||
tomli==1.2.2; python_version >= "3.6"
|
tomli==1.2.3; python_version >= "3.6"
|
||||||
tox==1.9.2; python_version >= "3.6"
|
tox==1.9.2; python_version >= "3.6"
|
||||||
typed-ast==1.4.3; python_version >= "3.6" and python_version < "3.8" or implementation_name == "cpython" and python_version < "3.8" and python_version >= "3.6"
|
typed-ast==1.5.1; python_version >= "3.6" and python_version < "3.8" or implementation_name == "cpython" and python_version < "3.8" and python_version >= "3.6"
|
||||||
types-cryptography==3.3.9; python_version >= "3.6"
|
types-cryptography==3.3.14; python_version >= "3.6"
|
||||||
types-enum34==1.1.1; python_version >= "3.6"
|
types-enum34==1.1.7; python_version >= "3.6"
|
||||||
types-ipaddress==1.0.1; python_version >= "3.6"
|
types-ipaddress==1.0.7; python_version >= "3.6"
|
||||||
types-mock==4.0.3; python_version >= "3.6"
|
types-mock==4.0.8; python_version >= "3.6"
|
||||||
types-pyopenssl==21.0.1; python_version >= "3.6"
|
types-pyopenssl==21.0.3; python_version >= "3.6"
|
||||||
types-pyrfc3339==1.1.0; python_version >= "3.6"
|
types-pyrfc3339==1.1.1; python_version >= "3.6"
|
||||||
types-python-dateutil==2.8.3; python_version >= "3.6"
|
types-python-dateutil==2.8.7; python_version >= "3.6"
|
||||||
types-pytz==2021.3.1; python_version >= "3.6"
|
types-pytz==2021.3.4; python_version >= "3.6"
|
||||||
types-requests==2.26.0; python_version >= "3.6"
|
types-requests==2.27.7; python_version >= "3.6"
|
||||||
types-setuptools==57.4.3; python_version >= "3.6"
|
types-setuptools==57.4.7; python_version >= "3.6"
|
||||||
types-six==1.16.2; python_version >= "3.6"
|
types-six==1.16.10; python_version >= "3.6"
|
||||||
typing-extensions==4.0.0; python_version >= "3.6" or python_version >= "3.6" and python_version < "3.10" or python_version < "3.8" and python_version >= "3.6"
|
types-urllib3==1.26.7; python_version >= "3.6"
|
||||||
|
typing-extensions==4.0.1; python_version >= "3.6" or python_version >= "3.6" and python_version < "3.10" or python_version < "3.8" and python_version >= "3.6"
|
||||||
uritemplate==3.0.1; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6"
|
uritemplate==3.0.1; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6"
|
||||||
urllib3==1.10.2
|
urllib3==1.10.2
|
||||||
virtualenv==20.10.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_version >= "3.6" and python_full_version >= "3.5.0"
|
virtualenv==20.13.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_version >= "3.6" and python_full_version >= "3.5.0"
|
||||||
websocket-client==0.59.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6"
|
websocket-client==0.59.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6"
|
||||||
wheel==0.33.6; (python_version >= "2.7" and python_full_version < "3.0.0") or (python_full_version >= "3.4.0")
|
wheel==0.33.6; (python_version >= "2.7" and python_full_version < "3.0.0") or (python_full_version >= "3.4.0")
|
||||||
wrapt==1.13.3; python_version >= "3.6" and python_full_version < "3.0.0" and python_version < "4.0" or python_version >= "3.6" and python_version < "4.0" and python_full_version >= "3.5.0"
|
wrapt==1.13.3; python_version >= "3.6" and python_full_version < "3.0.0" and python_version < "4.0" or python_version >= "3.6" and python_version < "4.0" and python_full_version >= "3.5.0"
|
||||||
|
|||||||
@@ -51,10 +51,6 @@ awscli = ">=1.19.62"
|
|||||||
# as a dependency here to ensure a version of cython is pinned for extra
|
# as a dependency here to ensure a version of cython is pinned for extra
|
||||||
# stability.
|
# stability.
|
||||||
cython = "*"
|
cython = "*"
|
||||||
# Due to better type annotations in new versions, mypy tests currently fail
|
|
||||||
# with new versions of josepy so we pin it back for now. Fixing this is being
|
|
||||||
# tracked by https://github.com/certbot/certbot/issues/9113.
|
|
||||||
josepy = "1.9.0"
|
|
||||||
# We install mock in our "external-mock" tox environment to test that we didn't
|
# We install mock in our "external-mock" tox environment to test that we didn't
|
||||||
# break Certbot's test API which used to always use mock objects from the 3rd
|
# break Certbot's test API which used to always use mock objects from the 3rd
|
||||||
# party mock library. We list the mock dependency here so that is pinned, but
|
# party mock library. We list the mock dependency here so that is pinned, but
|
||||||
|
|||||||
@@ -9,41 +9,41 @@ alabaster==0.7.12; python_version >= "3.6"
|
|||||||
apacheconfig==0.3.2; python_version >= "3.6"
|
apacheconfig==0.3.2; python_version >= "3.6"
|
||||||
appdirs==1.4.4; python_version >= "3.6" and python_full_version < "3.0.0" and python_version < "4.0" or python_version >= "3.6" and python_version < "4.0" and python_full_version >= "3.4.0"
|
appdirs==1.4.4; python_version >= "3.6" and python_full_version < "3.0.0" and python_version < "4.0" or python_version >= "3.6" and python_version < "4.0" and python_full_version >= "3.4.0"
|
||||||
appnope==0.1.2; python_version == "3.6" and sys_platform == "darwin" or python_version >= "3.7" and sys_platform == "darwin"
|
appnope==0.1.2; python_version == "3.6" and sys_platform == "darwin" or python_version >= "3.7" and sys_platform == "darwin"
|
||||||
astroid==2.8.6; python_version >= "3.6" and python_version < "4.0"
|
astroid==2.9.0; python_version >= "3.6" and python_version < "4.0"
|
||||||
atomicwrites==1.4.0; python_version >= "3.6" and python_full_version < "3.0.0" and sys_platform == "win32" or sys_platform == "win32" and python_version >= "3.6" and python_full_version >= "3.4.0"
|
atomicwrites==1.4.0; python_version >= "3.6" and python_full_version < "3.0.0" and sys_platform == "win32" or sys_platform == "win32" and python_version >= "3.6" and python_full_version >= "3.4.0"
|
||||||
attrs==21.2.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6"
|
attrs==21.4.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6"
|
||||||
awscli==1.22.11; python_version >= "3.6"
|
awscli==1.22.39; python_version >= "3.6"
|
||||||
azure-devops==6.0.0b4; python_version >= "3.6"
|
azure-devops==6.0.0b4; python_version >= "3.6"
|
||||||
babel==2.9.1; python_version >= "3.6" and python_full_version < "3.0.0" or python_version >= "3.6" and python_full_version >= "3.4.0"
|
babel==2.9.1; python_version >= "3.6" and python_full_version < "3.0.0" or python_version >= "3.6" and python_full_version >= "3.4.0"
|
||||||
backcall==0.2.0; python_version == "3.6" or python_version >= "3.7"
|
backcall==0.2.0; python_version == "3.6" or python_version >= "3.7"
|
||||||
bcrypt==3.2.0; python_version >= "3.6"
|
bcrypt==3.2.0; python_version >= "3.6"
|
||||||
beautifulsoup4==4.10.0; python_full_version > "3.0.0" and python_version >= "3.6" or python_version >= "3.6" and python_version < "4.0" and python_full_version > "3.0.0"
|
beautifulsoup4==4.10.0; python_full_version > "3.0.0" and python_version >= "3.6" or python_version >= "3.6" and python_version < "4.0" and python_full_version > "3.0.0"
|
||||||
bleach==4.1.0; python_version >= "3.6"
|
bleach==4.1.0; python_version >= "3.6"
|
||||||
boto3==1.20.11; python_version >= "3.6"
|
boto3==1.20.39; python_version >= "3.6"
|
||||||
botocore==1.23.11; python_version >= "3.6"
|
botocore==1.23.39; python_version >= "3.6"
|
||||||
cachecontrol==0.12.10; python_version >= "3.6" and python_version < "4.0"
|
cachecontrol==0.12.10; python_version >= "3.6" and python_version < "4.0"
|
||||||
cached-property==1.5.2; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6"
|
cached-property==1.5.2; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6"
|
||||||
cachetools==4.2.4; python_version >= "3.5" and python_version < "4.0" and (python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3.6")
|
cachetools==4.2.4; python_version >= "3.5" and python_version < "4.0" and (python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3.6")
|
||||||
cachy==0.3.0; python_version >= "3.6" and python_full_version < "3.0.0" and python_version < "4.0" or python_version >= "3.6" and python_version < "4.0" and python_full_version >= "3.4.0"
|
cachy==0.3.0; python_version >= "3.6" and python_full_version < "3.0.0" and python_version < "4.0" or python_version >= "3.6" and python_version < "4.0" and python_full_version >= "3.4.0"
|
||||||
certifi==2021.10.8; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3.6" or python_version >= "3.6"
|
certifi==2021.10.8; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3.6" or python_version >= "3.6"
|
||||||
cffi==1.15.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_version >= "3.6" and python_full_version >= "3.4.0" or python_version >= "3.6"
|
cffi==1.15.0; python_version >= "3.6" or python_version >= "3.6"
|
||||||
charset-normalizer==2.0.7; python_full_version >= "3.6.0" and python_version >= "3.6"
|
charset-normalizer==2.0.10; python_full_version >= "3.6.0" and python_version >= "3.6"
|
||||||
cleo==1.0.0a4; python_version >= "3.6" and python_version < "4.0"
|
cleo==1.0.0a4; python_version >= "3.6" and python_version < "4.0"
|
||||||
cloudflare==2.8.15; python_version >= "3.6"
|
cloudflare==2.8.15; python_version >= "3.6"
|
||||||
colorama==0.4.3; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6" or python_version >= "3.6" and python_full_version < "3.0.0" and sys_platform == "win32" or python_full_version >= "3.5.0" and python_version >= "3.6" and sys_platform == "win32" or python_version >= "3.6" and python_full_version < "3.0.0" and python_version < "4.0" and sys_platform == "win32" or python_version >= "3.6" and python_version < "4.0" and sys_platform == "win32" and python_full_version >= "3.5.0" or python_version >= "3.6" and python_full_version < "3.0.0" and platform_system == "Windows" or python_version >= "3.6" and python_full_version >= "3.5.0" and platform_system == "Windows" or python_version == "3.6" and python_full_version < "3.0.0" and sys_platform == "win32" or python_version == "3.6" and sys_platform == "win32" and python_full_version >= "3.5.0" or python_version >= "3.7" and python_full_version < "3.0.0" and sys_platform == "win32" or python_version >= "3.7" and sys_platform == "win32" and python_full_version >= "3.5.0"
|
colorama==0.4.3; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6" or python_version >= "3.6" and python_full_version < "3.0.0" and sys_platform == "win32" or python_full_version >= "3.5.0" and python_version >= "3.6" and sys_platform == "win32" or python_version >= "3.6" and python_full_version < "3.0.0" and python_version < "4.0" and sys_platform == "win32" or python_version >= "3.6" and python_version < "4.0" and sys_platform == "win32" and python_full_version >= "3.5.0" or python_version >= "3.6" and python_full_version < "3.0.0" and platform_system == "Windows" or python_version >= "3.6" and python_full_version >= "3.5.0" and platform_system == "Windows" or python_version == "3.6" and python_full_version < "3.0.0" and sys_platform == "win32" or python_version == "3.6" and sys_platform == "win32" and python_full_version >= "3.5.0" or python_version >= "3.7" and python_full_version < "3.0.0" and sys_platform == "win32" or python_version >= "3.7" and sys_platform == "win32" and python_full_version >= "3.5.0"
|
||||||
configargparse==1.5.3; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6"
|
configargparse==1.5.3; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6"
|
||||||
configobj==5.0.6; python_version >= "3.6"
|
configobj==5.0.6; python_version >= "3.6"
|
||||||
coverage==6.1.2; python_version >= "3.6" or python_version >= "3.6"
|
coverage==6.2; python_version >= "3.6" or python_version >= "3.6"
|
||||||
crashtest==0.3.1; python_version >= "3.6" and python_version < "4.0"
|
crashtest==0.3.1; python_version >= "3.6" and python_version < "4.0"
|
||||||
cryptography==36.0.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3.6" or python_version >= "3.6" or python_version >= "3.6" and python_version < "4.0" or python_version >= "3.6" and python_version < "4.0" and sys_platform == "linux"
|
cryptography==36.0.1; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3.6" or python_version >= "3.6" or python_version >= "3.6" and python_version < "4.0" or python_version >= "3.6" and python_version < "4.0" and sys_platform == "linux"
|
||||||
cython==0.29.24; (python_version >= "2.6" and python_full_version < "3.0.0") or (python_full_version >= "3.3.0")
|
cython==0.29.26; (python_version >= "2.6" and python_full_version < "3.0.0") or (python_full_version >= "3.3.0")
|
||||||
dataclasses==0.8; python_version >= "3.6" and python_version < "3.7"
|
dataclasses==0.8; python_version >= "3.6" and python_version < "3.7"
|
||||||
decorator==5.1.0; python_version == "3.6" or python_version > "3.6" or python_version >= "3.5" or python_version >= "3.7"
|
decorator==5.1.1; python_version == "3.6" or python_version > "3.6" or python_version >= "3.5" or python_version >= "3.7"
|
||||||
deprecated==1.2.13; python_version >= "3.6" and python_full_version < "3.0.0" or python_version >= "3.6" and python_full_version >= "3.4.0"
|
deprecated==1.2.13; python_version >= "3.6" and python_full_version < "3.0.0" or python_version >= "3.6" and python_full_version >= "3.4.0"
|
||||||
distlib==0.3.3; python_version >= "3.6" and python_full_version < "3.0.0" and python_version < "4.0" or python_version >= "3.6" and python_version < "4.0" and python_full_version >= "3.4.0" or python_version >= "3.6"
|
distlib==0.3.4; python_version >= "3.6" and python_full_version < "3.0.0" and python_version < "4.0" or python_version >= "3.6" and python_version < "4.0" and python_full_version >= "3.4.0" or python_version >= "3.6"
|
||||||
distro==1.6.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6" or python_version >= "3.6"
|
distro==1.6.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6" or python_version >= "3.6"
|
||||||
dns-lexicon==3.8.3; python_version >= "3.6" and python_version < "4.0"
|
dns-lexicon==3.8.5; python_version >= "3.6" and python_version < "4.0"
|
||||||
dnspython==2.1.0; python_version >= "3.6"
|
dnspython==2.2.0; python_version >= "3.6" and python_version < "4.0"
|
||||||
docker-compose==1.26.2; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6"
|
docker-compose==1.26.2; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6"
|
||||||
docker==4.2.2; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6"
|
docker==4.2.2; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6"
|
||||||
dockerpty==0.4.1; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6"
|
dockerpty==0.4.1; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6"
|
||||||
@@ -52,12 +52,12 @@ docutils==0.15.2; python_version >= "3.6" and python_full_version < "3.0.0" or p
|
|||||||
entrypoints==0.3; python_version >= "3.6" and python_version < "4.0"
|
entrypoints==0.3; python_version >= "3.6" and python_version < "4.0"
|
||||||
execnet==1.9.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6"
|
execnet==1.9.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6"
|
||||||
fabric==2.6.0; python_version >= "3.6"
|
fabric==2.6.0; python_version >= "3.6"
|
||||||
filelock==3.4.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_version >= "3.6" and python_full_version >= "3.5.0" or python_version >= "3.6" and python_full_version < "3.0.0" and python_version < "4.0" or python_version >= "3.6" and python_version < "4.0" and python_full_version >= "3.4.0" or python_version >= "3.6" and python_version < "4.0"
|
filelock==3.4.1; python_version >= "3.6" and python_full_version < "3.0.0" or python_version >= "3.6" and python_full_version >= "3.5.0" or python_version >= "3.6" and python_full_version < "3.0.0" and python_version < "4.0" or python_version >= "3.6" and python_version < "4.0" and python_full_version >= "3.4.0" or python_version >= "3.6" and python_version < "4.0"
|
||||||
google-api-core==2.2.2; python_version >= "3.6"
|
google-api-core==2.4.0; python_version >= "3.6"
|
||||||
google-api-python-client==2.31.0; python_version >= "3.6"
|
google-api-python-client==2.36.0; python_version >= "3.6"
|
||||||
google-auth-httplib2==0.1.0; python_version >= "3.6"
|
google-auth-httplib2==0.1.0; python_version >= "3.6"
|
||||||
google-auth==2.3.3; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3.6"
|
google-auth==2.3.3; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3.6"
|
||||||
googleapis-common-protos==1.53.0; python_version >= "3.6"
|
googleapis-common-protos==1.54.0; python_version >= "3.6"
|
||||||
html5lib==1.1; python_version >= "3.6" and python_full_version < "3.0.0" and python_version < "4.0" or python_version >= "3.6" and python_version < "4.0" and python_full_version >= "3.5.0"
|
html5lib==1.1; python_version >= "3.6" and python_full_version < "3.0.0" and python_version < "4.0" or python_version >= "3.6" and python_version < "4.0" and python_full_version >= "3.5.0"
|
||||||
httplib2==0.20.2; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6"
|
httplib2==0.20.2; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6"
|
||||||
idna==3.3; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3.6" or python_version >= "3.6" and python_version < "4.0"
|
idna==3.3; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3.6" or python_version >= "3.6" and python_version < "4.0"
|
||||||
@@ -68,58 +68,60 @@ iniconfig==1.1.1; python_version >= "3.6"
|
|||||||
invoke==1.6.0; python_version >= "3.6"
|
invoke==1.6.0; python_version >= "3.6"
|
||||||
ipdb==0.13.9; python_version >= "3.6"
|
ipdb==0.13.9; python_version >= "3.6"
|
||||||
ipython-genutils==0.2.0
|
ipython-genutils==0.2.0
|
||||||
ipython==7.16.1; python_version == "3.6"
|
ipython==7.16.3; python_version == "3.6"
|
||||||
ipython==7.29.0; python_version >= "3.7"
|
ipython==7.31.1; python_version >= "3.7"
|
||||||
isodate==0.6.0; python_version >= "3.6"
|
isodate==0.6.1; python_version >= "3.6"
|
||||||
isort==5.8.0; python_version >= "3.6" and python_version < "4.0"
|
isort==5.8.0; python_version >= "3.6" and python_version < "4.0"
|
||||||
jedi==0.18.1; python_version == "3.6" or python_version >= "3.7"
|
jedi==0.17.2; python_version == "3.6" and python_full_version < "3.0.0" or python_version == "3.6" and python_full_version >= "3.5.0"
|
||||||
|
jedi==0.18.1; python_version >= "3.7"
|
||||||
jeepney==0.7.1; python_version >= "3.6" and python_version < "4.0" and sys_platform == "linux"
|
jeepney==0.7.1; python_version >= "3.6" and python_version < "4.0" and sys_platform == "linux"
|
||||||
jinja2==3.0.3; python_version >= "3.6" or python_version >= "3.6"
|
jinja2==3.0.3; python_version >= "3.6" or python_version >= "3.6"
|
||||||
jmespath==0.10.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.3.0" and python_version >= "3.6"
|
jmespath==0.10.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.3.0" and python_version >= "3.6"
|
||||||
josepy==1.9.0; python_version >= "3.6"
|
josepy==1.12.0; python_version >= "3.6"
|
||||||
jsonlines==2.0.0; python_version >= "3.6"
|
jsonlines==3.0.0; python_version >= "3.6"
|
||||||
jsonpickle==2.0.0; python_version >= "3.6"
|
jsonpickle==2.1.0; python_version >= "3.6"
|
||||||
jsonschema==3.2.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6"
|
jsonschema==3.2.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6"
|
||||||
keyring==22.3.0; python_version >= "3.6" and python_version < "4.0" or python_version >= "3.6"
|
keyring==22.3.0; python_version >= "3.6" and python_version < "4.0" or python_version >= "3.6"
|
||||||
lazy-object-proxy==1.6.0; python_version >= "3.6" and python_full_version < "3.0.0" and python_version < "4.0" or python_version >= "3.6" and python_version < "4.0" and python_full_version >= "3.6.0"
|
lazy-object-proxy==1.7.1; python_version >= "3.6" and python_version < "4.0"
|
||||||
lockfile==0.12.2
|
lockfile==0.12.2
|
||||||
markupsafe==2.0.1; python_version >= "3.6"
|
markupsafe==2.0.1; python_version >= "3.6"
|
||||||
matplotlib-inline==0.1.3; python_version >= "3.7"
|
matplotlib-inline==0.1.3; python_version >= "3.7"
|
||||||
mccabe==0.6.1; python_version >= "3.6" and python_version < "4.0"
|
mccabe==0.6.1; python_version >= "3.6" and python_version < "4.0"
|
||||||
mock==4.0.3; python_version >= "3.6"
|
mock==4.0.3; python_version >= "3.6"
|
||||||
msgpack==1.0.2; python_version >= "3.6" and python_version < "4.0"
|
msgpack==1.0.3; python_version >= "3.6" and python_version < "4.0"
|
||||||
msrest==0.6.21; python_version >= "3.6"
|
msrest==0.6.21; python_version >= "3.6"
|
||||||
mypy-extensions==0.4.3; python_version >= "3.6"
|
mypy-extensions==0.4.3; python_version >= "3.6"
|
||||||
mypy==0.910; python_version >= "3.6"
|
mypy==0.931; python_version >= "3.6"
|
||||||
oauth2client==4.1.3; python_version >= "3.6"
|
oauth2client==4.1.3; python_version >= "3.6"
|
||||||
oauthlib==3.1.1; python_version >= "3.6" and python_full_version < "3.0.0" or python_version >= "3.6" and python_full_version >= "3.4.0"
|
oauthlib==3.1.1; python_version >= "3.6" and python_full_version < "3.0.0" or python_version >= "3.6" and python_full_version >= "3.4.0"
|
||||||
packaging==20.9; python_version >= "3.6" and python_full_version < "3.0.0" and python_version < "4.0" or python_version >= "3.6" and python_version < "4.0" and python_full_version >= "3.4.0" or python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6" or python_version >= "3.6" and python_full_version >= "3.5.0"
|
packaging==20.9; python_version >= "3.6" and python_full_version < "3.0.0" and python_version < "4.0" or python_version >= "3.6" and python_version < "4.0" and python_full_version >= "3.4.0" or python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6" or python_version >= "3.6" and python_full_version >= "3.5.0"
|
||||||
paramiko==2.8.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6" or python_version >= "3.6"
|
paramiko==2.9.2; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6" or python_version >= "3.6"
|
||||||
parsedatetime==2.6; python_version >= "3.6"
|
parsedatetime==2.6; python_version >= "3.6"
|
||||||
parso==0.8.2; python_version == "3.6"
|
parso==0.7.1; python_version == "3.6" and python_full_version < "3.0.0" or python_version == "3.6" and python_full_version >= "3.5.0"
|
||||||
|
parso==0.8.3; python_version >= "3.7"
|
||||||
pathlib2==2.3.6; python_version >= "3.6"
|
pathlib2==2.3.6; python_version >= "3.6"
|
||||||
pexpect==4.8.0; python_version >= "3.6" and python_version < "4.0" or python_version == "3.6" and sys_platform != "win32" or python_version >= "3.7" and sys_platform != "win32"
|
pexpect==4.8.0; python_version >= "3.6" and python_version < "4.0" or python_version == "3.6" and sys_platform != "win32" or python_version >= "3.7" and sys_platform != "win32"
|
||||||
pickleshare==0.7.5; python_version == "3.6" or python_version >= "3.7"
|
pickleshare==0.7.5; python_version == "3.6" or python_version >= "3.7"
|
||||||
pip==21.3.1; python_version >= "3.6"
|
pip==21.3.1; python_version >= "3.6"
|
||||||
pkginfo==1.8.1; python_version >= "3.6" and python_version < "4.0" or python_version >= "3.6"
|
pkginfo==1.8.2; python_version >= "3.6" and python_version < "4.0" or python_version >= "3.6"
|
||||||
platformdirs==2.4.0; python_version >= "3.6" and python_version < "4.0"
|
platformdirs==2.4.0; python_version >= "3.6" and python_version < "4.0"
|
||||||
pluggy==1.0.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_version >= "3.6" and python_full_version >= "3.5.0" or python_version >= "3.6"
|
pluggy==1.0.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_version >= "3.6" and python_full_version >= "3.5.0" or python_version >= "3.6"
|
||||||
ply==3.11; python_version >= "3.6"
|
ply==3.11; python_version >= "3.6"
|
||||||
poetry-core==1.1.0a6; python_version >= "3.6" and python_version < "4.0"
|
poetry-core==1.1.0a6; python_version >= "3.6" and python_version < "4.0"
|
||||||
poetry==1.2.0a2; python_version >= "3.6" and python_version < "4.0"
|
poetry==1.2.0a2; python_version >= "3.6" and python_version < "4.0"
|
||||||
prompt-toolkit==3.0.22; python_version == "3.6" and python_full_version >= "3.6.2" or python_version >= "3.7" and python_full_version >= "3.6.2"
|
prompt-toolkit==3.0.24; python_version == "3.6" and python_full_version >= "3.6.2" or python_version >= "3.7" and python_full_version >= "3.6.2"
|
||||||
protobuf==3.19.1; python_version >= "3.6"
|
protobuf==3.19.3; python_version >= "3.6"
|
||||||
ptyprocess==0.7.0; python_version >= "3.6" and python_version < "4.0"
|
ptyprocess==0.7.0; python_version >= "3.6" and python_version < "4.0"
|
||||||
py==1.11.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6"
|
py==1.11.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6"
|
||||||
pyasn1-modules==0.2.8; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3.6" or python_version >= "3.6"
|
pyasn1-modules==0.2.8; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3.6" or python_version >= "3.6"
|
||||||
pyasn1==0.4.8; python_version >= "3.6" and python_version < "4" or python_version >= "3.6"
|
pyasn1==0.4.8; python_version >= "3.6" and python_version < "4" or python_version >= "3.6"
|
||||||
pycparser==2.21; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6"
|
pycparser==2.21; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6"
|
||||||
pygithub==1.55; python_version >= "3.6"
|
pygithub==1.55; python_version >= "3.6"
|
||||||
pygments==2.10.0; python_version >= "3.6" or python_version == "3.6" or python_version >= "3.7"
|
pygments==2.11.2; python_version >= "3.6" or python_version == "3.6" or python_version >= "3.7"
|
||||||
pyjwt==2.3.0; python_version >= "3.6"
|
pyjwt==2.3.0; python_version >= "3.6"
|
||||||
pylev==1.4.0; python_version >= "3.6" and python_version < "4.0"
|
pylev==1.4.0; python_version >= "3.6" and python_version < "4.0"
|
||||||
pylint==2.11.1; python_version >= "3.6" and python_version < "4.0"
|
pylint==2.12.0; python_version >= "3.6" and python_version < "4.0"
|
||||||
pynacl==1.4.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_version >= "3.6" and python_full_version >= "3.4.0"
|
pynacl==1.5.0; python_version >= "3.6" or python_version >= "3.6"
|
||||||
pynsist==2.7; python_version >= "3.6"
|
pynsist==2.7; python_version >= "3.6"
|
||||||
pyopenssl==21.0.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3.6"
|
pyopenssl==21.0.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3.6"
|
||||||
pyparsing==3.0.6; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6" or python_version >= "3.6" or python_version >= "3.6" and python_full_version < "3.0.0" and python_version < "4.0" or python_version >= "3.6" and python_version < "4.0" and python_full_version >= "3.4.0"
|
pyparsing==3.0.6; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6" or python_version >= "3.6" or python_version >= "3.6" and python_full_version < "3.0.0" and python_version < "4.0" or python_version >= "3.6" and python_version < "4.0" and python_full_version >= "3.4.0"
|
||||||
@@ -127,36 +129,36 @@ pypiwin32==223; sys_platform == "win32" and python_version >= "3.6" and (python_
|
|||||||
pyrfc3339==1.1; python_version >= "3.6"
|
pyrfc3339==1.1; python_version >= "3.6"
|
||||||
pyrsistent==0.18.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6"
|
pyrsistent==0.18.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6"
|
||||||
pytest-cov==3.0.0; python_version >= "3.6" or python_version >= "3.6"
|
pytest-cov==3.0.0; python_version >= "3.6" or python_version >= "3.6"
|
||||||
pytest-forked==1.3.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6"
|
pytest-forked==1.4.0; python_version >= "3.6"
|
||||||
pytest-xdist==2.4.0; python_version >= "3.6" or python_version >= "3.6"
|
pytest-xdist==2.5.0; python_version >= "3.6" or python_version >= "3.6"
|
||||||
pytest==6.2.5; python_version >= "3.6" or python_version >= "3.6" or python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6"
|
pytest==6.2.5; python_version >= "3.6" or python_version >= "3.6"
|
||||||
python-augeas==1.1.0; python_version >= "3.6"
|
python-augeas==1.1.0; python_version >= "3.6"
|
||||||
python-dateutil==2.8.2; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.3.0" and python_version >= "3.6"
|
python-dateutil==2.8.2; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.3.0" and python_version >= "3.6"
|
||||||
python-digitalocean==1.17.0; python_version >= "3.6"
|
python-digitalocean==1.17.0; python_version >= "3.6"
|
||||||
python-dotenv==0.19.2; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6"
|
python-dotenv==0.19.2; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6"
|
||||||
pytz==2021.3; python_version >= "3.6" and python_full_version < "3.0.0" or python_version >= "3.6" and python_full_version >= "3.4.0" or python_version >= "3.6"
|
pytz==2021.3; python_version >= "3.6" and python_full_version < "3.0.0" or python_version >= "3.6" and python_full_version >= "3.4.0" or python_version >= "3.6"
|
||||||
pywin32-ctypes==0.2.0; python_version >= "3.6" and python_version < "4.0" and sys_platform == "win32"
|
pywin32-ctypes==0.2.0; python_version >= "3.6" and python_version < "4.0" and sys_platform == "win32"
|
||||||
pywin32==302; sys_platform == "win32" and python_version >= "3.6" or sys_platform == "win32" and python_version >= "3.6" and (python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6")
|
pywin32==303; sys_platform == "win32" and python_version >= "3.6" or sys_platform == "win32" and python_version >= "3.6" and (python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6")
|
||||||
pyyaml==5.4.1; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3.6" or python_version >= "3.6" and python_full_version < "3.0.0" and python_version < "4.0" or python_version >= "3.6" and python_version < "4.0" and python_full_version >= "3.6.0"
|
pyyaml==5.4.1; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3.6" or python_version >= "3.6" and python_full_version < "3.0.0" and python_version < "4.0" or python_version >= "3.6" and python_version < "4.0" and python_full_version >= "3.6.0"
|
||||||
readme-renderer==30.0; python_version >= "3.6"
|
readme-renderer==32.0; python_version >= "3.6"
|
||||||
requests-download==0.1.2; python_version >= "3.6"
|
requests-download==0.1.2; python_version >= "3.6"
|
||||||
requests-file==1.5.1; python_version >= "3.6" and python_version < "4.0"
|
requests-file==1.5.1; python_version >= "3.6" and python_version < "4.0"
|
||||||
requests-oauthlib==1.3.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_version >= "3.6" and python_full_version >= "3.4.0"
|
requests-oauthlib==1.3.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_version >= "3.6" and python_full_version >= "3.4.0"
|
||||||
requests-toolbelt==0.9.1; python_version >= "3.6" and python_version < "4.0" or python_version >= "3.6" or python_version >= "3.6"
|
requests-toolbelt==0.9.1; python_version >= "3.6" and python_version < "4.0" or python_version >= "3.6" or python_version >= "3.6"
|
||||||
requests==2.26.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3.6" or python_version >= "3.6" and python_full_version < "3.0.0" and python_version < "4.0" or python_version >= "3.6" and python_version < "4.0" and python_full_version >= "3.6.0"
|
requests==2.27.1; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3.6" or python_version >= "3.6" and python_full_version < "3.0.0" and python_version < "4.0" or python_version >= "3.6" and python_version < "4.0" and python_full_version >= "3.6.0"
|
||||||
rfc3986==1.5.0; python_version >= "3.6"
|
rfc3986==1.5.0; python_version >= "3.6"
|
||||||
rsa==4.7.2; python_version >= "3.6" and python_version < "4" or python_version >= "3.5" and python_version < "4" and (python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3.6")
|
rsa==4.7.2; python_version >= "3.6" and python_version < "4" or python_version >= "3.5" and python_version < "4" and (python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3.6")
|
||||||
s3transfer==0.5.0; python_version >= "3.6"
|
s3transfer==0.5.0; python_version >= "3.6"
|
||||||
secretstorage==3.3.1; python_version >= "3.6" and python_version < "4.0" and sys_platform == "linux"
|
secretstorage==3.3.1; python_version >= "3.6" and python_version < "4.0" and sys_platform == "linux"
|
||||||
semantic-version==2.8.5; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6"
|
semantic-version==2.8.5; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6"
|
||||||
setuptools-rust==0.12.1; python_version >= "3.6"
|
setuptools-rust==0.12.1; python_version >= "3.6"
|
||||||
setuptools==59.2.0; python_version >= "3.6" or python_version >= "3.6" or python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6" or python_full_version >= "3.4.0" and python_version >= "3.6" or python_full_version >= "3.6.0" and python_version >= "3.6" or python_version == "3.6" or python_version >= "3.7" or python_version >= "3.6" and python_version < "4.0"
|
setuptools==59.6.0; python_version >= "3.6" or python_version >= "3.6" or python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6" or python_full_version >= "3.4.0" and python_version >= "3.6" or python_full_version >= "3.6.0" and python_version >= "3.6" or python_version == "3.6" or python_version >= "3.7" or python_version >= "3.6" and python_version < "4.0"
|
||||||
shellingham==1.4.0; python_version >= "3.6" and python_version < "4.0"
|
shellingham==1.4.0; python_version >= "3.6" and python_version < "4.0"
|
||||||
six==1.16.0; python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.3.0" or python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6" or python_full_version >= "3.6.0" and python_version >= "3.6" or python_full_version >= "3.3.0" and python_version >= "3.6" or python_version >= "3.6" and python_full_version >= "3.5.0" or python_version >= "3.6" and python_full_version < "3.0.0" and python_version < "4.0" or python_version >= "3.6" and python_version < "4.0" and python_full_version >= "3.5.0" or python_version >= "3.6" and python_version < "4.0" and python_full_version >= "3.4.0" or python_version >= "3.6" and python_version < "4.0" and python_full_version >= "3.3.0" or python_version == "3.6" and python_full_version < "3.0.0" or python_version == "3.6" and python_full_version >= "3.3.0"
|
six==1.16.0; python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.3.0" or python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6" or python_full_version >= "3.6.0" and python_version >= "3.6" or python_full_version >= "3.3.0" and python_version >= "3.6" or python_version >= "3.6" and python_full_version >= "3.5.0" or python_version >= "3.6" and python_full_version < "3.0.0" and python_version < "4.0" or python_version >= "3.6" and python_version < "4.0" and python_full_version >= "3.5.0" or python_version >= "3.6" and python_version < "4.0" and python_full_version >= "3.4.0" or python_version >= "3.6" and python_version < "4.0" and python_full_version >= "3.3.0" or python_version == "3.6" and python_full_version < "3.0.0" or python_version == "3.6" and python_full_version >= "3.3.0"
|
||||||
snowballstemmer==2.2.0; python_version >= "3.6"
|
snowballstemmer==2.2.0; python_version >= "3.6"
|
||||||
soupsieve==2.3.1; python_full_version > "3.0.0" and python_version >= "3.6"
|
soupsieve==2.3.1; python_full_version > "3.0.0" and python_version >= "3.6"
|
||||||
sphinx-rtd-theme==1.0.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_version >= "3.6" and python_full_version >= "3.4.0"
|
sphinx-rtd-theme==1.0.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_version >= "3.6" and python_full_version >= "3.4.0"
|
||||||
sphinx==4.3.0; python_version >= "3.6" or python_version >= "3.6" and python_full_version < "3.0.0" or python_version >= "3.6" and python_full_version >= "3.4.0"
|
sphinx==4.3.2; python_version >= "3.6" or python_version >= "3.6" and python_full_version < "3.0.0" or python_version >= "3.6" and python_full_version >= "3.4.0"
|
||||||
sphinxcontrib-applehelp==1.0.2; python_version >= "3.6"
|
sphinxcontrib-applehelp==1.0.2; python_version >= "3.6"
|
||||||
sphinxcontrib-devhelp==1.0.2; python_version >= "3.6"
|
sphinxcontrib-devhelp==1.0.2; python_version >= "3.6"
|
||||||
sphinxcontrib-htmlhelp==2.0.0; python_version >= "3.6"
|
sphinxcontrib-htmlhelp==2.0.0; python_version >= "3.6"
|
||||||
@@ -166,32 +168,33 @@ sphinxcontrib-serializinghtml==1.1.5; python_version >= "3.6"
|
|||||||
texttable==1.6.4; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6"
|
texttable==1.6.4; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6"
|
||||||
tldextract==3.1.2; python_version >= "3.6" and python_version < "4.0"
|
tldextract==3.1.2; python_version >= "3.6" and python_version < "4.0"
|
||||||
toml==0.10.2; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.3.0" and python_version >= "3.6" or python_version == "3.6" and python_full_version < "3.0.0" or python_version > "3.6" and python_full_version < "3.0.0" or python_version == "3.6" and python_full_version >= "3.3.0" or python_version > "3.6" and python_full_version >= "3.3.0" or python_version >= "3.6" and python_full_version < "3.0.0" and python_version < "4.0" or python_version >= "3.6" and python_version < "4.0" and python_full_version >= "3.3.0" or python_version >= "3.6" and python_full_version >= "3.5.0"
|
toml==0.10.2; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.3.0" and python_version >= "3.6" or python_version == "3.6" and python_full_version < "3.0.0" or python_version > "3.6" and python_full_version < "3.0.0" or python_version == "3.6" and python_full_version >= "3.3.0" or python_version > "3.6" and python_full_version >= "3.3.0" or python_version >= "3.6" and python_full_version < "3.0.0" and python_version < "4.0" or python_version >= "3.6" and python_version < "4.0" and python_full_version >= "3.3.0" or python_version >= "3.6" and python_full_version >= "3.5.0"
|
||||||
tomli==1.2.2; python_version >= "3.6"
|
tomli==1.2.3; python_version >= "3.6"
|
||||||
tomlkit==0.7.2; python_version >= "3.6" and python_full_version < "3.0.0" and python_version < "4.0" or python_version >= "3.6" and python_version < "4.0" and python_full_version >= "3.5.0"
|
tomlkit==0.8.0; python_version >= "3.6" and python_version < "4.0"
|
||||||
tox==3.24.4; python_version >= "3.6" and python_full_version < "3.0.0" or python_version >= "3.6" and python_full_version >= "3.5.0"
|
tox==3.24.5; python_version >= "3.6" and python_full_version < "3.0.0" or python_version >= "3.6" and python_full_version >= "3.5.0"
|
||||||
tqdm==4.62.3; python_version >= "3.6" and python_full_version < "3.0.0" or python_version >= "3.6" and python_full_version >= "3.4.0"
|
tqdm==4.62.3; python_version >= "3.6" and python_full_version < "3.0.0" or python_version >= "3.6" and python_full_version >= "3.4.0"
|
||||||
traitlets==4.3.3
|
traitlets==4.3.3
|
||||||
twine==3.3.0; python_version >= "3.6"
|
twine==3.3.0; python_version >= "3.6"
|
||||||
typed-ast==1.4.3; python_version >= "3.6" and python_version < "3.8" or implementation_name == "cpython" and python_version < "3.8" and python_version >= "3.6"
|
typed-ast==1.5.1; python_version >= "3.6" and python_version < "3.8" or implementation_name == "cpython" and python_version < "3.8" and python_version >= "3.6"
|
||||||
types-cryptography==3.3.8; python_version >= "3.6"
|
types-cryptography==3.3.14; python_version >= "3.6"
|
||||||
types-enum34==1.1.1; python_version >= "3.6"
|
types-enum34==1.1.7; python_version >= "3.6"
|
||||||
types-ipaddress==1.0.1; python_version >= "3.6"
|
types-ipaddress==1.0.7; python_version >= "3.6"
|
||||||
types-mock==4.0.3; python_version >= "3.6"
|
types-mock==4.0.8; python_version >= "3.6"
|
||||||
types-pyopenssl==21.0.0; python_version >= "3.6"
|
types-pyopenssl==21.0.3; python_version >= "3.6"
|
||||||
types-pyrfc3339==1.1.0; python_version >= "3.6"
|
types-pyrfc3339==1.1.1; python_version >= "3.6"
|
||||||
types-python-dateutil==2.8.2; python_version >= "3.6"
|
types-python-dateutil==2.8.7; python_version >= "3.6"
|
||||||
types-pytz==2021.3.0; python_version >= "3.6"
|
types-pytz==2021.3.4; python_version >= "3.6"
|
||||||
types-requests==2.26.0; python_version >= "3.6"
|
types-requests==2.27.7; python_version >= "3.6"
|
||||||
types-setuptools==57.4.2; python_version >= "3.6"
|
types-setuptools==57.4.7; python_version >= "3.6"
|
||||||
types-six==1.16.2; python_version >= "3.6"
|
types-six==1.16.10; python_version >= "3.6"
|
||||||
typing-extensions==4.0.0; python_version >= "3.6" or python_version >= "3.6" and python_version < "3.10"
|
types-urllib3==1.26.7; python_version >= "3.6"
|
||||||
|
typing-extensions==4.0.1; python_version >= "3.6" or python_version >= "3.6" and python_version < "3.10" or python_version < "3.8" and python_version >= "3.6"
|
||||||
uritemplate==4.1.1; python_version >= "3.6"
|
uritemplate==4.1.1; python_version >= "3.6"
|
||||||
urllib3==1.26.7; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version < "4" and python_version >= "3.6" or python_full_version >= "3.5.0" and python_version < "4" and python_version >= "3.6"
|
urllib3==1.26.8; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version < "4" and python_version >= "3.6" or python_full_version >= "3.5.0" and python_version < "4" and python_version >= "3.6"
|
||||||
virtualenv==20.4.4; python_version >= "3.6" and python_full_version < "3.0.0" and python_version < "4.0" or python_version >= "3.6" and python_version < "4.0" and python_full_version >= "3.4.0" or python_version >= "3.6" and python_full_version < "3.0.0" or python_version >= "3.6" and python_full_version >= "3.5.0"
|
virtualenv==20.4.4; python_version >= "3.6" and python_full_version < "3.0.0" and python_version < "4.0" or python_version >= "3.6" and python_version < "4.0" and python_full_version >= "3.4.0" or python_version >= "3.6" and python_full_version < "3.0.0" or python_version >= "3.6" and python_full_version >= "3.5.0"
|
||||||
wcwidth==0.2.5; python_version == "3.6" and python_full_version >= "3.6.2"
|
wcwidth==0.2.5; python_version == "3.6" and python_full_version >= "3.6.2"
|
||||||
webencodings==0.5.1; python_version >= "3.6" and python_full_version < "3.0.0" and python_version < "4.0" or python_version >= "3.6" and python_version < "4.0" and python_full_version >= "3.5.0" or python_version >= "3.6"
|
webencodings==0.5.1; python_version >= "3.6" and python_full_version < "3.0.0" and python_version < "4.0" or python_version >= "3.6" and python_version < "4.0" and python_full_version >= "3.5.0" or python_version >= "3.6"
|
||||||
websocket-client==0.59.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6" or python_full_version >= "3.5.0" and python_version >= "3.6"
|
websocket-client==0.59.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6" or python_full_version >= "3.5.0" and python_version >= "3.6"
|
||||||
wheel==0.37.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_version >= "3.6" and python_full_version >= "3.5.0"
|
wheel==0.37.1; python_version >= "3.6" and python_full_version < "3.0.0" or python_version >= "3.6" and python_full_version >= "3.5.0"
|
||||||
wrapt==1.13.3; python_version >= "3.6" and python_full_version < "3.0.0" or python_version >= "3.6" and python_full_version >= "3.5.0" or python_version >= "3.6" and python_full_version < "3.0.0" and python_version < "4.0" or python_version >= "3.6" and python_version < "4.0" and python_full_version >= "3.5.0"
|
wrapt==1.13.3; python_version >= "3.6" and python_full_version < "3.0.0" or python_version >= "3.6" and python_full_version >= "3.5.0" or python_version >= "3.6" and python_full_version < "3.0.0" and python_version < "4.0" or python_version >= "3.6" and python_version < "4.0" and python_full_version >= "3.5.0"
|
||||||
yarg==0.1.9; python_version >= "3.6"
|
yarg==0.1.9; python_version >= "3.6"
|
||||||
zipp==3.6.0; python_version >= "3.6" and python_full_version < "3.0.0" and python_version < "3.8" or python_version >= "3.6" and python_version < "3.8" and python_full_version >= "3.5.0" or python_version >= "3.6" and python_full_version < "3.0.0" and python_version < "3.7" or python_version >= "3.6" and python_version < "3.7" and python_full_version >= "3.4.0"
|
zipp==3.6.0; python_version >= "3.6" and python_full_version < "3.0.0" and python_version < "3.8" or python_version >= "3.6" and python_version < "3.8" and python_full_version >= "3.5.0" or python_version >= "3.6" and python_full_version < "3.0.0" and python_version < "3.7" or python_version >= "3.6" and python_version < "3.7" and python_full_version >= "3.4.0"
|
||||||
|
|||||||
Reference in New Issue
Block a user