Merge branch 'master' into typed-jose-fields

This commit is contained in:
Adrien Ferrand 2022-01-19 21:29:46 +01:00
commit bb9e7c92d9
52 changed files with 912 additions and 763 deletions

View file

@ -54,12 +54,8 @@ jobs:
done
displayName: Run integration tests for Docker images
- job: installer_build
strategy:
matrix:
win-2019:
vmImage: windows-2019
win-2022:
vmImage: windows-2022
pool:
vmImage: vs2017-win2016
steps:
- task: UsePythonVersion@0
inputs:
@ -91,11 +87,17 @@ jobs:
matrix:
win2019:
imageName: windows-2019
win2022:
imageName: windows-2022
win2016:
imageName: vs2017-win2016
pool:
vmImage: $(imageName)
steps:
- powershell: |
if ($PSVersionTable.PSVersion.Major -ne 5) {
throw "Powershell version is not 5.x"
}
condition: eq(variables['imageName'], 'vs2017-win2016')
displayName: Check Powershell 5.x is used in vs2017-win2016
- task: UsePythonVersion@0
inputs:
versionSpec: 3.9

View file

@ -13,15 +13,15 @@ jobs:
PYTHON_VERSION: 3.10
TOXENV: py310-cover
windows-py36:
IMAGE_NAME: windows-2019
IMAGE_NAME: vs2017-win2016
PYTHON_VERSION: 3.6
TOXENV: py36-win
windows-py39-cover:
IMAGE_NAME: windows-2019
IMAGE_NAME: vs2017-win2016
PYTHON_VERSION: 3.9
TOXENV: py39-cover-win
windows-integration-certbot:
IMAGE_NAME: windows-2019
IMAGE_NAME: vs2017-win2016
PYTHON_VERSION: 3.9
TOXENV: integration-certbot
linux-oldest-tests-1:

View file

@ -3,7 +3,7 @@ stages:
jobs:
- job: prepare
pool:
vmImage: win2019
vmImage: vs2017-win2016
steps:
# If we change the output filename from `release_notes.md`, it should also be changed in tools/create_github_release.py
- bash: |

View file

