Beispiel #1
0
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)
    if block and '\n' not in block[-1]:  # could be "   \n  " or ["\n"] !
        block.append(nginxparser.UnspacedList('\n'))
Beispiel #2
0
 def setUp(self):
     self.a = ["\n    ", "things", " ", "quirk"]
     self.b = ["y", " "]
     self.l = self.a[:]
     self.l2 = self.b[:]
     self.ul = UnspacedList(self.l)
     self.ul2 = UnspacedList(self.l2)
Beispiel #3
0
    def test_double_redirect(self):
        # Test that we add one redirect for each domain
        example_conf = self.config.parser.abs_path('sites-enabled/example.com')
        self.config.enhance("example.com", "redirect")
        self.config.enhance("example.org", "redirect")

        expected1 = UnspacedList(_redirect_block_for_domain("example.com"))[0]
        expected2 = UnspacedList(_redirect_block_for_domain("example.org"))[0]

        generated_conf = self.config.parser.parsed[example_conf]
        self.assertTrue(util.contains_at_depth(generated_conf, expected1, 2))
        self.assertTrue(util.contains_at_depth(generated_conf, expected2, 2))
Beispiel #4
0
 def test_is_dirty(self):
     self.assertEqual(False, self.ul2.is_dirty())
     ul3 = UnspacedList([])
     ul3.append(self.ul)
     self.assertEqual(False, self.ul.is_dirty())
     self.assertEqual(True, ul3.is_dirty())
     ul4 = UnspacedList([[1], [2, 3, 4]])
     self.assertEqual(False, ul4.is_dirty())
     ul4[1][2] = 5
     self.assertEqual(True, ul4.is_dirty())
Beispiel #5
0
 def test_is_dirty(self):
     self.assertIs(self.ul2.is_dirty(), False)
     ul3 = UnspacedList([])
     ul3.append(self.ul)
     self.assertIs(self.ul.is_dirty(), False)
     self.assertIs(ul3.is_dirty(), True)
     ul4 = UnspacedList([[1], [2, 3, 4]])
     self.assertIs(ul4.is_dirty(), False)
     ul4[1][2] = 5
     self.assertIs(ul4.is_dirty(), True)
Beispiel #6
0
    def test_deploy_no_match_add_redirect(self):
        default_conf = self.config.parser.abs_path('sites-enabled/default')
        foo_conf = self.config.parser.abs_path('foo.conf')
        del self.config.parser.parsed[foo_conf][2][1][0][1][
            0]  # remove default_server
        self.config.version = (1, 3, 1)

        self.config.deploy_cert("www.nomatch.com", "example/cert.pem",
                                "example/key.pem", "example/chain.pem",
                                "example/fullchain.pem")

        self.config.deploy_cert("nomatch.com", "example/cert.pem",
                                "example/key.pem", "example/chain.pem",
                                "example/fullchain.pem")

        self.config.enhance("www.nomatch.com", "redirect")

        self.config.save()

        self.config.parser.load()

        expected = UnspacedList(
            _redirect_block_for_domain("www.nomatch.com"))[0]

        generated_conf = self.config.parser.parsed[default_conf]
        self.assertTrue(util.contains_at_depth(generated_conf, expected, 2))
Beispiel #7
0
    def test_dump_as_string(self):
        dumped = dumps(UnspacedList([
            ['user', ' ', 'www-data'],
            [['\n', 'server', ' '], [
                ['\n    ', 'listen', ' ', '80'],
                ['\n    ', 'server_name', ' ', 'foo.com'],
                ['\n    ', 'root', ' ', '/home/ubuntu/sites/foo/'],
                [['\n\n    ', 'location', ' ', '/status', ' '], [
                    ['\n        ', 'check_status', ''],
                    [['\n\n        ', 'types', ' '],
                    [['\n            ', 'image/jpeg', ' ', 'jpg']]],
                ]]
            ]]]))

        self.assertEqual(dumped.split('\n'),
                         'user www-data;\n'
                         'server {\n'
                         '    listen 80;\n'
                         '    server_name foo.com;\n'
                         '    root /home/ubuntu/sites/foo/;\n'
                         '\n'
                         '    location /status {\n'
                         '        check_status;\n'
                         '\n'
                         '        types {\n'
                         '            image/jpeg jpg;}}}'.split('\n'))
Beispiel #8
0
 def test_set(self):
     ul3 = copy.deepcopy(self.ul)
     ul3[0] = "zither"
     l = ["\n ", "zather", "zest"]
     ul3[1] = UnspacedList(l)
     self.assertEqual(ul3, ["zither", ["zather", "zest"]])
     self.assertEqual(ul3.spaced, [self.a[0], "zither", " ", l])
