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:
Adrien Ferrand
2022-01-24 15:16:19 -08:00
committed by GitHub
co-authored by Brad Warren
parent 7198f43008
commit dac0b2c187
32 changed files with 398 additions and 325 deletions
+4 -4
View File
@@ -54,9 +54,9 @@ class Account:
cross-machine migration scenarios.
"""
creation_dt = acme_fields.RFC3339Field("creation_dt")
creation_host = jose.Field("creation_host")
register_to_eff = jose.Field("register_to_eff", omitempty=True)
creation_dt: datetime.datetime = acme_fields.rfc3339("creation_dt")
creation_host: str = jose.field("creation_host")
register_to_eff: str = jose.field("register_to_eff", omitempty=True)
def __init__(self, regr: messages.RegistrationResource, key: jose.JWK,
meta: Optional['Meta'] = None) -> None:
@@ -135,7 +135,7 @@ class RegistrationResourceWithNewAuthzrURI(messages.RegistrationResource):
continue to write out this field for some time so older
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):
+8 -7
View File
@@ -6,6 +6,7 @@ from typing import Dict
from typing import Iterable
from typing import List
from typing import Optional
from typing import Sequence
from typing import Tuple
from typing import Type
@@ -272,7 +273,7 @@ class AuthHandler:
self.auth.cleanup(achalls)
def _challenge_factory(self, authzr: messages.AuthorizationResource,
path: List[int]) -> List[achallenges.AnnotatedChallenge]:
path: Sequence[int]) -> List[achallenges.AnnotatedChallenge]:
"""Construct Namedtuple Challenges
: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],
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.
.. 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],
preferences: List[Type[challenges.Challenge]],
combinations: Tuple[List[int], ...]
) -> List[int]:
combinations: Tuple[Tuple[int, ...], ...]
) -> Tuple[int, ...]:
"""Find challenge path with server hints.
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
best_combo: Optional[List[int]] = None
best_combo: Optional[Tuple[int, ...]] = None
# Set above completing all of the available challenges
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],
preferences: List[Type[challenges.Challenge]]) -> List[int]:
preferences: List[Type[challenges.Challenge]]) -> Tuple[int, ...]:
"""Find challenge path without server hints.
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:
raise _report_no_chall_path(challbs)
return path
return tuple(path)
def _report_no_chall_path(challbs: List[messages.ChallengeBody]) -> errors.AuthorizationError:
+1 -1
View File
@@ -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 "
"acme.client.BackwardsCompatibleClientV2")
except messages.Error as e:
if e.code in ('invalidEmail', 'invalidContact'):
if e.code in ("invalidEmail", "invalidContact"):
if config.noninteractive_mode:
msg = ("The ACME server believes %s is an invalid email address. "
"Please ensure it is a valid email and attempt "
+1 -1
View File
@@ -98,7 +98,7 @@ permitted by DNS standards.)
super().__init__(*args, **kwargs)
self.reverter = reverter.Reverter(self.config)
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_any_challenge = False
@@ -30,7 +30,7 @@ logger = logging.getLogger(__name__)
if TYPE_CHECKING:
ServedType = DefaultDict[
acme_standalone.BaseDualNetworkedServers,
Set[achallenges.KeyAuthorizationAnnotatedChallenge]
Set[achallenges.AnnotatedChallenge]
]
+4 -4
View File
@@ -20,7 +20,7 @@ from certbot import crypto_util
from certbot import errors
from certbot import interfaces
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 os
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 = "
'{"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 "
"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 "
@@ -105,7 +105,7 @@ to serve all files under specified web root ({0})."""
def prepare(self) -> None: # pylint: disable=missing-function-docstring
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._create_challenge_dirs()
@@ -257,7 +257,7 @@ to serve all files under specified web root ({0})."""
self.performed[root_path].add(achall)
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:
root_path = self.full_roots.get(achall.domain, None)
if root_path is not None:
+1 -1
View File
@@ -29,7 +29,7 @@ class Reporter:
_msg_type = collections.namedtuple('_msg_type', 'priority text on_crash')
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
def add_message(self, msg: str, priority: int, on_crash: bool = True) -> None:
+2 -2
View File
@@ -18,8 +18,8 @@ try:
from urllib3.connectionpool import HTTPConnectionPool
except ImportError:
# Stub imports for oldest requirements, that will never be used in snaps.
HTTPConnection = object
HTTPConnectionPool = object
HTTPConnection = object # type: ignore[misc,assignment]
HTTPConnectionPool = object # type: ignore[misc,assignment]
_ARCH_TRIPLET_MAP = {
+4 -1
View File
@@ -290,8 +290,11 @@ def make_key(bits: int = 1024, key_type: str = "rsa",
try:
name = elliptic_curve.upper()
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(
curve=getattr(ec, elliptic_curve.upper(), None)(),
curve=curve(),
backend=default_backend()
)
else:
+3 -1
View File
@@ -18,6 +18,8 @@ from typing import Tuple
import pkg_resources
from acme import challenges
from certbot import achallenges
from certbot import configuration
from certbot import crypto_util
@@ -377,7 +379,7 @@ class ChallengePerformer:
if idx is not None:
self.indices.append(idx)
def perform(self) -> List[achallenges.KeyAuthorizationAnnotatedChallenge]:
def perform(self) -> List[challenges.KeyAuthorizationChallengeResponse]:
"""Perform all added challenges.
:returns: challenge responses
@@ -7,8 +7,8 @@ import josepy as jose
from requests.exceptions import HTTPError
from requests.exceptions import RequestException
from acme.challenges import Challenge
from certbot import errors
from certbot.achallenges import AnnotatedChallenge
from certbot.plugins import dns_test_common
from certbot.plugins.dns_common_lexicon import LexiconClient
from certbot.plugins.dns_test_common import _AuthenticatorCallableTestCase
@@ -33,7 +33,7 @@ class _AuthenticatorCallableLexiconTestCase(_AuthenticatorCallableTestCase, Prot
a mocked LexiconClient instance.
"""
mock_client: MagicMock
achall: Challenge
achall: AnnotatedChallenge
class _LexiconAwareTestCase(Protocol):
+3 -1
View File
@@ -1,5 +1,7 @@
"""ACME utilities for testing."""
import datetime
from typing import Any
from typing import Dict
from typing import Iterable
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)
for chall, status in zip(challs, statuses)
)
authz_kwargs = {
authz_kwargs: Dict[str, Any] = {
"identifier": messages.Identifier(
typ=messages.IDENTIFIER_FQDN, value=domain),
"challenges": challbs,