@ -34,10 +34,9 @@ class TLSServer(socketserver.TCPServer):
else:
self.address_family = socket.AF_INET
self.certs = kwargs.pop("certs", {})
self.method = kwargs.pop(
"method", crypto_util._DEFAULT_SSL_METHOD)
self.method = kwargs.pop("method", crypto_util._DEFAULT_SSL_METHOD)
self.allow_reuse_address = kwargs.pop("allow_reuse_address", True)
socketserver.TCPServer.__init__(self, *args, **kwargs)
super().__init__(*args, **kwargs)
def _wrap_sock(self) -> None:
self.socket = crypto_util.SSLSocket(
@ -190,7 +189,7 @@ class HTTPServer(BaseHTTPServer.HTTPServer):
self.address_family = socket.AF_INET6
else:
self.address_family = socket.AF_INET
BaseHTTPServer.HTTPServer.__init__(self, *args, **kwargs)
super().__init__(*args, **kwargs)
class HTTP01Server(HTTPServer, ACMEServerMixin):
@ -198,8 +197,8 @@ class HTTP01Server(HTTPServer, ACMEServerMixin):
def __init__(self, server_address: Tuple[str, int], resources: Set[challenges.HTTP01],
ipv6: bool = False, timeout: int = 30) -> None:
HTTPServer.__init__(
self, server_address, HTTP01RequestHandler.partial_init(
super().__init__(
server_address, HTTP01RequestHandler.partial_init(
simple_http_resources=resources, timeout=timeout), ipv6=ipv6)
@ -208,7 +207,7 @@ class HTTP01DualNetworkedServers(BaseDualNetworkedServers):
affect the other."""
def __init__(self, *args: Any, **kwargs: Any) -> None:
BaseDualNetworkedServers.__init__(self, HTTP01Server, *args, **kwargs)
super().__init__(HTTP01Server, *args, **kwargs)
class HTTP01RequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
@ -226,7 +225,7 @@ class HTTP01RequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
def __init__(self, *args: Any, **kwargs: Any) -> None:
self.simple_http_resources = kwargs.pop("simple_http_resources", set())
self._timeout = kwargs.pop('timeout', 30)
BaseHTTPServer.BaseHTTPRequestHandler.__init__(self, *args, **kwargs)
super().__init__(*args, **kwargs)
self.server: HTTP01Server
# In parent class BaseHTTPRequestHandler, 'timeout' is a class-level property but we

View file

@ -165,7 +165,6 @@ class TLSALPN01ServerTest(unittest.TestCase):
class BaseDualNetworkedServersTest(unittest.TestCase):
"""Test for acme.standalone.BaseDualNetworkedServers."""
class SingleProtocolServer(socketserver.TCPServer):
"""Server that only serves on a single protocol. FreeBSD has this behavior for AF_INET6."""
def __init__(self, *args, **kwargs):
@ -175,7 +174,7 @@ class BaseDualNetworkedServersTest(unittest.TestCase):
kwargs["bind_and_activate"] = False
else:
self.address_family = socket.AF_INET
socketserver.TCPServer.__init__(self, *args, **kwargs)
super().__init__(*args, **kwargs)
if ipv6:
# NB: On Windows, socket.IPPROTO_IPV6 constant may be missing.
# We use the corresponding value (41) instead.
@ -202,7 +201,6 @@ class BaseDualNetworkedServersTest(unittest.TestCase):
self.assertEqual(em.exception.errno, EADDRINUSE)
def test_ports_equal(self):
from acme.standalone import BaseDualNetworkedServers
servers = BaseDualNetworkedServers(

View file

@ -964,7 +964,9 @@ class ApacheConfigurator(common.Configurator):
logger.warning("Encountered a problem while parsing file: %s, skipping", path)
return None
for arg in args:
addrs.add(obj.Addr.fromstring(self.parser.get_arg(arg)))
addr = obj.Addr.fromstring(self.parser.get_arg(arg))
if addr:
addrs.add(addr)
is_ssl = False
if self.parser.find_dir("SSLEngine", "on", start=path, exclude=False):
@ -1094,7 +1096,9 @@ class ApacheConfigurator(common.Configurator):
"""
addrs = set()
for param in node.parameters:
addrs.add(obj.Addr.fromstring(param))
addr = obj.Addr.fromstring(param)
if addr:
addrs.add(addr)
is_ssl = False
# Exclusion to match the behavior in get_virtual_hosts_v2
@ -1647,9 +1651,10 @@ class ApacheConfigurator(common.Configurator):
for addr in ssl_addr_p:
old_addr = obj.Addr.fromstring(
str(self.parser.get_arg(addr)))
ssl_addr = old_addr.get_addr_obj("443")
self.parser.aug.set(addr, str(ssl_addr))
ssl_addrs.add(ssl_addr)
if old_addr:
ssl_addr = old_addr.get_addr_obj("443")
self.parser.aug.set(addr, str(ssl_addr))
ssl_addrs.add(ssl_addr)
return ssl_addrs

View file

@ -9,7 +9,7 @@ from certbot import errors
from certbot.compat import filesystem
from certbot.compat import os
from certbot.plugins import common
from certbot_apache._internal.obj import VirtualHost # pylint: disable=unused-import
from certbot_apache._internal.obj import VirtualHost
from certbot_apache._internal.parser import get_aug_path
if TYPE_CHECKING:

View file

@ -25,24 +25,29 @@ def _get_augeasnode_mock(filepath):
metadata=metadata)
return augeasnode_mock
class AugeasParserNodeTest(util.ApacheTest): # pylint: disable=too-many-public-methods
"""Test AugeasParserNode using available test configurations"""
def setUp(self): # pylint: disable=arguments-differ
super().setUp()
with mock.patch("certbot_apache._internal.configurator.ApacheConfigurator.get_parsernode_root") as mock_parsernode:
with mock.patch(
"certbot_apache._internal.configurator.ApacheConfigurator.get_parsernode_root"
) as mock_parsernode:
mock_parsernode.side_effect = _get_augeasnode_mock(
os.path.join(self.config_path, "apache2.conf"))
self.config = util.get_apache_configurator(
self.config_path, self.vhost_path, self.config_dir, self.work_dir, use_parsernode=True)
self.config_path, self.vhost_path, self.config_dir, self.work_dir,
use_parsernode=True,
)
self.vh_truth = util.get_vh_truth(
self.temp_dir, "debian_apache_2_4/multiple_vhosts")
def test_save(self):
with mock.patch('certbot_apache._internal.parser.ApacheParser.save') as mock_save:
self.config.parser_root.save("A save message")
self.assertTrue(mock_save.called)
self.assertIs(mock_save.called, True)
self.assertEqual(mock_save.call_args[0][0], "A save message")
def test_unsaved_files(self):
@ -67,7 +72,8 @@ class AugeasParserNodeTest(util.ApacheTest): # pylint: disable=too-many-public-
"/Anything": "Anything",
}
for test in testcases:
self.assertEqual(block._aug_get_name(test), testcases[test]) # pylint: disable=protected-access
# pylint: disable=protected-access
self.assertEqual(block._aug_get_name(test), testcases[test])
def test_find_blocks(self):
blocks = self.config.parser_root.find_blocks("VirtualHost", exclude=False)
@ -81,7 +87,7 @@ class AugeasParserNodeTest(util.ApacheTest): # pylint: disable=too-many-public-
def test_find_directive_found(self):
directives = self.config.parser_root.find_directives("Listen")
self.assertEqual(len(directives), 1)
self.assertTrue(directives[0].filepath.endswith("/apache2/ports.conf"))
self.assertIs(directives[0].filepath.endswith("/apache2/ports.conf"), True)
self.assertEqual(directives[0].parameters, (u'80',))
def test_find_directive_notfound(self):
@ -96,29 +102,29 @@ class AugeasParserNodeTest(util.ApacheTest): # pylint: disable=too-many-public-
servername = vh.find_directives("servername")
self.assertEqual(servername[0].parameters[0], "certbot.demo")
found = True
self.assertTrue(found)
self.assertIs(found, True)
def test_find_comments(self):
rootcomment = self.config.parser_root.find_comments(
"This is the main Apache server configuration file. "
)
self.assertEqual(len(rootcomment), 1)
self.assertTrue(rootcomment[0].filepath.endswith(
self.assertIs(rootcomment[0].filepath.endswith(
"debian_apache_2_4/multiple_vhosts/apache2/apache2.conf"
))
), True)
def test_set_parameters(self):
servernames = self.config.parser_root.find_directives("servername")
names: List[str] = []
for servername in servernames:
names += servername.parameters
self.assertFalse("going_to_set_this" in names)
self.assertNotIn("going_to_set_this", names)
servernames[0].set_parameters(["something", "going_to_set_this"])
servernames = self.config.parser_root.find_directives("servername")
names = []
for servername in servernames:
names += servername.parameters
self.assertTrue("going_to_set_this" in names)
self.assertIn("going_to_set_this", names)
def test_set_parameters_atinit(self):
from certbot_apache._internal.augeasparser import AugeasDirectiveNode
@ -131,7 +137,7 @@ class AugeasParserNodeTest(util.ApacheTest): # pylint: disable=too-many-public-
ancestor=assertions.PASS,
metadata=servernames[0].metadata
)
self.assertTrue(mock_set.called)
self.assertIs(mock_set.called, True)
self.assertEqual(
mock_set.call_args_list[0][0][0],
["test", "setting", "these"]
@ -151,7 +157,7 @@ class AugeasParserNodeTest(util.ApacheTest): # pylint: disable=too-many-public-
self.assertEqual(len(servername.parameters), 3)
servername.set_parameters(["thisshouldnotexistpreviously"])
found = True
self.assertTrue(found)
self.assertIs(found, True)
# Verify params
servernames = self.config.parser_root.find_directives("servername")
@ -161,7 +167,7 @@ class AugeasParserNodeTest(util.ApacheTest): # pylint: disable=too-many-public-
self.assertEqual(len(servername.parameters), 1)
servername.set_parameters(["thisshouldnotexistpreviously"])
found = True
self.assertTrue(found)
self.assertIs(found, True)
def test_add_child_comment(self):
newc = self.config.parser_root.add_child_comment("The content")
@ -201,7 +207,7 @@ class AugeasParserNodeTest(util.ApacheTest): # pylint: disable=too-many-public-
rpath,
self.config.parser_root.metadata["augeaspath"]
)
self.assertTrue(directive.startswith("NewBlock"))
self.assertIs(directive.startswith("NewBlock"), True)
def test_add_child_block_beginning(self):
self.config.parser_root.add_child_block(
@ -212,7 +218,7 @@ class AugeasParserNodeTest(util.ApacheTest): # pylint: disable=too-many-public-
root_path = self.config.parser_root.metadata["augeaspath"]
# Get first child
first = parser.aug.match("{}/*[1]".format(root_path))
self.assertTrue(first[0].endswith("Beginning"))
self.assertIs(first[0].endswith("Beginning"), True)
def test_add_child_block_append(self):
self.config.parser_root.add_child_block(
@ -222,7 +228,7 @@ class AugeasParserNodeTest(util.ApacheTest): # pylint: disable=too-many-public-
root_path = self.config.parser_root.metadata["augeaspath"]
# Get last child
last = parser.aug.match("{}/*[last()]".format(root_path))
self.assertTrue(last[0].endswith("VeryLast"))
self.assertIs(last[0].endswith("VeryLast"), True)
def test_add_child_block_append_alt(self):
self.config.parser_root.add_child_block(
@ -233,7 +239,7 @@ class AugeasParserNodeTest(util.ApacheTest): # pylint: disable=too-many-public-
root_path = self.config.parser_root.metadata["augeaspath"]
# Get last child
last = parser.aug.match("{}/*[last()]".format(root_path))
self.assertTrue(last[0].endswith("VeryLastAlt"))
self.assertIs(last[0].endswith("VeryLastAlt"), True)
def test_add_child_block_middle(self):
self.config.parser_root.add_child_block(
@ -244,7 +250,7 @@ class AugeasParserNodeTest(util.ApacheTest): # pylint: disable=too-many-public-
root_path = self.config.parser_root.metadata["augeaspath"]
# Augeas indices start at 1 :(
middle = parser.aug.match("{}/*[6]".format(root_path))
self.assertTrue(middle[0].endswith("Middle"))
self.assertIs(middle[0].endswith("Middle"), True)
def test_add_child_block_existing_name(self):
parser = self.config.parser_root.parser
@ -257,7 +263,7 @@ class AugeasParserNodeTest(util.ApacheTest): # pylint: disable=too-many-public-
)
new_block = parser.aug.match("{}/VirtualHost[2]".format(root_path))
self.assertEqual(len(new_block), 1)
self.assertTrue(vh.metadata["augeaspath"].endswith("VirtualHost[2]"))
self.assertIs(vh.metadata["augeaspath"].endswith("VirtualHost[2]"), True)
def test_node_init_error_bad_augeaspath(self):
from certbot_apache._internal.augeasparser import AugeasBlockNode
@ -302,7 +308,7 @@ class AugeasParserNodeTest(util.ApacheTest): # pylint: disable=too-many-public-
self.assertEqual(len(dirs), 1)
self.assertEqual(dirs[0].parameters, ("with", "parameters"))
# The new directive was added to the very first line of the config
self.assertTrue(dirs[0].metadata["augeaspath"].endswith("[1]"))
self.assertIs(dirs[0].metadata["augeaspath"].endswith("[1]"), True)
def test_add_child_directive_exception(self):
self.assertRaises(
@ -328,8 +334,8 @@ class AugeasParserNodeTest(util.ApacheTest): # pylint: disable=too-many-public-
ancs = vh.find_ancestors("Macro")
self.assertEqual(len(ancs), 0)
nonmacro_test = True
self.assertTrue(macro_test)
self.assertTrue(nonmacro_test)
self.assertIs(macro_test, True)
self.assertIs(nonmacro_test, True)
def test_find_ancestors_bad_path(self):
self.config.parser_root.metadata["augeaspath"] = ""

View file

@ -47,7 +47,7 @@ class AutoHSTSTest(util.ApacheTest):
self.config.parser.modules.pop("headers_module", None)
self.config.parser.modules.pop("mod_header.c", None)
self.config.enable_autohsts(mock.MagicMock(), ["ocspvhost.com"])
self.assertTrue(mock_enable.called)
self.assertIs(mock_enable.called, True)
@mock.patch("certbot_apache._internal.configurator.ApacheConfigurator.restart")
def test_autohsts_deploy_already_exists(self, _restart):
@ -74,7 +74,7 @@ class AutoHSTSTest(util.ApacheTest):
# Verify increased value
self.assertEqual(self.get_autohsts_value(self.vh_truth[7].path),
inc_val)
self.assertTrue(mock_prepare.called)
self.assertIs(mock_prepare.called, True)
@mock.patch("certbot_apache._internal.configurator.ApacheConfigurator.restart")
@mock.patch("certbot_apache._internal.configurator.ApacheConfigurator._autohsts_increase")
@ -88,7 +88,7 @@ class AutoHSTSTest(util.ApacheTest):
self.config.update_autohsts(mock.MagicMock())
# Freq not patched, so value shouldn't increase
self.assertFalse(mock_increase.called)
self.assertIs(mock_increase.called, False)
@mock.patch("certbot_apache._internal.configurator.ApacheConfigurator.restart")
@ -135,13 +135,13 @@ class AutoHSTSTest(util.ApacheTest):
# Time mock is used to make sure that the execution does not
# continue when no autohsts entries exist in pluginstorage
self.config.update_autohsts(mock.MagicMock())
self.assertFalse(mock_time.called)
self.assertIs(mock_time.called, False)
def test_autohsts_make_permanent_noop(self):
self.config.storage.put = mock.MagicMock()
self.config.deploy_autohsts(mock.MagicMock())
# Make sure that the execution does not continue when no entries in store
self.assertFalse(self.config.storage.put.called)
self.assertIs(self.config.storage.put.called, False)
@mock.patch("certbot_apache._internal.display_ops.select_vhost")
def test_autohsts_no_ssl_vhost(self, mock_select):
@ -150,15 +150,13 @@ class AutoHSTSTest(util.ApacheTest):
self.assertRaises(errors.PluginError,
self.config.enable_autohsts,
mock.MagicMock(), "invalid.example.com")
self.assertTrue(
"Certbot was not able to find SSL" in mock_log.call_args[0][0])
self.assertIn("Certbot was not able to find SSL", mock_log.call_args[0][0])
@mock.patch("certbot_apache._internal.configurator.ApacheConfigurator.restart")
@mock.patch("certbot_apache._internal.configurator.ApacheConfigurator.add_vhost_id")
def test_autohsts_dont_enhance_twice(self, mock_id, _restart):
mock_id.return_value = "1234567"
self.config.enable_autohsts(mock.MagicMock(),
["ocspvhost.com", "ocspvhost.com"])
self.config.enable_autohsts(mock.MagicMock(), ["ocspvhost.com", "ocspvhost.com"])
self.assertEqual(mock_id.call_count, 1)
def test_autohsts_remove_orphaned(self):
@ -168,7 +166,7 @@ class AutoHSTSTest(util.ApacheTest):
self.config._autohsts_save_state()
self.config.update_autohsts(mock.MagicMock())
self.assertFalse("orphan_id" in self.config._autohsts)
self.assertNotIn("orphan_id", self.config._autohsts)
# Make sure it's removed from the pluginstorage file as well
self.config._autohsts = None
self.config._autohsts_fetch_state()
@ -181,9 +179,8 @@ class AutoHSTSTest(util.ApacheTest):
self.config._autohsts_save_state()
with mock.patch("certbot_apache._internal.configurator.logger.error") as mock_log:
self.config.deploy_autohsts(mock.MagicMock())
self.assertTrue(mock_log.called)
self.assertTrue(
"VirtualHost with id orphan_id was not" in mock_log.call_args[0][0])
self.assertIs(mock_log.called, True)
self.assertIn("VirtualHost with id orphan_id was not", mock_log.call_args[0][0])
if __name__ == "__main__":

View file

@ -48,8 +48,7 @@ class CentOS6Tests(util.ApacheTest):
self.temp_dir, "centos6_apache/apache")
def test_get_parser(self):
self.assertTrue(isinstance(self.config.parser,
override_centos.CentOSParser))
self.assertIsInstance(self.config.parser, override_centos.CentOSParser)
def test_get_virtual_hosts(self):
"""Make sure all vhosts are being properly found."""
@ -72,9 +71,9 @@ class CentOS6Tests(util.ApacheTest):
"LoadModule", "ssl_module", exclude=False)
self.assertEqual(len(ssl_loadmods), 1)
# Make sure the LoadModule ssl_module is in ssl.conf (default)
self.assertTrue("ssl.conf" in ssl_loadmods[0])
self.assertIn("ssl.conf", ssl_loadmods[0])
# ...and that it's not inside of <IfModule>
self.assertFalse("IfModule" in ssl_loadmods[0])
self.assertNotIn("IfModule", ssl_loadmods[0])
# Get the example vhost
self.config.assoc["test.example.com"] = self.vh_truth[0]
@ -95,7 +94,7 @@ class CentOS6Tests(util.ApacheTest):
# ...and both of them should be wrapped in <IfModule !mod_ssl.c>
# lm[:-17] strips off /directive/arg[1] from the path.
ifmod_args = self.config.parser.get_all_args(lm[:-17])
self.assertTrue("!mod_ssl.c" in ifmod_args)
self.assertIn("!mod_ssl.c", ifmod_args)
@mock.patch("certbot_apache._internal.configurator.display_util.notify")
def test_loadmod_multiple(self, unused_mock_notify):
@ -107,7 +106,7 @@ class CentOS6Tests(util.ApacheTest):
pre_loadmods = self.config.parser.find_dir(
"LoadModule", "ssl_module", exclude=False)
# LoadModules are not within IfModule blocks
self.assertFalse(any("ifmodule" in m.lower() for m in pre_loadmods))
self.assertIs(any("ifmodule" in m.lower() for m in pre_loadmods), False)
self.config.assoc["test.example.com"] = self.vh_truth[0]
self.config.deploy_cert(
"random.demo", "example/cert.pem", "example/key.pem",
@ -116,7 +115,9 @@ class CentOS6Tests(util.ApacheTest):
"LoadModule", "ssl_module", exclude=False)
for mod in post_loadmods:
self.assertTrue(self.config.parser.not_modssl_ifmodule(mod)) #pylint: disable=no-member
with self.subTest(mod=mod):
# pylint: disable=no-member
self.assertIs(self.config.parser.not_modssl_ifmodule(mod), True)
@mock.patch("certbot_apache._internal.configurator.display_util.notify")
def test_loadmod_rootconf_exists(self, unused_mock_notify):
@ -207,20 +208,20 @@ class CentOS6Tests(util.ApacheTest):
post_loadmods = self.config.parser.find_dir("LoadModule",
"ssl_module",
exclude=False)
self.assertFalse(post_loadmods)
self.assertEqual(post_loadmods, [])
def test_no_ifmod_search_false(self):
#pylint: disable=no-member
self.assertFalse(self.config.parser.not_modssl_ifmodule(
self.assertIs(self.config.parser.not_modssl_ifmodule(
"/path/does/not/include/ifmod"
))
self.assertFalse(self.config.parser.not_modssl_ifmodule(
), False)
self.assertIs(self.config.parser.not_modssl_ifmodule(
""
))
self.assertFalse(self.config.parser.not_modssl_ifmodule(
), False)
self.assertIs(self.config.parser.not_modssl_ifmodule(
"/path/includes/IfModule/but/no/arguments"
))
), False)
if __name__ == "__main__":

View file

@ -34,6 +34,7 @@ def get_vh_truth(temp_dir, config_name):
]
return vh_truth
class FedoraRestartTest(util.ApacheTest):
"""Tests for Fedora specific self-signed certificate override"""
@ -140,8 +141,8 @@ class MultipleVhostsTestCentOS(util.ApacheTest):
self.assertEqual(mock_get.call_count, 3)
self.assertEqual(len(self.config.parser.modules), 4)
self.assertEqual(len(self.config.parser.variables), 2)
self.assertTrue("TEST2" in self.config.parser.variables)
self.assertTrue("mod_another.c" in self.config.parser.modules)
self.assertIn("TEST2", self.config.parser.variables)
self.assertIn("mod_another.c", self.config.parser.modules)
def test_get_virtual_hosts(self):
"""Make sure all vhosts are being properly found."""
@ -172,11 +173,11 @@ class MultipleVhostsTestCentOS(util.ApacheTest):
mock_osi.return_value = ("centos", "7")
self.config.parser.update_runtime_variables()
self.assertTrue("mock_define" in self.config.parser.variables)
self.assertTrue("mock_define_too" in self.config.parser.variables)
self.assertTrue("mock_value" in self.config.parser.variables)
self.assertIn("mock_define", self.config.parser.variables)
self.assertIn("mock_define_too", self.config.parser.variables)
self.assertIn("mock_value", self.config.parser.variables)
self.assertEqual("TRUE", self.config.parser.variables["mock_value"])
self.assertTrue("MOCK_NOSEP" in self.config.parser.variables)
self.assertIn("MOCK_NOSEP", self.config.parser.variables)
self.assertEqual("NOSEP_VAL", self.config.parser.variables["NOSEP_TWO"])
@mock.patch("certbot_apache._internal.configurator.util.run_script")

View file

@ -11,8 +11,7 @@ class ComplexParserTest(util.ParserTest):
"""Apache Parser Test."""
def setUp(self): # pylint: disable=arguments-differ
super().setUp(
"complex_parsing", "complex_parsing")
super().setUp("complex_parsing", "complex_parsing")
self.setup_variables()
# This needs to happen after due to setup_variables not being run
@ -78,12 +77,12 @@ class ComplexParserTest(util.ParserTest):
def test_load_modules(self):
"""If only first is found, there is bad variable parsing."""
self.assertTrue("status_module" in self.parser.modules)
self.assertTrue("mod_status.c" in self.parser.modules)
self.assertIn("status_module", self.parser.modules)
self.assertIn("mod_status.c", self.parser.modules)
# This is in an IfDefine
self.assertTrue("ssl_module" in self.parser.modules)
self.assertTrue("mod_ssl.c" in self.parser.modules)
self.assertIn("ssl_module", self.parser.modules)
self.assertIn("mod_ssl.c", self.parser.modules)
def verify_fnmatch(self, arg, hit=True):
"""Test if Include was correctly parsed."""

View file

@ -14,15 +14,13 @@ import util
class ConfiguratorReverterTest(util.ApacheTest):
"""Test for ApacheConfigurator reverter methods"""
def setUp(self): # pylint: disable=arguments-differ
super().setUp()
self.config = util.get_apache_configurator(
self.config_path, self.vhost_path, self.config_dir, self.work_dir)
self.vh_truth = util.get_vh_truth(
self.temp_dir, "debian_apache_2_4/multiple_vhosts")
self.vh_truth = util.get_vh_truth(self.temp_dir, "debian_apache_2_4/multiple_vhosts")
def tearDown(self):
shutil.rmtree(self.config_dir)
@ -30,17 +28,13 @@ class ConfiguratorReverterTest(util.ApacheTest):
shutil.rmtree(self.temp_dir)
def test_bad_save_checkpoint(self):
self.config.reverter.add_to_checkpoint = mock.Mock(
side_effect=errors.ReverterError)
self.config.parser.add_dir(
self.vh_truth[0].path, "Test", "bad_save_ckpt")
self.config.reverter.add_to_checkpoint = mock.Mock(side_effect=errors.ReverterError)
self.config.parser.add_dir(self.vh_truth[0].path, "Test", "bad_save_ckpt")
self.assertRaises(errors.PluginError, self.config.save)
def test_bad_save_finalize_checkpoint(self):
self.config.reverter.finalize_checkpoint = mock.Mock(
side_effect=errors.ReverterError)
self.config.parser.add_dir(
self.vh_truth[0].path, "Test", "bad_save_ckpt")
self.config.reverter.finalize_checkpoint = mock.Mock(side_effect=errors.ReverterError)
self.config.parser.add_dir(self.vh_truth[0].path, "Test", "bad_save_ckpt")
self.assertRaises(errors.PluginError, self.config.save, "Title")
def test_finalize_save(self):
@ -72,8 +66,7 @@ class ConfiguratorReverterTest(util.ApacheTest):
self.assertEqual(mock_load.call_count, 1)
def test_rollback_error(self):
self.config.reverter.rollback_checkpoints = mock.Mock(
side_effect=errors.ReverterError)
self.config.reverter.rollback_checkpoints = mock.Mock(side_effect=errors.ReverterError)
self.assertRaises(errors.PluginError, self.config.rollback_checkpoints)
def test_recovery_routine_reload(self):

View file

@ -83,8 +83,8 @@ class MultipleVhostsTest(util.ApacheTest):
self.config.prepare()
except errors.PluginError as err:
err_msg = str(err)
self.assertTrue("lock" in err_msg)
self.assertTrue(self.config.conf("server-root") in err_msg)
self.assertIn("lock", err_msg)
self.assertIn(self.config.conf("server-root"), err_msg)
else: # pragma: no cover
self.fail("Exception wasn't raised!")
@ -116,7 +116,8 @@ class MultipleVhostsTest(util.ApacheTest):
# Make sure that all (and only) the expected values exist
self.assertEqual(len(mock_add.call_args_list), len(found))
for e in exp:
self.assertTrue(e in found)
with self.subTest(e=e):
self.assertIn(e, found)
del os.environ["CERTBOT_DOCS"]
@ -130,11 +131,10 @@ class MultipleVhostsTest(util.ApacheTest):
from certbot_apache._internal.configurator import ApacheConfigurator
parameters = set(ApacheConfigurator.OS_DEFAULTS.__dict__.keys())
for cls in OVERRIDE_CLASSES.values():
self.assertTrue(parameters.issubset(set(cls.OS_DEFAULTS.__dict__.keys())))
self.assertIs(parameters.issubset(set(cls.OS_DEFAULTS.__dict__.keys())), True)
def test_constant(self):
self.assertTrue("debian_apache_2_4/multiple_vhosts/apache" in
self.config.options.server_root)
self.assertIn("debian_apache_2_4/multiple_vhosts/apache", self.config.options.server_root)
@certbot_util.patch_display_util()
def test_get_all_names(self, mock_getutility):
@ -162,9 +162,9 @@ class MultipleVhostsTest(util.ApacheTest):
names = self.config.get_all_names()
self.assertEqual(len(names), 9)
self.assertTrue("zombo.com" in names)
self.assertTrue("google.com" in names)
self.assertTrue("certbot.demo" in names)
self.assertIn("zombo.com", names)
self.assertIn("google.com", names)
self.assertIn("certbot.demo", names)
def test_get_bad_path(self):
self.assertEqual(apache_util.get_file_path(None), None)
@ -188,16 +188,14 @@ class MultipleVhostsTest(util.ApacheTest):
True, False)
# pylint: disable=protected-access
self.config._add_servernames(ssl_vh1)
self.assertTrue(
self.config._add_servername_alias("oy_vey", ssl_vh1) is None)
self.assertIsNone(self.config._add_servername_alias("oy_vey", ssl_vh1))
def test_add_servernames_alias(self):
self.config.parser.add_dir(
self.vh_truth[2].path, "ServerAlias", ["*.le.co"])
# pylint: disable=protected-access
self.config._add_servernames(self.vh_truth[2])
self.assertEqual(
self.vh_truth[2].get_names(), {"*.le.co", "ip-172-30-0-17"})
self.assertEqual(self.vh_truth[2].get_names(), {"*.le.co", "ip-172-30-0-17"})
def test_get_virtual_hosts(self):
"""Make sure all vhosts are being properly found."""
@ -246,8 +244,8 @@ class MultipleVhostsTest(util.ApacheTest):
self.vh_truth[0].get_names(), chosen_vhost.get_names())
# Make sure we go from HTTP -> HTTPS
self.assertFalse(self.vh_truth[0].ssl)
self.assertTrue(chosen_vhost.ssl)
self.assertIs(self.vh_truth[0].ssl, False)
self.assertIs(chosen_vhost.ssl, True)
@mock.patch("certbot_apache._internal.configurator.ApacheConfigurator._find_best_vhost")
@mock.patch("certbot_apache._internal.parser.ApacheParser.add_dir")
@ -256,7 +254,7 @@ class MultipleVhostsTest(util.ApacheTest):
ret_vh.enabled = False
mock_find.return_value = self.vh_truth[8]
self.config.choose_vhost("whatever.com")
self.assertTrue(mock_add.called)
self.assertIs(mock_add.called, True)
@mock.patch("certbot_apache._internal.display_ops.select_vhost")
def test_choose_vhost_select_vhost_with_temp(self, mock_select):
@ -291,23 +289,17 @@ class MultipleVhostsTest(util.ApacheTest):
def test_findbest_continues_on_short_domain(self):
# pylint: disable=protected-access
chosen_vhost = self.config._find_best_vhost("purple.com")
self.assertEqual(None, chosen_vhost)
self.assertIsNone(self.config._find_best_vhost("purple.com"))
def test_findbest_continues_on_long_domain(self):
# pylint: disable=protected-access
chosen_vhost = self.config._find_best_vhost("green.red.purple.com")
self.assertEqual(None, chosen_vhost)
self.assertIsNone(self.config._find_best_vhost("green.red.purple.com"))
def test_find_best_vhost(self):
# pylint: disable=protected-access
self.assertEqual(
self.vh_truth[3], self.config._find_best_vhost("certbot.demo"))
self.assertEqual(
self.vh_truth[0],
self.config._find_best_vhost("encryption-example.demo"))
self.assertEqual(
self.config._find_best_vhost("does-not-exist.com"), None)
self.assertEqual(self.vh_truth[3], self.config._find_best_vhost("certbot.demo"))
self.assertEqual(self.vh_truth[0], self.config._find_best_vhost("encryption-example.demo"))
self.assertEqual(self.config._find_best_vhost("does-not-exist.com"), None)
def test_find_best_vhost_variety(self):
# pylint: disable=protected-access
@ -345,11 +337,11 @@ class MultipleVhostsTest(util.ApacheTest):
self.config.parser.modules["mod_ssl.c"] = None
self.config.parser.modules["socache_shmcb_module"] = None
self.assertFalse(ssl_vhost.enabled)
self.assertIs(ssl_vhost.enabled, False)
self.config.deploy_cert(
"encryption-example.demo", "example/cert.pem", "example/key.pem",
"example/cert_chain.pem", "example/fullchain.pem")
self.assertTrue(ssl_vhost.enabled)
self.assertIs(ssl_vhost.enabled, True)
def test_no_duplicate_include(self):
def mock_find_dir(directive, argument, _):
@ -366,7 +358,7 @@ class MultipleVhostsTest(util.ApacheTest):
if a[0][1] == "Include" and a[0][2] == self.config.mod_ssl_conf:
tried_to_add = True
# Include should be added, find_dir is not patched, and returns falsy
self.assertTrue(tried_to_add)
self.assertIs(tried_to_add, True)
self.config.parser.find_dir = mock_find_dir
mock_add.reset_mock()
@ -395,20 +387,16 @@ class MultipleVhostsTest(util.ApacheTest):
f_args.append(self.config.parser.get_arg(d))
return f_args
# Verify that the dummy directives do not exist
self.assertFalse(
"insert_cert_file_path" in find_args(vhostpath,
"SSLCertificateFile"))
self.assertFalse(
"insert_key_file_path" in find_args(vhostpath,
"SSLCertificateKeyFile"))
self.assertNotIn(
"insert_cert_file_path", find_args(vhostpath, "SSLCertificateFile"))
self.assertNotIn(
"insert_key_file_path", find_args(vhostpath, "SSLCertificateKeyFile"))
orig_add_dummy(vhostpath)
# Verify that the dummy directives exist
self.assertTrue(
"insert_cert_file_path" in find_args(vhostpath,
"SSLCertificateFile"))
self.assertTrue(
"insert_key_file_path" in find_args(vhostpath,
"SSLCertificateKeyFile"))
self.assertIn(
"insert_cert_file_path", find_args(vhostpath, "SSLCertificateFile"))
self.assertIn(
"insert_key_file_path", find_args(vhostpath, "SSLCertificateKeyFile"))
# pylint: disable=protected-access
self.config._add_dummy_ssl_directives = mock_add_dummy_ssl
@ -420,8 +408,8 @@ class MultipleVhostsTest(util.ApacheTest):
self.config.save()
# Verify ssl_module was enabled.
self.assertTrue(self.vh_truth[1].enabled)
self.assertTrue("ssl_module" in self.config.parser.modules)
self.assertIs(self.vh_truth[1].enabled, True)
self.assertIn("ssl_module", self.config.parser.modules)
loc_cert = self.config.parser.find_dir(
"sslcertificatefile", "example/cert.pem", self.vh_truth[1].path)
@ -457,17 +445,15 @@ class MultipleVhostsTest(util.ApacheTest):
def test_is_name_vhost(self):
addr = obj.Addr.fromstring("*:80")
self.assertTrue(self.config.is_name_vhost(addr))
self.assertIs(self.config.is_name_vhost(addr), True)
self.config.version = (2, 2)
self.assertFalse(self.config.is_name_vhost(addr))
self.assertEqual(self.config.is_name_vhost(addr), [])
def test_add_name_vhost(self):
self.config.add_name_vhost(obj.Addr.fromstring("*:443"))
self.config.add_name_vhost(obj.Addr.fromstring("*:80"))
self.assertTrue(self.config.parser.find_dir(
"NameVirtualHost", "*:443", exclude=False))
self.assertTrue(self.config.parser.find_dir(
"NameVirtualHost", "*:80"))
self.assertTrue(self.config.parser.find_dir("NameVirtualHost", "*:443", exclude=False))
self.assertTrue(self.config.parser.find_dir("NameVirtualHost", "*:80"))
def test_add_listen_80(self):
mock_find = mock.Mock()
@ -476,8 +462,8 @@ class MultipleVhostsTest(util.ApacheTest):
self.config.parser.find_dir = mock_find
self.config.parser.add_dir = mock_add_dir
self.config.ensure_listen("80")
self.assertTrue(mock_add_dir.called)
self.assertTrue(mock_find.called)
self.assertIs(mock_add_dir.called, True)
self.assertIs(mock_find.called, True)
self.assertEqual(mock_add_dir.call_args[0][1], "Listen")
self.assertEqual(mock_add_dir.call_args[0][2], "80")
@ -502,13 +488,13 @@ class MultipleVhostsTest(util.ApacheTest):
# Test
self.config.ensure_listen("8080")
self.assertEqual(mock_add_dir.call_count, 3)
self.assertTrue(mock_add_dir.called)
self.assertIs(mock_add_dir.called, True)
self.assertEqual(mock_add_dir.call_args[0][1], "Listen")
call_found = False
for mock_call in mock_add_dir.mock_calls:
if mock_call[1][2] == ['1.2.3.4:8080']:
call_found = True
self.assertTrue(call_found)
self.assertIs(call_found, True)
@mock.patch("certbot_apache._internal.parser.ApacheParser.reset_modules")
def test_prepare_server_https(self, mock_reset):
@ -631,8 +617,8 @@ class MultipleVhostsTest(util.ApacheTest):
def test_make_vhost_ssl_nonsymlink(self):
ssl_vhost_slink = self.config.make_vhost_ssl(self.vh_truth[8])
self.assertTrue(ssl_vhost_slink.ssl)
self.assertTrue(ssl_vhost_slink.enabled)
self.assertIs(ssl_vhost_slink.ssl, True)
self.assertIs(ssl_vhost_slink.enabled, True)
self.assertEqual(ssl_vhost_slink.name, "nonsym.link")
def test_make_vhost_ssl_nonexistent_vhost_path(self):
@ -653,8 +639,8 @@ class MultipleVhostsTest(util.ApacheTest):
self.assertEqual(len(ssl_vhost.addrs), 1)
self.assertEqual({obj.Addr.fromstring("*:443")}, ssl_vhost.addrs)
self.assertEqual(ssl_vhost.name, "encryption-example.demo")
self.assertTrue(ssl_vhost.ssl)
self.assertFalse(ssl_vhost.enabled)
self.assertIs(ssl_vhost.ssl, True)
self.assertIs(ssl_vhost.enabled, False)
self.assertEqual(self.config.is_name_vhost(self.vh_truth[0]),
self.config.is_name_vhost(ssl_vhost))
@ -733,15 +719,14 @@ class MultipleVhostsTest(util.ApacheTest):
def test_get_ssl_vhost_path(self):
# pylint: disable=protected-access
self.assertTrue(
self.config._get_ssl_vhost_path("example_path").endswith(".conf"))
self.assertIs(self.config._get_ssl_vhost_path("example_path").endswith(".conf"), True)
def test_add_name_vhost_if_necessary(self):
# pylint: disable=protected-access
self.config.add_name_vhost = mock.Mock()
self.config.version = (2, 2)
self.config._add_name_vhost_if_necessary(self.vh_truth[0])
self.assertTrue(self.config.add_name_vhost.called)
self.assertIs(self.config.add_name_vhost.called, True)
new_addrs = set()
for addr in self.vh_truth[0].addrs:
@ -780,9 +765,9 @@ class MultipleVhostsTest(util.ApacheTest):
for i, achall in enumerate(achalls):
self.config.cleanup([achall])
if i == len(achalls) - 1:
self.assertTrue(mock_restart.called)
self.assertIs(mock_restart.called, True)
else:
self.assertFalse(mock_restart.called)
self.assertIs(mock_restart.called, False)
@mock.patch("certbot_apache._internal.configurator.ApacheConfigurator.restart")
@mock.patch("certbot_apache._internal.apache_util._get_runtime_cfg")
@ -795,10 +780,10 @@ class MultipleVhostsTest(util.ApacheTest):
self.config._chall_out.add(achall) # pylint: disable=protected-access
self.config.cleanup([achalls[-1]])
self.assertFalse(mock_restart.called)
self.assertIs(mock_restart.called, False)
self.config.cleanup(achalls)
self.assertTrue(mock_restart.called)
self.assertIs(mock_restart.called, True)
@mock.patch("certbot.util.run_script")
def test_get_version(self, mock_script):
@ -847,18 +832,18 @@ class MultipleVhostsTest(util.ApacheTest):
self.assertTrue(self.config.more_info())
def test_get_chall_pref(self):
self.assertTrue(isinstance(self.config.get_chall_pref(""), list))
self.assertIsInstance(self.config.get_chall_pref(""), list)
def test_install_ssl_options_conf(self):
path = os.path.join(self.work_dir, "test_it")
other_path = os.path.join(self.work_dir, "other_test_it")
self.config.install_ssl_options_conf(path, other_path)
self.assertTrue(os.path.isfile(path))
self.assertTrue(os.path.isfile(other_path))
self.assertIs(os.path.isfile(path), True)
self.assertIs(os.path.isfile(other_path), True)
# TEST ENHANCEMENTS
def test_supported_enhancements(self):
self.assertTrue(isinstance(self.config.supported_enhancements(), list))
self.assertIsInstance(self.config.supported_enhancements(), list)
def test_find_http_vhost_without_ancestor(self):
# pylint: disable=protected-access
@ -897,16 +882,16 @@ class MultipleVhostsTest(util.ApacheTest):
self.assertRaises(errors.PluginError, self.config.enhance,
"certbot.demo", "redirect")
# Check that correct logger.warning was printed
self.assertTrue("not able to find" in mock_log.call_args[0][0])
self.assertTrue("\"redirect\"" in mock_log.call_args[0][0])
self.assertIn("not able to find", mock_log.call_args[0][0])
self.assertIn("\"redirect\"", mock_log.call_args[0][0])
mock_log.reset_mock()
self.assertRaises(errors.PluginError, self.config.enhance,
"certbot.demo", "ensure-http-header", "Test")
# Check that correct logger.warning was printed
self.assertTrue("not able to find" in mock_log.call_args[0][0])
self.assertTrue("Test" in mock_log.call_args[0][0])
self.assertIn("not able to find", mock_log.call_args[0][0])
self.assertIn("Test", mock_log.call_args[0][0])
@mock.patch("certbot.util.exe_exists")
def test_ocsp_stapling(self, mock_exe):
@ -984,7 +969,7 @@ class MultipleVhostsTest(util.ApacheTest):
# pylint: disable=protected-access
http_vh = self.config._get_http_vhost(ssl_vh)
self.assertFalse(http_vh.ssl)
self.assertIs(http_vh.ssl, False)
@mock.patch("certbot.util.run_script")
@mock.patch("certbot.util.exe_exists")
@ -1039,7 +1024,7 @@ class MultipleVhostsTest(util.ApacheTest):
self.config.enhance("certbot.demo", "ensure-http-header",
"Upgrade-Insecure-Requests")
self.assertTrue("headers_module" in self.config.parser.modules)
self.assertIn("headers_module", self.config.parser.modules)
# Get the ssl vhost for certbot.demo
ssl_vhost = self.config.assoc["certbot.demo"]
@ -1091,8 +1076,8 @@ class MultipleVhostsTest(util.ApacheTest):
self.assertEqual(len(rw_rule), 3)
# [:-3] to remove the vhost index number
self.assertTrue(rw_engine[0].startswith(self.vh_truth[3].path[:-3]))
self.assertTrue(rw_rule[0].startswith(self.vh_truth[3].path[:-3]))
self.assertIs(rw_engine[0].startswith(self.vh_truth[3].path[:-3]), True)
self.assertIs(rw_rule[0].startswith(self.vh_truth[3].path[:-3]), True)
def test_rewrite_rule_exists(self):
# Skip the enable mod
@ -1101,7 +1086,7 @@ class MultipleVhostsTest(util.ApacheTest):
self.config.parser.add_dir(
self.vh_truth[3].path, "RewriteRule", ["Unknown"])
# pylint: disable=protected-access
self.assertTrue(self.config._is_rewrite_exists(self.vh_truth[3]))
self.assertIs(self.config._is_rewrite_exists(self.vh_truth[3]), True)
def test_rewrite_engine_exists(self):
# Skip the enable mod
@ -1141,10 +1126,10 @@ class MultipleVhostsTest(util.ApacheTest):
# three args to rw_rule + 1 arg for the pre existing rewrite
self.assertEqual(len(rw_rule), 5)
# [:-3] to remove the vhost index number
self.assertTrue(rw_engine[0].startswith(self.vh_truth[3].path[:-3]))
self.assertTrue(rw_rule[0].startswith(self.vh_truth[3].path[:-3]))
self.assertIs(rw_engine[0].startswith(self.vh_truth[3].path[:-3]), True)
self.assertIs(rw_rule[0].startswith(self.vh_truth[3].path[:-3]), True)
self.assertTrue("rewrite_module" in self.config.parser.modules)
self.assertIn("rewrite_module", self.config.parser.modules)
@mock.patch("certbot.util.run_script")
@mock.patch("certbot.util.exe_exists")
@ -1202,7 +1187,7 @@ class MultipleVhostsTest(util.ApacheTest):
"ApacheConfigurator._verify_no_certbot_redirect")
with mock.patch(verify_no_redirect) as mock_verify:
self.config.enhance("green.blue.purple.com", "redirect")
self.assertFalse(mock_verify.called)
self.assertIs(mock_verify.called, False)
def test_redirect_from_previous_run(self):
# Skip the enable mod
@ -1243,16 +1228,16 @@ class MultipleVhostsTest(util.ApacheTest):
def test_sift_rewrite_rule(self):
# pylint: disable=protected-access
small_quoted_target = "RewriteRule ^ \"http://\""
self.assertFalse(self.config._sift_rewrite_rule(small_quoted_target))
self.assertIs(self.config._sift_rewrite_rule(small_quoted_target), False)
https_target = "RewriteRule ^ https://satoshi"
self.assertTrue(self.config._sift_rewrite_rule(https_target))
self.assertIs(self.config._sift_rewrite_rule(https_target), True)
normal_target = "RewriteRule ^/(.*) http://www.a.com:1234/$1 [L,R]"
self.assertFalse(self.config._sift_rewrite_rule(normal_target))
self.assertIs(self.config._sift_rewrite_rule(normal_target), False)
not_rewriterule = "NotRewriteRule ^ ..."
self.assertFalse(self.config._sift_rewrite_rule(not_rewriterule))
self.assertIs(self.config._sift_rewrite_rule(not_rewriterule), False)
def get_key_and_achalls(self):
"""Return testing achallenges."""
@ -1281,15 +1266,13 @@ class MultipleVhostsTest(util.ApacheTest):
vhost = self.vh_truth[0]
vhost.enabled = False
vhost.filep = inc_path
self.assertFalse(self.config.parser.find_dir("Include", inc_path))
self.assertFalse(
os.path.dirname(inc_path) in self.config.parser.existing_paths)
self.assertEqual(self.config.parser.find_dir("Include", inc_path), [])
self.assertNotIn(os.path.dirname(inc_path), self.config.parser.existing_paths)
self.config.enable_site(vhost)
self.assertTrue(self.config.parser.find_dir("Include", inc_path))
self.assertTrue(
os.path.dirname(inc_path) in self.config.parser.existing_paths)
self.assertTrue(
os.path.basename(inc_path) in self.config.parser.existing_paths[
self.assertGreaterEqual(len(self.config.parser.find_dir("Include", inc_path)), 1)
self.assertIn(os.path.dirname(inc_path), self.config.parser.existing_paths)
self.assertIn(
os.path.basename(inc_path), self.config.parser.existing_paths[
os.path.dirname(inc_path)])
@mock.patch('certbot_apache._internal.configurator.display_util.notify')
@ -1312,7 +1295,7 @@ class MultipleVhostsTest(util.ApacheTest):
"example/cert.pem", "example/key.pem",
"example/cert_chain.pem")
# Test that we actually called add_include
self.assertTrue(mock_add.called)
self.assertIs(mock_add.called, True)
shutil.rmtree(tmp_path)
def test_deploy_cert_no_mod_ssl(self):
@ -1331,7 +1314,7 @@ class MultipleVhostsTest(util.ApacheTest):
ret_vh.enabled = True
self.config.enable_site(ret_vh)
# Make sure that we return early
self.assertFalse(mock_parsed.called)
self.assertIs(mock_parsed.called, False)
def test_enable_mod_unsupported(self):
self.assertRaises(errors.MisconfigurationError,
@ -1352,7 +1335,7 @@ class MultipleVhostsTest(util.ApacheTest):
# And the actual returned values
self.assertEqual(len(vhs), 1)
self.assertEqual(vhs[0].name, "certbot.demo")
self.assertTrue(vhs[0].ssl)
self.assertIs(vhs[0].ssl, True)
self.assertNotEqual(vhs[0], self.vh_truth[3])
@ -1364,7 +1347,7 @@ class MultipleVhostsTest(util.ApacheTest):
mock_select_vhs.return_value = [self.vh_truth[1]]
vhs = self.config._choose_vhosts_wildcard("*.certbot.demo",
create_ssl=False)
self.assertFalse(mock_makessl.called)
self.assertIs(mock_makessl.called, False)
self.assertEqual(vhs[0], self.vh_truth[1])
@mock.patch("certbot_apache._internal.configurator.ApacheConfigurator._vhosts_for_wildcard")
@ -1381,14 +1364,13 @@ class MultipleVhostsTest(util.ApacheTest):
self.assertEqual(mock_select_vhs.call_args[0][0][0], self.vh_truth[7])
self.assertEqual(len(mock_select_vhs.call_args_list), 1)
# Ensure that make_vhost_ssl was not called, vhost.ssl == true
self.assertFalse(mock_makessl.called)
self.assertIs(mock_makessl.called, False)
# And the actual returned values
self.assertEqual(len(vhs), 1)
self.assertTrue(vhs[0].ssl)
self.assertIs(vhs[0].ssl, True)
self.assertEqual(vhs[0], self.vh_truth[7])
@mock.patch('certbot_apache._internal.configurator.display_util.notify')
def test_deploy_cert_wildcard(self, unused_mock_notify):
# pylint: disable=protected-access
@ -1399,7 +1381,7 @@ class MultipleVhostsTest(util.ApacheTest):
with mock.patch(mock_d) as mock_dep:
self.config.deploy_cert("*.wildcard.example.org", "/tmp/path",
"/tmp/path", "/tmp/path", "/tmp/path")
self.assertTrue(mock_dep.called)
self.assertIs(mock_dep.called, True)
self.assertEqual(len(mock_dep.call_args_list), 1)
self.assertEqual(self.vh_truth[7], mock_dep.call_args_list[0][0][0])
@ -1421,7 +1403,7 @@ class MultipleVhostsTest(util.ApacheTest):
self.config._wildcard_vhosts["*.certbot.demo"] = [self.vh_truth[3]]
self.config.enhance("*.certbot.demo", "ensure-http-header",
"Upgrade-Insecure-Requests")
self.assertFalse(mock_choose.called)
self.assertIs(mock_choose.called, False)
@mock.patch("certbot_apache._internal.configurator.ApacheConfigurator._choose_vhosts_wildcard")
def test_enhance_wildcard_no_install(self, mock_choose):
@ -1431,7 +1413,7 @@ class MultipleVhostsTest(util.ApacheTest):
self.config.parser.modules["headers_module"] = None
self.config.enhance("*.certbot.demo", "ensure-http-header",
"Upgrade-Insecure-Requests")
self.assertTrue(mock_choose.called)
self.assertIs(mock_choose.called, True)
def test_add_vhost_id(self):
for vh in [self.vh_truth[0], self.vh_truth[1], self.vh_truth[2]]:
@ -1510,7 +1492,8 @@ class AugeasVhostsTest(util.ApacheTest):
names = (
"an.example.net", "another.example.net", "an.other.example.net")
for name in names:
self.assertFalse(name in self.config.choose_vhost(name).aliases)
with self.subTest(name=name):
self.assertNotIn(name, self.config.choose_vhost(name).aliases)
@mock.patch("certbot_apache._internal.obj.VirtualHost.conflicts")
def test_choose_vhost_without_matching_wildcard(self, mock_conflicts):
@ -1518,7 +1501,7 @@ class AugeasVhostsTest(util.ApacheTest):
mock_path = "certbot_apache._internal.display_ops.select_vhost"
with mock.patch(mock_path, lambda _, vhosts: vhosts[0]):
for name in ("a.example.net", "other.example.net"):
self.assertTrue(name in self.config.choose_vhost(name).aliases)
self.assertIn(name, self.config.choose_vhost(name).aliases)
@mock.patch("certbot_apache._internal.obj.VirtualHost.conflicts")
def test_choose_vhost_wildcard_not_found(self, mock_conflicts):
@ -1551,6 +1534,7 @@ class AugeasVhostsTest(util.ApacheTest):
self.assertRaises(errors.PluginError, self.config.make_vhost_ssl,
broken_vhost)
class MultiVhostsTest(util.ApacheTest):
"""Test configuration with multiple virtualhosts in a single file."""
# pylint: disable=protected-access
@ -1559,9 +1543,7 @@ class MultiVhostsTest(util.ApacheTest):
td = "debian_apache_2_4/multi_vhosts"
cr = "debian_apache_2_4/multi_vhosts/apache2"
vr = "debian_apache_2_4/multi_vhosts/apache2/sites-available"
super().setUp(test_dir=td,
config_root=cr,
vhost_root=vr)
super().setUp(test_dir=td, config_root=cr, vhost_root=vr)
self.config = util.get_apache_configurator(
self.config_path, self.vhost_path,
@ -1582,9 +1564,8 @@ class MultiVhostsTest(util.ApacheTest):
self.assertEqual(len(ssl_vhost.addrs), 1)
self.assertEqual({obj.Addr.fromstring("*:443")}, ssl_vhost.addrs)
self.assertEqual(ssl_vhost.name, "banana.vomit.com")
self.assertTrue(ssl_vhost.ssl)
self.assertFalse(ssl_vhost.enabled)
self.assertIs(ssl_vhost.ssl, True)
self.assertIs(ssl_vhost.enabled, False)
self.assertEqual(self.config.is_name_vhost(self.vh_truth[1]),
self.config.is_name_vhost(ssl_vhost))
@ -1616,8 +1597,7 @@ class MultiVhostsTest(util.ApacheTest):
ssl_vhost = self.config.make_vhost_ssl(self.vh_truth[4])
self.assertTrue(self.config.parser.find_dir(
"RewriteEngine", "on", ssl_vhost.path, False))
self.assertTrue(self.config.parser.find_dir("RewriteEngine", "on", ssl_vhost.path, False))
with open(ssl_vhost.filep) as the_file:
conf_text = the_file.read()
@ -1625,8 +1605,8 @@ class MultiVhostsTest(util.ApacheTest):
"\"https://new.example.com/docs/$1\" [R,L]")
uncommented_rewrite_rule = ("RewriteRule \"^/docs/(.+)\" "
"\"http://new.example.com/docs/$1\" [R,L]")
self.assertTrue(commented_rewrite_rule in conf_text)
self.assertTrue(uncommented_rewrite_rule in conf_text)
self.assertIn(commented_rewrite_rule, conf_text)
self.assertIn(uncommented_rewrite_rule, conf_text)
self.assertEqual(mock_notify.call_count, 1)
self.assertIn("Some rewrite rules", mock_notify.call_args[0][0])
@ -1650,12 +1630,12 @@ class MultiVhostsTest(util.ApacheTest):
"https://%{SERVER_NAME}%{REQUEST_URI} "
"[L,NE,R=permanent]")
self.assertTrue(not_commented_cond1 in conf_line_set)
self.assertTrue(not_commented_rewrite_rule in conf_line_set)
self.assertIn(not_commented_cond1, conf_line_set)
self.assertIn(not_commented_rewrite_rule, conf_line_set)
self.assertTrue(commented_cond1 in conf_line_set)
self.assertTrue(commented_cond2 in conf_line_set)
self.assertTrue(commented_rewrite_rule in conf_line_set)
self.assertIn(commented_cond1, conf_line_set)
self.assertIn(commented_cond2, conf_line_set)
self.assertIn(commented_rewrite_rule, conf_line_set)
self.assertEqual(mock_notify.call_count, 1)
self.assertIn("Some rewrite rules", mock_notify.call_args[0][0])
@ -1677,7 +1657,7 @@ class InstallSslOptionsConfTest(util.ApacheTest):
return crypto_util.sha256sum(self.config.pick_apache_config())
def _assert_current_file(self):
self.assertTrue(os.path.isfile(self.config.mod_ssl_conf))
self.assertIs(os.path.isfile(self.config.mod_ssl_conf), True)
self.assertEqual(crypto_util.sha256sum(self.config.mod_ssl_conf),
self._current_ssl_options_hash())
@ -1685,7 +1665,7 @@ class InstallSslOptionsConfTest(util.ApacheTest):
# prepare should have placed a file there
self._assert_current_file()
os.remove(self.config.mod_ssl_conf)
self.assertFalse(os.path.isfile(self.config.mod_ssl_conf))
self.assertIs(os.path.isfile(self.config.mod_ssl_conf), False)
self._call()
self._assert_current_file()
@ -1707,8 +1687,8 @@ class InstallSslOptionsConfTest(util.ApacheTest):
mod_ssl_conf.write("a new line for the wrong hash\n")
with mock.patch("certbot.plugins.common.logger") as mock_logger:
self._call()
self.assertFalse(mock_logger.warning.called)
self.assertTrue(os.path.isfile(self.config.mod_ssl_conf))
self.assertIs(mock_logger.warning.called, False)
self.assertIs(os.path.isfile(self.config.mod_ssl_conf), True)
self.assertEqual(crypto_util.sha256sum(
self.config.pick_apache_config()),
self._current_ssl_options_hash())
@ -1731,7 +1711,7 @@ class InstallSslOptionsConfTest(util.ApacheTest):
# only print warning once
with mock.patch("certbot.plugins.common.logger") as mock_logger:
self._call()
self.assertFalse(mock_logger.warning.called)
self.assertIs(mock_logger.warning.called, False)
def test_ssl_config_files_hash_in_all_hashes(self):
"""
@ -1747,12 +1727,14 @@ class InstallSslOptionsConfTest(util.ApacheTest):
"certbot_apache", os.path.join("_internal", "tls_configs"))
all_files = [os.path.join(tls_configs_dir, name) for name in os.listdir(tls_configs_dir)
if name.endswith('options-ssl-apache.conf')]
self.assertTrue(all_files)
self.assertGreaterEqual(len(all_files), 1)
for one_file in all_files:
file_hash = crypto_util.sha256sum(one_file)
self.assertTrue(file_hash in ALL_SSL_OPTIONS_HASHES,
"Constants.ALL_SSL_OPTIONS_HASHES must be appended with the sha256 "
"hash of {0} when it is updated.".format(one_file))
self.assertIn(
file_hash, ALL_SSL_OPTIONS_HASHES,
f"Constants.ALL_SSL_OPTIONS_HASHES must be appended with the sha256 "
f"hash of {one_file} when it is updated."
)
def test_openssl_version(self):
self.config._openssl_version = None
@ -1786,14 +1768,14 @@ class InstallSslOptionsConfTest(util.ApacheTest):
def test_current_version(self):
self.config.version = (2, 4, 10)
self.config._openssl_version = '1.0.2m'
self.assertTrue('old' in self.config.pick_apache_config())
self.assertIn('old', self.config.pick_apache_config())
self.config.version = (2, 4, 11)
self.config._openssl_version = '1.0.2m'
self.assertTrue('current' in self.config.pick_apache_config())
self.assertIn('current', self.config.pick_apache_config())
self.config._openssl_version = '1.0.2a'
self.assertTrue('old' in self.config.pick_apache_config())
self.assertIn('old', self.config.pick_apache_config())
def test_openssl_version_warns(self):
self.config._openssl_version = '1.0.2a'
@ -1802,14 +1784,14 @@ class InstallSslOptionsConfTest(util.ApacheTest):
self.config._openssl_version = None
with mock.patch("certbot_apache._internal.configurator.logger.warning") as mock_log:
self.assertEqual(self.config.openssl_version(), None)
self.assertTrue("Could not find ssl_module" in mock_log.call_args[0][0])
self.assertIn("Could not find ssl_module", mock_log.call_args[0][0])
# When no ssl_module is present at all
self.config._openssl_version = None
self.assertTrue("ssl_module" not in self.config.parser.modules)
self.assertNotIn("ssl_module", self.config.parser.modules)
with mock.patch("certbot_apache._internal.configurator.logger.warning") as mock_log:
self.assertEqual(self.config.openssl_version(), None)
self.assertTrue("Could not find ssl_module" in mock_log.call_args[0][0])
self.assertIn("Could not find ssl_module", mock_log.call_args[0][0])
# When ssl_module is statically linked but --apache-bin not provided
self.config._openssl_version = None
@ -1817,13 +1799,13 @@ class InstallSslOptionsConfTest(util.ApacheTest):
self.config.parser.modules['ssl_module'] = None
with mock.patch("certbot_apache._internal.configurator.logger.warning") as mock_log:
self.assertEqual(self.config.openssl_version(), None)
self.assertTrue("ssl_module is statically linked but" in mock_log.call_args[0][0])
self.assertIn("ssl_module is statically linked but", mock_log.call_args[0][0])
self.config.parser.modules['ssl_module'] = "/fake/path"
with mock.patch("certbot_apache._internal.configurator.logger.warning") as mock_log:
# Check that correct logger.warning was printed
self.assertEqual(self.config.openssl_version(), None)
self.assertTrue("Unable to read" in mock_log.call_args[0][0])
self.assertIn("Unable to read", mock_log.call_args[0][0])
contents_missing_openssl = b"these contents won't match the regex"
with mock.patch("certbot_apache._internal.configurator."
@ -1832,7 +1814,7 @@ class InstallSslOptionsConfTest(util.ApacheTest):
with mock.patch("certbot_apache._internal.configurator.logger.warning") as mock_log:
# Check that correct logger.warning was printed
self.assertEqual(self.config.openssl_version(), None)
self.assertTrue("Could not find OpenSSL" in mock_log.call_args[0][0])
self.assertIn("Could not find OpenSSL", mock_log.call_args[0][0])
def test_open_module_file(self):
mock_open = mock.mock_open(read_data="testing 12 3")

View file

@ -45,8 +45,7 @@ class MultipleVhostsTestDebian(util.ApacheTest):
def test_enable_mod_unsupported_dirs(self):
shutil.rmtree(os.path.join(self.config.parser.root, "mods-enabled"))
self.assertRaises(
errors.NotSupportedError, self.config.enable_mod, "ssl")
self.assertRaises(errors.NotSupportedError, self.config.enable_mod, "ssl")
@mock.patch("certbot.util.run_script")
@mock.patch("certbot.util.exe_exists")
@ -58,29 +57,29 @@ class MultipleVhostsTestDebian(util.ApacheTest):
mock_exe_exists.return_value = True
self.config.enable_mod("ssl")
self.assertTrue("ssl_module" in self.config.parser.modules)
self.assertTrue("mod_ssl.c" in self.config.parser.modules)
self.assertIn("ssl_module", self.config.parser.modules)
self.assertIn("mod_ssl.c", self.config.parser.modules)
self.assertTrue(mock_run_script.called)
self.assertIs(mock_run_script.called, True)
def test_deploy_cert_enable_new_vhost(self):
# Create
ssl_vhost = self.config.make_vhost_ssl(self.vh_truth[0])
self.config.parser.modules["ssl_module"] = None
self.config.parser.modules["mod_ssl.c"] = None
self.assertFalse(ssl_vhost.enabled)
self.assertIs(ssl_vhost.enabled, False)
with certbot_util.patch_display_util():
self.config.deploy_cert(
"encryption-example.demo", "example/cert.pem", "example/key.pem",
"example/cert_chain.pem", "example/fullchain.pem")
self.assertTrue(ssl_vhost.enabled)
self.assertIs(ssl_vhost.enabled, True)
# Make sure that we don't error out if symlink already exists
ssl_vhost.enabled = False
self.assertFalse(ssl_vhost.enabled)
self.assertIs(ssl_vhost.enabled, False)
self.config.deploy_cert(
"encryption-example.demo", "example/cert.pem", "example/key.pem",
"example/cert_chain.pem", "example/fullchain.pem")
self.assertTrue(ssl_vhost.enabled)
self.assertIs(ssl_vhost.enabled, True)
def test_enable_site_failure(self):
self.config.parser.root = "/tmp/nonexistent"
@ -110,8 +109,8 @@ class MultipleVhostsTestDebian(util.ApacheTest):
self.config.save()
# Verify ssl_module was enabled.
self.assertTrue(self.vh_truth[1].enabled)
self.assertTrue("ssl_module" in self.config.parser.modules)
self.assertIs(self.vh_truth[1].enabled, True)
self.assertIn("ssl_module", self.config.parser.modules)
loc_cert = self.config.parser.find_dir(
"sslcertificatefile", "example/fullchain.pem",
@ -170,7 +169,7 @@ class MultipleVhostsTestDebian(util.ApacheTest):
# This will create an ssl vhost for certbot.demo
self.config.choose_vhost("certbot.demo")
self.config.enhance("certbot.demo", "staple-ocsp")
self.assertTrue("socache_shmcb_module" in self.config.parser.modules)
self.assertIn("socache_shmcb_module", self.config.parser.modules)
@mock.patch("certbot.util.run_script")
@mock.patch("certbot.util.exe_exists")
@ -183,7 +182,7 @@ class MultipleVhostsTestDebian(util.ApacheTest):
self.config.choose_vhost("certbot.demo")
self.config.enhance("certbot.demo", "ensure-http-header",
"Strict-Transport-Security")
self.assertTrue("headers_module" in self.config.parser.modules)
self.assertIn("headers_module", self.config.parser.modules)
@mock.patch("certbot.util.run_script")
@mock.patch("certbot.util.exe_exists")
@ -194,10 +193,10 @@ class MultipleVhostsTestDebian(util.ApacheTest):
# This will create an ssl vhost for certbot.demo
self.config.choose_vhost("certbot.demo")
self.config.enhance("certbot.demo", "redirect")
self.assertTrue("rewrite_module" in self.config.parser.modules)
self.assertIn("rewrite_module", self.config.parser.modules)
def test_enable_site_already_enabled(self):
self.assertTrue(self.vh_truth[1].enabled)
self.assertIs(self.vh_truth[1].enabled, True)
self.config.enable_site(self.vh_truth[1])
def test_enable_site_call_parent(self):
@ -207,7 +206,7 @@ class MultipleVhostsTestDebian(util.ApacheTest):
vh = self.vh_truth[0]
vh.enabled = False
self.config.enable_site(vh)
self.assertTrue(e_s.called)
self.assertIs(e_s.called, True)
@mock.patch("certbot.util.exe_exists")
def test_enable_mod_no_disable(self, mock_exe_exists):

View file

@ -23,7 +23,7 @@ class SelectVhostMultiTest(unittest.TestCase):
self.base_dir, "debian_apache_2_4/multiple_vhosts")
def test_select_no_input(self):
self.assertFalse(select_vhost_multiple([]))
self.assertEqual(len(select_vhost_multiple([])), 0)
@certbot_util.patch_display_util()
def test_select_correct(self, mock_util):
@ -33,15 +33,15 @@ class SelectVhostMultiTest(unittest.TestCase):
vhs = select_vhost_multiple([self.vhosts[3],
self.vhosts[2],
self.vhosts[1]])
self.assertTrue(self.vhosts[2] in vhs)
self.assertTrue(self.vhosts[3] in vhs)
self.assertFalse(self.vhosts[1] in vhs)
self.assertIn(self.vhosts[2], vhs)
self.assertIn(self.vhosts[3], vhs)
self.assertNotIn(self.vhosts[1], vhs)
@certbot_util.patch_display_util()
def test_select_cancel(self, mock_util):
mock_util().checklist.return_value = (display_util.CANCEL, "whatever")
vhs = select_vhost_multiple([self.vhosts[2], self.vhosts[3]])
self.assertFalse(vhs)
self.assertEqual(vhs, [])
class SelectVhostTest(unittest.TestCase):
@ -68,7 +68,7 @@ class SelectVhostTest(unittest.TestCase):
try:
self._call(self.vhosts)
except errors.MissingCommandlineFlag as e:
self.assertTrue("vhost ambiguity" in str(e))
self.assertIn("vhost ambiguity", str(e))
@certbot_util.patch_display_util()
def test_more_info_cancel(self, mock_util):
@ -76,10 +76,10 @@ class SelectVhostTest(unittest.TestCase):
(display_util.CANCEL, -1),
]
self.assertEqual(None, self._call(self.vhosts))
self.assertIsNone(self._call(self.vhosts))
def test_no_vhosts(self):
self.assertEqual(self._call([]), None)
self.assertIsNone(self._call([]))
@mock.patch("certbot_apache._internal.display_ops.display_util")
@mock.patch("certbot_apache._internal.display_ops.logger")

View file

@ -53,20 +53,20 @@ class DualParserNodeTest(unittest.TestCase): # pylint: disable=too-many-public-
primary=self.block.secondary,
secondary=self.block.primary)
# Switched around
self.assertTrue(cnode.primary is self.comment.secondary)
self.assertTrue(cnode.secondary is self.comment.primary)
self.assertTrue(dnode.primary is self.directive.secondary)
self.assertTrue(dnode.secondary is self.directive.primary)
self.assertTrue(bnode.primary is self.block.secondary)
self.assertTrue(bnode.secondary is self.block.primary)
self.assertEqual(cnode.primary, self.comment.secondary)
self.assertEqual(cnode.secondary, self.comment.primary)
self.assertEqual(dnode.primary, self.directive.secondary)
self.assertEqual(dnode.secondary, self.directive.primary)
self.assertEqual(bnode.primary, self.block.secondary)
self.assertEqual(bnode.secondary, self.block.primary)
def test_set_params(self):
params = ("first", "second")
self.directive.primary.set_parameters = mock.Mock()
self.directive.secondary.set_parameters = mock.Mock()
self.directive.set_parameters(params)
self.assertTrue(self.directive.primary.set_parameters.called)
self.assertTrue(self.directive.secondary.set_parameters.called)
self.assertIs(self.directive.primary.set_parameters.called, True)
self.assertIs(self.directive.secondary.set_parameters.called, True)
def test_set_parameters(self):
pparams = mock.MagicMock()
@ -76,8 +76,8 @@ class DualParserNodeTest(unittest.TestCase): # pylint: disable=too-many-public-
self.directive.primary.set_parameters = pparams
self.directive.secondary.set_parameters = sparams
self.directive.set_parameters(("param", "seq"))
self.assertTrue(pparams.called)
self.assertTrue(sparams.called)
self.assertIs(pparams.called, True)
self.assertIs(sparams.called, True)
def test_delete_child(self):
pdel = mock.MagicMock()
@ -85,8 +85,8 @@ class DualParserNodeTest(unittest.TestCase): # pylint: disable=too-many-public-
self.block.primary.delete_child = pdel
self.block.secondary.delete_child = sdel
self.block.delete_child(self.comment)
self.assertTrue(pdel.called)
self.assertTrue(sdel.called)
self.assertIs(pdel.called, True)
self.assertIs(sdel.called, True)
def test_unsaved_files(self):
puns = mock.MagicMock()
@ -96,8 +96,8 @@ class DualParserNodeTest(unittest.TestCase): # pylint: disable=too-many-public-
self.block.primary.unsaved_files = puns
self.block.secondary.unsaved_files = suns
self.block.unsaved_files()
self.assertTrue(puns.called)
self.assertTrue(suns.called)
self.assertIs(puns.called, True)
self.assertIs(suns.called, True)
def test_getattr_equality(self):
self.directive.primary.variableexception = "value"
@ -140,8 +140,8 @@ class DualParserNodeTest(unittest.TestCase): # pylint: disable=too-many-public-
self.block.primary.add_child_block = mock_first
self.block.secondary.add_child_block = mock_second
self.block.add_child_block("Block")
self.assertTrue(mock_first.called)
self.assertTrue(mock_second.called)
self.assertIs(mock_first.called, True)
self.assertIs(mock_second.called, True)
def test_add_child_directive(self):
mock_first = mock.MagicMock(return_value=self.directive.primary)
@ -149,8 +149,8 @@ class DualParserNodeTest(unittest.TestCase): # pylint: disable=too-many-public-
self.block.primary.add_child_directive = mock_first
self.block.secondary.add_child_directive = mock_second
self.block.add_child_directive("Directive")
self.assertTrue(mock_first.called)
self.assertTrue(mock_second.called)
self.assertIs(mock_first.called, True)
self.assertIs(mock_second.called, True)
def test_add_child_comment(self):
mock_first = mock.MagicMock(return_value=self.comment.primary)
@ -158,8 +158,8 @@ class DualParserNodeTest(unittest.TestCase): # pylint: disable=too-many-public-
self.block.primary.add_child_comment = mock_first
self.block.secondary.add_child_comment = mock_second
self.block.add_child_comment("Comment")
self.assertTrue(mock_first.called)
self.assertTrue(mock_second.called)
self.assertIs(mock_first.called, True)
self.assertIs(mock_second.called, True)
def test_find_comments(self):
pri_comments = [augeasparser.AugeasCommentNode(comment="some comment",
@ -183,9 +183,9 @@ class DualParserNodeTest(unittest.TestCase): # pylint: disable=too-many-public-
# Check that every comment response is represented in the list of
# DualParserNode instances.
for p in p_dcoms:
self.assertTrue(p in p_coms)
self.assertIn(p, p_coms)
for s in s_dcoms:
self.assertTrue(s in s_coms)
self.assertIn(s, s_coms)
def test_find_blocks_first_passing(self):
youshallnotpass = [augeasparser.AugeasBlockNode(name="notpassing",
@ -207,8 +207,8 @@ class DualParserNodeTest(unittest.TestCase): # pylint: disable=too-many-public-
assertions.assertEqual(block.primary, block.secondary)
except AssertionError: # pragma: no cover
self.fail("Assertion should have passed")
self.assertTrue(assertions.isPassDirective(block.primary))
self.assertFalse(assertions.isPassDirective(block.secondary))
self.assertIs(assertions.isPassDirective(block.primary), True)
self.assertIs(assertions.isPassDirective(block.secondary), False)
def test_find_blocks_second_passing(self):
youshallnotpass = [augeasparser.AugeasBlockNode(name="notpassing",
@ -230,8 +230,8 @@ class DualParserNodeTest(unittest.TestCase): # pylint: disable=too-many-public-
assertions.assertEqual(block.primary, block.secondary)
except AssertionError: # pragma: no cover
self.fail("Assertion should have passed")
self.assertFalse(assertions.isPassDirective(block.primary))
self.assertTrue(assertions.isPassDirective(block.secondary))
self.assertIs(assertions.isPassDirective(block.primary), False)
self.assertIs(assertions.isPassDirective(block.secondary), True)
def test_find_dirs_first_passing(self):
notpassing = [augeasparser.AugeasDirectiveNode(name="notpassing",
@ -253,8 +253,8 @@ class DualParserNodeTest(unittest.TestCase): # pylint: disable=too-many-public-
assertions.assertEqual(directive.primary, directive.secondary)
except AssertionError: # pragma: no cover
self.fail("Assertion should have passed")
self.assertTrue(assertions.isPassDirective(directive.primary))
self.assertFalse(assertions.isPassDirective(directive.secondary))
self.assertIs(assertions.isPassDirective(directive.primary), True)
self.assertIs(assertions.isPassDirective(directive.secondary), False)
def test_find_dirs_second_passing(self):
notpassing = [augeasparser.AugeasDirectiveNode(name="notpassing",
@ -276,8 +276,8 @@ class DualParserNodeTest(unittest.TestCase): # pylint: disable=too-many-public-
assertions.assertEqual(directive.primary, directive.secondary)
except AssertionError: # pragma: no cover
self.fail("Assertion should have passed")
self.assertFalse(assertions.isPassDirective(directive.primary))
self.assertTrue(assertions.isPassDirective(directive.secondary))
self.assertIs(assertions.isPassDirective(directive.primary), False)
self.assertIs(assertions.isPassDirective(directive.secondary), True)
def test_find_coms_first_passing(self):
notpassing = [augeasparser.AugeasCommentNode(comment="notpassing",
@ -299,8 +299,8 @@ class DualParserNodeTest(unittest.TestCase): # pylint: disable=too-many-public-
assertions.assertEqual(comment.primary, comment.secondary)
except AssertionError: # pragma: no cover
self.fail("Assertion should have passed")
self.assertTrue(assertions.isPassComment(comment.primary))
self.assertFalse(assertions.isPassComment(comment.secondary))
self.assertIs(assertions.isPassComment(comment.primary), True)
self.assertIs(assertions.isPassComment(comment.secondary), False)
def test_find_coms_second_passing(self):
notpassing = [augeasparser.AugeasCommentNode(comment="notpassing",
@ -322,8 +322,8 @@ class DualParserNodeTest(unittest.TestCase): # pylint: disable=too-many-public-
assertions.assertEqual(comment.primary, comment.secondary)
except AssertionError: # pragma: no cover
self.fail("Assertion should have passed")
self.assertFalse(assertions.isPassComment(comment.primary))
self.assertTrue(assertions.isPassComment(comment.secondary))
self.assertIs(assertions.isPassComment(comment.primary), False)
self.assertIs(assertions.isPassComment(comment.secondary), True)
def test_find_blocks_no_pass_equal(self):
notpassing1 = [augeasparser.AugeasBlockNode(name="notpassing",
@ -341,8 +341,9 @@ class DualParserNodeTest(unittest.TestCase): # pylint: disable=too-many-public-
blocks = self.block.find_blocks("anything")
for block in blocks:
self.assertEqual(block.primary, block.secondary)
self.assertTrue(block.primary is not block.secondary)
with self.subTest(block=block):
self.assertEqual(block.primary, block.secondary)
self.assertIsNot(block.primary, block.secondary)
def test_find_dirs_no_pass_equal(self):
notpassing1 = [augeasparser.AugeasDirectiveNode(name="notpassing",
@ -360,8 +361,9 @@ class DualParserNodeTest(unittest.TestCase): # pylint: disable=too-many-public-
directives = self.block.find_directives("anything")
for directive in directives:
self.assertEqual(directive.primary, directive.secondary)
self.assertTrue(directive.primary is not directive.secondary)
with self.subTest(directive=directive):
self.assertEqual(directive.primary, directive.secondary)
self.assertIsNot(directive.primary, directive.secondary)
def test_find_comments_no_pass_equal(self):
notpassing1 = [augeasparser.AugeasCommentNode(comment="notpassing",
@ -379,8 +381,9 @@ class DualParserNodeTest(unittest.TestCase): # pylint: disable=too-many-public-
comments = self.block.find_comments("anything")
for comment in comments:
self.assertEqual(comment.primary, comment.secondary)
self.assertTrue(comment.primary is not comment.secondary)
with self.subTest(comment=comment):
self.assertEqual(comment.primary, comment.secondary)
self.assertIsNot(comment.primary, comment.secondary)
def test_find_blocks_no_pass_notequal(self):
notpassing1 = [augeasparser.AugeasBlockNode(name="notpassing",
@ -424,8 +427,8 @@ class DualParserNodeTest(unittest.TestCase): # pylint: disable=too-many-public-
self.block.primary.parsed_paths = mock_p
self.block.secondary.parsed_paths = mock_s
self.block.parsed_paths()
self.assertTrue(mock_p.called)
self.assertTrue(mock_s.called)
self.assertIs(mock_p.called, True)
self.assertIs(mock_s.called, True)
def test_parsed_paths_error(self):
mock_p = mock.MagicMock(return_value=['/path/file.conf'])
@ -441,5 +444,5 @@ class DualParserNodeTest(unittest.TestCase): # pylint: disable=too-many-public-
self.block.primary.find_ancestors = primarymock
self.block.secondary.find_ancestors = secondarymock
self.block.find_ancestors("anything")
self.assertTrue(primarymock.called)
self.assertTrue(secondarymock.called)
self.assertIs(primarymock.called, True)
self.assertIs(secondarymock.called, True)

View file

@ -134,8 +134,8 @@ class MultipleVhostsTestFedora(util.ApacheTest):
self.assertEqual(mock_get.call_count, 3)
self.assertEqual(len(self.config.parser.modules), 4)
self.assertEqual(len(self.config.parser.variables), 2)
self.assertTrue("TEST2" in self.config.parser.variables)
self.assertTrue("mod_another.c" in self.config.parser.modules)
self.assertIn("TEST2", self.config.parser.variables)
self.assertIn("mod_another.c", self.config.parser.modules)
@mock.patch("certbot_apache._internal.configurator.util.run_script")
def test_get_version(self, mock_run_script):
@ -172,11 +172,11 @@ class MultipleVhostsTestFedora(util.ApacheTest):
mock_osi.return_value = ("fedora", "29")
self.config.parser.update_runtime_variables()
self.assertTrue("mock_define" in self.config.parser.variables)
self.assertTrue("mock_define_too" in self.config.parser.variables)
self.assertTrue("mock_value" in self.config.parser.variables)
self.assertIn("mock_define", self.config.parser.variables)
self.assertIn("mock_define_too", self.config.parser.variables)
self.assertIn("mock_value", self.config.parser.variables)
self.assertEqual("TRUE", self.config.parser.variables["mock_value"])
self.assertTrue("MOCK_NOSEP" in self.config.parser.variables)
self.assertIn("MOCK_NOSEP", self.config.parser.variables)
self.assertEqual("NOSEP_VAL", self.config.parser.variables["NOSEP_TWO"])
@mock.patch("certbot_apache._internal.configurator.util.run_script")

View file

@ -63,8 +63,7 @@ class MultipleVhostsTestGentoo(util.ApacheTest):
self.temp_dir, "gentoo_apache/apache")
def test_get_parser(self):
self.assertTrue(isinstance(self.config.parser,
override_gentoo.GentooParser))
self.assertIsInstance(self.config.parser, override_gentoo.GentooParser)
def test_get_virtual_hosts(self):
"""Make sure all vhosts are being properly found."""
@ -91,7 +90,7 @@ class MultipleVhostsTestGentoo(util.ApacheTest):
with mock.patch("certbot_apache._internal.override_gentoo.GentooParser.update_modules"):
self.config.parser.update_runtime_variables()
for define in defines:
self.assertTrue(define in self.config.parser.variables)
self.assertIn(define, self.config.parser.variables)
@mock.patch("certbot_apache._internal.apache_util.parse_from_subprocess")
def test_no_binary_configdump(self, mock_subprocess):
@ -101,11 +100,11 @@ class MultipleVhostsTestGentoo(util.ApacheTest):
with mock.patch("certbot_apache._internal.override_gentoo.GentooParser.update_modules"):
self.config.parser.update_runtime_variables()
self.config.parser.reset_modules()
self.assertFalse(mock_subprocess.called)
self.assertIs(mock_subprocess.called, False)
self.config.parser.update_runtime_variables()
self.config.parser.reset_modules()
self.assertTrue(mock_subprocess.called)
self.assertIs(mock_subprocess.called, True)
@mock.patch("certbot_apache._internal.apache_util._get_runtime_cfg")
def test_opportunistic_httpd_runtime_parsing(self, mock_get):
@ -129,7 +128,7 @@ class MultipleVhostsTestGentoo(util.ApacheTest):
self.assertEqual(mock_get.call_count, 1)
self.assertEqual(len(self.config.parser.modules), 4)
self.assertTrue("mod_another.c" in self.config.parser.modules)
self.assertIn("mod_another.c", self.config.parser.modules)
@mock.patch("certbot_apache._internal.configurator.util.run_script")
def test_alt_restart_works(self, mock_run_script):

View file

@ -51,7 +51,7 @@ class ApacheHttp01Test(util.ApacheTest):
self.http = ApacheHttp01(self.config)
def test_empty_perform(self):
self.assertFalse(self.http.perform())
self.assertEqual(len(self.http.perform()), 0)
@mock.patch("certbot_apache._internal.configurator.ApacheConfigurator.enable_mod")
def test_enable_modules_apache_2_2(self, mock_enmod):
@ -77,7 +77,7 @@ class ApacheHttp01Test(util.ApacheTest):
self.http.prepare_http01_modules()
self.assertTrue(mock_enmod.called)
self.assertIs(mock_enmod.called, True)
calls = mock_enmod.call_args_list
other_calls = []
for call in calls:
@ -186,7 +186,7 @@ class ApacheHttp01Test(util.ApacheTest):
def common_perform_test(self, achalls, vhosts):
"""Tests perform with the given achalls."""
challenge_dir = self.http.challenge_dir
self.assertFalse(os.path.exists(challenge_dir))
self.assertIs(os.path.exists(challenge_dir), False)
for achall in achalls:
self.http.add_chall(achall)
@ -194,8 +194,8 @@ class ApacheHttp01Test(util.ApacheTest):
achall.response(self.account_key) for achall in achalls]
self.assertEqual(self.http.perform(), expected_response)
self.assertTrue(os.path.isdir(self.http.challenge_dir))
self.assertTrue(filesystem.has_min_permissions(self.http.challenge_dir, 0o755))
self.assertIs(os.path.isdir(self.http.challenge_dir), True)
self.assertIs(filesystem.has_min_permissions(self.http.challenge_dir, 0o755), True)
self._test_challenge_conf()
for achall in achalls:
@ -211,7 +211,7 @@ class ApacheHttp01Test(util.ApacheTest):
vhost.path)
self.assertEqual(len(matches), 1)
self.assertTrue(os.path.exists(challenge_dir))
self.assertIs(os.path.exists(challenge_dir), True)
@mock.patch("certbot_apache._internal.http_01.filesystem.makedirs")
def test_failed_makedirs(self, mock_makedirs):
@ -226,20 +226,20 @@ class ApacheHttp01Test(util.ApacheTest):
with open(self.http.challenge_conf_post) as f:
post_conf_contents = f.read()
self.assertTrue("RewriteEngine on" in pre_conf_contents)
self.assertTrue("RewriteRule" in pre_conf_contents)
self.assertIn("RewriteEngine on", pre_conf_contents)
self.assertIn("RewriteRule", pre_conf_contents)
self.assertTrue(self.http.challenge_dir in post_conf_contents)
self.assertIn(self.http.challenge_dir, post_conf_contents)
if self.config.version < (2, 4):
self.assertTrue("Allow from all" in post_conf_contents)
self.assertIn("Allow from all", post_conf_contents)
else:
self.assertTrue("Require all granted" in post_conf_contents)
self.assertIn("Require all granted", post_conf_contents)
def _test_challenge_file(self, achall):
name = os.path.join(self.http.challenge_dir, achall.chall.encode("token"))
validation = achall.validation(self.account_key)
self.assertTrue(filesystem.has_min_permissions(name, 0o644))
self.assertIs(filesystem.has_min_permissions(name, 0o644), True)
with open(name, 'rb') as f:
self.assertEqual(f.read(), validation.encode())

View file

@ -44,15 +44,14 @@ class VirtualHostTest(unittest.TestCase):
"fp", "vhp",
{Addr.fromstring("*:443"), Addr.fromstring("1.2.3.4:443")},
False, False)
self.assertTrue(complex_vh.conflicts([self.addr1]))
self.assertTrue(complex_vh.conflicts([self.addr2]))
self.assertFalse(complex_vh.conflicts([self.addr_default]))
self.assertIs(complex_vh.conflicts([self.addr1]), True)
self.assertIs(complex_vh.conflicts([self.addr2]), True)
self.assertIs(complex_vh.conflicts([self.addr_default]), False)
self.assertTrue(self.vhost1.conflicts([self.addr2]))
self.assertFalse(self.vhost1.conflicts([self.addr_default]))
self.assertIs(self.vhost1.conflicts([self.addr2]), True)
self.assertIs(self.vhost1.conflicts([self.addr_default]), False)
self.assertFalse(self.vhost2.conflicts([self.addr1,
self.addr_default]))
self.assertIs(self.vhost2.conflicts([self.addr1, self.addr_default]), False)
def test_same_server(self):
from certbot_apache._internal.obj import VirtualHost
@ -67,12 +66,12 @@ class VirtualHostTest(unittest.TestCase):
"fp", "vhp", {self.addr2, self.addr_default},
False, False, None)
self.assertTrue(self.vhost1.same_server(self.vhost2))
self.assertTrue(no_name1.same_server(no_name2))
self.assertIs(self.vhost1.same_server(self.vhost2), True)
self.assertIs(no_name1.same_server(no_name2), True)
self.assertFalse(self.vhost1.same_server(no_name1))
self.assertFalse(no_name1.same_server(no_name3))
self.assertFalse(no_name1.same_server(no_name4))
self.assertIs(self.vhost1.same_server(no_name1), False)
self.assertIs(no_name1.same_server(no_name3), False)
self.assertIs(no_name1.same_server(no_name4), False)
class AddrTest(unittest.TestCase):
@ -88,9 +87,9 @@ class AddrTest(unittest.TestCase):
self.addr_default = Addr.fromstring("_default_:443")
def test_wildcard(self):
self.assertFalse(self.addr.is_wildcard())
self.assertTrue(self.addr1.is_wildcard())
self.assertTrue(self.addr2.is_wildcard())
self.assertIs(self.addr.is_wildcard(), False)
self.assertIs(self.addr1.is_wildcard(), True)
self.assertIs(self.addr2.is_wildcard(), True)
def test_get_sni_addr(self):
from certbot_apache._internal.obj import Addr
@ -103,29 +102,29 @@ class AddrTest(unittest.TestCase):
def test_conflicts(self):
# Note: Defined IP is more important than defined port in match
self.assertTrue(self.addr.conflicts(self.addr1))
self.assertTrue(self.addr.conflicts(self.addr2))
self.assertTrue(self.addr.conflicts(self.addr_defined))
self.assertFalse(self.addr.conflicts(self.addr_default))
self.assertIs(self.addr.conflicts(self.addr1), True)
self.assertIs(self.addr.conflicts(self.addr2), True)
self.assertIs(self.addr.conflicts(self.addr_defined), True)
self.assertIs(self.addr.conflicts(self.addr_default), False)
self.assertFalse(self.addr1.conflicts(self.addr))
self.assertTrue(self.addr1.conflicts(self.addr_defined))
self.assertFalse(self.addr1.conflicts(self.addr_default))
self.assertIs(self.addr1.conflicts(self.addr), False)
self.assertIs(self.addr1.conflicts(self.addr_defined), True)
self.assertIs(self.addr1.conflicts(self.addr_default), False)
self.assertFalse(self.addr_defined.conflicts(self.addr1))
self.assertFalse(self.addr_defined.conflicts(self.addr2))
self.assertFalse(self.addr_defined.conflicts(self.addr))
self.assertFalse(self.addr_defined.conflicts(self.addr_default))
self.assertIs(self.addr_defined.conflicts(self.addr1), False)
self.assertIs(self.addr_defined.conflicts(self.addr2), False)
self.assertIs(self.addr_defined.conflicts(self.addr), False)
self.assertIs(self.addr_defined.conflicts(self.addr_default), False)
self.assertTrue(self.addr_default.conflicts(self.addr))
self.assertTrue(self.addr_default.conflicts(self.addr1))
self.assertTrue(self.addr_default.conflicts(self.addr_defined))
self.assertIs(self.addr_default.conflicts(self.addr), True)
self.assertIs(self.addr_default.conflicts(self.addr1), True)
self.assertIs(self.addr_default.conflicts(self.addr_defined), True)
# Self test
self.assertTrue(self.addr.conflicts(self.addr))
self.assertTrue(self.addr1.conflicts(self.addr1))
self.assertIs(self.addr.conflicts(self.addr), True)
self.assertIs(self.addr1.conflicts(self.addr1), True)
# This is a tricky one...
self.assertTrue(self.addr1.conflicts(self.addr2))
self.assertIs(self.addr1.conflicts(self.addr2), True)
def test_equal(self):
self.assertEqual(self.addr1, self.addr2)

View file

@ -42,7 +42,7 @@ class BasicParserTest(util.ParserTest):
self.assertEqual(self.parser.check_aug_version(),
["something"])
self.parser.aug.match.side_effect = RuntimeError
self.assertFalse(self.parser.check_aug_version())
self.assertIs(self.parser.check_aug_version(), False)
def test_find_config_root_no_root(self):
# pylint: disable=protected-access
@ -80,8 +80,7 @@ class BasicParserTest(util.ParserTest):
aug_default = "/files" + self.parser.loc["default"]
self.parser.add_dir(aug_default, "AddDirective", "test")
self.assertTrue(
self.parser.find_dir("AddDirective", "test", aug_default))
self.assertTrue(self.parser.find_dir("AddDirective", "test", aug_default))
self.parser.add_dir(aug_default, "AddList", ["1", "2", "3", "4"])
matches = self.parser.find_dir("AddList", None, aug_default)
@ -94,12 +93,9 @@ class BasicParserTest(util.ParserTest):
"AddDirectiveBeginning",
"testBegin")
self.assertTrue(
self.parser.find_dir("AddDirectiveBeginning", "testBegin", aug_default))
self.assertTrue(self.parser.find_dir("AddDirectiveBeginning", "testBegin", aug_default))
self.assertEqual(
self.parser.aug.get(aug_default+"/directive[1]"),
"AddDirectiveBeginning")
self.assertEqual(self.parser.aug.get(aug_default+"/directive[1]"), "AddDirectiveBeginning")
self.parser.add_dir_beginning(aug_default, "AddList", ["1", "2", "3", "4"])
matches = self.parser.find_dir("AddList", None, aug_default)
for i, match in enumerate(matches):
@ -108,11 +104,13 @@ class BasicParserTest(util.ParserTest):
for name in ("empty.conf", "no-directives.conf"):
conf = "/files" + os.path.join(self.parser.root, "sites-available", name)
self.parser.add_dir_beginning(conf, "AddDirectiveBeginning", "testBegin")
self.assertTrue(self.parser.find_dir("AddDirectiveBeginning", "testBegin", conf))
self.assertGreater(
len(self.parser.find_dir("AddDirectiveBeginning", "testBegin", conf)),
0
)
def test_empty_arg(self):
self.assertEqual(None,
self.parser.get_arg("/files/whatever/nonexistent"))
self.assertIsNone(self.parser.get_arg("/files/whatever/nonexistent"))
def test_add_dir_to_ifmodssl(self):
"""test add_dir_to_ifmodssl.
@ -131,7 +129,7 @@ class BasicParserTest(util.ParserTest):
matches = self.parser.find_dir("FakeDirective", "123")
self.assertEqual(len(matches), 1)
self.assertTrue("IfModule" in matches[0])
self.assertIn("IfModule", matches[0])
def test_add_dir_to_ifmodssl_multiple(self):
from certbot_apache._internal.parser import get_aug_path
@ -145,7 +143,7 @@ class BasicParserTest(util.ParserTest):
matches = self.parser.find_dir("FakeDirective")
self.assertEqual(len(matches), 3)
self.assertTrue("IfModule" in matches[0])
self.assertIn("IfModule", matches[0])
def test_get_aug_path(self):
from certbot_apache._internal.parser import get_aug_path
@ -170,7 +168,7 @@ class BasicParserTest(util.ParserTest):
with mock.patch("certbot_apache._internal.parser.logger") as mock_logger:
self.parser.parse_modules()
# Make sure that we got None return value and logged the file
self.assertTrue(mock_logger.debug.called)
self.assertIs(mock_logger.debug.called, True)
@mock.patch("certbot_apache._internal.parser.ApacheParser.find_dir")
@mock.patch("certbot_apache._internal.apache_util._get_runtime_cfg")
@ -328,7 +326,7 @@ class BasicParserTest(util.ParserTest):
self.parser.add_comment(get_aug_path(self.parser.loc["name"]), "123456")
comm = self.parser.find_comments("123456")
self.assertEqual(len(comm), 1)
self.assertTrue(self.parser.loc["name"] in comm[0])
self.assertIn(self.parser.loc["name"], comm[0])
class ParserInitTest(util.ApacheTest):

View file

@ -32,7 +32,7 @@ class ConfiguratorParserNodeTest(util.ApacheTest): # pylint: disable=too-many-p
self.config.USE_PARSERNODE = True
vhosts = self.config.get_virtual_hosts()
# Legacy get_virtual_hosts() do not set the node
self.assertTrue(vhosts[0].node is not None)
self.assertIsNotNone(vhosts[0].node)
def test_parsernode_get_vhosts_mismatch(self):
vhosts = self.config.get_virtual_hosts_v2()

View file

@ -1,6 +1,5 @@
"""Common utilities for certbot_apache."""
import shutil
import sys
import unittest
import augeas
@ -14,7 +13,6 @@ except ImportError: # pragma: no cover
from certbot.compat import os
from certbot.plugins import common
from certbot.tests import util as test_util
from certbot.display import util as display_util
from certbot_apache._internal import configurator
from certbot_apache._internal import entrypoint
from certbot_apache._internal import obj

View file

@ -77,6 +77,15 @@ Examples
-d example.com \\
-d www.example.com
.. code-block:: bash
:caption: To acquire a wildcard certificate for ``*.example.com``
certbot certonly \\
--dns-digitalocean \\
--dns-digitalocean-credentials ~/.secrets/certbot/digitalocean.ini \\
-d example.com \\
-d '*.example.com'
.. code-block:: bash
:caption: To acquire a certificate for ``example.com``, waiting 60 seconds
for DNS propagation

View file

@ -184,9 +184,9 @@ class GoogleClientTest(unittest.TestCase):
with mock.patch(mock_get_rrs) as mock_rrs:
mock_rrs.return_value = {"rrdatas": ["sample-txt-contents"], "ttl": self.record_ttl}
client.add_txt_record(DOMAIN, self.record_name, self.record_content, self.record_ttl)
self.assertTrue(changes.create.called)
self.assertIs(changes.create.called, True)
deletions = changes.create.call_args_list[0][1]["body"]["deletions"][0]
self.assertTrue("sample-txt-contents" in deletions["rrdatas"])
self.assertIn("sample-txt-contents", deletions["rrdatas"])
self.assertEqual(self.record_ttl, deletions["ttl"])
@mock.patch('oauth2client.service_account.ServiceAccountCredentials.from_json_keyfile_name')
@ -201,9 +201,9 @@ class GoogleClientTest(unittest.TestCase):
custom_ttl = 300
mock_rrs.return_value = {"rrdatas": ["sample-txt-contents"], "ttl": custom_ttl}
client.add_txt_record(DOMAIN, self.record_name, self.record_content, self.record_ttl)
self.assertTrue(changes.create.called)
self.assertIs(changes.create.called, True)
deletions = changes.create.call_args_list[0][1]["body"]["deletions"][0]
self.assertTrue("sample-txt-contents" in deletions["rrdatas"])
self.assertIn("sample-txt-contents", deletions["rrdatas"])
self.assertEqual(custom_ttl, deletions["ttl"]) #otherwise HTTP 412
@mock.patch('oauth2client.service_account.ServiceAccountCredentials.from_json_keyfile_name')
@ -214,7 +214,7 @@ class GoogleClientTest(unittest.TestCase):
[{'managedZones': [{'id': self.zone}]}])
client.add_txt_record(DOMAIN, "_acme-challenge.example.org",
"example-txt-contents", self.record_ttl)
self.assertFalse(changes.create.called)
self.assertIs(changes.create.called, False)
@mock.patch('oauth2client.service_account.ServiceAccountCredentials.from_json_keyfile_name')
@mock.patch('certbot_dns_google._internal.dns_google.open',
@ -357,7 +357,7 @@ class GoogleClientTest(unittest.TestCase):
client, unused_changes = self._setUp_client_with_mock(
[{'managedZones': [{'id': self.zone}]}])
not_found = client.get_existing_txt_rrset(self.zone, "nonexistent.tld")
self.assertEqual(not_found, None)
self.assertIsNone(not_found)
@mock.patch('oauth2client.service_account.ServiceAccountCredentials.from_json_keyfile_name')
@mock.patch('certbot_dns_google._internal.dns_google.open',
@ -367,7 +367,7 @@ class GoogleClientTest(unittest.TestCase):
[{'managedZones': [{'id': self.zone}]}], API_ERROR)
# Record name mocked in setUp
found = client.get_existing_txt_rrset(self.zone, "_acme-challenge.example.org")
self.assertEqual(found, None)
self.assertIsNone(found)
@mock.patch('oauth2client.service_account.ServiceAccountCredentials.from_json_keyfile_name')
@mock.patch('certbot_dns_google._internal.dns_google.open',
@ -411,5 +411,6 @@ class DummyResponse:
def __init__(self):
self.status = 200
if __name__ == "__main__":
unittest.main() # pragma: no cover

View file

@ -18,6 +18,7 @@ TOKEN = 'a-token'
TOKEN_V3 = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ64'
TOKEN_V4 = '0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef'
class AuthenticatorTest(test_util.TempDirTestCase,
dns_test_common_lexicon.BaseLexiconAuthenticatorTest):
@ -119,6 +120,7 @@ class AuthenticatorTest(test_util.TempDirTestCase,
auth._setup_credentials()
self.assertRaises(errors.PluginError, auth._get_linode_client)
class LinodeLexiconClientTest(unittest.TestCase, dns_test_common_lexicon.BaseLexiconClientTest):
DOMAIN_NOT_FOUND = Exception('Domain not found')
@ -131,6 +133,7 @@ class LinodeLexiconClientTest(unittest.TestCase, dns_test_common_lexicon.BaseLex
self.provider_mock = mock.MagicMock()
self.client.provider = self.provider_mock
class Linode4LexiconClientTest(unittest.TestCase, dns_test_common_lexicon.BaseLexiconClientTest):
DOMAIN_NOT_FOUND = Exception('Domain not found')
@ -143,5 +146,6 @@ class Linode4LexiconClientTest(unittest.TestCase, dns_test_common_lexicon.BaseLe
self.provider_mock = mock.MagicMock()
self.client.provider = self.provider_mock
if __name__ == "__main__":
unittest.main() # pragma: no cover

View file

@ -23,6 +23,7 @@ SECRET = 'SSB3b25kZXIgd2hvIHdpbGwgYm90aGVyIHRvIGRlY29kZSB0aGlzIHRleHQK'
VALID_CONFIG = {"rfc2136_server": SERVER, "rfc2136_name": NAME, "rfc2136_secret": SECRET}
TIMEOUT = 45
class AuthenticatorTest(test_util.TempDirTestCase, dns_test_common.BaseAuthenticatorTest):
def setUp(self):
@ -113,7 +114,7 @@ class RFC2136ClientTest(unittest.TestCase):
self.rfc2136_client.add_txt_record("bar", "baz", 42)
query_mock.assert_called_with(mock.ANY, SERVER, TIMEOUT, PORT)
self.assertTrue("bar. 42 IN TXT \"baz\"" in str(query_mock.call_args[0][0]))
self.assertIn('bar. 42 IN TXT "baz"', str(query_mock.call_args[0][0]))
@mock.patch("dns.query.tcp")
def test_add_txt_record_wraps_errors(self, query_mock):
@ -146,7 +147,7 @@ class RFC2136ClientTest(unittest.TestCase):
self.rfc2136_client.del_txt_record("bar", "baz")
query_mock.assert_called_with(mock.ANY, SERVER, TIMEOUT, PORT)
self.assertTrue("bar. 0 NONE TXT \"baz\"" in str(query_mock.call_args[0][0]))
self.assertIn('bar. 0 NONE TXT "baz"', str(query_mock.call_args[0][0]))
@mock.patch("dns.query.tcp")
def test_del_txt_record_wraps_errors(self, query_mock):

View file

@ -6,30 +6,38 @@ import socket
import subprocess
import tempfile
import time
from typing import Any
from typing import Callable
from typing import Dict
from typing import Iterable
from typing import List
from typing import Mapping
from typing import Optional
from typing import Sequence
from typing import Set
from typing import Text
from typing import Tuple
from typing import Type
from typing import Union
from certbot_nginx._internal import constants
from certbot_nginx._internal import display_ops
from certbot_nginx._internal import http_01
from certbot_nginx._internal import nginxparser
from certbot_nginx._internal import obj
from certbot_nginx._internal import parser
import OpenSSL
import pkg_resources
from acme import challenges
from acme import crypto_util as acme_crypto_util
from certbot import achallenges
from certbot import crypto_util
from certbot import errors
from certbot import util
from certbot.display import util as display_util
from certbot.compat import os
from certbot.display import util as display_util
from certbot.plugins import common
from certbot_nginx._internal import constants
from certbot_nginx._internal import display_ops
from certbot_nginx._internal import http_01
from certbot_nginx._internal import nginxparser
from certbot_nginx._internal import obj # pylint: disable=unused-import
from certbot_nginx._internal import parser
NAME_RANK = 0
START_WILDCARD_RANK = 1
@ -70,7 +78,7 @@ class NginxConfigurator(common.Configurator):
SSL_DIRECTIVES = ['ssl_certificate', 'ssl_certificate_key', 'ssl_dhparam']
@classmethod
def add_parser_arguments(cls, add):
def add_parser_arguments(cls, add: Callable[..., None]) -> None:
default_server_root = _determine_default_server_root()
add("server-root", default=constants.CLI_DEFAULTS["server_root"],
help="Nginx server root directory. (default: %s)" % default_server_root)
@ -82,11 +90,11 @@ class NginxConfigurator(common.Configurator):
"to apply when reloading.")
@property
def nginx_conf(self):
def nginx_conf(self) -> str:
"""Nginx config file path."""
return os.path.join(self.conf("server_root"), "nginx.conf")
def __init__(self, *args, **kwargs):
def __init__(self, *args: Any, **kwargs: Any) -> None:
"""Initialize an Nginx Configurator.
:param tup version: version of Nginx as a tuple (1, 4, 7)
@ -125,7 +133,7 @@ class NginxConfigurator(common.Configurator):
self.parser: parser.NginxParser
@property
def mod_ssl_conf_src(self):
def mod_ssl_conf_src(self) -> str:
"""Full absolute path to SSL configuration file source."""
# Why all this complexity? Well, we want to support Mozilla's intermediate
@ -159,22 +167,23 @@ class NginxConfigurator(common.Configurator):
"certbot_nginx", os.path.join("_internal", "tls_configs", config_filename))
@property
def mod_ssl_conf(self):
def mod_ssl_conf(self) -> str:
"""Full absolute path to SSL configuration file."""
return os.path.join(self.config.config_dir, constants.MOD_SSL_CONF_DEST)
@property
def updated_mod_ssl_conf_digest(self):
def updated_mod_ssl_conf_digest(self) -> str:
"""Full absolute path to digest of updated SSL configuration file."""
return os.path.join(self.config.config_dir, constants.UPDATED_MOD_SSL_CONF_DIGEST)
def install_ssl_options_conf(self, options_ssl, options_ssl_digest):
def install_ssl_options_conf(self, options_ssl: str, options_ssl_digest: str) -> None:
"""Copy Certbot's SSL options file into the system's config dir if required."""
return common.install_version_controlled_file(options_ssl, options_ssl_digest,
common.install_version_controlled_file(
options_ssl, options_ssl_digest,
self.mod_ssl_conf_src, constants.ALL_SSL_OPTIONS_HASHES)
# This is called in determine_authenticator and determine_installer
def prepare(self):
def prepare(self) -> None:
"""Prepare the authenticator/installer.
:raises .errors.NoInstallationError: If Nginx ctl cannot be found
@ -210,8 +219,8 @@ class NginxConfigurator(common.Configurator):
raise errors.PluginError('Unable to lock {0}'.format(self.conf('server-root')))
# Entry point in main.py for installing cert
def deploy_cert(self, domain, cert_path, key_path,
chain_path=None, fullchain_path=None):
def deploy_cert(self, domain: str, cert_path: str, key_path: str, chain_path: str,
fullchain_path: str) -> None:
"""Deploys certificate to specified virtual host.
.. note:: Aborts if the vhost is missing ssl_certificate or
@ -234,7 +243,8 @@ class NginxConfigurator(common.Configurator):
display_util.notify("Successfully deployed certificate for {} to {}"
.format(domain, vhost.filep))
def _deploy_cert(self, vhost, cert_path, key_path, chain_path, fullchain_path): # pylint: disable=unused-argument
def _deploy_cert(self, vhost: obj.VirtualHost, _cert_path: str, key_path: str,
_chain_path: str, fullchain_path: str) -> None:
"""
Helper function for deploy_cert() that handles the actual deployment
this exists because we might want to do multiple deployments per
@ -244,8 +254,7 @@ class NginxConfigurator(common.Configurator):
cert_directives = [['\n ', 'ssl_certificate', ' ', fullchain_path],
['\n ', 'ssl_certificate_key', ' ', key_path]]
self.parser.update_or_add_server_directives(vhost,
cert_directives)
self.parser.update_or_add_server_directives(vhost, cert_directives)
logger.info("Deploying Certificate to VirtualHost %s", vhost.filep)
self.save_notes += ("Changed vhost at %s with addresses of %s\n" %
@ -254,7 +263,8 @@ class NginxConfigurator(common.Configurator):
self.save_notes += "\tssl_certificate %s\n" % fullchain_path
self.save_notes += "\tssl_certificate_key %s\n" % key_path
def _choose_vhosts_wildcard(self, domain, prefer_ssl, no_ssl_filter_port=None):
def _choose_vhosts_wildcard(self, domain: str, prefer_ssl: bool,
no_ssl_filter_port: Optional[str] = None) -> List[obj.VirtualHost]:
"""Prompts user to choose vhosts to install a wildcard certificate for"""
if prefer_ssl:
vhosts_cache = self._wildcard_vhosts
@ -303,12 +313,13 @@ class NginxConfigurator(common.Configurator):
#######################
# Vhost parsing methods
#######################
def _choose_vhost_single(self, target_name):
def _choose_vhost_single(self, target_name: str) -> List[obj.VirtualHost]:
matches = self._get_ranked_matches(target_name)
vhosts = [x for x in [self._select_best_name_match(matches)] if x is not None]
return vhosts
def choose_vhosts(self, target_name, create_if_no_match=False):
def choose_vhosts(self, target_name: str,
create_if_no_match: bool = False) -> List[obj.VirtualHost]:
"""Chooses a virtual host based on the given domain name.
.. note:: This makes the vhost SSL-enabled if it isn't already. Follows
@ -352,7 +363,7 @@ class NginxConfigurator(common.Configurator):
return vhosts
def ipv6_info(self, port):
def ipv6_info(self, port: str) -> Tuple[bool, bool]:
"""Returns tuple of booleans (ipv6_active, ipv6only_present)
ipv6_active is true if any server block listens ipv6 address in any port
@ -365,9 +376,6 @@ class NginxConfigurator(common.Configurator):
configuration, and existence of ipv6only directive for specified port
:rtype: tuple of type (bool, bool)
"""
# port should be a string, but it's easy to mess up, so let's
# make sure it is one
port = str(port)
vhosts = self.parser.get_vhosts()
ipv6_active = False
ipv6only_present = False
@ -377,10 +385,10 @@ class NginxConfigurator(common.Configurator):
ipv6_active = True
if addr.ipv6only and addr.get_port() == port:
ipv6only_present = True
return (ipv6_active, ipv6only_present)
return ipv6_active, ipv6only_present
def _vhost_from_duplicated_default(self, domain: str, allow_port_mismatch: bool, port: str
) -> obj.VirtualHost:
def _vhost_from_duplicated_default(self, domain: str, allow_port_mismatch: bool,
port: str) -> obj.VirtualHost:
"""if allow_port_mismatch is False, only server blocks with matching ports will be
used as a default server block template.
"""
@ -395,7 +403,7 @@ class NginxConfigurator(common.Configurator):
self._add_server_name_to_vhost(self.new_vhost, domain)
return self.new_vhost
def _add_server_name_to_vhost(self, vhost, domain):
def _add_server_name_to_vhost(self, vhost: obj.VirtualHost, domain: str) -> None:
vhost.names.add(domain)
name_block = [['\n ', 'server_name']]
for name in vhost.names:
@ -403,7 +411,8 @@ class NginxConfigurator(common.Configurator):
name_block[0].append(name)
self.parser.update_or_add_server_directives(vhost, name_block)
def _get_default_vhost(self, domain, allow_port_mismatch, port):
def _get_default_vhost(self, domain: str, allow_port_mismatch: bool,
port: str) -> obj.VirtualHost:
"""Helper method for _vhost_from_duplicated_default; see argument documentation there"""
vhost_list = self.parser.get_vhosts()
# if one has default_server set, return that one
@ -424,10 +433,11 @@ class NginxConfigurator(common.Configurator):
# TODO: present a list of vhosts for user to choose from
raise errors.MisconfigurationError("Could not automatically find a matching server"
" block for %s. Set the `server_name` directive to use the Nginx installer." % domain)
raise errors.MisconfigurationError("Could not automatically find a matching server "
f"block for {domain}. Set the `server_name` directive "
"to use the Nginx installer.")
def _get_ranked_matches(self, target_name):
def _get_ranked_matches(self, target_name: str) -> List[Dict[str, Any]]:
"""Returns a ranked list of vhosts that match target_name.
The ranking gives preference to SSL vhosts.
@ -440,7 +450,8 @@ class NginxConfigurator(common.Configurator):
vhost_list = self.parser.get_vhosts()
return self._rank_matches_by_name_and_ssl(vhost_list, target_name)
def _select_best_name_match(self, matches):
def _select_best_name_match(self,
matches: Sequence[Mapping[str, Any]]) -> Optional[obj.VirtualHost]:
"""Returns the best name match of a ranked list of vhosts.
:param list matches: list of dicts containing the vhost, the matching name,
@ -460,7 +471,8 @@ class NginxConfigurator(common.Configurator):
# Exact or regex match
return matches[0]['vhost']
def _rank_matches_by_name(self, vhost_list, target_name):
def _rank_matches_by_name(self, vhost_list: Iterable[obj.VirtualHost],
target_name: str) -> List[Dict[str, Any]]:
"""Returns a ranked list of vhosts from vhost_list that match target_name.
This method should always be followed by a call to _select_best_name_match.
@ -497,7 +509,8 @@ class NginxConfigurator(common.Configurator):
'rank': REGEX_RANK})
return sorted(matches, key=lambda x: x['rank'])
def _rank_matches_by_name_and_ssl(self, vhost_list, target_name):
def _rank_matches_by_name_and_ssl(self, vhost_list: Iterable[obj.VirtualHost],
target_name: str) -> List[Dict[str, Any]]:
"""Returns a ranked list of vhosts from vhost_list that match target_name.
The ranking gives preference to SSLishness before name match level.
@ -610,7 +623,7 @@ class NginxConfigurator(common.Configurator):
def _vhost_listening_on_port_no_ssl(self, vhost: obj.VirtualHost, port: str) -> bool:
return self._vhost_listening(vhost, port, False)
def _get_redirect_ranked_matches(self, target_name, port):
def _get_redirect_ranked_matches(self, target_name: str, port: str) -> List[Dict[str, Any]]:
"""Gets a ranked list of plaintextish port-listening vhosts matching target_name
Filter all hosts for those listening on port without using ssl.
@ -625,14 +638,14 @@ class NginxConfigurator(common.Configurator):
"""
all_vhosts = self.parser.get_vhosts()
def _vhost_matches(vhost, port):
def _vhost_matches(vhost: obj.VirtualHost, port: str) -> bool:
return self._vhost_listening_on_port_no_ssl(vhost, port)
matching_vhosts = [vhost for vhost in all_vhosts if _vhost_matches(vhost, port)]
return self._rank_matches_by_name(matching_vhosts, target_name)
def get_all_names(self):
def get_all_names(self) -> Set[str]:
"""Returns all names found in the Nginx Configuration.
:returns: All ServerNames, ServerAliases, and reverse DNS entries for
@ -670,7 +683,7 @@ class NginxConfigurator(common.Configurator):
return util.get_filtered_names(all_names)
def _get_snakeoil_paths(self):
def _get_snakeoil_paths(self) -> Tuple[str, str]:
"""Generate invalid certs that let us create ssl directives for Nginx"""
# TODO: generate only once
tmp_dir = os.path.join(self.config.work_dir, "snakeoil")
@ -688,7 +701,7 @@ class NginxConfigurator(common.Configurator):
cert_file.write(cert_pem)
return cert_path, le_key.file
def _make_server_ssl(self, vhost):
def _make_server_ssl(self, vhost: obj.VirtualHost) -> None:
"""Make a server SSL.
Make a server SSL by adding new listen and SSL directives.
@ -698,7 +711,7 @@ class NginxConfigurator(common.Configurator):
"""
https_port = self.config.https_port
ipv6info = self.ipv6_info(https_port)
ipv6info = self.ipv6_info(str(https_port))
ipv6_block = ['']
ipv4_block = ['']
@ -745,11 +758,12 @@ class NginxConfigurator(common.Configurator):
##################################
# enhancement methods (Installer)
##################################
def supported_enhancements(self):
def supported_enhancements(self) -> List[str]:
"""Returns currently supported enhancements."""
return ['redirect', 'ensure-http-header', 'staple-ocsp']
def enhance(self, domain, enhancement, options=None):
def enhance(self, domain: str, enhancement: str,
options: Optional[Union[str, List[str]]] = None) -> None:
"""Enhance configuration.
:param str domain: domain to enhance
@ -761,16 +775,16 @@ class NginxConfigurator(common.Configurator):
"""
try:
return self._enhance_func[enhancement](domain, options)
self._enhance_func[enhancement](domain, options)
except (KeyError, ValueError):
raise errors.PluginError(
"Unsupported enhancement: {0}".format(enhancement))
def _has_certbot_redirect(self, vhost, domain):
def _has_certbot_redirect(self, vhost: obj.VirtualHost, domain: str) -> bool:
test_redirect_block = _test_block_from_block(_redirect_block_for_domain(domain))
return vhost.contains_list(test_redirect_block)
def _set_http_header(self, domain, header_substring):
def _set_http_header(self, domain: str, header_substring: Union[str, List[str], None]) -> None:
"""Enables header identified by header_substring on domain.
If the vhost is listening plaintextishly, separates out the relevant
@ -784,7 +798,10 @@ class NginxConfigurator(common.Configurator):
:raises .errors.PluginError: If no viable HTTPS host can be created or
set with header header_substring.
"""
if not header_substring in constants.HEADER_ARGS:
if not isinstance(header_substring, str):
raise errors.NotSupportedError("Invalid header_substring type "
f"{type(header_substring)}, expected a str.")
if header_substring not in constants.HEADER_ARGS:
raise errors.NotSupportedError(
f"{header_substring} is not supported by the nginx plugin.")
@ -808,7 +825,7 @@ class NginxConfigurator(common.Configurator):
['\n']]
self.parser.add_server_directives(vhost, header_directives)
def _add_redirect_block(self, vhost, domain):
def _add_redirect_block(self, vhost: obj.VirtualHost, domain: str) -> None:
"""Add redirect directive to vhost
"""
redirect_block = _redirect_block_for_domain(domain)
@ -816,7 +833,8 @@ class NginxConfigurator(common.Configurator):
self.parser.add_server_directives(
vhost, redirect_block, insert_at_top=True)
def _split_block(self, vhost, only_directives=None):
def _split_block(self, vhost: obj.VirtualHost, only_directives: Optional[List[str]] = None
) -> Tuple[obj.VirtualHost, obj.VirtualHost]:
"""Splits this "virtual host" (i.e. this nginx server block) into
separate HTTP and HTTPS blocks.
@ -829,13 +847,13 @@ class NginxConfigurator(common.Configurator):
"""
http_vhost = self.parser.duplicate_vhost(vhost, only_directives=only_directives)
def _ssl_match_func(directive):
def _ssl_match_func(directive: str) -> bool:
return 'ssl' in directive
def _ssl_config_match_func(directive):
def _ssl_config_match_func(directive: str) -> bool:
return self.mod_ssl_conf in directive
def _no_ssl_match_func(directive):
def _no_ssl_match_func(directive: str) -> bool:
return 'ssl' not in directive
# remove all ssl addresses and related directives from the new block
@ -849,7 +867,8 @@ class NginxConfigurator(common.Configurator):
self.parser.remove_server_directives(vhost, 'listen', match_func=_no_ssl_match_func)
return http_vhost, vhost
def _enable_redirect(self, domain, unused_options):
def _enable_redirect(self, domain: str,
unused_options: Optional[Union[str, List[str]]]) -> None:
"""Redirect all equivalent HTTP traffic to ssl_vhost.
If the vhost is listening plaintextishly, separate out the
@ -876,7 +895,7 @@ class NginxConfigurator(common.Configurator):
for vhost in vhosts:
self._enable_redirect_single(domain, vhost)
def _enable_redirect_single(self, domain, vhost):
def _enable_redirect_single(self, domain: str, vhost: obj.VirtualHost) -> None:
"""Redirect all equivalent HTTP traffic to ssl_vhost.
If the vhost is listening plaintextishly, separate out the
@ -905,7 +924,8 @@ class NginxConfigurator(common.Configurator):
logger.info("Redirecting all traffic on port %s to ssl in %s",
self.DEFAULT_LISTEN_PORT, vhost.filep)
def _enable_ocsp_stapling(self, domain, chain_path):
def _enable_ocsp_stapling(self, domain: str,
chain_path: Optional[Union[str, List[str]]]) -> None:
"""Include OCSP response in TLS handshake
:param str domain: domain to enable OCSP response for
@ -913,11 +933,15 @@ class NginxConfigurator(common.Configurator):
:type chain_path: `str` or `None`
"""
if not isinstance(chain_path, str) and chain_path is not None:
raise errors.NotSupportedError(f"Invalid chain_path type {type(chain_path)}, "
"expected a str or None.")
vhosts = self.choose_vhosts(domain)
for vhost in vhosts:
self._enable_ocsp_stapling_single(vhost, chain_path)
def _enable_ocsp_stapling_single(self, vhost, chain_path):
def _enable_ocsp_stapling_single(self, vhost: obj.VirtualHost,
chain_path: Optional[str]) -> None:
"""Include OCSP response in TLS handshake
:param str vhost: vhost to enable OCSP response for
@ -957,7 +981,7 @@ class NginxConfigurator(common.Configurator):
######################################
# Nginx server management (Installer)
######################################
def restart(self):
def restart(self) -> None:
"""Restarts nginx server.
:raises .errors.MisconfigurationError: If either the reload fails.
@ -965,7 +989,7 @@ class NginxConfigurator(common.Configurator):
"""
nginx_restart(self.conf('ctl'), self.nginx_conf, self.conf('sleep-seconds'))
def config_test(self):
def config_test(self) -> None:
"""Check the configuration of Nginx for errors.
:raises .errors.MisconfigurationError: If config_test fails
@ -976,7 +1000,7 @@ class NginxConfigurator(common.Configurator):
except errors.SubprocessError as err:
raise errors.MisconfigurationError(str(err))
def _nginx_version(self):
def _nginx_version(self) -> str:
"""Return results of nginx -V
:returns: version text
@ -1000,7 +1024,7 @@ class NginxConfigurator(common.Configurator):
"Unable to run %s -V" % self.conf('ctl'))
return text
def get_version(self):
def get_version(self) -> Tuple[int, ...]:
"""Return version of Nginx Server.
Version is returned as tuple. (ie. 2.4.7 = (2, 4, 7))
@ -1045,7 +1069,7 @@ class NginxConfigurator(common.Configurator):
return nginx_version
def _get_openssl_version(self):
def _get_openssl_version(self) -> str:
"""Return version of OpenSSL linked to Nginx.
Version is returned as string. If no version can be found, empty string is returned.
@ -1067,7 +1091,7 @@ class NginxConfigurator(common.Configurator):
return ""
return matches[0]
def more_info(self):
def more_info(self) -> str:
"""Human-readable string to help understand the module"""
return (
"Configures Nginx to authenticate and install HTTPS.{0}"
@ -1077,7 +1101,8 @@ class NginxConfigurator(common.Configurator):
version=".".join(str(i) for i in self.version))
)
def auth_hint(self, failed_achalls): # pragma: no cover
def auth_hint(self, # pragma: no cover
failed_achalls: Iterable[achallenges.AnnotatedChallenge]) -> str:
return (
"The Certificate Authority failed to verify the temporary nginx configuration changes "
"made by Certbot. Ensure the listed domains point to this nginx server and that it is "
@ -1087,7 +1112,7 @@ class NginxConfigurator(common.Configurator):
###################################################
# Wrapper functions for Reverter class (Installer)
###################################################
def save(self, title=None, temporary=False):
def save(self, title: str = None, temporary: bool = False) -> None:
"""Saves all changes to the configuration files.
:param str title: The title of the save. If a title is given, the
@ -1111,7 +1136,7 @@ class NginxConfigurator(common.Configurator):
if title and not temporary:
self.finalize_checkpoint(title)
def recovery_routine(self):
def recovery_routine(self) -> None:
"""Revert all previously modified files.
Reverts all modified files that have not been saved as a checkpoint
@ -1123,7 +1148,7 @@ class NginxConfigurator(common.Configurator):
self.new_vhost = None
self.parser.load()
def revert_challenge_config(self):
def revert_challenge_config(self) -> None:
"""Used to cleanup challenge configurations.
:raises .errors.PluginError: If unable to revert the challenge config.
@ -1133,7 +1158,7 @@ class NginxConfigurator(common.Configurator):
self.new_vhost = None
self.parser.load()
def rollback_checkpoints(self, rollback=1):
def rollback_checkpoints(self, rollback: int = 1) -> None:
"""Rollback saved checkpoints.
:param int rollback: Number of checkpoints to revert
@ -1149,12 +1174,13 @@ class NginxConfigurator(common.Configurator):
###########################################################################
# Challenges Section for Authenticator
###########################################################################
def get_chall_pref(self, unused_domain):
def get_chall_pref(self, unused_domain: str) -> List[Type[challenges.Challenge]]:
"""Return list of challenge preferences."""
return [challenges.HTTP01]
# Entry point in main.py for performing challenges
def perform(self, achalls):
def perform(self, achalls: List[achallenges.AnnotatedChallenge]
) -> List[challenges.HTTP01Response]:
"""Perform the configuration related challenge.
This function currently assumes all challenges will be fulfilled.
@ -1163,7 +1189,7 @@ class NginxConfigurator(common.Configurator):
"""
self._chall_out += len(achalls)
responses = [None] * len(achalls)
responses: List[Optional[challenges.HTTP01Response]] = [None] * len(achalls)
http_doer = http_01.NginxHttp01(self)
for i, achall in enumerate(achalls):
@ -1183,10 +1209,10 @@ class NginxConfigurator(common.Configurator):
for i, resp in enumerate(http_response):
responses[http_doer.indices[i]] = resp
return responses
return [response for response in responses if response]
# called after challenges are performed
def cleanup(self, achalls):
def cleanup(self, achalls: List[achallenges.AnnotatedChallenge]) -> None:
"""Revert all challenges."""
self._chall_out -= len(achalls)
@ -1196,13 +1222,13 @@ class NginxConfigurator(common.Configurator):
self.restart()
def _test_block_from_block(block):
def _test_block_from_block(block: List[Any]) -> List[Any]:
test_block = nginxparser.UnspacedList(block)
parser.comment_directive(test_block, 0)
return test_block[:-1]
def _redirect_block_for_domain(domain):
def _redirect_block_for_domain(domain: str) -> List[Any]:
updated_domain = domain
match_symbol = '='
if util.is_wildcard_domain(domain):
@ -1218,7 +1244,7 @@ def _redirect_block_for_domain(domain):
return redirect_block
def nginx_restart(nginx_ctl, nginx_conf, sleep_duration):
def nginx_restart(nginx_ctl: str, nginx_conf: str, sleep_duration: int) -> None:
"""Restarts the Nginx Server.
.. todo:: Nginx restart is fatal if the configuration references
@ -1263,10 +1289,10 @@ def nginx_restart(nginx_ctl, nginx_conf, sleep_duration):
time.sleep(sleep_duration)
def _determine_default_server_root():
def _determine_default_server_root() -> str:
if os.environ.get("CERTBOT_DOCS") == "1":
default_server_root = "%s or %s" % (constants.LINUX_SERVER_ROOT,
constants.FREEBSD_DARWIN_SERVER_ROOT)
default_server_root = (f"{constants.LINUX_SERVER_ROOT} "
f"or {constants.FREEBSD_DARWIN_SERVER_ROOT}")
else:
default_server_root = constants.CLI_DEFAULTS["server_root"]
return default_server_root

View file

@ -52,7 +52,8 @@ ALL_SSL_OPTIONS_HASHES = [
]
"""SHA256 hashes of the contents of all versions of MOD_SSL_CONF_SRC"""
def os_constant(key):
def os_constant(key: str) -> Any:
# XXX TODO: In the future, this could return different constants
# based on what OS we are running under. To see an
# approach to how to handle different OSes, see the
@ -61,11 +62,12 @@ def os_constant(key):
"""
Get a constant value for operating system
:param key: name of cli constant
:param str key: name of cli constant
:return: value of constant for active os
"""
return CLI_DEFAULTS[key]
HSTS_ARGS = ['\"max-age=31536000\"', ' ', 'always']
HEADER_ARGS = {'Strict-Transport-Security': HSTS_ARGS}

View file

@ -1,12 +1,17 @@
"""Contains UI methods for Nginx operations."""
import logging
from typing import Iterable
from typing import List
from typing import Optional
from certbot_nginx._internal.obj import VirtualHost
from certbot.display import util as display_util
logger = logging.getLogger(__name__)
def select_vhost_multiple(vhosts):
def select_vhost_multiple(vhosts: Optional[Iterable[VirtualHost]]) -> List[VirtualHost]:
"""Select multiple Vhosts to install the certificate for
:param vhosts: Available Nginx VirtualHosts
:type vhosts: :class:`list` of type `~obj.Vhost`
@ -28,7 +33,7 @@ def select_vhost_multiple(vhosts):
return []
def _reversemap_vhosts(names, vhosts):
def _reversemap_vhosts(names: Iterable[str], vhosts: Iterable[VirtualHost]) -> List[VirtualHost]:
"""Helper function for select_vhost_multiple for mapping string
representations back to actual vhost objects"""
return_vhosts = []

View file

@ -2,17 +2,20 @@
import io
import logging
from typing import Any
from typing import List
from typing import Optional
from typing import TYPE_CHECKING
from certbot_nginx._internal import nginxparser
from certbot_nginx._internal.obj import Addr
from acme import challenges
from certbot import achallenges
from acme.challenges import HTTP01Response
from certbot import errors
from certbot.achallenges import KeyAuthorizationAnnotatedChallenge
from certbot.compat import os
from certbot.plugins import common
from certbot_nginx._internal import nginxparser
from certbot_nginx._internal import obj
if TYPE_CHECKING:
from certbot_nginx._internal.configurator import NginxConfigurator
@ -46,7 +49,7 @@ class NginxHttp01(common.ChallengePerformer):
self.challenge_conf = os.path.join(
configurator.config.config_dir, "le_http_01_cert_challenge.conf")
def perform(self):
def perform(self) -> List[HTTP01Response]:
"""Perform a challenge on Nginx.
:returns: list of :class:`certbot.acme.challenges.HTTP01Response`
@ -66,7 +69,7 @@ class NginxHttp01(common.ChallengePerformer):
return responses
def _mod_config(self):
def _mod_config(self) -> None:
"""Modifies Nginx config to include server_names_hash_bucket_size directive
and server challenge blocks.
@ -113,39 +116,40 @@ class NginxHttp01(common.ChallengePerformer):
with io.open(self.challenge_conf, "w", encoding="utf-8") as new_conf:
nginxparser.dump(config, new_conf)
def _default_listen_addresses(self):
def _default_listen_addresses(self) -> List[Addr]:
"""Finds addresses for a challenge block to listen on.
:returns: list of :class:`certbot_nginx._internal.obj.Addr` to apply
:rtype: list
"""
addresses: List[obj.Addr] = []
addresses: List[Optional[Addr]] = []
default_addr = "%s" % self.configurator.config.http01_port
ipv6_addr = "[::]:{0}".format(
self.configurator.config.http01_port)
port = self.configurator.config.http01_port
ipv6, ipv6only = self.configurator.ipv6_info(port)
ipv6, ipv6only = self.configurator.ipv6_info(str(port))
if ipv6:
# If IPv6 is active in Nginx configuration
if not ipv6only:
# If ipv6only=on is not already present in the config
ipv6_addr = ipv6_addr + " ipv6only=on"
addresses = [obj.Addr.fromstring(default_addr),
obj.Addr.fromstring(ipv6_addr)]
addresses = [Addr.fromstring(default_addr),
Addr.fromstring(ipv6_addr)]
logger.debug(("Using default addresses %s and %s for authentication."),
default_addr,
ipv6_addr)
else:
addresses = [obj.Addr.fromstring(default_addr)]
addresses = [Addr.fromstring(default_addr)]
logger.debug("Using default address %s for authentication.",
default_addr)
return addresses
def _get_validation_path(self, achall):
return [address for address in addresses if address]
def _get_validation_path(self, achall: KeyAuthorizationAnnotatedChallenge) -> str:
return os.sep + os.path.join(challenges.HTTP01.URI_ROOT_PATH, achall.chall.encode("token"))
def _make_server_block(self, achall: achallenges.KeyAuthorizationAnnotatedChallenge) -> List:
def _make_server_block(self, achall: KeyAuthorizationAnnotatedChallenge) -> List[Any]:
"""Creates a server block for a challenge.
:param achall: Annotated HTTP-01 challenge
@ -168,7 +172,8 @@ class NginxHttp01(common.ChallengePerformer):
# TODO: do we want to return something else if they otherwise access this block?
return [['server'], block]
def _location_directive_for_achall(self, achall):
def _location_directive_for_achall(self, achall: KeyAuthorizationAnnotatedChallenge
) -> List[Any]:
validation = achall.validation(achall.account_key)
validation_path = self._get_validation_path(achall)
@ -177,9 +182,8 @@ class NginxHttp01(common.ChallengePerformer):
['return', ' ', '200', ' ', validation]]]
return location_directive
def _make_or_mod_server_block(self, achall: achallenges.KeyAuthorizationAnnotatedChallenge
) -> Optional[List]:
def _make_or_mod_server_block(self, achall: KeyAuthorizationAnnotatedChallenge
) -> Optional[List[Any]]:
"""Modifies server blocks to respond to a challenge. Returns a new HTTP server block
to add to the configuration if an existing one can't be found.
@ -192,7 +196,7 @@ class NginxHttp01(common.ChallengePerformer):
"""
http_vhosts, https_vhosts = self.configurator.choose_auth_vhosts(achall.domain)
new_vhost: Optional[list] = None
new_vhost: Optional[List[Any]] = None
if not http_vhosts:
# Couldn't find either a matching name+port server block
# or a port+default_server block, so create a dummy block
@ -205,8 +209,8 @@ class NginxHttp01(common.ChallengePerformer):
self.configurator.parser.add_server_directives(vhost, location_directive)
rewrite_directive = [['rewrite', ' ', '^(/.well-known/acme-challenge/.*)',
' ', '$1', ' ', 'break']]
self.configurator.parser.add_server_directives(vhost,
rewrite_directive, insert_at_top=True)
' ', '$1', ' ', 'break']]
self.configurator.parser.add_server_directives(
vhost, rewrite_directive, insert_at_top=True)
return new_vhost

