Changeset - 988ac40d5cec
[Not reviewed]
0 7 0
Branko Majic (branko) - 6 years ago 2018-03-03 17:17:14
branko@majic.rs
GC-15: Implemented scenario for server certificate issuance where user has not initialised the CA hierarchy:

- Added functional test that tests if correct error is shown to user
in case he/she has not initialised the CA hierarchy.
- Introduced new function to check if storage is initialised.
- Added initial simplified server command implementation.
7 files changed with 122 insertions and 1 deletions:
0 comments (0 inline, 0 general)
functional_tests/test_server.py
Show inline comments
 
@@ -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
gimmecert/cli.py
Show inline comments
 
@@ -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.
gimmecert/commands.py
Show inline comments
 
@@ -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, ""
gimmecert/storage.py
Show inline comments
 
@@ -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
tests/test_cli.py
Show inline comments
 
@@ -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')
tests/test_commands.py
Show inline comments
 
@@ -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
tests/test_storage.py
Show inline comments
 
@@ -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
0 comments (0 inline, 0 general)