Changeset - f2df385b1493
[Not reviewed]
gc-3
0 5 0
Branko Majic (branko) - 6 years ago 2018-02-28 17:33:19
branko@majic.rs
GC-3: Added ability to provide custom CA base name to init command:

- Added functional test to cover the new scenario (providing CA base
name).
- Updated init command to accept the CA base name to be used when
constructing the CA subject DN.
- Updated the existing tests to pass-in the CA base name explicitly.
- Updated the CLI code to allow for user to pass-in the CA base name
via option (both short and long form available).
5 files changed with 86 insertions and 12 deletions:
0 comments (0 inline, 0 general)
functional_tests/test_init.py
Show inline comments
 
@@ -119,3 +119,49 @@ def test_initialisation_on_existing_directory(tmpdir):
 
    assert exit_code == 0
 
    assert stderr == ""
 
    assert "CA hierarchy has already been initialised." in stdout
 

	
 

	
 
def test_initialisation_with_custom_base_name(tmpdir):
 
    # John has been using the tool for a while now in a number of test
 
    # environments. Unfortunately, he has started to mix-up
 
    # certificates coming from different envioronments where the
 
    # project directories have the same name. What he would like to do
 
    # is to be able to specify the base name explicitly, instead of
 
    # letting the tool pick it for him.
 

	
 
    # John decides to check the command help from CLI.
 
    stdout, _, _ = run_command('gimmecert', 'init', '-h')
 

	
 
    # Amongst the different options, he notices one in particular that
 
    # draws his attention. The option seems to be usable for
 
    # specifying the base name for the CAs - exactly what he needed.
 
    assert "--ca-base-name" in stdout
 
    assert "-b" in stdout
 

	
 
    # John switches to his project directory.
 
    tmpdir.chdir()
 

	
 
    # This time around he runs the command using the newly-found
 
    # option.
 
    stdout, stderr, exit_code = run_command('gimmecert', 'init', '--ca-base-name', 'My Project')
 

	
 
    # Command finishes execution with success, and he is informed that
 
    # his CA hierarchy has been initialised..
 
    assert exit_code == 0
 
    assert stderr == ""
 
    assert "CA hierarchy initialised." in stdout
 

	
 
    # Just before he starts using the CA certificates further, he
 
    # decides to double-check the results. He runs a couple of
 
    # commands to get the issuer and subject DN from generated
 
    # certificate.
 
    issuer_dn, _, _ = run_command('openssl', 'x509', '-noout', '-issuer', '-in', '.gimmecert/ca/level1.cert.pem')
 
    subject_dn, _, _ = run_command('openssl', 'x509', '-noout', '-subject', '-in', '.gimmecert/ca/level1.cert.pem')
 
    issuer_dn = issuer_dn.replace('issuer=', '', 1)
 
    subject_dn = subject_dn.replace('subject=', '', 1)
 

	
 
    # To his delight, both the issuer and subject DN are identical,
 
    # and now they are based on his custom-provided name instead of
 
    # project name.
 
    assert issuer_dn.rstrip() == subject_dn.rstrip() == "CN = My Project Level 1"
 
    assert tmpdir.basename not in issuer_dn
gimmecert/cli.py
Show inline comments
 
@@ -43,11 +43,14 @@ Examples:
 
@subcommand_parser
 
def setup_init_subcommand_parser(parser, subparsers):
 
    subparser = subparsers.add_parser('init', description='Initialise CA hierarchy.')
 
    subparser.add_argument('--ca-base-name', '-b', help="Base name to use for CA naming. Default is to use the working directory base name.")
 

	
 
    def init_wrapper(args):
 
        project_directory = os.getcwd()
 
        if args.ca_base_name is None:
 
            args.ca_base_name = os.path.basename(project_directory)
 

	
 
        if init(project_directory):
 
        if init(project_directory, args.ca_base_name):
 
            print("CA hierarchy initialised. Generated artefacts:")
 
            print("    CA Level 1 private key: .gimmecert/ca/level1.key.pem")
 
            print("    CA Level 1 certificate: .gimmecert/ca/level1.cert.pem")
gimmecert/commands.py
Show inline comments
 
@@ -24,7 +24,7 @@ import gimmecert.crypto
 
import gimmecert.storage
 

	
 

	
 
def init(project_directory):
 
def init(project_directory, ca_base_name):
 
    """
 
    Initialises the necessary directory and CA hierarchies for use in
 
    the specified directory.
 
@@ -32,6 +32,9 @@ def init(project_directory):
 
    :param project_directory: Path to directory where the structure should be initialised. Should be top-level project directory normally.
 
    :type project_directory: str
 

	
 
    :param ca_base_name: Base name to use for constructing CA subject DNs.
 
    :type ca_base_name: str
 

	
 
    :returns: False, if directory has been initialised in previous run, True if project has been initialised in this run.
 
    :rtype: bool
 
    """
 
@@ -50,7 +53,7 @@ def init(project_directory):
 
    gimmecert.storage.initialise_storage(project_directory)
 

	
 
    # Generate private key and certificate.
 
    level1_dn = gimmecert.crypto.get_dn("%s Level 1" % os.path.basename(project_directory))
 
    level1_dn = gimmecert.crypto.get_dn("%s Level 1" % ca_base_name)
 
    not_before, not_after = gimmecert.crypto.get_validity_range()
 
    level1_private_key = gimmecert.crypto.generate_private_key()
 
    level1_certificate = gimmecert.crypto.issue_certificate(level1_dn, level1_dn, level1_private_key, level1_private_key.public_key(), not_before, not_after)