View file

@ -2,14 +2,23 @@
# Forked from https://github.com/fatiherikli/nginxparser (MIT Licensed)
import copy
import logging
import typing
from typing import Any
from typing import IO
from typing import Iterable
from typing import Iterator
from typing import List
from typing import overload
from typing import Tuple
from typing import TYPE_CHECKING
from typing import Union
from pyparsing import Combine
from pyparsing import Forward
from pyparsing import Group
from pyparsing import Literal
from pyparsing import Optional
from pyparsing import ParseResults
from pyparsing import QuotedString
from pyparsing import Regex
from pyparsing import restOfLine
@ -17,6 +26,9 @@ from pyparsing import stringEnd
from pyparsing import White
from pyparsing import ZeroOrMore
if TYPE_CHECKING:
from typing_extensions import SupportsIndex # typing.SupportsIndex not supported on Python 3.6
logger = logging.getLogger(__name__)
@ -59,23 +71,24 @@ class RawNginxParser:
script = ZeroOrMore(contents) + space + stringEnd
script.parseWithTabs().leaveWhitespace()
def __init__(self, source):
def __init__(self, source: str) -> None:
self.source = source
def parse(self):
def parse(self) -> ParseResults:
"""Returns the parsed tree."""
return self.script.parseString(self.source)
def as_list(self):
def as_list(self) -> List[Any]:
"""Returns the parsed tree as a list."""
return self.parse().asList()
class RawNginxDumper:
"""A class that dumps nginx configuration from the provided tree."""
def __init__(self, blocks):
def __init__(self, blocks: List[Any]) -> None:
self.blocks = blocks
def __iter__(self, blocks=None):
def __iter__(self, blocks: typing.Optional[List[Any]] = None) -> Iterator[str]:
"""Iterates the dumped nginx content."""
blocks = blocks or self.blocks
for b0 in blocks:
@ -100,7 +113,7 @@ class RawNginxDumper:
semicolon = ""
yield "".join(item) + semicolon
def __str__(self):
def __str__(self) -> str:
"""Return the parsed block as a string."""
return ''.join(self)
@ -108,28 +121,37 @@ class RawNginxDumper:
spacey = lambda x: (isinstance(x, str) and x.isspace()) or x == ''
class UnspacedList(list):
class UnspacedList(List[Any]):
"""Wrap a list [of lists], making any whitespace entries magically invisible"""
def __init__(self, list_source):
def __init__(self, list_source: Iterable[Any]) -> None:
# ensure our argument is not a generator, and duplicate any sublists
self.spaced = copy.deepcopy(list(list_source))
self.dirty = False
# Turn self into a version of the source list that has spaces removed
# and all sub-lists also UnspacedList()ed
list.__init__(self, list_source)
super().__init__(list_source)
for i, entry in reversed(list(enumerate(self))):
if isinstance(entry, list):
sublist = UnspacedList(entry)
list.__setitem__(self, i, sublist)
super().__setitem__(i, sublist)
self.spaced[i] = sublist.spaced
elif spacey(entry):
# don't delete comments
if "#" not in self[:i]:
list.__delitem__(self, i)
super().__delitem__(i)
def _coerce(self, inbound):
@overload
def _coerce(self, inbound: None) -> Tuple[None, None]: ...
@overload
def _coerce(self, inbound: str) -> Tuple[str, str]: ...
@overload
def _coerce(self, inbound: List[Any]) -> Tuple["UnspacedList", List[Any]]: ...
def _coerce(self, inbound: Any) -> Tuple[Any, Any]:
"""
Coerce some inbound object to be appropriately usable in this object
@ -138,100 +160,114 @@ class UnspacedList(list):
:rtype: tuple
"""
if not isinstance(inbound, list): # str or None
if not isinstance(inbound, list): # str or None
return inbound, inbound
else:
if not hasattr(inbound, "spaced"):
inbound = UnspacedList(inbound)
return inbound, inbound.spaced
def insert(self, i, x):
def insert(self, i: int, x: Any) -> None:
"""Insert object before index."""
item, spaced_item = self._coerce(x)
slicepos = self._spaced_position(i) if i < len(self) else len(self.spaced)
self.spaced.insert(slicepos, spaced_item)
if not spacey(item):
list.insert(self, i, item)
super().insert(i, item)
self.dirty = True
def append(self, x):
def append(self, x: Any) -> None:
"""Append object to the end of the list."""
item, spaced_item = self._coerce(x)
self.spaced.append(spaced_item)
if not spacey(item):
list.append(self, item)
super().append(item)
self.dirty = True
def extend(self, x):
def extend(self, x: Any) -> None:
"""Extend list by appending elements from the iterable."""
item, spaced_item = self._coerce(x)
self.spaced.extend(spaced_item)
list.extend(self, item)
super().extend(item)
self.dirty = True
def __add__(self, other):
l = copy.deepcopy(self)
l.extend(other)
l.dirty = True
return l
def __add__(self, other: List[Any]) -> "UnspacedList":
new_list = copy.deepcopy(self)
new_list.extend(other)
new_list.dirty = True
return new_list
def pop(self, _i=None):
def pop(self, *args: Any, **kwargs: Any) -> None:
"""Function pop() is not implemented for UnspacedList"""
raise NotImplementedError("UnspacedList.pop() not yet implemented")
def remove(self, _):
def remove(self, *args: Any, **kwargs: Any) -> None:
"""Function remove() is not implemented for UnspacedList"""
raise NotImplementedError("UnspacedList.remove() not yet implemented")
def reverse(self):
def reverse(self) -> None:
"""Function reverse() is not implemented for UnspacedList"""
raise NotImplementedError("UnspacedList.reverse() not yet implemented")
def sort(self, _cmp=None, _key=None, _Rev=None):
def sort(self, *_args: Any, **_kwargs: Any) -> None:
"""Function sort() is not implemented for UnspacedList"""
raise NotImplementedError("UnspacedList.sort() not yet implemented")
def __setslice__(self, _i, _j, _newslice):
def __setslice__(self, *args: Any, **kwargs: Any) -> None:
raise NotImplementedError("Slice operations on UnspacedLists not yet implemented")
def __setitem__(self, i, value):
def __setitem__(self, i: Union["SupportsIndex", slice], value: Any) -> None:
if isinstance(i, slice):
raise NotImplementedError("Slice operations on UnspacedLists not yet implemented")
item, spaced_item = self._coerce(value)
self.spaced.__setitem__(self._spaced_position(i), spaced_item)
if not spacey(item):
list.__setitem__(self, i, item)
super().__setitem__(i, item)
self.dirty = True
def __delitem__(self, i):
def __delitem__(self, i: Union["SupportsIndex", slice]) -> None:
if isinstance(i, slice):
raise NotImplementedError("Slice operations on UnspacedLists not yet implemented")
self.spaced.__delitem__(self._spaced_position(i))
list.__delitem__(self, i)
super().__delitem__(i)
self.dirty = True
def __deepcopy__(self, memo):
def __deepcopy__(self, memo: Any) -> "UnspacedList":
new_spaced = copy.deepcopy(self.spaced, memo=memo)
l = UnspacedList(new_spaced)
l.dirty = self.dirty
return l
new_list = UnspacedList(new_spaced)
new_list.dirty = self.dirty
return new_list
def is_dirty(self):
def is_dirty(self) -> bool:
"""Recurse through the parse tree to figure out if any sublists are dirty"""
if self.dirty:
return True
return any((isinstance(x, UnspacedList) and x.is_dirty() for x in self))
def _spaced_position(self, idx):
"Convert from indexes in the unspaced list to positions in the spaced one"
def _spaced_position(self, idx: "SupportsIndex") -> int:
"""Convert from indexes in the unspaced list to positions in the spaced one"""
int_idx = idx.__index__()
pos = spaces = 0
# Normalize indexes like list[-1] etc, and save the result
if idx < 0:
idx = len(self) + idx
if not 0 <= idx < len(self):
if int_idx < 0:
int_idx = len(self) + int_idx
if not 0 <= int_idx < len(self):
raise IndexError("list index out of range")
idx0 = idx
# Count the number of spaces in the spaced list before idx in the unspaced one
while idx != -1:
int_idx0 = int_idx
# Count the number of spaces in the spaced list before int_idx in the unspaced one
while int_idx != -1:
if spacey(self.spaced[pos]):
spaces += 1
else:
idx -= 1
int_idx -= 1
pos += 1
return idx0 + spaces
return int_idx0 + spaces
# Shortcut functions to respect Python's serialization interface
# (like pyyaml, picker or json)
def loads(source):
def loads(source: str) -> UnspacedList:
"""Parses from a string.
:param str source: The string to parse
@ -242,34 +278,34 @@ def loads(source):
return UnspacedList(RawNginxParser(source).as_list())
def load(_file):
def load(file_: IO[Any]) -> UnspacedList:
"""Parses from a file.
:param file _file: The file to parse
:param file file_: The file to parse
:returns: The parsed tree
:rtype: list
"""
return loads(_file.read())
return loads(file_.read())
def dumps(blocks: UnspacedList) -> str:
"""Dump to a Unicode string.
:param UnspacedList block: The parsed tree
:param UnspacedList blocks: The parsed tree
:rtype: six.text_type
"""
return str(RawNginxDumper(blocks.spaced))
def dump(blocks: UnspacedList, _file: IO[Any]) -> None:
def dump(blocks: UnspacedList, file_: IO[Any]) -> None:
"""Dump to a file.
:param UnspacedList block: The parsed tree
:param IO[Any] _file: The file stream to dump to. It must be opened with
:param UnspacedList blocks: The parsed tree
:param IO[Any] file_: The file stream to dump to. It must be opened with
Unicode encoding.
:rtype: None
"""
_file.write(dumps(blocks))
file_.write(dumps(blocks))

View file

@ -1,10 +1,17 @@
"""Module contains classes used by the Nginx Configurator."""
import re
from typing import Any
from typing import List
from typing import Optional
from typing import Sequence
from typing import Set
from typing import Union
from certbot.plugins import common
ADD_HEADER_DIRECTIVE = 'add_header'
class Addr(common.Addr):
r"""Represents an Nginx address, i.e. what comes after the 'listen'
directive.
@ -34,7 +41,8 @@ class Addr(common.Addr):
UNSPECIFIED_IPV4_ADDRESSES = ('', '*', '0.0.0.0')
CANONICAL_UNSPECIFIED_ADDRESS = UNSPECIFIED_IPV4_ADDRESSES[0]
def __init__(self, host, port, ssl, default, ipv6, ipv6only):
def __init__(self, host: str, port: str, ssl: bool, default: bool,
ipv6: bool, ipv6only: bool) -> None:
super().__init__((host, port))
self.ssl = ssl
self.default = default
@ -43,7 +51,7 @@ class Addr(common.Addr):
self.unspecified_address = host in self.UNSPECIFIED_IPV4_ADDRESSES
@classmethod
def fromstring(cls, str_addr):
def fromstring(cls, str_addr: str) -> Optional["Addr"]:
"""Initialize Addr from string."""
parts = str_addr.split(' ')
ssl = False
@ -94,7 +102,7 @@ class Addr(common.Addr):
return cls(host, port, ssl, default, ipv6, ipv6only)
def to_string(self, include_default=True):
def to_string(self, include_default: bool = True) -> str:
"""Return string representation of Addr"""
parts = ''
if self.tup[0] and self.tup[1]:
@ -111,18 +119,18 @@ class Addr(common.Addr):
return parts
def __str__(self):
def __str__(self) -> str:
return self.to_string()
def __repr__(self):
def __repr__(self) -> str:
return "Addr(" + self.__str__() + ")"
def __hash__(self): # pylint: disable=useless-super-delegation
def __hash__(self) -> int: # pylint: disable=useless-super-delegation
# Python 3 requires explicit overridden for __hash__
# See certbot-apache/certbot_apache/_internal/obj.py for more information
return super().__hash__()
def super_eq(self, other):
def super_eq(self, other: "Addr") -> bool:
"""Check ip/port equality, with IPv6 support.
"""
# If both addresses got an unspecified address, then make sure the
@ -134,7 +142,7 @@ class Addr(common.Addr):
other.tup[1]), other.ipv6)
return super().__eq__(other)
def __eq__(self, other):
def __eq__(self, other: Any) -> bool:
if isinstance(other, self.__class__):
return (self.super_eq(other) and
self.ssl == other.ssl and
@ -158,7 +166,8 @@ class VirtualHost:
"""
def __init__(self, filep, addrs, ssl, enabled, names, raw, path):
def __init__(self, filep: str, addrs: Sequence[Addr], ssl: bool, enabled: bool,
names: Set[str], raw: List[Any], path: List[int]) -> None:
"""Initialize a VH."""
self.filep = filep
self.addrs = addrs
@ -168,7 +177,7 @@ class VirtualHost:
self.raw = raw
self.path = path
def __str__(self):
def __str__(self) -> str:
addr_str = ", ".join(str(addr) for addr in sorted(self.addrs, key=str))
# names might be a set, and it has different representations in Python
# 2 and 3. Force it to be a list here for consistent outputs
@ -179,10 +188,10 @@ class VirtualHost:
"enabled: %s" % (self.filep, addr_str,
list(self.names), self.ssl, self.enabled))
def __repr__(self):
def __repr__(self) -> str:
return "VirtualHost(" + self.__str__().replace("\n", ", ") + ")\n"
def __eq__(self, other):
def __eq__(self, other: Any) -> bool:
if isinstance(other, self.__class__):
return (self.filep == other.filep and
sorted(self.addrs, key=str) == sorted(other.addrs, key=str) and
@ -193,12 +202,12 @@ class VirtualHost:
return False
def __hash__(self):
def __hash__(self) -> int:
return hash((self.filep, tuple(self.path),
tuple(self.addrs), tuple(self.names),
self.ssl, self.enabled))
def has_header(self, header_name):
def has_header(self, header_name: str) -> bool:
"""Determine if this server block has a particular header set.
:param str header_name: The name of the header to check for, e.g.
'Strict-Transport-Security'
@ -206,7 +215,7 @@ class VirtualHost:
found = _find_directive(self.raw, ADD_HEADER_DIRECTIVE, header_name)
return found is not None
def contains_list(self, test):
def contains_list(self, test: List[Any]) -> bool:
"""Determine if raw server block contains test list at top level
"""
for i in range(0, len(self.raw) - len(test) + 1):
@ -214,7 +223,7 @@ class VirtualHost:
return True
return False
def ipv6_enabled(self):
def ipv6_enabled(self) -> bool:
"""Return true if one or more of the listen directives in vhost supports
IPv6"""
for a in self.addrs:
@ -222,7 +231,7 @@ class VirtualHost:
return True
return False
def ipv4_enabled(self):
def ipv4_enabled(self) -> bool:
"""Return true if one or more of the listen directives in vhost are IPv4
only"""
if not self.addrs:
@ -232,7 +241,7 @@ class VirtualHost:
return True
return False
def display_repr(self):
def display_repr(self) -> str:
"""Return a representation of VHost to be used in dialog"""
return (
"File: {filename}\n"
@ -244,7 +253,9 @@ class VirtualHost:
names=", ".join(self.names),
https="Yes" if self.ssl else "No"))
def _find_directive(directives, directive_name, match_content=None):
def _find_directive(directives: Optional[Union[str, List[Any]]], directive_name: str,
match_content: Optional[Any] = None) -> Optional[Any]:
"""Find a directive of type directive_name in directives. If match_content is given,
Searches for `match_content` in the directive arguments.
"""

