/mandos/trunk

To get this branch, use:
bzr branch http://bzr.recompile.se/loggerhead/mandos/trunk

« back to all changes in this revision

Viewing changes to mandos-ctl

  • Committer: Teddy Hogeborn
  • Date: 2019-03-08 22:57:26 UTC
  • Revision ID: teddy@recompile.se-20190308225726-9tmoaw98l7dmzteh
mandos-ctl: Bug fix: close an open file

* mandos-ctl (TestSetSecretCmd): Don't open /dev/null in the class
                                 definition.

Show diffs side-by-side

added added

removed removed

Lines of Context:
44
44
import logging
45
45
import io
46
46
import tempfile
 
47
import contextlib
47
48
 
48
49
import dbus
49
50
 
644
645
    return commands
645
646
 
646
647
 
647
 
def main():
648
 
    parser = argparse.ArgumentParser()
649
 
 
650
 
    add_command_line_options(parser)
651
 
 
652
 
    options = parser.parse_args()
 
648
def check_option_syntax(parser, options):
 
649
    """Apply additional restrictions on options, not expressible in
 
650
argparse"""
653
651
 
654
652
    def has_actions(options):
655
653
        return any((options.enable,
683
681
    if options.is_enabled and len(options.client) > 1:
684
682
        parser.error("--is-enabled requires exactly one client")
685
683
 
 
684
 
 
685
def main():
 
686
    parser = argparse.ArgumentParser()
 
687
 
 
688
    add_command_line_options(parser)
 
689
 
 
690
    options = parser.parse_args()
 
691
 
 
692
    check_option_syntax(parser, options)
 
693
 
686
694
    clientnames = options.client
687
695
 
688
696
    try:
1093
1101
class TestSetSecretCmd(TestValueArgumentPropertyCmd):
1094
1102
    command = SetSecretCmd
1095
1103
    property = "Secret"
1096
 
    values_to_set = [open("/dev/null", "rb"),
 
1104
    values_to_set = [io.BytesIO(b""),
1097
1105
                     io.BytesIO(b"secret\0xyzzy\nbar")]
1098
1106
    values_to_get = [b"", b"secret\0xyzzy\nbar"]
1099
1107
 
1155
1163
        """Assert that parsing ARGS should result in an instance of
1156
1164
COMMAND_CLS with (optionally) all supplied attributes (CMD_ATTRS)."""
1157
1165
        options = self.parser.parse_args(args)
 
1166
        check_option_syntax(self.parser, options)
1158
1167
        commands = commands_from_options(options)
1159
1168
        self.assertEqual(len(commands), 1)
1160
1169
        command = commands[0]
1325
1334
        self.assert_command_from_args(["-V", "foo"], IsEnabledCmd)
1326
1335
 
1327
1336
 
 
1337
class Test_check_option_syntax(unittest.TestCase):
 
1338
    # This mostly corresponds to the definition from has_actions() in
 
1339
    # check_option_syntax()
 
1340
    actions = {
 
1341
        # The actual values set here are not that important, but we do
 
1342
        # at least stick to the correct types, even though they are
 
1343
        # never used
 
1344
        "enable": True,
 
1345
        "disable": True,
 
1346
        "bump_timeout": True,
 
1347
        "start_checker": True,
 
1348
        "stop_checker": True,
 
1349
        "is_enabled": True,
 
1350
        "remove": True,
 
1351
        "checker": "x",
 
1352
        "timeout": datetime.timedelta(),
 
1353
        "extended_timeout": datetime.timedelta(),
 
1354
        "interval": datetime.timedelta(),
 
1355
        "approved_by_default": True,
 
1356
        "approval_delay": datetime.timedelta(),
 
1357
        "approval_duration": datetime.timedelta(),
 
1358
        "host": "x",
 
1359
        "secret": io.BytesIO(b"x"),
 
1360
        "approve": True,
 
1361
        "deny": True,
 
1362
    }
 
1363
 
 
1364
    def setUp(self):
 
1365
        self.parser = argparse.ArgumentParser()
 
1366
        add_command_line_options(self.parser)
 
1367
 
 
1368
    @contextlib.contextmanager
 
1369
    def assertParseError(self):
 
1370
        with self.assertRaises(SystemExit) as e:
 
1371
            with self.temporarily_suppress_stderr():
 
1372
                yield
 
1373
        # Exit code from argparse is guaranteed to be "2".  Reference:
 
1374
        # https://docs.python.org/3/library/argparse.html#exiting-methods
 
1375
        self.assertEqual(e.exception.code, 2)
 
1376
 
 
1377
    @staticmethod
 
1378
    @contextlib.contextmanager
 
1379
    def temporarily_suppress_stderr():
 
1380
        null = os.open(os.path.devnull, os.O_RDWR)
 
1381
        stderrcopy = os.dup(sys.stderr.fileno())
 
1382
        os.dup2(null, sys.stderr.fileno())
 
1383
        os.close(null)
 
1384
        try:
 
1385
            yield
 
1386
        finally:
 
1387
            # restore stderr
 
1388
            os.dup2(stderrcopy, sys.stderr.fileno())
 
1389
            os.close(stderrcopy)
 
1390
 
 
1391
    def check_option_syntax(self, options):
 
1392
        check_option_syntax(self.parser, options)
 
1393
 
 
1394
    def test_actions_requires_client_or_all(self):
 
1395
        for action, value in self.actions.items():
 
1396
            options = self.parser.parse_args()
 
1397
            setattr(options, action, value)
 
1398
            with self.assertParseError():
 
1399
                self.check_option_syntax(options)
 
1400
 
 
1401
    def test_actions_conflicts_with_verbose(self):
 
1402
        for action, value in self.actions.items():
 
1403
            options = self.parser.parse_args()
 
1404
            setattr(options, action, value)
 
1405
            options.verbose = True
 
1406
            with self.assertParseError():
 
1407
                self.check_option_syntax(options)
 
1408
 
 
1409
    def test_dump_json_conflicts_with_verbose(self):
 
1410
        options = self.parser.parse_args()
 
1411
        options.dump_json = True
 
1412
        options.verbose = True
 
1413
        with self.assertParseError():
 
1414
            self.check_option_syntax(options)
 
1415
 
 
1416
    def test_dump_json_conflicts_with_action(self):
 
1417
        for action, value in self.actions.items():
 
1418
            options = self.parser.parse_args()
 
1419
            setattr(options, action, value)
 
1420
            options.dump_json = True
 
1421
            with self.assertParseError():
 
1422
                self.check_option_syntax(options)
 
1423
 
 
1424
    def test_all_can_not_be_alone(self):
 
1425
        options = self.parser.parse_args()
 
1426
        options.all = True
 
1427
        with self.assertParseError():
 
1428
            self.check_option_syntax(options)
 
1429
 
 
1430
    def test_all_is_ok_with_any_action(self):
 
1431
        for action, value in self.actions.items():
 
1432
            options = self.parser.parse_args()
 
1433
            setattr(options, action, value)
 
1434
            options.all = True
 
1435
            self.check_option_syntax(options)
 
1436
 
 
1437
    def test_is_enabled_fails_without_client(self):
 
1438
        options = self.parser.parse_args()
 
1439
        options.is_enabled = True
 
1440
        with self.assertParseError():
 
1441
            self.check_option_syntax(options)
 
1442
 
 
1443
    def test_is_enabled_works_with_one_client(self):
 
1444
        options = self.parser.parse_args()
 
1445
        options.is_enabled = True
 
1446
        options.client = ["foo"]
 
1447
        self.check_option_syntax(options)
 
1448
 
 
1449
    def test_is_enabled_fails_with_two_clients(self):
 
1450
        options = self.parser.parse_args()
 
1451
        options.is_enabled = True
 
1452
        options.client = ["foo", "barbar"]
 
1453
        with self.assertParseError():
 
1454
            self.check_option_syntax(options)
 
1455
 
 
1456
 
1328
1457
 
1329
1458
def should_only_run_tests():
1330
1459
    parser = argparse.ArgumentParser(add_help=False)