tests/test_cli.py
Show inline comments
 
@@ -162,9 +162,31 @@ def test_setup_init_subcommand_sets_function_callback():
 

	
 
@mock.patch('sys.argv', ['gimmecert', 'init'])
 
@mock.patch('gimmecert.cli.init')
 
def test_init_command_invoked_with_correct_parameters(mock_init, tmpdir):
 
def test_init_command_invoked_with_correct_parameters_no_options(mock_init, tmpdir):
 
    tmpdir.chdir()
 

	
 
    gimmecert.cli.main()
 

	
 
    mock_init.assert_called_once_with(tmpdir.strpath)
 
    mock_init.assert_called_once_with(tmpdir.strpath, tmpdir.basename)
 

	
 

	
 
@mock.patch('sys.argv', ['gimmecert', 'init', '-b', 'My Project'])
 
@mock.patch('gimmecert.cli.init')
 
def test_init_command_invoked_with_correct_parameters_with_options(mock_init, tmpdir):
 
    tmpdir.chdir()
 

	
 
    gimmecert.cli.main()
 

	
 
    mock_init.assert_called_once_with(tmpdir.strpath, 'My Project')
 

	
 

	
 
@mock.patch('sys.argv', ['gimmecert', 'init', '--ca-base-name', 'My Project'])
 
def test_init_command_accepts_ca_base_name_option_long_form():
 

	
 
    gimmecert.cli.main()  # Should not raise
 

	
 

	
 
@mock.patch('sys.argv', ['gimmecert', 'init', '-b', 'My Project'])
 
def test_init_command_accepts_ca_base_name_option_short_form():
 

	
 
    gimmecert.cli.main()  # Should not raise
tests/test_commands.py
Show inline comments
 
@@ -29,7 +29,7 @@ def test_init_sets_up_directory_structure(tmpdir):
 

	
 
    tmpdir.chdir()
 

	
 
    gimmecert.commands.init(tmpdir.strpath)
 
    gimmecert.commands.init(tmpdir.strpath, tmpdir.basename)
 

	
 
    assert os.path.exists(base_dir.strpath)
 
    assert os.path.exists(ca_dir.strpath)
 
@@ -38,7 +38,7 @@ def test_init_sets_up_directory_structure(tmpdir):
 
def test_init_generates_ca_artifacts(tmpdir):
 
    tmpdir.chdir()
 

	
 
    gimmecert.commands.init(tmpdir.strpath)
 
    gimmecert.commands.init(tmpdir.strpath, tmpdir.basename)
 

	
 
    assert os.path.exists(tmpdir.join('.gimmecert', 'ca', 'level1.key.pem').strpath)
 
    assert os.path.exists(tmpdir.join('.gimmecert', 'ca', 'level1.cert.pem').strpath)
 
@@ -48,7 +48,7 @@ def test_init_generates_ca_artifacts(tmpdir):
 
def test_init_returns_true_if_directory_has_not_been_previously_initialised(tmpdir):
 
    tmpdir.chdir()
 

	
 
    initialised = gimmecert.commands.init(tmpdir.strpath)
 
    initialised = gimmecert.commands.init(tmpdir.strpath, tmpdir.basename)
 

	
 
    assert initialised is True
 

	
 
@@ -56,8 +56,8 @@ def test_init_returns_true_if_directory_has_not_been_previously_initialised(tmpd
 
def test_init_returns_false_if_directory_has_been_previously_initialised(tmpdir):
 
    tmpdir.chdir()
 

	
 
    gimmecert.commands.init(tmpdir.strpath)
 
    initialised = gimmecert.commands.init(tmpdir.strpath)
 
    gimmecert.commands.init(tmpdir.strpath, tmpdir.basename)
 
    initialised = gimmecert.commands.init(tmpdir.strpath, tmpdir.basename)
 

	
 
    assert initialised is False
 

	
 
@@ -65,13 +65,13 @@ def test_init_returns_false_if_directory_has_been_previously_initialised(tmpdir)
 
def test_init_does_not_overwrite_artifcats_if_already_initialised(tmpdir):
 
    tmpdir.chdir()
 

	
 
    gimmecert.commands.init(tmpdir.strpath)
 
    gimmecert.commands.init(tmpdir.strpath, tmpdir.basename)
 

	
 
    level1_private_key_before = tmpdir.join('.gimmecert', 'ca', 'level1.key.pem').read()
 
    level1_certificate_before = tmpdir.join('.gimmecert', 'ca', 'level1.cert.pem').read()
 
    full_chain_before = tmpdir.join('.gimmecert', 'ca', 'chain-full.cert.pem').read()
 

	
 
    gimmecert.commands.init(tmpdir.strpath)
 
    gimmecert.commands.init(tmpdir.strpath, tmpdir.basename)
 

	
 
    level1_private_key_after = tmpdir.join('.gimmecert', 'ca', 'level1.key.pem').read()
 
    level1_certificate_after = tmpdir.join('.gimmecert', 'ca', 'level1.cert.pem').read()
0 comments (0 inline, 0 general)