Files @ 5a918cd2502e
Branch filter:

Location: gimmecert/functional_tests/test_status.py

branko
Noticket: Switching to development version.
# -*- coding: utf-8 -*-
#
# Copyright (C) 2018, 2020, 2024 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 <http://www.gnu.org/licenses/>.
#


from .base import run_command

import pytest


def test_status_command_available_with_help():
    # John has used Gimmecert for issuing server and client
    # certificates in one of his projects. Since the project has been
    # quite hectic, he has started to loose track of all the different
    # server and client certificates he has issued. He realises that
    # he needs to check what has been issued. Although this should be
    # possible by simply listing the directory content, John hopes
    # that there might be a convenience command for this instead that
    # provides some extra info as well. He starts off by running the
    # command without any paramrters to get a listing of available
    # commands.
    stdout, stderr, exit_code = run_command("gimmecert")

    # Looking at output, John notices the status command.
    assert exit_code == 0
    assert stderr == ""
    assert "status" in stdout

    # He goes ahead and has a look at command invocation to check what
    # kind of parameters he might need to provide.
    stdout, stderr, exit_code = run_command("gimmecert", "status", "-h")

    # John can see that the command does not take any additional
    # arguments.
    assert exit_code == 0
    assert stderr == ""
    assert stdout.split('\n')[0] == "usage: gimmecert status [-h]"


def test_status_on_uninitialised_directory(tmpdir):
    # John wants to see the status of current set of artefacts
    # generated by Gimmecert.

    # He swithces over to his project directory.
    tmpdir.chdir()

    # John runs the status command.
    stdout, stderr, exit_code = run_command('gimmecert', 'status')

    # It turns out that the directory has not been previously
    # initialised. In spite of this, the tool reports success, and
    # informs John that Gimmecert has not been initialised yet.
    assert exit_code == 0
    assert "CA hierarchy has not been initialised in current directory." in stdout