View file

@ -5,20 +5,26 @@ import glob
import io
import logging
import re
from typing import Any
from typing import Callable
from typing import cast
from typing import Dict
from typing import Iterable
from typing import List
from typing import Mapping
from typing import Optional
from typing import Sequence
from typing import Set
from typing import Tuple
from typing import Union
from certbot_nginx._internal import nginxparser
from certbot_nginx._internal import obj
from certbot_nginx._internal.nginxparser import UnspacedList
import pyparsing
from certbot import errors
from certbot.compat import os
from certbot_nginx._internal import nginxparser
from certbot_nginx._internal import obj
from certbot_nginx._internal.nginxparser import UnspacedList
logger = logging.getLogger(__name__)
@ -32,8 +38,8 @@ class NginxParser:
"""
def __init__(self, root):
self.parsed: Dict[str, Union[List, nginxparser.UnspacedList]] = {}
def __init__(self, root: str) -> None:
self.parsed: Dict[str, UnspacedList] = {}
self.root = os.path.abspath(root)
self.config_root = self._find_config_root()
@ -42,14 +48,14 @@ class NginxParser:
# not enable sites from there.
self.load()
def load(self):
def load(self) -> None:
"""Loads Nginx files into a parsed tree.
"""
self.parsed = {}
self._parse_recursively(self.config_root)
def _parse_recursively(self, filepath):
def _parse_recursively(self, filepath: str) -> None:
"""Parses nginx config files recursively by looking at 'include'
directives inside 'http' and 'server' blocks. Note that this only
reads Nginx files that potentially declare a virtual host.
@ -77,7 +83,7 @@ class NginxParser:
if _is_include_directive(server_entry):
self._parse_recursively(server_entry[1])
def abs_path(self, path):
def abs_path(self, path: str) -> str:
"""Converts a relative path to an absolute path relative to the root.
Does nothing for paths that are already absolute.
@ -90,7 +96,7 @@ class NginxParser:
return os.path.normpath(os.path.join(self.root, path))
return os.path.normpath(path)
def _build_addr_to_ssl(self):
def _build_addr_to_ssl(self) -> Dict[Tuple[str, str], bool]:
"""Builds a map from address to whether it listens on ssl in any server block
"""
servers = self._get_raw_servers()
@ -107,11 +113,11 @@ class NginxParser:
addr_to_ssl[addr_tuple] = addr.ssl or addr_to_ssl[addr_tuple]
return addr_to_ssl
def _get_raw_servers(self) -> Dict:
def _get_raw_servers(self) -> Dict[str, Union[List[Any], UnspacedList]]:
# pylint: disable=cell-var-from-loop
"""Get a map of unparsed all server blocks
"""
servers: Dict[str, Union[List, nginxparser.UnspacedList]] = {}
servers: Dict[str, Union[List[Any], nginxparser.UnspacedList]] = {}
for filename, tree in self.parsed.items():
servers[filename] = []
srv = servers[filename] # workaround undefined loop var in lambdas
@ -126,7 +132,7 @@ class NginxParser:
servers[filename][i] = (new_server, path)
return servers
def get_vhosts(self):
def get_vhosts(self) -> List[obj.VirtualHost]:
"""Gets list of all 'virtual hosts' found in Nginx configuration.
Technically this is a misnomer because Nginx does not have virtual
hosts, it has 'server blocks'.
@ -158,7 +164,7 @@ class NginxParser:
return vhosts
def _update_vhosts_addrs_ssl(self, vhosts):
def _update_vhosts_addrs_ssl(self, vhosts: Iterable[obj.VirtualHost]) -> None:
"""Update a list of raw parsed vhosts to include global address sslishness
"""
addr_to_ssl = self._build_addr_to_ssl()
@ -168,7 +174,7 @@ class NginxParser:
if addr.ssl:
vhost.ssl = True
def _get_included_directives(self, block):
def _get_included_directives(self, block: UnspacedList) -> UnspacedList:
"""Returns array with the "include" directives expanded out by
concatenating the contents of the included file to the block.
@ -188,7 +194,7 @@ class NginxParser:
pass
return result
def _parse_files(self, filepath, override=False):
def _parse_files(self, filepath: str, override: bool = False) -> List[UnspacedList]:
"""Parse files from a glob
:param str filepath: Nginx config file path
@ -219,7 +225,7 @@ class NginxParser:
logger.warning("Could not parse file: %s due to %s", item, err)
return trees
def _find_config_root(self):
def _find_config_root(self) -> str:
"""Return the Nginx Configuration Root file."""
location = ['nginx.conf']
@ -230,7 +236,7 @@ class NginxParser:
raise errors.NoInstallationError(
"Could not find Nginx root configuration file (nginx.conf)")
def filedump(self, ext='tmp', lazy=True):
def filedump(self, ext: str = 'tmp', lazy: bool = True) -> None:
"""Dumps parsed configurations into files.
:param str ext: The file extension to use for the dumped files. If
@ -255,7 +261,7 @@ class NginxParser:
except IOError:
logger.error("Could not open file for writing: %s", filename)
def parse_server(self, server):
def parse_server(self, server: UnspacedList) -> Dict[str, Any]:
"""Parses a list of server directives, accounting for global address sslishness.
:param list server: list of directives in a server block
@ -266,7 +272,7 @@ class NginxParser:
_apply_global_addr_ssl(addr_to_ssl, parsed_server)
return parsed_server
def has_ssl_on_directive(self, vhost):
def has_ssl_on_directive(self, vhost: obj.VirtualHost) -> bool:
"""Does vhost have ssl on for all ports?
:param :class:`~certbot_nginx._internal.obj.VirtualHost` vhost: The vhost in question
@ -284,7 +290,8 @@ class NginxParser:
return False
def add_server_directives(self, vhost, directives, insert_at_top=False):
def add_server_directives(self, vhost: obj.VirtualHost, directives: List[Any],
insert_at_top: bool = False) -> None:
"""Add directives to the server block identified by vhost.
This method modifies vhost to be fully consistent with the new directives.
@ -305,7 +312,8 @@ class NginxParser:
self._modify_server_directives(vhost,
functools.partial(_add_directives, directives, insert_at_top))
def update_or_add_server_directives(self, vhost, directives, insert_at_top=False):
def update_or_add_server_directives(self, vhost: obj.VirtualHost, directives: List[Any],
insert_at_top: bool = False) -> None:
"""Add or replace directives in the server block identified by vhost.
This method modifies vhost to be fully consistent with the new directives.
@ -327,7 +335,8 @@ class NginxParser:
self._modify_server_directives(vhost,
functools.partial(_update_or_add_directives, directives, insert_at_top))
def remove_server_directives(self, vhost, directive_name, match_func=None):
def remove_server_directives(self, vhost: obj.VirtualHost, directive_name: str,
match_func: Optional[Callable[[Any], bool]] = None) -> None:
"""Remove all directives of type directive_name.
:param :class:`~certbot_nginx._internal.obj.VirtualHost` vhost: The vhost
@ -339,7 +348,8 @@ class NginxParser:
self._modify_server_directives(vhost,
functools.partial(_remove_directives, directive_name, match_func))
def _update_vhost_based_on_new_directives(self, vhost, directives_list):
def _update_vhost_based_on_new_directives(self, vhost: obj.VirtualHost,
directives_list: UnspacedList) -> None:
new_server = self._get_included_directives(directives_list)
parsed_server = self.parse_server(new_server)
vhost.addrs = parsed_server['addrs']
@ -347,7 +357,8 @@ class NginxParser:
vhost.names = parsed_server['names']
vhost.raw = new_server
def _modify_server_directives(self, vhost, block_func):
def _modify_server_directives(self, vhost: obj.VirtualHost,
block_func: Callable[[List[Any]], None]) -> None:
filename = vhost.filep
try:
result = self.parsed[filename]
@ -364,7 +375,7 @@ class NginxParser:
def duplicate_vhost(self, vhost_template: obj.VirtualHost,
remove_singleton_listen_params: bool = False,
only_directives: Optional[List] = None) -> obj.VirtualHost:
only_directives: Optional[List[Any]] = None) -> obj.VirtualHost:
"""Duplicate the vhost in the configuration files.
:param :class:`~certbot_nginx._internal.obj.VirtualHost` vhost_template: The vhost
@ -417,7 +428,7 @@ class NginxParser:
return new_vhost
def _parse_ssl_options(ssl_options):
def _parse_ssl_options(ssl_options: Optional[str]) -> List[UnspacedList]:
if ssl_options is not None:
try:
with io.open(ssl_options, "r", encoding="utf-8") as _file:
@ -429,9 +440,12 @@ def _parse_ssl_options(ssl_options):
"Only UTF-8 encoding is supported.", ssl_options)
except pyparsing.ParseBaseException as err:
logger.warning("Could not parse file: %s due to %s", ssl_options, err)
return []
return UnspacedList([])
def _do_for_subarray(entry, condition, func, path=None):
def _do_for_subarray(entry: List[Any], condition: Callable[[List[Any]], bool],
func: Callable[[List[Any], List[int]], None],
path: Optional[List[int]] = None) -> None:
"""Executes a function for a subarray of a nested array if it matches
the given condition.
@ -450,7 +464,7 @@ def _do_for_subarray(entry, condition, func, path=None):
_do_for_subarray(item, condition, func, path + [index])
def get_best_match(target_name, names):
def get_best_match(target_name: str, names: Iterable[str]) -> Tuple[Optional[str], Optional[str]]:
"""Finds the best match for target_name out of names using the Nginx
name-matching rules (exact > longest wildcard starting with * >
longest wildcard ending with * > regex).
@ -479,29 +493,29 @@ def get_best_match(target_name, names):
if exact:
# There can be more than one exact match; e.g. eff.org, .eff.org
match = min(exact, key=len)
return ('exact', match)
return 'exact', match
if wildcard_start:
# Return the longest wildcard
match = max(wildcard_start, key=len)
return ('wildcard_start', match)
return 'wildcard_start', match
if wildcard_end:
# Return the longest wildcard
match = max(wildcard_end, key=len)
return ('wildcard_end', match)
return 'wildcard_end', match
if regex:
# Just return the first one for now
match = regex[0]
return ('regex', match)
return 'regex', match
return (None, None)
return None, None
def _exact_match(target_name, name):
def _exact_match(target_name: str, name: str) -> bool:
target_lower = target_name.lower()
return name.lower() in (target_lower, '.' + target_lower)
def _wildcard_match(target_name, name, start):
def _wildcard_match(target_name: str, name: str, start: bool) -> bool:
# Degenerate case
if name == '*':
return True
@ -526,7 +540,7 @@ def _wildcard_match(target_name, name, start):
return target_name_lower.endswith('.' + name_lower)
def _regex_match(target_name, name):
def _regex_match(target_name: str, name: str) -> bool:
# Must start with a tilde
if len(name) < 2 or name[0] != '~':
return False
@ -534,13 +548,13 @@ def _regex_match(target_name, name):
# After tilde is a perl-compatible regex
try:
regex = re.compile(name[1:])
return re.match(regex, target_name)
return bool(re.match(regex, target_name))
except re.error: # pragma: no cover
# perl-compatible regexes are sometimes not recognized by python
return False
def _is_include_directive(entry):
def _is_include_directive(entry: Any) -> bool:
"""Checks if an nginx parsed entry is an 'include' directive.
:param list entry: the parsed entry
@ -552,7 +566,8 @@ def _is_include_directive(entry):
len(entry) == 2 and entry[0] == 'include' and
isinstance(entry[1], str))
def _is_ssl_on_directive(entry):
def _is_ssl_on_directive(entry: Any) -> bool:
"""Checks if an nginx parsed entry is an 'ssl on' directive.
:param list entry: the parsed entry
@ -564,14 +579,18 @@ def _is_ssl_on_directive(entry):
len(entry) == 2 and entry[0] == 'ssl' and
entry[1] == 'on')
def _add_directives(directives, insert_at_top, block):
def _add_directives(directives: List[Any], insert_at_top: bool,
block: UnspacedList) -> None:
"""Adds directives to a config block."""
for directive in directives:
_add_directive(block, directive, insert_at_top)
if block and '\n' not in block[-1]: # could be " \n " or ["\n"] !
block.append(nginxparser.UnspacedList('\n'))
def _update_or_add_directives(directives, insert_at_top, block):
def _update_or_add_directives(directives: List[Any], insert_at_top: bool,
block: UnspacedList) -> None:
"""Adds or replaces directives in a config block."""
for directive in directives:
_update_or_add_directive(block, directive, insert_at_top)
@ -584,7 +603,8 @@ REPEATABLE_DIRECTIVES = {'server_name', 'listen', INCLUDE, 'rewrite', 'add_heade
COMMENT = ' managed by Certbot'
COMMENT_BLOCK = [' ', '#', COMMENT]
def comment_directive(block, location):
def comment_directive(block: UnspacedList, location: int) -> None:
"""Add a ``#managed by Certbot`` comment to the end of the line at location.
:param list block: The block containing the directive to be commented
@ -603,40 +623,45 @@ def comment_directive(block, location):
if next_entry is not None and "\n" not in next_entry:
block.insert(location + 2, '\n')
def _comment_out_directive(block, location, include_location):
def _comment_out_directive(block: UnspacedList, location: int, include_location: str) -> None:
"""Comment out the line at location, with a note of explanation."""
comment_message = ' duplicated in {0}'.format(include_location)
# add the end comment
# create a dumpable object out of block[location] (so it includes the ;)
directive = block[location]
new_dir_block = nginxparser.UnspacedList([]) # just a wrapper
new_dir_block = nginxparser.UnspacedList([]) # just a wrapper
new_dir_block.append(directive)
dumped = nginxparser.dumps(new_dir_block)
commented = dumped + ' #' + comment_message # add the comment directly to the one-line string
new_dir = nginxparser.loads(commented) # reload into UnspacedList
commented = dumped + ' #' + comment_message # add the comment directly to the one-line string
new_dir = nginxparser.loads(commented) # reload into UnspacedList
# add the beginning comment
insert_location = 0
if new_dir[0].spaced[0] != new_dir[0][0]: # if there's whitespace at the beginning
if new_dir[0].spaced[0] != new_dir[0][0]: # if there's whitespace at the beginning
insert_location = 1
new_dir[0].spaced.insert(insert_location, "# ") # comment out the line
new_dir[0].spaced.append(";") # directly add in the ;, because now dumping won't work properly
new_dir[0].spaced.insert(insert_location, "# ") # comment out the line
new_dir[0].spaced.append(";") # directly add in the ;, because now dumping won't work properly
dumped = nginxparser.dumps(new_dir)
new_dir = nginxparser.loads(dumped) # reload into an UnspacedList
new_dir = nginxparser.loads(dumped) # reload into an UnspacedList
block[location] = new_dir[0] # set the now-single-line-comment directive back in place
def _find_location(block, directive_name, match_func=None):
def _find_location(block: UnspacedList, directive_name: str,
match_func: Optional[Callable[[Any], bool]] = None) -> Optional[int]:
"""Finds the index of the first instance of directive_name in block.
If no line exists, use None."""
return next((index for index, line in enumerate(block) \
if line and line[0] == directive_name and (match_func is None or match_func(line))), None)
return next((index for index, line in enumerate(block) if (
line and line[0] == directive_name and (match_func is None or match_func(line)))), None)
def _is_whitespace_or_comment(directive):
def _is_whitespace_or_comment(directive: Sequence[Any]) -> bool:
"""Is this directive either a whitespace or comment directive?"""
return len(directive) == 0 or directive[0] == '#'
def _add_directive(block, directive, insert_at_top):
def _add_directive(block: UnspacedList, directive: Sequence[Any], insert_at_top: bool) -> None:
if not isinstance(directive, nginxparser.UnspacedList):
directive = nginxparser.UnspacedList(directive)
if _is_whitespace_or_comment(directive):
@ -653,10 +678,11 @@ def _add_directive(block, directive, insert_at_top):
# handle flat include files
directive_name = directive[0]
def can_append(loc, dir_name):
def can_append(loc: Optional[int], dir_name: str) -> bool:
""" Can we append this directive to the block? """
return loc is None or (isinstance(dir_name, str)
and dir_name in REPEATABLE_DIRECTIVES)
and dir_name in REPEATABLE_DIRECTIVES)
err_fmt = 'tried to insert directive "{0}" but found conflicting "{1}".'
@ -672,10 +698,14 @@ def _add_directive(block, directive, insert_at_top):
included_dir_name = included_directive[0]
if (not _is_whitespace_or_comment(included_directive)
and not can_append(included_dir_loc, included_dir_name)):
if block[included_dir_loc] != included_directive:
raise errors.MisconfigurationError(err_fmt.format(included_directive,
block[included_dir_loc]))
_comment_out_directive(block, included_dir_loc, directive[1])
# By construction of can_append(), included_dir_loc cannot be None at that point
resolved_included_dir_loc = cast(int, included_dir_loc)
if block[resolved_included_dir_loc] != included_directive:
raise errors.MisconfigurationError(err_fmt.format(
included_directive, block[resolved_included_dir_loc]))
_comment_out_directive(block, resolved_included_dir_loc, directive[1])
if can_append(location, directive_name):
if insert_at_top:
@ -687,14 +717,22 @@ def _add_directive(block, directive, insert_at_top):
else:
block.append(directive)
comment_directive(block, len(block) - 1)
elif block[location] != directive:
raise errors.MisconfigurationError(err_fmt.format(directive, block[location]))
return
def _update_directive(block, directive, location):
# By construction of can_append(), location cannot be None at that point
resolved_location = cast(int, location)
if block[resolved_location] != directive:
raise errors.MisconfigurationError(err_fmt.format(directive, block[resolved_location]))
def _update_directive(block: UnspacedList, directive: Sequence[Any], location: int) -> None:
block[location] = directive
comment_directive(block, location)
def _update_or_add_directive(block, directive, insert_at_top):
def _update_or_add_directive(block: UnspacedList, directive: Sequence[Any],
insert_at_top: bool) -> None:
if not isinstance(directive, nginxparser.UnspacedList):
directive = nginxparser.UnspacedList(directive)
if _is_whitespace_or_comment(directive):
@ -711,10 +749,13 @@ def _update_or_add_directive(block, directive, insert_at_top):
_add_directive(block, directive, insert_at_top)
def _is_certbot_comment(directive):
def _is_certbot_comment(directive: Sequence[Any]) -> bool:
return '#' in directive and COMMENT in directive
def _remove_directives(directive_name, match_func, block):
def _remove_directives(directive_name: str, match_func: Callable[[Any], bool],
block: UnspacedList) -> None:
"""Removes directives of name directive_name from a config block if match_func matches.
"""
while True:
@ -726,7 +767,9 @@ def _remove_directives(directive_name, match_func, block):
del block[location + 1]
del block[location]
def _apply_global_addr_ssl(addr_to_ssl, parsed_server):
def _apply_global_addr_ssl(addr_to_ssl: Mapping[Tuple[str, str], bool],
parsed_server: Dict[str, Any]) -> None:
"""Apply global sslishness information to the parsed server block
"""
for addr in parsed_server['addrs']:
@ -734,7 +777,8 @@ def _apply_global_addr_ssl(addr_to_ssl, parsed_server):
if addr.ssl:
parsed_server['ssl'] = True
def _parse_server_raw(server):
def _parse_server_raw(server: UnspacedList) -> Dict[str, Any]:
"""Parses a list of server directives.
:param list server: list of directives in a server block

View file

@ -5,7 +5,14 @@ raw lists of tokens from pyparsing. """
import abc
import logging
from typing import Any
from typing import Callable
from typing import Iterator
from typing import List
from typing import Optional
from typing import Sequence
from typing import Tuple
from typing import Type
from certbot import errors
@ -23,24 +30,24 @@ class Parsable:
__metaclass__ = abc.ABCMeta
def __init__(self, parent=None):
self._data: List[object] = []
def __init__(self, parent: Optional["Parsable"] = None):
self._data: List[Any] = []
self._tabs = None
self.parent = parent
@classmethod
def parsing_hooks(cls):
def parsing_hooks(cls) -> Tuple[Type["Block"], Type["Sentence"], Type["Statements"]]:
"""Returns object types that this class should be able to `parse` recusrively.
The order of the objects indicates the order in which the parser should
try to parse each subitem.
:returns: A list of Parsable classes.
:rtype list:
"""
return (Block, Sentence, Statements)
return Block, Sentence, Statements
@staticmethod
@abc.abstractmethod
def should_parse(lists):
def should_parse(lists: Any) -> bool:
""" Returns whether the contents of `lists` can be parsed into this object.
:returns: Whether `lists` can be parsed as this object.
@ -49,7 +56,7 @@ class Parsable:
raise NotImplementedError()
@abc.abstractmethod
def parse(self, raw_list, add_spaces=False):
def parse(self, raw_list: List[Any], add_spaces: bool = False) -> None:
""" Loads information into this object from underlying raw_list structure.
Each Parsable object might make different assumptions about the structure of
raw_list.
@ -64,7 +71,8 @@ class Parsable:
raise NotImplementedError()
@abc.abstractmethod
def iterate(self, expanded=False, match=None):
def iterate(self, expanded: bool = False,
match: Optional[Callable[["Parsable"], bool]] = None) -> Iterator[Any]:
""" Iterates across this object. If this object is a leaf object, only yields
itself. If it contains references other parsing objects, and `expanded` is set,
this function should first yield itself, then recursively iterate across all of them.
@ -77,7 +85,7 @@ class Parsable:
raise NotImplementedError()
@abc.abstractmethod
def get_tabs(self):
def get_tabs(self) -> str:
""" Guess at the tabbing style of this parsed object, based on whitespace.
If this object is a leaf, it deducts the tabbing based on its own contents.
@ -90,7 +98,7 @@ class Parsable:
raise NotImplementedError()
@abc.abstractmethod
def set_tabs(self, tabs=" "):
def set_tabs(self, tabs: str = " ") -> None:
"""This tries to set and alter the tabbing of the current object to a desired
whitespace string. Primarily meant for objects that were constructed, so they
can conform to surrounding whitespace.
@ -99,7 +107,7 @@ class Parsable:
"""
raise NotImplementedError()
def dump(self, include_spaces=False):
def dump(self, include_spaces: bool = False) -> List[Any]:
""" Dumps back to pyparsing-like list tree. The opposite of `parse`.
Note: if this object has not been modified, `dump` with `include_spaces=True`
@ -121,17 +129,17 @@ class Statements(Parsable):
an extra `_trailing_whitespace` string to keep track of the whitespace that does not
precede any more statements.
"""
def __init__(self, parent=None):
def __init__(self, parent: Optional[Parsable] = None):
super().__init__(parent)
self._trailing_whitespace = None
# ======== Begin overridden functions
@staticmethod
def should_parse(lists):
def should_parse(lists: Any) -> bool:
return isinstance(lists, list)
def set_tabs(self, tabs=" "):
def set_tabs(self, tabs: str = " ") -> None:
""" Sets the tabbing for this set of statements. Does this by calling `set_tabs`
on each of the child statements.
@ -144,7 +152,7 @@ class Statements(Parsable):
if self.parent is not None:
self._trailing_whitespace = "\n" + self.parent.get_tabs()
def parse(self, raw_list, add_spaces=False):
def parse(self, raw_list: List[Any], add_spaces: bool = False) -> None:
""" Parses a list of statements.
Expects all elements in `raw_list` to be parseable by `type(self).parsing_hooks`,
with an optional whitespace string at the last index of `raw_list`.
@ -157,14 +165,14 @@ class Statements(Parsable):
raw_list = raw_list[:-1]
self._data = [parse_raw(elem, self, add_spaces) for elem in raw_list]
def get_tabs(self):
def get_tabs(self) -> str:
""" Takes a guess at the tabbing of all contained Statements by retrieving the
tabbing of the first Statement."""
if self._data:
return self._data[0].get_tabs()
return ""
def dump(self, include_spaces=False):
def dump(self, include_spaces: bool = False) -> List[Any]:
""" Dumps this object by first dumping each statement, then appending its
trailing whitespace (if `include_spaces` is set) """
data = super().dump(include_spaces)
@ -172,7 +180,8 @@ class Statements(Parsable):
return data + [self._trailing_whitespace]
return data
def iterate(self, expanded=False, match=None):
def iterate(self, expanded: bool = False,
match: Optional[Callable[["Parsable"], bool]] = None) -> Iterator[Any]:
""" Combines each statement's iterator. """
for elem in self._data:
for sub_elem in elem.iterate(expanded, match):
@ -181,7 +190,7 @@ class Statements(Parsable):
# ======== End overridden functions
def _space_list(list_):
def _space_list(list_: Sequence[Any]) -> List[str]:
""" Inserts whitespace between adjacent non-whitespace tokens. """
spaced_statement: List[str] = []
for i in reversed(range(len(list_))):
@ -197,7 +206,7 @@ class Sentence(Parsable):
# ======== Begin overridden functions
@staticmethod
def should_parse(lists):
def should_parse(lists: Any) -> bool:
""" Returns True if `lists` can be parseable as a `Sentence`-- that is,
every element is a string type.
@ -205,38 +214,39 @@ class Sentence(Parsable):
:returns: whether this lists is parseable by `Sentence`.
"""
return isinstance(lists, list) and len(lists) > 0 and \
all(isinstance(elem, str) for elem in lists)
return (isinstance(lists, list) and len(lists) > 0 and
all(isinstance(elem, str) for elem in lists))
def parse(self, raw_list, add_spaces=False):
def parse(self, raw_list: List[Any], add_spaces: bool = False) -> None:
""" Parses a list of string types into this object.
If add_spaces is set, adds whitespace tokens between adjacent non-whitespace tokens."""
if add_spaces:
raw_list = _space_list(raw_list)
if not isinstance(raw_list, list) or \
any(not isinstance(elem, str) for elem in raw_list):
if (not isinstance(raw_list, list)
or any(not isinstance(elem, str) for elem in raw_list)):
raise errors.MisconfigurationError("Sentence parsing expects a list of string types.")
self._data = raw_list
def iterate(self, expanded=False, match=None):
def iterate(self, expanded: bool = False,
match: Optional[Callable[[Parsable], bool]] = None) -> Iterator[Any]:
""" Simply yields itself. """
if match is None or match(self):
yield self
def set_tabs(self, tabs=" "):
def set_tabs(self, tabs: str = " ") -> None:
""" Sets the tabbing on this sentence. Inserts a newline and `tabs` at the
beginning of `self._data`. """
if self._data[0].isspace():
return
self._data.insert(0, "\n" + tabs)
def dump(self, include_spaces=False):
def dump(self, include_spaces: bool = False) -> List[Any]:
""" Dumps this sentence. If include_spaces is set, includes whitespace tokens."""
if not include_spaces:
return self.words
return self._data
def get_tabs(self):
def get_tabs(self) -> str:
""" Guesses at the tabbing of this sentence. If the first element is whitespace,
returns the whitespace after the rightmost newline in the string. """
first = self._data[0]
@ -248,14 +258,14 @@ class Sentence(Parsable):
# ======== End overridden functions
@property
def words(self):
def words(self) -> List[str]:
""" Iterates over words, but without spaces. Like Unspaced List. """
return [word.strip("\"\'") for word in self._data if not word.isspace()]
def __getitem__(self, index):
def __getitem__(self, index: int) -> str:
return self.words[index]
def __contains__(self, word):
def __contains__(self, word: str) -> bool:
return word in self.words
@ -270,13 +280,13 @@ class Block(Parsable):
names = ["block", " ", "name", " "]
contents = [["\n ", "content", " ", "1"], ["\n ", "content", " ", "2"], "\n"]
"""
def __init__(self, parent=None):
def __init__(self, parent: Optional[Parsable] = None) -> None:
super().__init__(parent)
self.names: Sentence = None
self.contents: Block = None
self.names: Optional[Sentence] = None
self.contents: Optional[Block] = None
@staticmethod
def should_parse(lists):
def should_parse(lists: Any) -> bool:
""" Returns True if `lists` can be parseable as a `Block`-- that is,
it's got a length of 2, the first element is a `Sentence` and the second can be
a `Statements`.
@ -287,13 +297,14 @@ class Block(Parsable):
return isinstance(lists, list) and len(lists) == 2 and \
Sentence.should_parse(lists[0]) and isinstance(lists[1], list)
def set_tabs(self, tabs=" "):
def set_tabs(self, tabs: str = " ") -> None:
""" Sets tabs by setting equivalent tabbing on names, then adding tabbing
to contents."""
self.names.set_tabs(tabs)
self.contents.set_tabs(tabs + " ")
def iterate(self, expanded=False, match=None):
def iterate(self, expanded: bool = False,
match: Optional[Callable[[Parsable], bool]] = None) -> Iterator[Any]:
""" Iterator over self, and if expanded is set, over its contents. """
if match is None or match(self):
yield self
@ -301,7 +312,7 @@ class Block(Parsable):
for elem in self.contents.iterate(expanded, match):
yield elem
def parse(self, raw_list, add_spaces=False):
def parse(self, raw_list: List[Any], add_spaces: bool = False) -> None:
""" Parses a list that resembles a block.
The assumptions that this routine makes are:
@ -323,11 +334,12 @@ class Block(Parsable):
self.contents.parse(raw_list[1], add_spaces)
self._data = [self.names, self.contents]
def get_tabs(self):
def get_tabs(self) -> str:
""" Guesses tabbing by retrieving tabbing guess of self.names. """
return self.names.get_tabs()
def _is_comment(parsed_obj):
def _is_comment(parsed_obj: Parsable) -> bool:
""" Checks whether parsed_obj is a comment.
:param .Parsable parsed_obj:
@ -339,7 +351,8 @@ def _is_comment(parsed_obj):
return False
return parsed_obj.words[0] == "#"
def _is_certbot_comment(parsed_obj):
def _is_certbot_comment(parsed_obj: Parsable) -> bool:
""" Checks whether parsed_obj is a "managed by Certbot" comment.
:param .Parsable parsed_obj:
@ -356,7 +369,8 @@ def _is_certbot_comment(parsed_obj):
return False
return True
def _certbot_comment(parent, preceding_spaces=4):
def _certbot_comment(parent: Parsable, preceding_spaces: int = 4) -> Sentence:
""" A "Managed by Certbot" comment.
:param int preceding_spaces: Number of spaces between the end of the previous
statement and the comment.
@ -367,7 +381,8 @@ def _certbot_comment(parent, preceding_spaces=4):
result.parse([" " * preceding_spaces] + COMMENT_BLOCK)
return result
def _choose_parser(parent, list_):
def _choose_parser(parent: Parsable, list_: Any) -> Parsable:
""" Choose a parser from type(parent).parsing_hooks, depending on whichever hook
returns True first. """
hooks = Parsable.parsing_hooks()
@ -379,7 +394,8 @@ def _choose_parser(parent, list_):
raise errors.MisconfigurationError(
"None of the parsing hooks succeeded, so we don't know how to parse this set of lists.")
def parse_raw(lists_, parent=None, add_spaces=False):
def parse_raw(lists_: Any, parent: Optional[Parsable] = None, add_spaces: bool = False) -> Parsable:
""" Primary parsing factory function.
:param list lists_: raw lists from pyparsing to parse.

View file

@ -24,7 +24,6 @@ import test_util as util
class NginxConfiguratorTest(util.NginxTest):
"""Test a semi complex vhost configuration."""
def setUp(self):
super().setUp()
@ -79,8 +78,8 @@ class NginxConfiguratorTest(util.NginxTest):
self.config.prepare()
except errors.PluginError as err:
err_msg = str(err)
self.assertTrue("lock" in err_msg)
self.assertTrue(self.config.conf("server-root") in err_msg)
self.assertIn("lock", err_msg)
self.assertIn(self.config.conf("server-root"), err_msg)
else: # pragma: no cover
self.fail("Exception wasn't raised!")
@ -192,8 +191,9 @@ class NginxConfiguratorTest(util.NginxTest):
'69.255.225.155']
for name in bad_results:
self.assertRaises(errors.MisconfigurationError,
self.config.choose_vhosts, name)
with self.subTest(name=name):
self.assertRaises(errors.MisconfigurationError,
self.config.choose_vhosts, name)
def test_ipv6only(self):
# ipv6_info: (ipv6_active, ipv6only_present)
@ -215,7 +215,7 @@ class NginxConfiguratorTest(util.NginxTest):
self.assertFalse(addr.ipv6only)
def test_more_info(self):
self.assertTrue('nginx.conf' in self.config.more_info())
self.assertIn('nginx.conf', self.config.more_info())
def test_deploy_cert_requires_fullchain_path(self):
self.config.version = (1, 3, 1)
@ -547,7 +547,7 @@ class NginxConfiguratorTest(util.NginxTest):
self.config.enhance("www.example.com", "redirect")
generated_conf = self.config.parser.parsed[example_conf]
self.assertTrue(util.contains_at_depth(generated_conf, expected, 2))
self.assertIs(util.contains_at_depth(generated_conf, expected, 2), True)
# Test that we successfully add a redirect when there is
# no listen directive
@ -557,7 +557,7 @@ class NginxConfiguratorTest(util.NginxTest):
expected = UnspacedList(_redirect_block_for_domain("migration.com"))[0]
generated_conf = self.config.parser.parsed[migration_conf]
self.assertTrue(util.contains_at_depth(generated_conf, expected, 2))
self.assertIs(util.contains_at_depth(generated_conf, expected, 2), True)
def test_split_for_redirect(self):
example_conf = self.config.parser.abs_path('sites-enabled/example.com')
@ -627,7 +627,7 @@ class NginxConfiguratorTest(util.NginxTest):
"Strict-Transport-Security")
expected = ['add_header', 'Strict-Transport-Security', '"max-age=31536000"', 'always']
generated_conf = self.config.parser.parsed[example_conf]
self.assertTrue(util.contains_at_depth(generated_conf, expected, 2))
self.assertIs(util.contains_at_depth(generated_conf, expected, 2), True)
def test_multiple_headers_hsts(self):
headers_conf = self.config.parser.abs_path('sites-enabled/headers.com')
@ -635,7 +635,7 @@ class NginxConfiguratorTest(util.NginxTest):
"Strict-Transport-Security")
expected = ['add_header', 'Strict-Transport-Security', '"max-age=31536000"', 'always']
generated_conf = self.config.parser.parsed[headers_conf]
self.assertTrue(util.contains_at_depth(generated_conf, expected, 2))
self.assertIs(util.contains_at_depth(generated_conf, expected, 2), True)
def test_http_header_hsts_twice(self):
self.config.enhance("www.example.com", "ensure-http-header",
@ -645,7 +645,6 @@ class NginxConfiguratorTest(util.NginxTest):
self.config.enhance, "www.example.com",
"ensure-http-header", "Strict-Transport-Security")
@mock.patch('certbot_nginx._internal.obj.VirtualHost.contains_list')
def test_certbot_redirect_exists(self, mock_contains_list):
# Test that we add no redirect statement if there is already a
@ -867,7 +866,7 @@ class NginxConfiguratorTest(util.NginxTest):
vhs = self.config._choose_vhosts_wildcard("*.com",
prefer_ssl=True)
# Check that the dialog was called with migration.com
self.assertTrue(vhost in mock_select_vhs.call_args[0][0])
self.assertIn(vhost, mock_select_vhs.call_args[0][0])
# And the actual returned values
self.assertEqual(len(vhs), 1)
@ -883,7 +882,7 @@ class NginxConfiguratorTest(util.NginxTest):
vhs = self.config._choose_vhosts_wildcard("*.com",
prefer_ssl=False)
# Check that the dialog was called with migration.com
self.assertTrue(vhost in mock_select_vhs.call_args[0][0])
self.assertIn(vhost, mock_select_vhs.call_args[0][0])
# And the actual returned values
self.assertEqual(len(vhs), 1)
@ -928,7 +927,7 @@ class NginxConfiguratorTest(util.NginxTest):
if 'summer.com' in x.names][0]
mock_dialog.return_value = [vhost]
self.config.enhance("*.com", "staple-ocsp", "example/chain.pem")
self.assertTrue(mock_dialog.called)
self.assertIs(mock_dialog.called, True)
@mock.patch("certbot_nginx._internal.display_ops.select_vhost_multiple")
def test_enhance_wildcard_double_redirect(self, mock_dialog):
@ -1042,7 +1041,8 @@ class InstallSslOptionsConfTest(util.NginxTest):
f.write("hashofanoldversion")
with mock.patch("certbot.plugins.common.logger") as mock_logger:
self._call()
self.assertEqual(mock_logger.warning.call_args[0][0],
self.assertEqual(
mock_logger.warning.call_args[0][0],
"%s has been manually modified; updated file "
"saved to %s. We recommend updating %s for security purposes.")
self.assertEqual(crypto_util.sha256sum(self.config.mod_ssl_conf_src),
@ -1054,9 +1054,11 @@ class InstallSslOptionsConfTest(util.NginxTest):
def test_current_file_hash_in_all_hashes(self):
from certbot_nginx._internal.constants import ALL_SSL_OPTIONS_HASHES
self.assertTrue(self._current_ssl_options_hash() in ALL_SSL_OPTIONS_HASHES,
self.assertIn(
self._current_ssl_options_hash(), ALL_SSL_OPTIONS_HASHES,
"Constants.ALL_SSL_OPTIONS_HASHES must be appended"
" with the sha256 hash of self.config.mod_ssl_conf when it is updated.")
" with the sha256 hash of self.config.mod_ssl_conf when it is updated."
)
def test_ssl_config_files_hash_in_all_hashes(self):
"""
@ -1077,9 +1079,11 @@ class InstallSslOptionsConfTest(util.NginxTest):
self.assertTrue(all_files)
for one_file in all_files:
file_hash = crypto_util.sha256sum(one_file)
self.assertTrue(file_hash in ALL_SSL_OPTIONS_HASHES,
"Constants.ALL_SSL_OPTIONS_HASHES must be appended with the sha256 "
"hash of {0} when it is updated.".format(one_file))
self.assertIn(
file_hash, ALL_SSL_OPTIONS_HASHES,
f"Constants.ALL_SSL_OPTIONS_HASHES must be appended with the sha256 "
f"hash of {one_file} when it is updated."
)
def test_nginx_version_uses_correct_config(self):
self.config.version = (1, 5, 8)
@ -1127,7 +1131,7 @@ class DetermineDefaultServerRootTest(certbot_test_util.ConfigTestCase):
self.assertIn("/usr/local/etc/nginx", server_root)
self.assertIn("/etc/nginx", server_root)
else:
self.assertTrue(server_root in ("/etc/nginx", "/usr/local/etc/nginx"))
self.assertIn(server_root, ("/etc/nginx", "/usr/local/etc/nginx"))
if __name__ == "__main__":

