simplify code when no combos in authzr

This commit is contained in:
Brad Warren
2016-02-26 15:32:11 -08:00
parent 6c4e29fb49
commit f1eacc7461
4 changed files with 27 additions and 124 deletions
+18 -41
View File
@@ -9,7 +9,6 @@ from acme import challenges
from acme import messages from acme import messages
from letsencrypt import achallenges from letsencrypt import achallenges
from letsencrypt import constants
from letsencrypt import errors from letsencrypt import errors
from letsencrypt import error_handler from letsencrypt import error_handler
from letsencrypt import interfaces from letsencrypt import interfaces
@@ -396,10 +395,7 @@ def _find_smart_path(challbs, preferences, combinations):
combo_total = 0 combo_total = 0
if not best_combo: if not best_combo:
msg = ("Client does not support any combination of challenges that " _report_no_chall_path()
"will satisfy the CA.")
logger.fatal(msg)
raise errors.AuthorizationError(msg)
return best_combo return best_combo
@@ -408,48 +404,29 @@ 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
server. This function returns the best path that does not contain server. This function either returns a path containing all
multiple mutually exclusive challenges. challenges provided by the CA or raises an exception.
""" """
assert len(preferences) == len(set(preferences))
path = [] path = []
satisfied = set() for i, challb in enumerate(challbs):
for pref_c in preferences: # supported is set to True if the challenge type is supported
for i, offered_challb in enumerate(challbs): supported = next((True for pref_c in preferences
if (isinstance(offered_challb.chall, pref_c) and if isinstance(challb.chall, pref_c)), False)
is_preferred(offered_challb, satisfied)): if supported:
path.append(i) path.append(i)
satisfied.add(offered_challb) else:
_report_no_chall_path()
return path return path
def mutually_exclusive(obj1, obj2, groups, different=False): def _report_no_chall_path():
"""Are two objects mutually exclusive?""" """Logs and raises an error that no satisfiable chall path exists."""
for group in groups: msg = ("Client with the currently selected authenticator does not support "
obj1_present = False "any combination of challenges that will satisfy the CA.")
obj2_present = False logger.fatal(msg)
raise errors.AuthorizationError(msg)
for obj_cls in group:
obj1_present |= isinstance(obj1, obj_cls)
obj2_present |= isinstance(obj2, obj_cls)
if obj1_present and obj2_present and (
not different or not isinstance(obj1, obj2.__class__)):
return False
return True
def is_preferred(offered_challb, satisfied,
exclusive_groups=constants.EXCLUSIVE_CHALLENGES):
"""Return whether or not the challenge is preferred in path."""
for challb in satisfied:
if not mutually_exclusive(
offered_challb.chall, challb.chall, exclusive_groups,
different=True):
return False
return True
_ACME_PREFIX = "urn:acme:error:" _ACME_PREFIX = "urn:acme:error:"
-5
View File
@@ -44,11 +44,6 @@ RENEWER_DEFAULTS = dict(
"""Defaults for renewer script.""" """Defaults for renewer script."""
EXCLUSIVE_CHALLENGES = frozenset([frozenset([
challenges.TLSSNI01, challenges.HTTP01])])
"""Mutually exclusive challenges."""
ENHANCEMENTS = ["redirect", "http-header", "ocsp-stapling", "spdy"] ENHANCEMENTS = ["redirect", "http-header", "ocsp-stapling", "spdy"]
"""List of possible :class:`letsencrypt.interfaces.IInstaller` """List of possible :class:`letsencrypt.interfaces.IInstaller`
enhancements. enhancements.
+8 -3
View File
@@ -59,9 +59,14 @@ def gen_combos(challbs):
else: else:
cont_chall.append(i) cont_chall.append(i)
# Gen combos for 1 of each type, lowest index first (makes testing easier) if cont_chall:
return tuple((i, j) if i < j else (j, i) # Gen combos for 1 of each type, lowest
for i in dv_chall for j in cont_chall) # index included first (makes testing easier)
return tuple((i, j) if i < j else (j, i)
for i in dv_chall for j in cont_chall)
else:
# completing a single DV chall satisfies the CA
return tuple((i,) for i in dv_chall)
def chall_to_challb(chall, status): # pylint: disable=redefined-outer-name def chall_to_challb(chall, status): # pylint: disable=redefined-outer-name
+1 -75
View File
@@ -299,7 +299,7 @@ class GenChallengePathTest(unittest.TestCase):
def test_common_case(self): def test_common_case(self):
"""Given TLSSNI01 and HTTP01 with appropriate combos.""" """Given TLSSNI01 and HTTP01 with appropriate combos."""
challbs = (acme_util.TLSSNI01_P, acme_util.HTTP01_P) challbs = (acme_util.TLSSNI01_P, acme_util.HTTP01_P)
prefs = [challenges.TLSSNI01] prefs = [challenges.TLSSNI01, challenges.HTTP01]
combos = ((0,), (1,)) combos = ((0,), (1,))
# Smart then trivial dumb path test # Smart then trivial dumb path test
@@ -318,9 +318,6 @@ class GenChallengePathTest(unittest.TestCase):
combos = acme_util.gen_combos(challbs) combos = acme_util.gen_combos(challbs)
self.assertEqual(self._call(challbs, prefs, combos), (0, 2)) self.assertEqual(self._call(challbs, prefs, combos), (0, 2))
# dumb_path() trivial test
self.assertTrue(self._call(challbs, prefs, None))
def test_full_cont_server(self): def test_full_cont_server(self):
challbs = (acme_util.RECOVERY_CONTACT_P, challbs = (acme_util.RECOVERY_CONTACT_P,
acme_util.POP_P, acme_util.POP_P,
@@ -336,9 +333,6 @@ class GenChallengePathTest(unittest.TestCase):
combos = acme_util.gen_combos(challbs) combos = acme_util.gen_combos(challbs)
self.assertEqual(self._call(challbs, prefs, combos), (1, 3)) self.assertEqual(self._call(challbs, prefs, combos), (1, 3))
# Dumb path trivial test
self.assertTrue(self._call(challbs, prefs, None))
def test_not_supported(self): def test_not_supported(self):
challbs = (acme_util.POP_P, acme_util.TLSSNI01_P) challbs = (acme_util.POP_P, acme_util.TLSSNI01_P)
prefs = [challenges.TLSSNI01] prefs = [challenges.TLSSNI01]
@@ -348,74 +342,6 @@ class GenChallengePathTest(unittest.TestCase):
errors.AuthorizationError, self._call, challbs, prefs, combos) errors.AuthorizationError, self._call, challbs, prefs, combos)
class MutuallyExclusiveTest(unittest.TestCase):
"""Tests for letsencrypt.auth_handler.mutually_exclusive."""
# pylint: disable=missing-docstring,too-few-public-methods
class A(object):
pass
class B(object):
pass
class C(object):
pass
class D(C):
pass
@classmethod
def _call(cls, chall1, chall2, different=False):
from letsencrypt.auth_handler import mutually_exclusive
return mutually_exclusive(chall1, chall2, groups=frozenset([
frozenset([cls.A, cls.B]), frozenset([cls.A, cls.C]),
]), different=different)
def test_group_members(self):
self.assertFalse(self._call(self.A(), self.B()))
self.assertFalse(self._call(self.A(), self.C()))
def test_cross_group(self):
self.assertTrue(self._call(self.B(), self.C()))
def test_same_type(self):
self.assertFalse(self._call(self.A(), self.A(), different=False))
self.assertTrue(self._call(self.A(), self.A(), different=True))
# in particular...
obj = self.A()
self.assertFalse(self._call(obj, obj, different=False))
self.assertTrue(self._call(obj, obj, different=True))
def test_subclass(self):
self.assertFalse(self._call(self.A(), self.D()))
self.assertFalse(self._call(self.D(), self.A()))
class IsPreferredTest(unittest.TestCase):
"""Tests for letsencrypt.auth_handler.is_preferred."""
@classmethod
def _call(cls, chall, satisfied):
from letsencrypt.auth_handler import is_preferred
return is_preferred(chall, satisfied, exclusive_groups=frozenset([
frozenset([challenges.TLSSNI01, challenges.HTTP01]),
frozenset([challenges.DNS, challenges.HTTP01]),
]))
def test_empty_satisfied(self):
self.assertTrue(self._call(acme_util.DNS_P, frozenset()))
def test_mutually_exclusvie(self):
self.assertFalse(
self._call(
acme_util.TLSSNI01_P, frozenset([acme_util.HTTP01_P])))
def test_mutually_exclusive_same_type(self):
self.assertTrue(
self._call(acme_util.TLSSNI01_P, frozenset([acme_util.TLSSNI01_P])))
class ReportFailedChallsTest(unittest.TestCase): class ReportFailedChallsTest(unittest.TestCase):
"""Tests for letsencrypt.auth_handler._report_failed_challs.""" """Tests for letsencrypt.auth_handler._report_failed_challs."""
# pylint: disable=protected-access # pylint: disable=protected-access