Files
ITWorx_MCP_Hub-Public/tests/e2e/tls.py
T

113 lines
4.2 KiB
Python

"""Self-signed local CA + leaf cert generation for the WP-230D E2E harness.
Playwright drives a real browser through a real PKCE/BFF login redirect, and
`OIDCSettings`/`DiscoveryDocument` both hard-require an ``https://`` issuer
(see `services/control_api/auth/config.py`), so the fake OIDC provider and
the web reverse-proxy origin the browser visits both need to terminate real
TLS. This generates one throwaway CA per harness run and a leaf cert for
127.0.0.1/localhost signed by it, so the *server-side* `OIDCProviderClient`
can do genuine certificate-chain verification against a real trust root
(`verify=<ca path>`) rather than disabling verification. The browser side
separately uses Playwright's `ignoreHTTPSErrors`, since trusting an
ephemeral local test CA in the OS/browser store is unnecessary for a
throwaway process-local fixture.
"""
from __future__ import annotations
import datetime
import ipaddress
from dataclasses import dataclass
from pathlib import Path
from cryptography import x509
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.x509.oid import NameOID
@dataclass(frozen=True)
class LocalTLSMaterial:
ca_cert_path: Path
cert_path: Path
key_path: Path
def generate_local_tls_material(output_dir: Path) -> LocalTLSMaterial:
output_dir.mkdir(parents=True, exist_ok=True)
now = datetime.datetime.now(datetime.UTC)
not_before = now - datetime.timedelta(minutes=5)
not_after = now + datetime.timedelta(hours=6)
ca_key = rsa.generate_private_key(public_exponent=65537, key_size=2048)
ca_name = x509.Name([x509.NameAttribute(NameOID.COMMON_NAME, "ITWorx MCP Hub E2E Local CA")])
ca_cert = (
x509.CertificateBuilder()
.subject_name(ca_name)
.issuer_name(ca_name)
.public_key(ca_key.public_key())
.serial_number(x509.random_serial_number())
.not_valid_before(not_before)
.not_valid_after(not_after)
.add_extension(x509.BasicConstraints(ca=True, path_length=0), critical=True)
.add_extension(
x509.KeyUsage(
digital_signature=False,
content_commitment=False,
key_encipherment=False,
data_encipherment=False,
key_agreement=False,
key_cert_sign=True,
crl_sign=True,
encipher_only=False,
decipher_only=False,
),
critical=True,
)
.add_extension(
x509.SubjectKeyIdentifier.from_public_key(ca_key.public_key()), critical=False
)
.sign(ca_key, hashes.SHA256())
)
leaf_key = rsa.generate_private_key(public_exponent=65537, key_size=2048)
leaf_name = x509.Name([x509.NameAttribute(NameOID.COMMON_NAME, "localhost")])
leaf_cert = (
x509.CertificateBuilder()
.subject_name(leaf_name)
.issuer_name(ca_name)
.public_key(leaf_key.public_key())
.serial_number(x509.random_serial_number())
.not_valid_before(not_before)
.not_valid_after(not_after)
.add_extension(
x509.SubjectAlternativeName(
[
x509.DNSName("localhost"),
x509.IPAddress(ipaddress.ip_address("127.0.0.1")),
]
),
critical=False,
)
.add_extension(x509.BasicConstraints(ca=False, path_length=None), critical=True)
.add_extension(
x509.AuthorityKeyIdentifier.from_issuer_public_key(ca_key.public_key()),
critical=False,
)
.sign(ca_key, hashes.SHA256())
)
ca_cert_path = output_dir / "ca.pem"
cert_path = output_dir / "leaf.pem"
key_path = output_dir / "leaf-key.pem"
ca_cert_path.write_bytes(ca_cert.public_bytes(serialization.Encoding.PEM))
cert_path.write_bytes(leaf_cert.public_bytes(serialization.Encoding.PEM))
key_path.write_bytes(
leaf_key.private_bytes(
serialization.Encoding.PEM,
serialization.PrivateFormat.TraditionalOpenSSL,
serialization.NoEncryption(),
)
)
return LocalTLSMaterial(ca_cert_path=ca_cert_path, cert_path=cert_path, key_path=key_path)