Beispiel #9
0
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):
        # whitespace or comment
        block.append(directive)
        return

    location = _find_location(block, directive[0])

    # we can update directive
    if location is not None:
        _update_directive(block, directive, location)
        return

    _add_directive(block, directive, insert_at_top)
Beispiel #10
0
 def test_insert(self):
     x = UnspacedList(
         [['\n    ', 'listen', '       ', '69.50.225.155:9000'],
          ['\n    ', 'listen', '       ', '127.0.0.1'],
          ['\n    ', 'server_name', ' ', '.example.com'],
          ['\n    ', 'server_name', ' ', 'example.*'], '\n',
          ['listen', ' ', '5001', ' ', 'ssl']])
     x.insert(5, "FROGZ")
     self.assertEqual(
         x, [['listen', '69.50.225.155:9000'], ['listen', '127.0.0.1'],
             ['server_name', '.example.com'], ['server_name', 'example.*'],
             ['listen', '5001', 'ssl'], 'FROGZ'])
     self.assertEqual(
         x.spaced, [['\n    ', 'listen', '       ', '69.50.225.155:9000'],
                    ['\n    ', 'listen', '       ', '127.0.0.1'],
                    ['\n    ', 'server_name', ' ', '.example.com'],
                    ['\n    ', 'server_name', ' ', 'example.*'], '\n',
                    ['listen', ' ', '5001', ' ', 'ssl'], 'FROGZ'])
