Skip to content

Commit

Permalink
load CSRs on module load after all
Browse files Browse the repository at this point in the history
  • Loading branch information
mathiasertl committed Sep 8, 2023
1 parent 65ba1ab commit c589f33
Show file tree
Hide file tree
Showing 3 changed files with 56 additions and 93 deletions.
60 changes: 19 additions & 41 deletions ca/django_ca/tests/api/test_sign_cert.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,13 +85,10 @@ def request(client: Client, data: Dict[str, Any]) -> HttpResponse:

@freeze_time(timestamps["everything_valid"])
def test_sign_ca_values(
api_client: Client,
usable_root: CertificateAuthority,
root_cert_csr_pem: x509.CertificateSigningRequest,
expected_response: Dict[str, Any],
api_client: Client, usable_root: CertificateAuthority, expected_response: Dict[str, Any]
) -> None:
"""Test that CA extensions are added."""
response = request(api_client, {"csr": root_cert_csr_pem, "subject": default_subject})
response = request(api_client, {"csr": certs["root-cert"]["csr"]["pem"], "subject": default_subject})
assert response.status_code == HTTPStatus.OK, response.content

# Get certificate and validate some properties
Expand Down Expand Up @@ -122,7 +119,6 @@ def test_sign_ca_values(
def test_sign_certificate_with_parameters(
api_client: Client,
usable_root: CertificateAuthority,
root_cert_csr_pem: x509.CertificateSigningRequest,
expected_response: Dict[str, Any],
) -> None:
"""Test signing with parameters."""
Expand All @@ -134,7 +130,7 @@ def test_sign_certificate_with_parameters(
response = request(
api_client,
{
"csr": root_cert_csr_pem,
"csr": certs["root-cert"]["csr"]["pem"],
"subject": default_subject,
"autogenerated": True,
"profile": "server",
Expand All @@ -159,17 +155,14 @@ def test_sign_certificate_with_parameters(

@freeze_time(timestamps["everything_valid"])
def test_sign_certificate_with_extensions(
api_client: Client,
usable_root: CertificateAuthority,
root_cert_csr_pem: x509.CertificateSigningRequest,
expected_response: Dict[str, Any],
api_client: Client, usable_root: CertificateAuthority, expected_response: Dict[str, Any]
) -> None:
"""Test signing certificates with extensions."""

response = request(
api_client,
{
"csr": root_cert_csr_pem,
"csr": certs["root-cert"]["csr"]["pem"],
"subject": default_subject,
"extensions": {
"authority_information_access": {
Expand Down Expand Up @@ -294,17 +287,14 @@ def test_sign_certificate_with_extensions(

@freeze_time(timestamps["everything_valid"])
def test_sign_certificate_with_subject_alternative_name(
api_client: Client,
usable_root: CertificateAuthority,
root_cert_csr_pem: x509.CertificateSigningRequest,
expected_response: Dict[str, Any],
api_client: Client, usable_root: CertificateAuthority, expected_response: Dict[str, Any]
) -> None:
"""Test signing certificates with an additional subject alternative name."""

response = request(
api_client,
{
"csr": root_cert_csr_pem,
"csr": certs["root-cert"]["csr"]["pem"],
"subject": default_subject,
"extensions": {
"subject_alternative_name": {
Expand Down Expand Up @@ -338,14 +328,12 @@ def test_sign_certificate_with_subject_alternative_name(

@pytest.mark.usefixtures("tmpcadir")
@freeze_time(timestamps["everything_valid"])
def test_crldp_with_full_name_and_relative_name(
api_client: Client, root_cert_csr_pem: x509.CertificateSigningRequest
) -> None:
def test_crldp_with_full_name_and_relative_name(api_client: Client) -> None:
"""Test sending a CRL Distribution point with a full_name and a relative_name"""
response = request(
api_client,
{
"csr": root_cert_csr_pem,
"csr": certs["root-cert"]["csr"]["pem"],
"subject": default_subject,
"extensions": {
"crl_distribution_points": {
Expand Down Expand Up @@ -381,14 +369,12 @@ def test_crldp_with_full_name_and_relative_name(

@pytest.mark.usefixtures("tmpcadir")
@freeze_time(timestamps["everything_valid"])
def test_crldp_with_no_full_name_or_relative_name(
api_client: Client, root_cert_csr_pem: x509.CertificateSigningRequest
) -> None:
def test_crldp_with_no_full_name_or_relative_name(api_client: Client) -> None:
"""Test sending a CRL Distribution point with neither a full name nor a relative name."""
response = request(
api_client,
{
"csr": root_cert_csr_pem,
"csr": certs["root-cert"]["csr"]["pem"],
"subject": default_subject,
"extensions": {
"crl_distribution_points": {
Expand Down Expand Up @@ -421,14 +407,12 @@ def test_crldp_with_no_full_name_or_relative_name(

@pytest.mark.usefixtures("tmpcadir")
@freeze_time(timestamps["everything_valid"])
def test_with_invalid_key_usage(
api_client: Client, root_cert_csr_pem: x509.CertificateSigningRequest
) -> None:
def test_with_invalid_key_usage(api_client: Client) -> None:
"""Test sending an invalid key usage."""
response = request(
api_client,
{
"csr": root_cert_csr_pem,
"csr": certs["root-cert"]["csr"]["pem"],
"subject": default_subject,
"extensions": {"key_usage": {"value": ["unknown"]}},
},
Expand All @@ -447,21 +431,19 @@ def test_with_invalid_key_usage(

@pytest.mark.usefixtures("tmpcadir", "usable_root")
@freeze_time(timestamps["everything_expired"])
def test_expired_ca(api_client: Client, root_cert_csr_pem: x509.CertificateSigningRequest) -> None:
def test_expired_ca(api_client: Client) -> None:
"""Test that you can *not* sign a certificate for an expired CA."""

response = request(api_client, {"csr": root_cert_csr_pem, "subject": default_subject})
response = request(api_client, {"csr": certs["root-cert"]["csr"]["pem"], "subject": default_subject})
assert response.status_code == HTTPStatus.NOT_FOUND, response.content
assert response.json() == {"detail": "Not Found"}, response.json()


@pytest.mark.usefixtures("root")
@freeze_time(timestamps["everything_valid"])
def test_private_key_unavailable(
api_client: Client, root_cert_csr_pem: x509.CertificateSigningRequest
) -> None:
def test_private_key_unavailable(api_client: Client) -> None:
"""Test the error when no private key is available."""
response = request(api_client, {"csr": root_cert_csr_pem, "subject": default_subject})
response = request(api_client, {"csr": certs["root-cert"]["csr"]["pem"], "subject": default_subject})
assert response.status_code == HTTPStatus.BAD_REQUEST, response.content
assert response.json() == {
"detail": "This certificate authority can not be used to sign certificates via the API."
Expand All @@ -470,16 +452,12 @@ def test_private_key_unavailable(

@pytest.mark.usefixtures("tmpcadir")
@freeze_time(timestamps["everything_valid"])
def test_disabled_ca(
api_client: Client,
root: CertificateAuthority,
root_cert_csr_pem: x509.CertificateSigningRequest,
) -> None:
def test_disabled_ca(api_client: Client, root: CertificateAuthority) -> None:
"""Test that you cannot sign a certificate for a disabled CA."""
root.enabled = False
root.save()

response = request(api_client, {"csr": root_cert_csr_pem, "subject": default_subject})
response = request(api_client, {"csr": certs["root-cert"]["csr"]["pem"], "subject": default_subject})
assert response.status_code == HTTPStatus.NOT_FOUND, response.content
assert response.json() == {"detail": "Not Found"}, response.json()

Expand Down
59 changes: 34 additions & 25 deletions ca/django_ca/tests/base/conftest_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@

import cryptography
from cryptography import x509
from cryptography.hazmat.primitives import serialization

import django
from django.conf import settings
Expand Down Expand Up @@ -152,28 +151,6 @@ def fixture() -> Iterator[x509.Certificate]:
return fixture


def generate_csr_fixture(name: str) -> typing.Callable[[], Iterator[x509.CertificateSigningRequest]]:
"""Generate fixture for a loaded CSR (root_cert_csr, ...)."""

@pytest.fixture(scope="session")
def fixture() -> Iterator[x509.CertificateSigningRequest]:
yield load_csr(name)

return fixture


def generate_csr_pem_fixture(name: str) -> typing.Callable[["SubRequest"], Iterator[str]]:
"""Generate fixture for a loaded CSR (root_cert_csr, ...)."""

@pytest.fixture(scope="session")
def fixture(request: "SubRequest") -> Iterator[str]:
sanitized_name = name.replace("-", "_")
csr = request.getfixturevalue(f"{sanitized_name}_csr")
yield csr.public_bytes(serialization.Encoding.PEM).decode("utf-8")

return fixture


def generate_ca_fixture(name: str) -> typing.Callable[["SubRequest", Any], Iterator[CertificateAuthority]]:
"""Function to generate CA fixtures (root, child, ...)."""

Expand Down Expand Up @@ -222,11 +199,10 @@ def fixture(request: "SubRequest") -> Iterator[Certificate]:
sanitized_name = name.replace("-", "_")
data = fixture_data["certs"][name]
ca = request.getfixturevalue(data["ca"])
csr = request.getfixturevalue(f"{sanitized_name}_csr")
pub = request.getfixturevalue(f"{sanitized_name}_pub")

with freeze_time(timestamps["everything_valid"]):
cert = load_cert(ca, csr, pub, data.get("profile", ""))
cert = load_cert(ca, certs[name]["csr"]["pem"], pub, data.get("profile", ""))

yield cert # NOTE: Yield must be outside the freeze-time block, or durations are wrong

Expand Down Expand Up @@ -280,3 +256,36 @@ def load_cert(
with open(os.path.join(settings.FIXTURES_DIR, "cert-data.json"), encoding="utf-8") as cert_data_stream:
fixture_data = json.load(cert_data_stream)
certs = fixture_data["certs"]

# Define various classes of certificates
usable_ca_names = [
name for name, conf in fixture_data["certs"].items() if conf["type"] == "ca" and conf.get("key_filename")
]
unusable_ca_names = [
name
for name, conf in fixture_data["certs"].items()
if conf["type"] == "ca" and name not in usable_ca_names
]
all_ca_names = usable_ca_names + unusable_ca_names

usable_cert_names = [
name
for name, conf in fixture_data["certs"].items()
if conf["type"] == "cert" and conf["cat"] == "generated"
]
unusable_cert_names = [
name
for name, conf in fixture_data["certs"].items()
if conf["type"] == "cert" and name not in usable_ca_names
]
interesting_certificate_names = ["child-cert", "all-extensions", "alt-extensions", "no-extensions"]
all_cert_names = usable_cert_names + unusable_cert_names

# Load CSRs into certs
for name in usable_cert_names:
with open(os.path.join(settings.FIXTURES_DIR, f"{name}.csr"), "rb") as stream:
_csr_pem = stream.read()
certs[name]["csr"] = {
"der": x509.load_pem_x509_csr(_csr_pem),
"pem": _csr_pem.decode("utf-8"),
}
30 changes: 3 additions & 27 deletions ca/django_ca/tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,14 +37,14 @@
from django_ca.models import Certificate
from django_ca.profiles import profiles
from django_ca.tests.base.conftest_helpers import (
fixture_data,
generate_ca_fixture,
generate_cert_fixture,
generate_csr_fixture,
generate_csr_pem_fixture,
generate_pub_fixture,
generate_usable_ca_fixture,
interesting_certificate_names,
setup_pragmas,
usable_ca_names,
usable_cert_names,
)
from django_ca.tests.base.typehints import User
from django_ca.utils import ca_storage
Expand Down Expand Up @@ -157,28 +157,6 @@ def tmpcadir(tmp_path: Path, settings: SettingsWrapper) -> Iterator[SettingsWrap


# CAs that can be used for signing certificates
usable_ca_names = [
name for name, conf in fixture_data["certs"].items() if conf["type"] == "ca" and conf.get("key_filename")
]
unusable_ca_names = [
name
for name, conf in fixture_data["certs"].items()
if conf["type"] == "ca" and name not in usable_ca_names
]
all_ca_names = usable_ca_names + unusable_ca_names

usable_cert_names = [
name
for name, conf in fixture_data["certs"].items()
if conf["type"] == "cert" and conf["cat"] == "generated"
]
unusable_cert_names = [
name
for name, conf in fixture_data["certs"].items()
if conf["type"] == "cert" and name not in usable_ca_names
]
interesting_certificate_names = ["child-cert", "all-extensions", "alt-extensions", "no-extensions"]
all_cert_names = usable_cert_names + unusable_cert_names

# Dynamically inject repetitive fixtures:
# https://github.com/pytest-dev/pytest/issues/2424
Expand All @@ -188,6 +166,4 @@ def tmpcadir(tmp_path: Path, settings: SettingsWrapper) -> Iterator[SettingsWrap
for name in usable_ca_names + usable_cert_names:
globals()[f"{name.replace('-', '_')}_pub"] = generate_pub_fixture(name)
for name in usable_cert_names:
globals()[f"{name.replace('-', '_')}_csr"] = generate_csr_fixture(name)
globals()[f"{name.replace('-', '_')}_csr_pem"] = generate_csr_pem_fixture(name)
globals()[name.replace("-", "_")] = generate_cert_fixture(name)

0 comments on commit c589f33

Please sign in to comment.