View file

@ -27,15 +27,16 @@ class SelectVhostMultiTest(util.NginxTest):
vhs = select_vhost_multiple([self.vhosts[3],
self.vhosts[2],
self.vhosts[1]])
self.assertTrue(self.vhosts[2] in vhs)
self.assertTrue(self.vhosts[3] in vhs)
self.assertFalse(self.vhosts[1] in vhs)
self.assertIn(self.vhosts[2], vhs)
self.assertIn(self.vhosts[3], vhs)
self.assertNotIn(self.vhosts[1], vhs)
@certbot_util.patch_display_util()
def test_select_cancel(self, mock_util):
mock_util().checklist.return_value = (display_util.CANCEL, "whatever")
vhs = select_vhost_multiple([self.vhosts[2], self.vhosts[3]])
self.assertFalse(vhs)
self.assertEqual(len(vhs), 0)
self.assertEqual(vhs, [])
if __name__ == "__main__":

View file

@ -432,15 +432,15 @@ class TestUnspacedList(unittest.TestCase):
self.assertEqual(ul3, ["some", "things", "why", "did", "whether"])
def test_is_dirty(self):
self.assertEqual(False, self.ul2.is_dirty())
self.assertIs(self.ul2.is_dirty(), False)
ul3 = UnspacedList([])
ul3.append(self.ul)
self.assertEqual(False, self.ul.is_dirty())
self.assertEqual(True, ul3.is_dirty())
self.assertIs(self.ul.is_dirty(), False)
self.assertIs(ul3.is_dirty(), True)
ul4 = UnspacedList([[1], [2, 3, 4]])
self.assertEqual(False, ul4.is_dirty())
self.assertIs(ul4.is_dirty(), False)
ul4[1][2] = 5
self.assertEqual(True, ul4.is_dirty())
self.assertIs(ul4.is_dirty(), True)
if __name__ == '__main__':