@pytest.mark.parametrize("ca_key_specification, default_key_representation", [
    ("rsa:2048", "2048-bit RSA"),
    ("ecdsa:secp521r1", "secp521r1 ECDSA"),
])
def test_status_on_initialised_directory(tmpdir, ca_key_specification, default_key_representation):
    # John is interested in finding out a bit more about what
    # certificates have been already issued in one of the projects he
    # had initialised before.
    tmpdir.chdir()

    run_command('gimmecert', 'init', '-k', ca_key_specification, '-d', '3', '-b', 'My Project')

    run_command('gimmecert', 'server', 'myserver1', '-k', 'rsa:1024')
    run_command('gimmecert', 'server', 'myserver2', 'myservice.example.com', 'myotherservice.example.com')
    run_command("openssl", "req", "-new", "-newkey", "rsa:2048", "-nodes", "-keyout", "myserver3.key.pem",
                "-subj", "/CN=myserver3", "-out", "myserver3.csr.pem")
    run_command('gimmecert', 'server', '--csr', 'myserver3.csr.pem', 'myserver3')
    run_command('gimmecert', 'server', 'myserver4', '-k', 'ecdsa:secp256r1')

    run_command('gimmecert', 'client', 'myclient1', '-k', 'rsa:1024')
    run_command('gimmecert', 'client', 'myclient2')
    run_command("openssl", "req", "-new", "-newkey", "rsa:2048", "-nodes", "-keyout", "myclient3.key.pem",
                "-subj", "/CN=myclient3", "-out", "myclient3.csr.pem")
    run_command('gimmecert', 'client', '--csr', 'myclient3.csr.pem', 'myclient3')
    run_command('gimmecert', 'client', 'myclient4', '-k', 'ecdsa:secp192r1')

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

    # John runs the status command.
    stdout, stderr, exit_code = run_command('gimmecert', 'status')
    stdout_lines = stdout.split("\n")

    # Command exits with success without any errors being
    # reported.
    assert exit_code == 0
    assert stderr == ""

    # John notices that information is laid-out in three separate
    # sections - one for CA hierachy, one for server certificates, and
    # one for client certificates.
    assert "CA hierarchy" in stdout
    assert "Server certificates" in stdout
    assert "Client certificates" in stdout

    # John first has a look at information about the CA
    # hierarchy. First thing he can see is information about the
    # default key algorithm in use. This is followed by the hierarchy
    # tree presented using indentation. Each CA is listed with its
    # full subject DN, as well as not before and not after dates. In
    # addition, the final CA in chain is marked as end entity issuing
    # CA.
    index_default_key_algorithm = stdout_lines.index("Default key algorithm: %s" % default_key_representation)  # Should not raise
    index_ca_1 = stdout_lines.index("CN=My Project Level 1 CA")  # Should not raise
    index_ca_2 = stdout_lines.index("CN=My Project Level 2 CA")  # Should not raise
    index_ca_3 = stdout_lines.index("CN=My Project Level 3 CA [END ENTITY ISSUING CA]")  # Should not raise

    assert index_default_key_algorithm < index_ca_1
    assert index_ca_1 < index_ca_2
    assert index_ca_2 < index_ca_3

    assert stdout_lines[index_ca_1+1].startswith("    Validity: ")
    assert stdout_lines[index_ca_1+2].startswith("    Certificate: ")

    assert stdout_lines[index_ca_2+1].startswith("    Validity: ")
    assert stdout_lines[index_ca_2+2].startswith("    Certificate: ")

    assert stdout_lines[index_ca_3+1].startswith("    Validity: ")
    assert stdout_lines[index_ca_3+2].startswith("    Certificate: ")

    # In addition to CA information, path to full certificate chain is
    # output as well.
    assert "Full certificate chain:" in stdout

    # John then has a look at server certificates. These are presented
    # in a list, and for each certificate is listed with subject DN,
    # not before, not after, and included DNS names. Information for
    # each server is followed by key algorithm information, and paths
    # to private key and certificate.
    index_myserver1 = stdout_lines.index("CN=myserver1")  # Should not raise
    index_myserver2 = stdout_lines.index("CN=myserver2")  # Should not raise
    index_myserver3 = stdout_lines.index("CN=myserver3")  # Should not raise
    index_myserver4 = stdout_lines.index("CN=myserver4")  # Should not raise

    assert stdout_lines[index_myserver1+1].startswith("    Validity: ")
    assert stdout_lines[index_myserver1+2] == "    DNS: myserver1"
    assert stdout_lines[index_myserver1+3] == "    Key algorithm: 1024-bit RSA"
    assert stdout_lines[index_myserver1+4] == "    Private key: .gimmecert/server/myserver1.key.pem"
    assert stdout_lines[index_myserver1+5] == "    Certificate: .gimmecert/server/myserver1.cert.pem"

    assert stdout_lines[index_myserver2+1].startswith("    Validity: ")
    assert stdout_lines[index_myserver2+2] == "    DNS: myserver2, myservice.example.com, myotherservice.example.com"
    assert stdout_lines[index_myserver2+3] == "    Key algorithm: %s" % default_key_representation
    assert stdout_lines[index_myserver2+4] == "    Private key: .gimmecert/server/myserver2.key.pem"
    assert stdout_lines[index_myserver2+5] == "    Certificate: .gimmecert/server/myserver2.cert.pem"

    assert stdout_lines[index_myserver3+1].startswith("    Validity: ")
    assert stdout_lines[index_myserver3+2] == "    DNS: myserver3"
    assert stdout_lines[index_myserver3+3] == "    Key algorithm: 2048-bit RSA"
    assert stdout_lines[index_myserver3+4] == "    CSR: .gimmecert/server/myserver3.csr.pem"
    assert stdout_lines[index_myserver3+5] == "    Certificate: .gimmecert/server/myserver3.cert.pem"

    assert stdout_lines[index_myserver4+2] == "    DNS: myserver4"
    assert stdout_lines[index_myserver4+3] == "    Key algorithm: secp256r1 ECDSA"
    assert stdout_lines[index_myserver4+4] == "    Private key: .gimmecert/server/myserver4.key.pem"
    assert stdout_lines[index_myserver4+5] == "    Certificate: .gimmecert/server/myserver4.cert.pem"

    # For client certificates, John can see that for each certificate
    # he can see its subject DN and validity. Information for each
    # client is followed by key algorithm and paths to private key and
    # certificate.
    index_myclient1 = stdout_lines.index("CN=myclient1")  # Should not raise
    index_myclient2 = stdout_lines.index("CN=myclient2")  # Should not raise
    index_myclient3 = stdout_lines.index("CN=myclient3")  # Should not raise
    index_myclient4 = stdout_lines.index("CN=myclient4")  # Should not raise

    assert stdout_lines[index_myclient1+1].startswith("    Validity: ")
    assert stdout_lines[index_myclient1+2] == "    Key algorithm: 1024-bit RSA"
    assert stdout_lines[index_myclient1+3] == "    Private key: .gimmecert/client/myclient1.key.pem"
    assert stdout_lines[index_myclient1+4] == "    Certificate: .gimmecert/client/myclient1.cert.pem"

    assert stdout_lines[index_myclient2+1].startswith("    Validity: ")
    assert stdout_lines[index_myclient2+2] == "    Key algorithm: %s" % default_key_representation
    assert stdout_lines[index_myclient2+3] == "    Private key: .gimmecert/client/myclient2.key.pem"
    assert stdout_lines[index_myclient2+4] == "    Certificate: .gimmecert/client/myclient2.cert.pem"

    assert stdout_lines[index_myclient3+1].startswith("    Validity: ")
    assert stdout_lines[index_myclient3+2] == "    Key algorithm: 2048-bit RSA"
    assert stdout_lines[index_myclient3+3] == "    CSR: .gimmecert/client/myclient3.csr.pem"
    assert stdout_lines[index_myclient3+4] == "    Certificate: .gimmecert/client/myclient3.cert.pem"

    assert stdout_lines[index_myclient4+1].startswith("    Validity: ")
    assert stdout_lines[index_myclient4+2] == "    Key algorithm: secp192r1 ECDSA"
    assert stdout_lines[index_myclient4+3] == "    Private key: .gimmecert/client/myclient4.key.pem"
    assert stdout_lines[index_myclient4+4] == "    Certificate: .gimmecert/client/myclient4.cert.pem"