diff --git a/letsencrypt/cli.py b/letsencrypt/cli.py index 7dd0a7771..96b30d25e 100644 --- a/letsencrypt/cli.py +++ b/letsencrypt/cli.py @@ -675,6 +675,11 @@ def run(config, plugins): # pylint: disable=too-many-branches,too-many-locals def obtain_cert(config, plugins, lineage=None): """Implements "certonly": authenticate & obtain cert, but do not install it.""" + if config.domains and config.csr is not None: + # TODO: --csr could have a priority, when --domains is + # supplied, check if CSR matches given domains? + return "--domains and --csr are mutually exclusive" + try: # installers are used in auth mode to determine domain names installer, authenticator = choose_configurator_plugins(config, plugins, "certonly") @@ -689,7 +694,8 @@ def obtain_cert(config, plugins, lineage=None): # This is a special case; cert and chain are simply saved if config.csr is not None: assert lineage is None, "Did not expect a CSR with a RenewableCert" - certr, chain = le_client.obtain_certificate_from_csr(_process_domain) + certr, chain = le_client.obtain_certificate_from_csr(le_util.CSR( + file=config.csr[0], data=config.csr[1], form="der")) if config.dry_run: logger.info( "Dry run: skipping saving certificate to %s", config.cert_path) diff --git a/letsencrypt/client.py b/letsencrypt/client.py index 413409ded..57b21a55f 100644 --- a/letsencrypt/client.py +++ b/letsencrypt/client.py @@ -228,34 +228,21 @@ class Client(object): authzr) return certr, self.acme.fetch_chain(certr) - def obtain_certificate_from_csr(self, domain_callback): + def obtain_certificate_from_csr(self, csr): """Obtain certficiate from CSR. - :param function(config, domains) domain_callback: callback for each - domain extracted from the CSR, to ensure that webroot-map and similar - housekeeping in cli.py is performed correctly + :param .le_util.CSR csr: DER-encoded Certificate Signing + Request. :returns: `.CertificateResource` and certificate chain (as returned by `.fetch_chain`). :rtype: tuple """ - - #raise TypeError("About to call %r" % le_util.CSR) - csr = le_util.CSR(file=self.config.csr[0], data=self.config.csr[1], form="der") - # TODO: add CN to domains? - domains = crypto_util.get_sans_from_csr(csr.data, OpenSSL.crypto.FILETYPE_ASN1) - for d in domains: - domain_callback(self.config, d) - - csr_domains, config_domains = set(domains), set(self.config.domains) - if csr_domains != config_domains: - raise errors.ConfigurationError( - "Inconsistent domain requests:\ncsr: {0}\ncli config: {1}" - .format(", ".join(csr_domains), ", ".join(config_domains)) - ) - - return self._obtain_certificate(domains, csr) + return self._obtain_certificate( + # TODO: add CN to domains? + crypto_util.get_sans_from_csr( + csr.data, OpenSSL.crypto.FILETYPE_ASN1), csr) def obtain_certificate(self, domains): """Obtains a certificate from the ACME server. diff --git a/letsencrypt/plugins/webroot.py b/letsencrypt/plugins/webroot.py index 3f5bc6d28..f8176417c 100644 --- a/letsencrypt/plugins/webroot.py +++ b/letsencrypt/plugins/webroot.py @@ -49,10 +49,8 @@ to serve all files under specified web root ({0}).""" path_map = self.conf("map") if not path_map: - raise errors.PluginError( - "Missing parts of webroot configuration; please set either " - "--webroot-path and --domains, or --webroot-map. Run with " - " --help webroot for examples.") + raise errors.PluginError("--{0} must be set".format( + self.option_name("path"))) for name, path in path_map.items(): if not os.path.isdir(path): raise errors.PluginError(path + " does not exist or is not a directory") diff --git a/letsencrypt/tests/cli_test.py b/letsencrypt/tests/cli_test.py index 76b7676c3..ff11b1dde 100644 --- a/letsencrypt/tests/cli_test.py +++ b/letsencrypt/tests/cli_test.py @@ -326,10 +326,8 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods self.assertEqual(config.fullchain_path, os.path.abspath(fullchain)) def test_certonly_bad_args(self): - _, _, _, _ = self._call(['-d', 'foo.bar', 'certonly', '--csr', CSR]) - # self.assertEqual(ret, '--domains and --csr are mutually exclusive') - # self.assertRaises(errors.Error, self._call, - # ['-d', 'foo.bar', 'certonly', '--csr', CSR]) + ret, _, _, _ = self._call(['-d', 'foo.bar', 'certonly', '--csr', CSR]) + self.assertEqual(ret, '--domains and --csr are mutually exclusive') # ret, _, _, _ = self._call(['-a', 'bad_auth', 'certonly']) self.assertRaises(errors.PluginSelectionError, self._call, @@ -640,6 +638,7 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods self._make_dummy_renewal_config() with mock.patch('letsencrypt.storage.RenewableCert') as mock_rc: mock_lineage = mock.MagicMock() + mock_lineage.fullchain = "somepath/fullchain.pem" if renewalparams is not None: mock_lineage.configuration = {'renewalparams': renewalparams} if names is not None: @@ -689,6 +688,7 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods self._make_dummy_renewal_config() with mock.patch('letsencrypt.storage.RenewableCert') as mock_rc: mock_lineage = mock.MagicMock() + mock_lineage.fullchain = "somewhere/fullchain.pem" mock_rc.return_value = mock_lineage mock_lineage.configuration = { 'renewalparams': {'authenticator': 'webroot'}} diff --git a/letsencrypt/tests/client_test.py b/letsencrypt/tests/client_test.py index 6a8899c3b..2f117f80c 100644 --- a/letsencrypt/tests/client_test.py +++ b/letsencrypt/tests/client_test.py @@ -82,7 +82,6 @@ class ClientTest(unittest.TestCase): no_verify_ssl=False, config_dir="/etc/letsencrypt") # pylint: disable=star-args self.account = mock.MagicMock(**{"key.pem": KEY}) - self.eg_domains = ["example.com", "www.example.com"] from letsencrypt.client import Client with mock.patch("letsencrypt.client.acme_client.Client") as acme: @@ -102,7 +101,8 @@ class ClientTest(unittest.TestCase): self.acme.fetch_chain.return_value = mock.sentinel.chain def _check_obtain_certificate(self): - self.client.auth_handler.get_authorizations.assert_called_once_with(self.eg_domains) + self.client.auth_handler.get_authorizations.assert_called_once_with( + ["example.com", "www.example.com"]) self.acme.request_issuance.assert_called_once_with( jose.ComparableX509(OpenSSL.crypto.load_certificate_request( OpenSSL.crypto.FILETYPE_ASN1, CSR_SAN)), @@ -111,21 +111,11 @@ class ClientTest(unittest.TestCase): def test_obtain_certificate_from_csr(self): self._mock_obtain_certificate() - mock_process_domain = mock.MagicMock() - test_csr = le_util.CSR(form="der", file=None, data=CSR_SAN) - with mock.patch("letsencrypt.client.le_util.CSR") as mock_CSR: - mock_CSR.return_value = test_csr - self.client.config.domains = self.eg_domains - self.assertEqual( - (mock.sentinel.certr, mock.sentinel.chain), - self.client.obtain_certificate_from_csr(mock_process_domain)) - - # make sure cli processing occurred - cli_processed = (call[0][1] for call in mock_process_domain.call_args_list) - self.assertEqual(set(cli_processed), set(("example.com", "www.example.com"))) - - # and that the cert was obtained correctly - self._check_obtain_certificate() + self.assertEqual( + (mock.sentinel.certr, mock.sentinel.chain), + self.client.obtain_certificate_from_csr(le_util.CSR( + form="der", file=None, data=CSR_SAN))) + self._check_obtain_certificate() @mock.patch("letsencrypt.client.crypto_util") def test_obtain_certificate(self, mock_crypto_util):