View file

@ -19,37 +19,37 @@ class AddrTest(unittest.TestCase):
def test_fromstring(self):
self.assertEqual(self.addr1.get_addr(), "192.168.1.1")
self.assertEqual(self.addr1.get_port(), "")
self.assertFalse(self.addr1.ssl)
self.assertFalse(self.addr1.default)
self.assertIs(self.addr1.ssl, False)
self.assertIs(self.addr1.default, False)
self.assertEqual(self.addr2.get_addr(), "192.168.1.1")
self.assertEqual(self.addr2.get_port(), "*")
self.assertTrue(self.addr2.ssl)
self.assertFalse(self.addr2.default)
self.assertIs(self.addr2.ssl, True)
self.assertIs(self.addr2.default, False)
self.assertEqual(self.addr3.get_addr(), "192.168.1.1")
self.assertEqual(self.addr3.get_port(), "80")
self.assertFalse(self.addr3.ssl)
self.assertFalse(self.addr3.default)
self.assertIs(self.addr3.ssl, False)
self.assertIs(self.addr3.default, False)
self.assertEqual(self.addr4.get_addr(), "*")
self.assertEqual(self.addr4.get_port(), "80")
self.assertTrue(self.addr4.ssl)
self.assertTrue(self.addr4.default)
self.assertIs(self.addr4.ssl, True)
self.assertIs(self.addr4.default, True)
self.assertEqual(self.addr5.get_addr(), "myhost")
self.assertEqual(self.addr5.get_port(), "")
self.assertFalse(self.addr5.ssl)
self.assertFalse(self.addr5.default)
self.assertIs(self.addr5.ssl, False)
self.assertIs(self.addr5.default, False)
self.assertEqual(self.addr6.get_addr(), "")
self.assertEqual(self.addr6.get_port(), "80")
self.assertFalse(self.addr6.ssl)
self.assertTrue(self.addr6.default)
self.assertIs(self.addr6.ssl, False)
self.assertIs(self.addr6.default, True)
self.assertTrue(self.addr8.default)
self.assertIs(self.addr8.default, True)
self.assertEqual(None, self.addr7)
self.assertIsNone(self.addr7)
def test_str(self):
self.assertEqual(str(self.addr1), "192.168.1.1")
@ -177,10 +177,10 @@ class VirtualHostTest(unittest.TestCase):
self.assertEqual(stringified, str(self.vhost1))
def test_has_header(self):
self.assertTrue(self.vhost_has_hsts.has_header('Strict-Transport-Security'))
self.assertFalse(self.vhost_has_hsts.has_header('Bogus-Header'))
self.assertFalse(self.vhost1.has_header('Strict-Transport-Security'))
self.assertFalse(self.vhost1.has_header('Bogus-Header'))
self.assertIs(self.vhost_has_hsts.has_header('Strict-Transport-Security'), True)
self.assertIs(self.vhost_has_hsts.has_header('Bogus-Header'), False)
self.assertIs(self.vhost1.has_header('Strict-Transport-Security'), False)
self.assertIs(self.vhost1.has_header('Bogus-Header'), False)
def test_contains_list(self):
from certbot_nginx._internal.obj import VirtualHost
@ -224,5 +224,6 @@ class VirtualHostTest(unittest.TestCase):
self.assertTrue(vhost_haystack.contains_list(test_needle))
self.assertFalse(vhost_bad_haystack.contains_list(test_needle))
if __name__ == "__main__":
unittest.main() # pragma: no cover

