mirror of
https://github.com/certbot/certbot.git
synced 2026-08-03 03:32:13 +02:00
in progress change
This commit is contained in:
+11
-1
@@ -33,14 +33,16 @@ class AuthHandler(object):
|
||||
and values are :class:`acme.messages.AuthorizationResource`
|
||||
:ivar list achalls: DV challenges in the form of
|
||||
:class:`certbot.achallenges.AnnotatedChallenge`
|
||||
:ivar list pref_challs: A list of user specified preferred challenges
|
||||
|
||||
"""
|
||||
def __init__(self, auth, acme, account):
|
||||
def __init__(self, auth, acme, account, pref_challs):
|
||||
self.auth = auth
|
||||
self.acme = acme
|
||||
|
||||
self.account = account
|
||||
self.authzr = dict()
|
||||
self.pref_challs = pref_challs
|
||||
|
||||
# List must be used to keep responses straight.
|
||||
self.achalls = []
|
||||
@@ -246,6 +248,14 @@ class AuthHandler(object):
|
||||
"""
|
||||
# Make sure to make a copy...
|
||||
chall_prefs = []
|
||||
plugin_pref = self.auth.get_chall_pref(domain)
|
||||
if self.pref_challs:
|
||||
out = [pref for pref in self.pref_challs if pref in plugin_pref]
|
||||
if out:
|
||||
return out
|
||||
else:
|
||||
raise errors.AuthorizationError(
|
||||
"None of the selected challenges are supported by the selected plugins")
|
||||
chall_prefs.extend(self.auth.get_chall_pref(domain))
|
||||
return chall_prefs
|
||||
|
||||
|
||||
@@ -13,6 +13,8 @@ import six
|
||||
|
||||
import certbot
|
||||
|
||||
from acme import challenges
|
||||
|
||||
from certbot import constants
|
||||
from certbot import crypto_util
|
||||
from certbot import errors
|
||||
@@ -844,6 +846,13 @@ def prepare_and_parse_args(plugins, args, detect_defaults=False): # pylint: dis
|
||||
"security", "--strict-permissions", action="store_true",
|
||||
help="Require that all configuration files are owned by the current "
|
||||
"user; only needed if your config is somewhere unsafe like /tmp/")
|
||||
helpful.add(
|
||||
"security", "--preferred-challenges", dest="pref_chall",
|
||||
action=_PrefChallAction, default=[],
|
||||
help="Specify which challenges you'd prefer to use. If any of those "
|
||||
"challenges are valid for your authenticator they will be used. "
|
||||
"Otherwise Certbot will not attempt authorization. The first "
|
||||
"challenge listed that is supported by the plugin will be used.")
|
||||
helpful.add(
|
||||
"renew", "--pre-hook",
|
||||
help="Command to be run in a shell before obtaining any certificates."
|
||||
@@ -1032,3 +1041,34 @@ def add_domains(args_or_config, domains):
|
||||
args_or_config.domains.append(domain)
|
||||
|
||||
return validated_domains
|
||||
|
||||
class _PrefChallAction(argparse.Action):
|
||||
"""Action class for parsing preferred challenges."""
|
||||
|
||||
def __call__(self, parser, namespace, pref_chall, option_string=None):
|
||||
"""Just wrap add_pref_challs in argparseese."""
|
||||
_ = add_pref_challs(namespace, pref_chall)
|
||||
|
||||
def add_pref_challs(namespace, pref_challs):
|
||||
"""Parses and validates user specified challenge types.
|
||||
|
||||
Adds challenges (in order) to the configuration object.
|
||||
|
||||
:param namespace: parsed command line arguments
|
||||
:type namespace: argparse.Namespace or
|
||||
configuration.NamespaceConfig
|
||||
:param str pref_challs: one or more comma separated challenge types
|
||||
|
||||
:returns: Challenge objects which match the validated string inputs
|
||||
:rtype: `list`
|
||||
"""
|
||||
challs = pref_challs.split(",")
|
||||
unrecognized = [name for name in challs if name not in challenges.Challenge.TYPES]
|
||||
if unrecognized:
|
||||
raise argparse.ArgumentTypeError(
|
||||
"Unrecognized challenges: {0}".format(", ".join(unrecognized)))
|
||||
|
||||
out = [challenges.Challenge.TYPES[name] for name in challs]
|
||||
print(namespace)
|
||||
namespace.pref_chall.extend(out)
|
||||
return out
|
||||
|
||||
+1
-1
@@ -186,7 +186,7 @@ class Client(object):
|
||||
|
||||
if auth is not None:
|
||||
self.auth_handler = auth_handler.AuthHandler(
|
||||
auth, self.acme, self.account)
|
||||
auth, self.acme, self.account, self.config.pref_chall)
|
||||
else:
|
||||
self.auth_handler = None
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ import argparse
|
||||
import collections
|
||||
import logging
|
||||
import socket
|
||||
import sys
|
||||
import threading
|
||||
|
||||
import OpenSSL
|
||||
@@ -12,6 +13,7 @@ import zope.interface
|
||||
from acme import challenges
|
||||
from acme import standalone as acme_standalone
|
||||
|
||||
from certbot import cli
|
||||
from certbot import errors
|
||||
from certbot import interfaces
|
||||
|
||||
@@ -110,38 +112,17 @@ class ServerManager(object):
|
||||
in six.iteritems(self._instances))
|
||||
|
||||
|
||||
SUPPORTED_CHALLENGES = [challenges.TLSSNI01, challenges.HTTP01]
|
||||
|
||||
|
||||
def supported_challenges_validator(data):
|
||||
"""Supported challenges validator for the `argparse`.
|
||||
|
||||
It should be passed as `type` argument to `add_argument`.
|
||||
|
||||
"""
|
||||
challs = data.split(",")
|
||||
|
||||
# tls-sni-01 was dvsni during private beta
|
||||
if "dvsni" in challs:
|
||||
logger.info("Updating legacy standalone_supported_challenges value")
|
||||
challs = [challenges.TLSSNI01.typ if chall == "dvsni" else chall
|
||||
for chall in challs]
|
||||
data = ",".join(challs)
|
||||
|
||||
unrecognized = [name for name in challs
|
||||
if name not in challenges.Challenge.TYPES]
|
||||
if unrecognized:
|
||||
raise argparse.ArgumentTypeError(
|
||||
"Unrecognized challenges: {0}".format(", ".join(unrecognized)))
|
||||
|
||||
choices = set(chall.typ for chall in SUPPORTED_CHALLENGES)
|
||||
if not set(challs).issubset(choices):
|
||||
raise argparse.ArgumentTypeError(
|
||||
"Plugin does not support the following (valid) "
|
||||
"challenges: {0}".format(", ".join(set(challs) - choices)))
|
||||
|
||||
return data
|
||||
class supported_challenges_wrapper(argparse.Action):
|
||||
"""Wrapper for the depricated supported challenges flag"""
|
||||
|
||||
def __call__(self, parser, namespace, pref_chall, option_string=None):
|
||||
# print deprecation warning
|
||||
sys.stderr.write("WARNING: The standalone specific supported challenges flag is depricated")
|
||||
sys.stderr.write("\nPlease use the --preferred-challenges flag instead.\n")
|
||||
#call cli version - move namespace back into it
|
||||
_ = cli.add_pref_challs(namespace, pref_chall)
|
||||
|
||||
@zope.interface.implementer(interfaces.IAuthenticator)
|
||||
@zope.interface.provider(interfaces.IPluginFactory)
|
||||
@@ -178,14 +159,12 @@ class Authenticator(common.Plugin):
|
||||
def add_parser_arguments(cls, add):
|
||||
add("supported-challenges",
|
||||
help="Supported challenges. Preferred in the order they are listed.",
|
||||
type=supported_challenges_validator,
|
||||
default=",".join(chall.typ for chall in SUPPORTED_CHALLENGES))
|
||||
action=supported_challenges_wrapper, dest="pref_chall")
|
||||
|
||||
@property
|
||||
def supported_challenges(self):
|
||||
"""Challenges supported by this plugin."""
|
||||
return [challenges.Challenge.TYPES[name] for name in
|
||||
self.conf("supported-challenges").split(",")]
|
||||
return self.config.pref_chall
|
||||
|
||||
@property
|
||||
def _necessary_ports(self):
|
||||
@@ -208,7 +187,7 @@ class Authenticator(common.Plugin):
|
||||
|
||||
def get_chall_pref(self, domain):
|
||||
# pylint: disable=unused-argument,missing-docstring
|
||||
return self.supported_challenges
|
||||
return [challenges.TLSSNI01, challenges.HTTP01]
|
||||
|
||||
def perform(self, achalls): # pylint: disable=missing-docstring
|
||||
renewer = self.config.verb == "renew"
|
||||
|
||||
@@ -67,29 +67,17 @@ class ServerManagerTest(unittest.TestCase):
|
||||
class SupportedChallengesValidatorTest(unittest.TestCase):
|
||||
"""Tests for plugins.standalone.supported_challenges_validator."""
|
||||
|
||||
def _call(self, data):
|
||||
from certbot.plugins.standalone import (
|
||||
supported_challenges_validator)
|
||||
return supported_challenges_validator(data)
|
||||
|
||||
def test_correct(self):
|
||||
self.assertEqual("tls-sni-01", self._call("tls-sni-01"))
|
||||
self.assertEqual("http-01", self._call("http-01"))
|
||||
self.assertEqual("tls-sni-01,http-01", self._call("tls-sni-01,http-01"))
|
||||
self.assertEqual("http-01,tls-sni-01", self._call("http-01,tls-sni-01"))
|
||||
|
||||
def test_unrecognized(self):
|
||||
assert "foo" not in challenges.Challenge.TYPES
|
||||
self.assertRaises(argparse.ArgumentTypeError, self._call, "foo")
|
||||
|
||||
def test_not_subset(self):
|
||||
self.assertRaises(argparse.ArgumentTypeError, self._call, "dns")
|
||||
|
||||
def test_dvsni(self):
|
||||
self.assertEqual("tls-sni-01", self._call("dvsni"))
|
||||
self.assertEqual("http-01,tls-sni-01", self._call("http-01,dvsni"))
|
||||
self.assertEqual("tls-sni-01,http-01", self._call("dvsni,http-01"))
|
||||
def setUp(self):
|
||||
self.parser = argparse.ArgumentParser()
|
||||
from certbot.plugins import standalone
|
||||
standalone.Authenticator.add_parser_arguments(self.parser.add_argument)
|
||||
|
||||
def test_standalone_flag(self):
|
||||
config = self.parser.parse_args(["--supported_challenges", "http-01"])
|
||||
http = challenges.Challenge.TYPES["http-01"]
|
||||
tls = challenges.Challenge.TYPES["tls-sni-01"]
|
||||
print config
|
||||
self.assertEqual(config.pref_chall, [tls, http])
|
||||
|
||||
class AuthenticatorTest(unittest.TestCase):
|
||||
"""Tests for certbot.plugins.standalone.Authenticator."""
|
||||
|
||||
@@ -24,7 +24,7 @@ class ChallengeFactoryTest(unittest.TestCase):
|
||||
from certbot.auth_handler import AuthHandler
|
||||
|
||||
# Account is mocked...
|
||||
self.handler = AuthHandler(None, None, mock.Mock(key="mock_key"))
|
||||
self.handler = AuthHandler(None, None, mock.Mock(key="mock_key"), [])
|
||||
|
||||
self.dom = "test"
|
||||
self.handler.authzr[self.dom] = acme_util.gen_authzr(
|
||||
@@ -74,7 +74,7 @@ class GetAuthorizationsTest(unittest.TestCase):
|
||||
self.mock_net = mock.MagicMock(spec=acme_client.Client)
|
||||
|
||||
self.handler = AuthHandler(
|
||||
self.mock_auth, self.mock_net, self.mock_account)
|
||||
self.mock_auth, self.mock_net, self.mock_account, [])
|
||||
|
||||
logging.disable(logging.CRITICAL)
|
||||
|
||||
@@ -189,7 +189,7 @@ class PollChallengesTest(unittest.TestCase):
|
||||
# Account and network are mocked...
|
||||
self.mock_net = mock.MagicMock()
|
||||
self.handler = AuthHandler(
|
||||
None, self.mock_net, mock.Mock(key="mock_key"))
|
||||
None, self.mock_net, mock.Mock(key="mock_key"), [])
|
||||
|
||||
self.doms = ["0", "1", "2"]
|
||||
self.handler.authzr[self.doms[0]] = acme_util.gen_authzr(
|
||||
|
||||
@@ -1035,6 +1035,24 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods
|
||||
namespace = parse(short_args)
|
||||
self.assertTrue(namespace.text_mode)
|
||||
|
||||
#TODO massage this to work in cli
|
||||
def test_correct(self):
|
||||
self.assertEqual("tls-sni-01", self._call("tls-sni-01"))
|
||||
self.assertEqual("http-01", self._call("http-01"))
|
||||
self.assertEqual("tls-sni-01,http-01", self._call("tls-sni-01,http-01"))
|
||||
self.assertEqual("http-01,tls-sni-01", self._call("http-01,tls-sni-01"))
|
||||
|
||||
def test_unrecognized(self):
|
||||
assert "foo" not in challenges.Challenge.TYPES
|
||||
self.assertRaises(argparse.ArgumentTypeError, self._call, "foo")
|
||||
|
||||
def test_not_subset(self):
|
||||
self.assertRaises(argparse.ArgumentTypeError, self._call, "dns")
|
||||
|
||||
def test_dvsni(self):
|
||||
self.assertEqual("tls-sni-01", self._call("dvsni"))
|
||||
self.assertEqual("http-01,tls-sni-01", self._call("http-01,dvsni"))
|
||||
self.assertEqual("tls-sni-01,http-01", self._call("dvsni,http-01"))
|
||||
|
||||
class DetermineAccountTest(unittest.TestCase):
|
||||
"""Tests for certbot.cli._determine_account."""
|
||||
|
||||
Reference in New Issue
Block a user