"""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=`) 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)