View file

@ -35,8 +35,8 @@ class CommentHelpersTest(unittest.TestCase):
self.assertTrue(_is_certbot_comment(comment))
self.assertEqual(comment.dump(), COMMENT_BLOCK)
self.assertEqual(comment.dump(True), [' '] + COMMENT_BLOCK)
self.assertEqual(_certbot_comment(None, 2).dump(True),
[' '] + COMMENT_BLOCK)
self.assertEqual(_certbot_comment(None, 2).dump(True), [' '] + COMMENT_BLOCK)
class ParsingHooksTest(unittest.TestCase):
def test_is_sentence(self):
@ -94,6 +94,7 @@ class ParsingHooksTest(unittest.TestCase):
parse_raw([], add_spaces=True)
fake_parser1.parse.called_with([None, True])
class SentenceTest(unittest.TestCase):
def setUp(self):
from certbot_nginx._internal.parser_obj import Sentence
@ -140,6 +141,7 @@ class SentenceTest(unittest.TestCase):
self.sentence.parse(['\n\t \n', 'tabs'])
self.assertEqual(self.sentence.get_tabs(), '')
class BlockTest(unittest.TestCase):
def setUp(self):
from certbot_nginx._internal.parser_obj import Block
@ -244,8 +246,8 @@ class StatementsTest(unittest.TestCase):
def test_parse_hides_trailing_whitespace(self):
self.statements.parse(self.raw + ['\n\n '])
self.assertTrue(isinstance(self.statements.dump()[-1], list))
self.assertTrue(self.statements.dump(True)[-1].isspace())
self.assertIsInstance(self.statements.dump()[-1], list)
self.assertIs(self.statements.dump(True)[-1].isspace(), True)
self.assertEqual(self.statements.dump(True)[-1], '\n\n ')
def test_iterate(self):
@ -254,5 +256,6 @@ class StatementsTest(unittest.TestCase):
for i, elem in enumerate(self.statements.iterate(match=lambda x: 'sentence' in x)):
self.assertEqual(expected[i], elem.dump())
if __name__ == "__main__":
unittest.main() # pragma: no cover