Beispiel #11
0
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
    :param int location: The location within ``block`` of the directive to be commented
    """
    next_entry = block[location + 1] if location + 1 < len(block) else None
    if isinstance(next_entry, list) and next_entry:
        if len(next_entry
               ) >= 2 and next_entry[-2] == "#" and COMMENT in next_entry[-1]:
            return
        if isinstance(next_entry, nginxparser.UnspacedList):
            next_entry = next_entry.spaced[0]
        else:
            next_entry = next_entry[0]

    block.insert(location + 1, COMMENT_BLOCK[:])
    if next_entry is not None and "\n" not in next_entry:
        block.insert(location + 2, '\n')
Beispiel #12
0
    def test_redirect_enhance(self):
        # Test that we successfully add a redirect when there is
        # a listen directive
        expected = UnspacedList(_redirect_block_for_domain("www.example.com"))[0]

        example_conf = self.config.parser.abs_path('sites-enabled/example.com')
        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))

        # Test that we successfully add a redirect when there is
        # no listen directive
        migration_conf = self.config.parser.abs_path('sites-enabled/migration.com')
        self.config.enhance("migration.com", "redirect")

        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))
Beispiel #13
0
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:
                return nginxparser.load(_file)
        except IOError:
            logger.warning("Missing NGINX TLS options file: %s", ssl_options)
        except UnicodeDecodeError:
            logger.warning(
                "Could not read file: %s due to invalid character. "
                "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 UnspacedList([])
Beispiel #14
0
    def test_dump_as_file(self):
        with open(util.get_data_filename('nginx.conf')) as handle:
            parsed = load(handle)
        parsed[-1][-1].append(UnspacedList([['server'],
                               [['listen', ' ', '443', ' ', 'ssl'],
                                ['server_name', ' ', 'localhost'],
                                ['ssl_certificate', ' ', 'cert.pem'],
                                ['ssl_certificate_key', ' ', 'cert.key'],
                                ['ssl_session_cache', ' ', 'shared:SSL:1m'],
                                ['ssl_session_timeout', ' ', '5m'],
                                ['ssl_ciphers', ' ', 'HIGH:!aNULL:!MD5'],
                                [['location', ' ', '/'],
                                 [['root', ' ', 'html'],
                                  ['index', ' ', 'index.html', ' ', 'index.htm']]]]]))

        with tempfile.TemporaryFile(mode='w+t') as f:
            dump(parsed, f)
            f.seek(0)
            parsed_new = load(f)
        self.assertEqual(parsed, parsed_new)
Beispiel #15
0
class TestUnspacedList(unittest.TestCase):
    """Test the UnspacedList data structure"""
    def setUp(self):
        self.a = ["\n    ", "things", " ", "quirk"]
        self.b = ["y", " "]
        self.l = self.a[:]
        self.l2 = self.b[:]
        self.ul = UnspacedList(self.l)
        self.ul2 = UnspacedList(self.l2)

    def test_construction(self):
        self.assertEqual(self.ul, ["things", "quirk"])
        self.assertEqual(self.ul2, ["y"])

    def test_append(self):
        ul3 = copy.deepcopy(self.ul)
        ul3.append("wise")
        self.assertEqual(ul3, ["things", "quirk", "wise"])
        self.assertEqual(ul3.spaced, self.a + ["wise"])

    def test_add(self):
        ul3 = self.ul + self.ul2
        self.assertEqual(ul3, ["things", "quirk", "y"])
        self.assertEqual(ul3.spaced, self.a + self.b)
        self.assertEqual(self.ul.spaced, self.a)
        ul3 = self.ul + self.l2
        self.assertEqual(ul3, ["things", "quirk", "y"])
        self.assertEqual(ul3.spaced, self.a + self.b)

    def test_extend(self):
        ul3 = copy.deepcopy(self.ul)
        ul3.extend(self.ul2)
        self.assertEqual(ul3, ["things", "quirk", "y"])
        self.assertEqual(ul3.spaced, self.a + self.b)
        self.assertEqual(self.ul.spaced, self.a)

    def test_set(self):
        ul3 = copy.deepcopy(self.ul)
        ul3[0] = "zither"
        l = ["\n ", "zather", "zest"]
        ul3[1] = UnspacedList(l)
        self.assertEqual(ul3, ["zither", ["zather", "zest"]])
        self.assertEqual(ul3.spaced, [self.a[0], "zither", " ", l])

    def test_get(self):
        self.assertRaises(IndexError, self.ul2.__getitem__, 2)
        self.assertRaises(IndexError, self.ul2.__getitem__, -3)

    def test_insert(self):
        x = UnspacedList(
                [['\n    ', 'listen', '       ', '69.50.225.155:9000'],
                ['\n    ', 'listen', '       ', '127.0.0.1'],
                ['\n    ', 'server_name', ' ', '.example.com'],
                ['\n    ', 'server_name', ' ', 'example.*'], '\n',
                ['listen', ' ', '5001', ' ', 'ssl']])
        x.insert(5, "FROGZ")
        self.assertEqual(x,
            [['listen', '69.50.225.155:9000'], ['listen', '127.0.0.1'],
            ['server_name', '.example.com'], ['server_name', 'example.*'],
            ['listen', '5001', 'ssl'], 'FROGZ'])
        self.assertEqual(x.spaced,
            [['\n    ', 'listen', '       ', '69.50.225.155:9000'],
            ['\n    ', 'listen', '       ', '127.0.0.1'],
            ['\n    ', 'server_name', ' ', '.example.com'],
            ['\n    ', 'server_name', ' ', 'example.*'], '\n',
            ['listen', ' ', '5001', ' ', 'ssl'],
            'FROGZ'])

    def test_rawlists(self):
        ul3 = copy.deepcopy(self.ul)
        ul3.insert(0, "some")
        ul3.append("why")
        ul3.extend(["did", "whether"])
        del ul3[2]
        self.assertEqual(ul3, ["some", "things", "why", "did", "whether"])

    def test_is_dirty(self):
        self.assertEqual(False, self.ul2.is_dirty())
        ul3 = UnspacedList([])
        ul3.append(self.ul)
        self.assertEqual(False, self.ul.is_dirty())
        self.assertEqual(True, ul3.is_dirty())
        ul4 = UnspacedList([[1], [2, 3, 4]])
        self.assertEqual(False, ul4.is_dirty())
        ul4[1][2] = 5
        self.assertEqual(True, ul4.is_dirty())
Beispiel #16
0
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):
        # whitespace or comment
        block.append(directive)
        return

    location = _find_location(block, directive[0])

    # Append or prepend directive. Fail if the name is not a repeatable directive name,
    # and there is already a copy of that directive with a different value
    # in the config file.

    # handle flat include files

    directive_name = directive[0]

    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)

    err_fmt = 'tried to insert directive "{0}" but found conflicting "{1}".'

    # Give a better error message about the specific directive than Nginx's "fail to restart"
    if directive_name == INCLUDE:
        # in theory, we might want to do this recursively, but in practice, that's really not
        # necessary because we know what file we're talking about (and if we don't recurse, we
        # just give a worse error message)
        included_directives = _parse_ssl_options(directive[1])

        for included_directive in included_directives:
            included_dir_loc = _find_location(block, included_directive[0])
            included_dir_name = included_directive[0]
            if (not _is_whitespace_or_comment(included_directive)
                    and not can_append(included_dir_loc, included_dir_name)):

                # 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:
            # Add a newline so the comment doesn't comment
            # out existing directives
            block.insert(0, nginxparser.UnspacedList('\n'))
            block.insert(0, directive)
            comment_directive(block, 0)
        else:
            block.append(directive)
            comment_directive(block, len(block) - 1)
        return

    # 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]))