diff --git a/functional_tests/test_server.py b/functional_tests/test_server.py index 095fc57bba6436a1f58f2b1a52d5a19a5580012c..fdc91ec81c1b87539078b0cf420ac2953d48b39e 100644 --- a/functional_tests/test_server.py +++ b/functional_tests/test_server.py @@ -43,3 +43,19 @@ def test_server_command_available_with_help(): assert stderr == "" assert stdout.startswith("usage: gimmecert server") assert stdout.split('\n')[0].endswith(" entity_name [dns_name [dns_name ...]]") # First line of help. + + +def test_server_command_requires_initialised_hierarchy(tmpdir): + # John is about to issue a server certificate. He switches to his + # project directory. + tmpdir.chdir() + + # John tries to issue a server certificate. + stdout, stderr, exit_code = run_command("gimmecert", "server", "myserver") + + # Unfortunately, John has forgotten to initialise the CA hierarchy + # from within this directory, and is instead presented with an + # error. + assert stdout == "" + assert stderr == "CA hierarchy must be initialised prior to issuing server certificates. Run the gimmecert init command first.\n" + assert exit_code != 0 diff --git a/gimmecert/cli.py b/gimmecert/cli.py index 33543ec68e0aa826a0b1ff08b7a28d8243c24e47..c22c16513d8240a1b4770b73cccb85d43b92890c 100644 --- a/gimmecert/cli.py +++ b/gimmecert/cli.py @@ -21,9 +21,13 @@ import argparse import os +import sys from .decorators import subcommand_parser, get_subcommand_parser_setup_functions -from .commands import init +from .commands import init, server + + +ERROR_GENERIC = 10 DESCRIPTION = """\ @@ -80,8 +84,20 @@ def setup_server_subcommand_parser(parser, subparsers): subparser.add_argument('entity_name', help='Name of the server entity.') subparser.add_argument('dns_name', nargs='*', help='Additional DNS names to include in subject alternative name.') + def server_wrapper(args): + project_directory = os.getcwd() + + status, message = server(project_directory, args.entity_name) + + if status is False: + print(message, file=sys.stderr) + exit(ERROR_GENERIC) + + subparser.set_defaults(func=server_wrapper) + return subparser + def get_parser(): """ Sets-up and returns a CLI argument parser. diff --git a/gimmecert/commands.py b/gimmecert/commands.py index 411a440ab1d750566694c8d4fd7007318fff36c8..3c652c6b3ec05ee39806098cc4d5e55d9fcaf793 100644 --- a/gimmecert/commands.py +++ b/gimmecert/commands.py @@ -68,3 +68,24 @@ def init(project_directory, ca_base_name, ca_hierarchy_depth): gimmecert.storage.write_certificate_chain(full_chain, full_chain_path) return True + + +def server(project_directory, entity_name): + """ + Generates a server private key and issues a server certificate + using the CA hierarchy initialised within the specified directory. + + :param project_directory: Path to project directory under which the CA artifacats etc will be looked-up. + :type project_directory: str + + :param entity_name: Name of the server entity. Name will be used in subject DN and DNS subject alternative name. + :type entity_name: str + + :returns: Tuple consisting out of status and message to show to user. + :rtype: (bool, str) + """ + + if not gimmecert.storage.is_initialised(project_directory): + return False, "CA hierarchy must be initialised prior to issuing server certificates. Run the gimmecert init command first." + + return True, "" diff --git a/gimmecert/storage.py b/gimmecert/storage.py index c966c9227a69eca65f961c50912d2b1d027fb11b..cba2053f81207d598c8f6fd50f265b5d82777ed9 100644 --- a/gimmecert/storage.py +++ b/gimmecert/storage.py @@ -106,3 +106,18 @@ def write_certificate_chain(certificate_chain, path): with open(path, 'wb') as certificate_chain_file: certificate_chain_file.write(chain_pem) + + +def is_initialised(project_directory): + """ + Checks if Gimmecert has been initialised in designated project + directory. + + :param project_directory: Path to project directory to check. + :type project_directory: str + """ + + if os.path.exists(os.path.join(project_directory, '.gimmecert')): + return True + + return False diff --git a/tests/test_cli.py b/tests/test_cli.py index b13ce68e6aae3c7e130efea845f717aceafe456f..1af7abd2362ea9fc033e8bee15fc843fb1d89f42 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -256,3 +256,24 @@ def test_setup_server_subcommand_succeeds_with_entity_name_argument_and_one_dns_ def test_setup_server_subcommand_succeeds_with_entity_name_argument_and_four_dns_names(): gimmecert.cli.main() # Should not raise. + + +def test_setup_server_subcommand_sets_function_callback(): + parser = argparse.ArgumentParser() + subparsers = parser.add_subparsers() + + subparser = gimmecert.cli.setup_server_subcommand_parser(parser, subparsers) + + assert callable(subparser.get_default('func')) + + +@mock.patch('sys.argv', ['gimmecert', 'server', 'myserver']) +@mock.patch('gimmecert.cli.server') +def test_server_command_invoked_with_correct_parameters(mock_server, tmpdir): + mock_server.return_value = True, "Bogus" + + tmpdir.chdir() + + gimmecert.cli.main() + + mock_server.assert_called_once_with(tmpdir.strpath, 'myserver') diff --git a/tests/test_commands.py b/tests/test_commands.py index 87c2d56cee23ad48b726d7b5214bb2012c36a847..dcfa068c36d48787613241fad975c149e9aa3ae0 100644 --- a/tests/test_commands.py +++ b/tests/test_commands.py @@ -135,3 +135,21 @@ def test_init_does_not_overwrite_artifcats_if_already_initialised(tmpdir): assert level1_private_key_before == level1_private_key_after assert level1_certificate_before == level1_certificate_after assert full_chain_before == full_chain_after + + +def test_server_returns_status_and_message(tmpdir): + tmpdir.chdir() + + status, message = gimmecert.commands.server(tmpdir.strpath, 'myserver') + + assert isinstance(status, bool) + assert isinstance(message, str) + + +def test_server_reports_error_if_directory_is_not_initialised(tmpdir): + tmpdir.chdir() + + status, message = gimmecert.commands.server(tmpdir.strpath, 'myserver') + + assert status is False + assert "must be initialised" in message diff --git a/tests/test_storage.py b/tests/test_storage.py index 646d7cd8bb960b6a345a76fd352cd59f49788566..ef4b200081976c6bde7488aa704f6918251213de 100644 --- a/tests/test_storage.py +++ b/tests/test_storage.py @@ -82,3 +82,17 @@ def test_write_certificate_chain(tmpdir): expected_content = b"%s\n%s\n%s" % (level1_pem, level2_pem, level3_pem) assert content == expected_content + + +def test_is_initialised_returns_true_if_directory_is_initialised(tmpdir): + tmpdir.chdir() + + gimmecert.storage.initialise_storage(tmpdir.strpath) + + assert gimmecert.storage.is_initialised(tmpdir.strpath) is True + + +def test_is_initialised_returns_false_if_directory_is_not_initialised(tmpdir): + tmpdir.chdir() + + assert gimmecert.storage.is_initialised(tmpdir.strpath) is False