View file

@ -203,8 +203,7 @@ class NginxParserTest(util.NginxTest):
mock_vhost.raw = [['listen', '80'],
['ssl', 'on'],
['server_name', '*.www.foo.com', '*.www.example.com']]
self.assertTrue(nparser.has_ssl_on_directive(mock_vhost))
self.assertIs(nparser.has_ssl_on_directive(mock_vhost), True)
def test_remove_server_directives(self):
nparser = parser.NginxParser(self.config_path)
@ -452,7 +451,7 @@ class NginxParserTest(util.NginxTest):
nparser.filedump(ext='')
# check properties of new vhost
self.assertFalse(next(iter(new_vhost.addrs)).default)
self.assertIs(next(iter(new_vhost.addrs)).default, False)
self.assertNotEqual(new_vhost.path, default.path)
# check that things are written to file correctly
@ -461,7 +460,7 @@ class NginxParserTest(util.NginxTest):
new_defaults = [x for x in new_vhosts if 'default' in x.filep]
self.assertEqual(len(new_defaults), 2)
new_vhost_parsed = new_defaults[1]
self.assertFalse(next(iter(new_vhost_parsed.addrs)).default)
self.assertIs(next(iter(new_vhost_parsed.addrs)).default, False)
self.assertEqual(next(iter(default.names)), next(iter(new_vhost_parsed.names)))
self.assertEqual(len(default.raw), len(new_vhost_parsed.raw))
self.assertTrue(next(iter(default.addrs)).super_eq(next(iter(new_vhost_parsed.addrs))))
@ -533,5 +532,6 @@ class NginxParserTest(util.NginxTest):
for output in log.output
))
if __name__ == "__main__":
unittest.main() # pragma: no cover

View file

@ -20,7 +20,7 @@ import pytz
from acme import fields as acme_fields
from acme import messages
from acme.client import ClientBase # pylint: disable=unused-import
from acme.client import ClientBase
from certbot import configuration
from certbot import errors
from certbot import interfaces
@ -125,6 +125,7 @@ class AccountMemoryStorage(interfaces.AccountStorage):
except KeyError:
raise errors.AccountNotFound(account_id)
class RegistrationResourceWithNewAuthzrURI(messages.RegistrationResource):
"""A backwards-compatible RegistrationResource with a new-authz URI.
@ -136,6 +137,7 @@ class RegistrationResourceWithNewAuthzrURI(messages.RegistrationResource):
"""
new_authzr_uri: str = jose.field('new_authzr_uri')
class AccountFileStorage(interfaces.AccountStorage):
"""Accounts file storage.

View file

@ -167,6 +167,7 @@ class ErrorHandler:
logger.debug("Calling signal %s", signum)
os.kill(os.getpid(), signum)
class ExitHandler(ErrorHandler):
"""Context manager for running code that must be cleaned up.
@ -175,5 +176,5 @@ class ExitHandler(ErrorHandler):
regular exit.
"""
def __init__(self, func: Callable[..., Any], *args: Any, **kwargs: Any) -> None:
ErrorHandler.__init__(self, func, *args, **kwargs)
super().__init__(func, *args, **kwargs)
self.call_on_regular_exit = True

View file

@ -22,7 +22,7 @@ from certbot import crypto_util
from certbot import errors
from certbot import util
from certbot.compat.os import getenv
from certbot.interfaces import RenewableCert # pylint: disable=unused-import
from certbot.interfaces import RenewableCert
logger = logging.getLogger(__name__)

View file

@ -256,7 +256,7 @@ class Addr:
self.ipv6 = ipv6
@classmethod
def fromstring(cls, str_addr: str) -> 'Addr':
def fromstring(cls, str_addr: str) -> Optional['Addr']:
"""Initialize Addr from string."""
if str_addr.startswith('['):
# ipv6 addresses starts with [

View file

@ -10,9 +10,9 @@ import string
import sys
import unittest
from certbot.compat import filesystem # pylint: disable=ungrouped-imports
from certbot.compat import os # pylint: disable=ungrouped-imports
import certbot.tests.util as test_util # pylint: disable=ungrouped-imports
from certbot.compat import filesystem
from certbot.compat import os
import certbot.tests.util as test_util
try:
import mock

View file

@ -293,7 +293,8 @@ class NoninteractiveDisplayTest(unittest.TestCase):
self.displayer.notification("message2", pause=False)
string = self.mock_stdout.write.call_args[0][0]
self.assertTrue("- - - " in string and ("message2" + os.linesep) in string)
self.assertIn("- - - ", string)
self.assertIn("message2" + os.linesep, string)
def test_input(self):
d = "an incomputable value"

View file

@ -18,7 +18,7 @@ import pytz
from certbot import crypto_util, configuration
from certbot import errors
from certbot import interfaces # pylint: disable=unused-import
from certbot import interfaces
from certbot import util
from certbot._internal import account
from certbot._internal import cli
@ -529,7 +529,6 @@ class DeleteIfAppropriateTest(test_util.ConfigTestCase):
def test_opt_in_deletion(self, mock_get_utility, mock_delete,
mock_cert_path_to_lineage, mock_full_archive_dir,
mock_match_and_check_overlaps, mock_renewal_file_for_certname):
# pylint: disable = unused-argument
config = self.config
config.namespace.delete_after_revoke = True
config.cert_path = "/some/reasonable/path"

View file

@ -133,7 +133,7 @@ class CredentialsConfigurationTest(test_util.TempDirTestCase):
def __init__(self, *args, **kwargs):
self.reset()
logging.Handler.__init__(self, *args, **kwargs)
super().__init__(*args, **kwargs)
def emit(self, record):
self.messages[record.levelname.lower()].append(record.getMessage())

View file

@ -7,10 +7,10 @@ from typing import Tuple
import unittest
import josepy as jose
import OpenSSL.crypto # pylint: disable=unused-import
import OpenSSL.crypto
from acme import challenges
from acme import standalone as acme_standalone # pylint: disable=unused-import
from acme import standalone as acme_standalone
from certbot import achallenges
from certbot import errors
from certbot.tests import acme_util
@ -22,7 +22,6 @@ except ImportError: # pragma: no cover
from unittest import mock
class ServerManagerTest(unittest.TestCase):
"""Tests for certbot._internal.plugins.standalone.ServerManager."""

View file

@ -20,8 +20,8 @@ install_and_test = python {toxinidir}/tools/install_and_test.py
dns_packages = certbot-dns-cloudflare certbot-dns-cloudxns certbot-dns-digitalocean certbot-dns-dnsimple certbot-dns-dnsmadeeasy certbot-dns-gehirn certbot-dns-google certbot-dns-linode certbot-dns-luadns certbot-dns-nsone certbot-dns-ovh certbot-dns-rfc2136 certbot-dns-route53 certbot-dns-sakuracloud
win_all_packages = acme[test] certbot[test] {[base]dns_packages} certbot-nginx
all_packages = {[base]win_all_packages} certbot-apache
fully_typed_source_paths = acme/acme certbot/certbot certbot-ci/certbot_integration_tests certbot-ci/snap_integration_tests certbot-ci/windows_installer_integration_tests certbot-compatibility-test/certbot_compatibility_test certbot-dns-cloudflare/certbot_dns_cloudflare certbot-dns-cloudxns/certbot_dns_cloudxns certbot-dns-digitalocean/certbot_dns_digitalocean certbot-dns-dnsimple/certbot_dns_dnsimple certbot-dns-dnsmadeeasy/certbot_dns_dnsmadeeasy certbot-dns-gehirn/certbot_dns_gehirn certbot-dns-google/certbot_dns_google certbot-dns-linode/certbot_dns_linode certbot-dns-luadns/certbot_dns_luadns certbot-dns-nsone/certbot_dns_nsone certbot-dns-ovh/certbot_dns_ovh certbot-dns-rfc2136/certbot_dns_rfc2136 certbot-dns-route53/certbot_dns_route53 certbot-dns-sakuracloud/certbot_dns_sakuracloud tests/lock_test.py
partially_typed_source_paths = certbot-apache/certbot_apache certbot-nginx/certbot_nginx
fully_typed_source_paths = acme/acme certbot/certbot certbot-ci/certbot_integration_tests certbot-ci/snap_integration_tests certbot-ci/windows_installer_integration_tests certbot-compatibility-test/certbot_compatibility_test certbot-dns-cloudflare/certbot_dns_cloudflare certbot-dns-cloudxns/certbot_dns_cloudxns certbot-dns-digitalocean/certbot_dns_digitalocean certbot-dns-dnsimple/certbot_dns_dnsimple certbot-dns-dnsmadeeasy/certbot_dns_dnsmadeeasy certbot-dns-gehirn/certbot_dns_gehirn certbot-dns-google/certbot_dns_google certbot-dns-linode/certbot_dns_linode certbot-dns-luadns/certbot_dns_luadns certbot-dns-nsone/certbot_dns_nsone certbot-dns-ovh/certbot_dns_ovh certbot-dns-rfc2136/certbot_dns_rfc2136 certbot-dns-route53/certbot_dns_route53 certbot-dns-sakuracloud/certbot_dns_sakuracloud certbot-nginx/certbot_nginx tests/lock_test.py
partially_typed_source_paths = certbot-apache/certbot_apache
[testenv]
passenv =