Adjust achallanges to be used with ChallengeBody

This commit is contained in:
Jakub Warmuz
2015-04-14 13:40:56 +00:00
parent 1672e07b2c
commit fc52600c4d
2 changed files with 44 additions and 39 deletions
+16 -13
View File
@@ -1,17 +1,20 @@
"""Client annotated ACME challenges. """Client annotated ACME challenges.
Please use names such as ``achall`` to distiguish from variables "of type" Please use names such as ``achall`` to distiguish from variables "of type"
:class:`letsencrypt.acme.challenges.Challenge` (denoted by ``chall``):: :class:`letsencrypt.acme.challenges.Challenge` (denoted by ``chall``)
and :class:`.ChallengeBody` (denoted by ``challb``)::
from letsencrypt.acme import challenges from letsencrypt.acme import challenges
from letsencrypt.acme import messages2
from letsencrypt.client import achallenges from letsencrypt.client import achallenges
chall = challenges.DNS(token='foo') chall = challenges.DNS(token='foo')
achall = achallenges.DNS(chall=chall, domain='example.com') challb = messages2.ChallengeBody(chall=chall)
achall = achallenges.DNS(chall=challb, domain='example.com')
Note, that all annotated challenges act as a proxy objects:: Note, that all annotated challenges act as a proxy objects::
achall.token == chall.token achall.token == challb.token
""" """
from letsencrypt.acme import challenges from letsencrypt.acme import challenges
@@ -29,19 +32,19 @@ class AnnotatedChallenge(jose_util.ImmutableMap):
Wraps around server provided challenge and annotates with data Wraps around server provided challenge and annotates with data
useful for the client. useful for the client.
:ivar chall: Wrapped `~.ChallengeBody` (or just `~.challenges.Challenge`). :ivar challb: Wrapped `~.ChallengeBody`.
""" """
__slots__ = ('chall',) __slots__ = ('challb',)
acme_type = NotImplemented acme_type = NotImplemented
def __getattr__(self, name): def __getattr__(self, name):
return getattr(self.chall, name) return getattr(self.challb, name)
class DVSNI(AnnotatedChallenge): class DVSNI(AnnotatedChallenge):
"""Client annotated "dvsni" ACME challenge.""" """Client annotated "dvsni" ACME challenge."""
__slots__ = ('chall', 'domain', 'key') __slots__ = ('challb', 'domain', 'key')
acme_type = challenges.DVSNI acme_type = challenges.DVSNI
def gen_cert_and_response(self, s=None): # pylint: disable=invalid-name def gen_cert_and_response(self, s=None): # pylint: disable=invalid-name
@@ -55,35 +58,35 @@ class DVSNI(AnnotatedChallenge):
""" """
response = challenges.DVSNIResponse(s=s) response = challenges.DVSNIResponse(s=s)
cert_pem = crypto_util.make_ss_cert(self.key.pem, [ cert_pem = crypto_util.make_ss_cert(self.key.pem, [
self.nonce_domain, self.domain, response.z_domain(self.chall)]) self.nonce_domain, self.domain, response.z_domain(self.challb)])
return cert_pem, response return cert_pem, response
class SimpleHTTPS(AnnotatedChallenge): class SimpleHTTPS(AnnotatedChallenge):
"""Client annotated "simpleHttps" ACME challenge.""" """Client annotated "simpleHttps" ACME challenge."""
__slots__ = ('chall', 'domain', 'key') __slots__ = ('challb', 'domain', 'key')
acme_type = challenges.SimpleHTTPS acme_type = challenges.SimpleHTTPS
class DNS(AnnotatedChallenge): class DNS(AnnotatedChallenge):
"""Client annotated "dns" ACME challenge.""" """Client annotated "dns" ACME challenge."""
__slots__ = ('chall', 'domain') __slots__ = ('challb', 'domain')
acme_type = challenges.DNS acme_type = challenges.DNS
class RecoveryContact(AnnotatedChallenge): class RecoveryContact(AnnotatedChallenge):
"""Client annotated "recoveryContact" ACME challenge.""" """Client annotated "recoveryContact" ACME challenge."""
__slots__ = ('chall', 'domain') __slots__ = ('challb', 'domain')
acme_type = challenges.RecoveryContact acme_type = challenges.RecoveryContact
class RecoveryToken(AnnotatedChallenge): class RecoveryToken(AnnotatedChallenge):
"""Client annotated "recoveryToken" ACME challenge.""" """Client annotated "recoveryToken" ACME challenge."""
__slots__ = ('chall', 'domain') __slots__ = ('challb', 'domain')
acme_type = challenges.RecoveryToken acme_type = challenges.RecoveryToken
class ProofOfPossession(AnnotatedChallenge): class ProofOfPossession(AnnotatedChallenge):
"""Client annotated "proofOfPossession" ACME challenge.""" """Client annotated "proofOfPossession" ACME challenge."""
__slots__ = ('chall', 'domain') __slots__ = ('challb', 'domain')
acme_type = challenges.ProofOfPossession acme_type = challenges.ProofOfPossession
+28 -26
View File
@@ -138,7 +138,7 @@ class AuthHandler(object): # pylint: disable=too-many-instance-attributes
for achall, resp in itertools.izip(achalls, resps): for achall, resp in itertools.izip(achalls, resps):
# Don't send challenges for None and False authenticator responses # Don't send challenges for None and False authenticator responses
if resp: if resp:
challr = self.network.answer_challenge(achall.chall, resp) challr = self.network.answer_challenge(achall.challb, resp)
if achall.domain in chall_update: if achall.domain in chall_update:
chall_update[achall.domain].append(achall) chall_update[achall.domain].append(achall)
else: else:
@@ -267,31 +267,32 @@ class AuthHandler(object): # pylint: disable=too-many-instance-attributes
cont_chall = set() cont_chall = set()
for index in path: for index in path:
chall = self.authzr[domain].body.challenges[index] challb = self.authzr[domain].body.challenges[index]
chall = challb.chall
if isinstance(chall, challenges.DVSNI): if isinstance(chall, challenges.DVSNI):
logging.info(" DVSNI challenge for %s.", domain) logging.info(" DVSNI challenge for %s.", domain)
achall = achallenges.DVSNI( achall = achallenges.DVSNI(
chall=chall, domain=domain, key=self.authkey) challb=challb, domain=domain, key=self.authkey)
elif isinstance(chall, challenges.SimpleHTTPS): elif isinstance(chall, challenges.SimpleHTTPS):
logging.info(" SimpleHTTPS challenge for %s.", domain) logging.info(" SimpleHTTPS challenge for %s.", domain)
achall = achallenges.SimpleHTTPS( achall = achallenges.SimpleHTTPS(
chall=chall, domain=domain, key=self.authkey) challb=challb, domain=domain, key=self.authkey)
elif isinstance(chall, challenges.DNS): elif isinstance(chall, challenges.DNS):
logging.info(" DNS challenge for %s.", domain) logging.info(" DNS challenge for %s.", domain)
achall = achallenges.DNS(chall=chall, domain=domain) achall = achallenges.DNS(challb=challb, domain=domain)
elif isinstance(chall, challenges.RecoveryToken): elif isinstance(chall, challenges.RecoveryToken):
logging.info(" Recovery Token Challenge for %s.", domain) logging.info(" Recovery Token Challenge for %s.", domain)
achall = achallenges.RecoveryToken(chall=chall, domain=domain) achall = achallenges.RecoveryToken(challb=challb, domain=domain)
elif isinstance(chall, challenges.RecoveryContact): elif isinstance(chall, challenges.RecoveryContact):
logging.info(" Recovery Contact Challenge for %s.", domain) logging.info(" Recovery Contact Challenge for %s.", domain)
achall = achallenges.RecoveryContact( achall = achallenges.RecoveryContact(
chall=chall, domain=domain) challb=challb, domain=domain)
elif isinstance(chall, challenges.ProofOfPossession): elif isinstance(chall, challenges.ProofOfPossession):
logging.info(" Proof-of-Possession Challenge for %s", domain) logging.info(" Proof-of-Possession Challenge for %s", domain)
achall = achallenges.ProofOfPossession( achall = achallenges.ProofOfPossession(
chall=chall, domain=domain) challb=challb, domain=domain)
else: else:
raise errors.LetsEncryptClientError( raise errors.LetsEncryptClientError(
@@ -306,15 +307,15 @@ class AuthHandler(object): # pylint: disable=too-many-instance-attributes
return dv_chall, cont_chall return dv_chall, cont_chall
def gen_challenge_path(challs, preferences, combinations): def gen_challenge_path(challbs, preferences, combinations):
"""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.
:param tuple challs: A tuple of challenges :param tuple challbs: A tuple of challenges
(:class:`letsencrypt.acme.challenges.Challenge`) from (:class:`letsencrypt.acme.messages2.Challenge`) from
:class:`letsencrypt.acme.messages.Challenge` server message to :class:`letsencrypt.acme.messages2.AuthorizationResource` to be
be fulfilled by the client in order to prove possession of the fulfilled by the client in order to prove possession of the
identifier. identifier.
:param list preferences: List of challenge preferences for domain :param list preferences: List of challenge preferences for domain
@@ -333,12 +334,12 @@ def gen_challenge_path(challs, preferences, combinations):
""" """
if combinations: if combinations:
return _find_smart_path(challs, preferences, combinations) return _find_smart_path(challbs, preferences, combinations)
else: else:
return _find_dumb_path(challs, preferences) return _find_dumb_path(challbs, preferences)
def _find_smart_path(challs, preferences, combinations): def _find_smart_path(challbs, preferences, combinations):
"""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
@@ -360,8 +361,8 @@ def _find_smart_path(challs, preferences, combinations):
combo_total = 0 combo_total = 0
for combo in combinations: for combo in combinations:
for challenge_index in combo: for challenge_index in combo:
combo_total += chall_cost.get(challs[ combo_total += chall_cost.get(challbs[
challenge_index].__class__, max_cost) challenge_index].chall.__class__, max_cost)
if combo_total < best_combo_cost: if combo_total < best_combo_cost:
best_combo = combo best_combo = combo
@@ -378,7 +379,7 @@ def _find_smart_path(challs, preferences, combinations):
return best_combo return best_combo
def _find_dumb_path(challs, preferences): def _find_dumb_path(challbs, preferences):
"""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
@@ -391,11 +392,11 @@ def _find_dumb_path(challs, preferences):
path = [] path = []
satisfied = set() satisfied = set()
for pref_c in preferences: for pref_c in preferences:
for i, offered_chall in enumerate(challs): for i, offered_challb in enumerate(challbs):
if (isinstance(offered_chall, pref_c) and if (isinstance(offered_challb.chall, pref_c) and
is_preferred(offered_chall, satisfied)): is_preferred(offered_challb, satisfied)):
path.append(i) path.append(i)
satisfied.add(offered_chall) satisfied.add(offered_challb)
return path return path
@@ -415,11 +416,12 @@ def mutually_exclusive(obj1, obj2, groups, different=False):
return True return True
def is_preferred(offered_chall, satisfied, def is_preferred(offered_challb, satisfied,
exclusive_groups=constants.EXCLUSIVE_CHALLENGES): exclusive_groups=constants.EXCLUSIVE_CHALLENGES):
"""Return whether or not the challenge is preferred in path.""" """Return whether or not the challenge is preferred in path."""
for chall in satisfied: for challb in satisfied:
if not mutually_exclusive( if not mutually_exclusive(
offered_chall, chall, exclusive_groups, different=True): offered_challb.chall, challb.chall, exclusive_groups,
different=True):
return False return False
return True return True