separate out functions rename errors

This commit is contained in:
James Kasten 2015-04-07 14:36:59 -07:00
parent 8a60b87081
commit f5d25c392b
3 changed files with 59 additions and 81 deletions

View file

@ -1,4 +1,5 @@
"""ACME AuthHandler."""
import itertools
import logging
import sys
@ -31,7 +32,7 @@ class AuthHandler(object): # pylint: disable=too-many-instance-attributes
:ivar list domains: list of str domains to get authorization
:ivar dict authkey: Authorized Keys for each domain.
values are of type :class:`letsencrypt.client.le_util.Key`
:ivar dict authzr: ACME Challenge messages with domain as a key.
:ivar dict authzr: ACME Authorization Resource dict where keys are domains.
:ivar list dv_c: Keys - DV challenges in the form of
:class:`letsencrypt.client.achallenges.Indexed`
:ivar list cont_c: Keys - Continuity challenges in the
@ -59,30 +60,51 @@ class AuthHandler(object): # pylint: disable=too-many-instance-attributes
(`completed`, `failed`)
rtype: tuple
:raises LetsEncryptAuthHandlerError: If unable to retrieve all
:raises AuthHandlerError: If unable to retrieve all
authorizations
"""
progress = True
while self.msgs and progress:
progress = False
self._satisfy_challenges()
self._choose_challenges(domains)
cont_resp, dv_resp = self._get_responses()
logging.info("Ready for verification...")
delete_list = []
# Send all Responses
self._respond(cont_resp, dv_resp)
for dom in self.domains:
if self._path_satisfied(dom):
self.acme_authorization(dom)
delete_list.append(dom)
def _choose_challenges(self, domains):
logging.info("Performing the following challenges:")
for dom in domains:
path = gen_challenge_path(
self.authzr[dom].challenges,
self._get_chall_pref(dom),
self.authzr[dom].combinations)
# This avoids modifying while iterating over the list
if delete_list:
self._cleanup_state(delete_list)
progress = True
dom_dv_c, dom_cont_c = self._challenge_factory(
dom, path)
self.dv_c.extend(dom_dv_c)
self.cont_c.extend(dom_cont_c)
if not progress:
raise errors.LetsEncryptAuthHandlerError(
"Unable to solve challenges for requested names.")
def _get_responses(self):
"""Get Responses for challenges from authenticators."""
cont_resp = []
dv_resp = []
try:
if self.cont_c:
cont_resp = self.cont_auth.perform(self.cont_c)
if self.dv_c:
dv_resp = self.dv_auth.perform(self.dv_c)
# This will catch both specific types of errors.
except errors.AuthHandlerError as err:
logging.critical("Failure in setting up challenges.")
logging.info("Attempting to clean up outstanding challenges...")
self._cleanup_challenges()
raise errors.AuthHandlerError(
"Unable to perform challenges")
assert len(cont_resp) == len(self.cont_c)
assert len(dv_resp) == len(self.dv_c)
return cont_resp, dv_resp
def acme_authorization(self, domain):
"""Handle ACME "authorization" phase.
@ -113,67 +135,22 @@ class AuthHandler(object): # pylint: disable=too-many-instance-attributes
finally:
self._cleanup_challenges(domain)
def _satisfy_challenges(self):
"""Attempt to satisfy all saved challenge messages.
.. todo:: It might be worth it to try different challenges to
find one that doesn't throw an exception
.. todo:: separate into more functions
"""
logging.info("Performing the following challenges:")
for dom in self.domains:
path = gen_challenge_path(
self.authzr[dom].challenges,
self._get_chall_pref(dom),
self.authzr[dom].combinations)
dom_dv_c, dom_cont_c = self._challenge_factory(
dom, path)
self.dv_c.extend(dom_dv_c)
self.cont_c.extend(dom_cont_c)
cont_resp = []
dv_resp = []
try:
if self.cont_c:
cont_resp = self.cont_auth.perform(self.cont_c)
if self.dv_c:
dv_resp = self.dv_auth.perform(self.dv_c)
# This will catch both specific types of errors.
except errors.LetsEncryptAuthHandlerError as err:
logging.critical("Failure in setting up challenges:")
logging.critical(str(err))
logging.info("Attempting to clean up outstanding challenges...")
for dom in self.domains:
self._cleanup_challenges(dom)
raise errors.LetsEncryptAuthHandlerError(
"Unable to perform challenges")
assert len(cont_resp) == len(self.cont_c)
assert len(dv_resp) == len(self.dv_c)
logging.info("Ready for verification...")
# Send all Responses
self._respond(cont_resp, dv_resp)
def _respond(self, cont_resp, dv_resp):
"""Send/Recieve confirmation of all challenges.
.. note:: This method also cleans up the auth_handler state.
"""
completed = []
for chall, resp in itertools.izip(self.cont_c, cont_resp):
if cont_resp[i]:
self.network.answer_challenge(self.cont_c[i], cont_resp[i])
for i in range(len(dv_resp)):
if dv_resp[i]:
self.network.answer_challenge(self.dv_c[i], cont_resp[i])
to_check = self._send_responses(self.dv_c, dv_resp)
to_check.update(self._send_responses(self.cont_c, cont_resp))
def _send_responses(self, achalls, resps):
"""Send responses and make sure errors are handled."""
to_check = dict()
for achall, resp in itertools.izip(achalls, resps):
if resp:
to_check[achall.domain] = self.network.answer_challenge(
achall.chall, resp)
def _get_chall_pref(self, domain):
"""Return list of challenge preferences.
@ -181,8 +158,7 @@ class AuthHandler(object): # pylint: disable=too-many-instance-attributes
:param str domain: domain for which you are requesting preferences
"""
chall_prefs = []
chall_prefs.extend(self.cont_auth.get_chall_pref(domain))
chall_prefs = self.cont_auth.get_chall_pref(domain)
chall_prefs.extend(self.dv_auth.get_chall_pref(domain))
return chall_prefs
@ -216,7 +192,7 @@ class AuthHandler(object): # pylint: disable=too-many-instance-attributes
cont_chall = []
for index in path:
chall = self.msgs[domain].challenges[index]
chall = self.authzr[domain].challenges[index]
if isinstance(chall, challenges.DVSNI):
logging.info(" DVSNI challenge for %s.", domain)
@ -276,7 +252,7 @@ def gen_challenge_path(challs, preferences, combinations):
:returns: tuple of indices from ``challenges``.
:rtype: tuple
:raises letsencrypt.client.errors.LetsEncryptAuthHandlerError: If a
:raises letsencrypt.client.errors.AuthHandlerError: If a
path cannot be created that satisfies the CA given the preferences and
combinations.
@ -322,7 +298,7 @@ def _find_smart_path(challs, preferences, combinations):
msg = ("Client does not support any combination of challenges that "
"will satisfy the CA.")
logging.fatal(msg)
raise errors.LetsEncryptAuthHandlerError(msg)
raise errors.AuthHandlerError(msg)
return best_combo

View file

@ -18,15 +18,15 @@ class LetsEncryptReverterError(LetsEncryptClientError):
# Auth Handler Errors
class LetsEncryptAuthHandlerError(LetsEncryptClientError):
"""Let's Encrypt Auth Handler error."""
class AuthHandlerError(LetsEncryptClientError):
"""Auth Handler error."""
class LetsEncryptContAuthError(LetsEncryptAuthHandlerError):
class LetsEncryptContAuthError(AuthHandlerError):
"""Let's Encrypt Client Authenticator error."""
class LetsEncryptDvAuthError(LetsEncryptAuthHandlerError):
class LetsEncryptDvAuthError(AuthHandlerError):
"""Let's Encrypt DV Authenticator error."""

View file

@ -292,6 +292,8 @@ class Network(object):
raise errors.UnexpectedUpdate(challr.uri)
return challr
def poll_challenge(self, chall):
@classmethod
def retry_after(cls, response, default):
"""Compute next `poll` time based on response ``Retry-After`` header.