From 13068459212998ad688eb6aa7d7045d9247d48d8 2018-02-28 15:48:48 From: Branko Majic Date: 2018-02-28 15:48:48 Subject: [PATCH] GC-3: Refactored main functionality of the init command into separate function: - Introduced a new module (commands) where the majority of command implementation should reside. - The cli module should now be a thin wrapper around commands, in charge of processing input system arguments. - Refactored the tests accordingly. --- diff --git a/gimmecert/cli.py b/gimmecert/cli.py index 263ec76644bf19c339ca9f37dbb685d04caf4571..442d6c550d700586dc8259264e36b417a3f43bf9 100644 --- a/gimmecert/cli.py +++ b/gimmecert/cli.py @@ -23,8 +23,7 @@ import argparse import os from .decorators import subcommand_parser, get_subcommand_parser_setup_functions -from .storage import initialise_storage, write_private_key, write_certificate -from .crypto import generate_private_key, issue_certificate, get_dn, get_validity_range +from .commands import init DESCRIPTION = """\ @@ -39,31 +38,17 @@ Examples: def setup_init_subcommand_parser(parser, subparsers): subparser = subparsers.add_parser('init', description='Initialise CA hierarchy.') - def init(args): + def init_wrapper(args): project_directory = os.getcwd() - base_directory = os.path.join(os.getcwd(), '.gimmecert') - ca_directory = os.path.join(base_directory, 'ca') - level1_private_key_path = os.path.join(ca_directory, 'level1.key.pem') - level1_certificate_path = os.path.join(ca_directory, 'level1.cert.pem') - full_chain_path = os.path.join(ca_directory, 'chain-full.cert.pem') - level1_dn = get_dn("%s Level 1" % os.path.basename(project_directory)) - not_before, not_after = get_validity_range() - - initialise_storage(project_directory) - level1_private_key = generate_private_key() - level1_certificate = issue_certificate(level1_dn, level1_dn, level1_private_key, level1_private_key.public_key(), not_before, not_after) - full_chain = level1_certificate - - write_private_key(level1_private_key, level1_private_key_path) - write_certificate(level1_certificate, level1_certificate_path) - write_certificate(full_chain, full_chain_path) + + init(project_directory) 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") print(" Full certificate chain: .gimmecert/ca/chain-full.cert.pem") - subparser.set_defaults(func=init) + subparser.set_defaults(func=init_wrapper) return subparser diff --git a/gimmecert/commands.py b/gimmecert/commands.py new file mode 100644 index 0000000000000000000000000000000000000000..beee630892439099f713a678afb6a1a4abda9612 --- /dev/null +++ b/gimmecert/commands.py @@ -0,0 +1,58 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2018 Branko Majic +# +# This file is part of Gimmecert. +# +# Gimmecert is free software: you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the Free +# Software Foundation, either version 3 of the License, or (at your option) any +# later version. +# +# Gimmecert is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more +# details. +# +# You should have received a copy of the GNU General Public License along with +# Gimmecert. If not, see . +# + +import os + +import gimmecert.crypto +import gimmecert.storage + + +def init(project_directory): + """ + Initialises the necessary directory and CA hierarchies for use in + the specified directory. + + :param project_directory: Path to directory where the structure should be initialised. Should be top-level project directory normally. + :type project_directory: str + """ + + # Set-up various paths. + base_directory = os.path.join(project_directory, '.gimmecert') + ca_directory = os.path.join(base_directory, 'ca') + level1_private_key_path = os.path.join(ca_directory, 'level1.key.pem') + level1_certificate_path = os.path.join(ca_directory, 'level1.cert.pem') + full_chain_path = os.path.join(ca_directory, 'chain-full.cert.pem') + + # Initialise the 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)) + 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) + + # Grab the full CA certificate chain. + full_chain = level1_certificate + + # Write-out the artifacts. + gimmecert.storage.write_private_key(level1_private_key, level1_private_key_path) + gimmecert.storage.write_certificate(level1_certificate, level1_certificate_path) + gimmecert.storage.write_certificate(full_chain, full_chain_path) diff --git a/tests/test_cli.py b/tests/test_cli.py index c10dc783060cbf0bcf7790f3b55138f80599be64..c67bce3a7d87db2739f057addd0a622f71ad2e2c 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -20,7 +20,6 @@ import argparse -import os import gimmecert.cli import gimmecert.decorators @@ -162,22 +161,10 @@ def test_setup_init_subcommand_sets_function_callback(): @mock.patch('sys.argv', ['gimmecert', 'init']) -def test_init_subcommand_generates_ca_private_key(tmpdir): +@mock.patch('gimmecert.cli.init') +def test_init_command_invoked_with_correct_parameters(mock_init, tmpdir): tmpdir.chdir() gimmecert.cli.main() - print(tmpdir.listdir()) - - assert os.path.exists(tmpdir.join('.gimmecert', 'ca', 'level1.key.pem').strpath) - - -@mock.patch('sys.argv', ['gimmecert', 'init']) -def test_init_subcommand_generates_ca_certificate(tmpdir): - tmpdir.chdir() - - gimmecert.cli.main() - - print(tmpdir.listdir()) - - assert os.path.exists(tmpdir.join('.gimmecert', 'ca', 'level1.cert.pem').strpath) + mock_init.assert_called_once_with(tmpdir.strpath) diff --git a/tests/test_commands.py b/tests/test_commands.py new file mode 100644 index 0000000000000000000000000000000000000000..ba5c2205b1b695273c90aef9b1443cada71ca128 --- /dev/null +++ b/tests/test_commands.py @@ -0,0 +1,45 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2018 Branko Majic +# +# This file is part of Gimmecert. +# +# Gimmecert is free software: you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the Free +# Software Foundation, either version 3 of the License, or (at your option) any +# later version. +# +# Gimmecert is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more +# details. +# +# You should have received a copy of the GNU General Public License along with +# Gimmecert. If not, see . +# + +import os + +import gimmecert.commands + + +def test_init_sets_up_directory_structure(tmpdir): + base_dir = tmpdir.join('.gimmecert') + ca_dir = tmpdir.join('.gimmecert') + + tmpdir.chdir() + + gimmecert.commands.init(tmpdir.strpath) + + assert os.path.exists(base_dir.strpath) + assert os.path.exists(ca_dir.strpath) + + +def test_init_generates_ca_artifacts(tmpdir): + tmpdir.chdir() + + gimmecert.commands.init(tmpdir.strpath) + + assert os.path.exists(tmpdir.join('.gimmecert', 'ca', 'level1.key.pem').strpath) + assert os.path.exists(tmpdir.join('.gimmecert', 'ca', 'level1.cert.pem').strpath) + assert os.path.exists(tmpdir.join('.gimmecert', 'ca', 'chain-full.cert.pem').strpath)