Initial curated ITWorx_MCP_Hub source from 368fba691ea8d23403b4d56b53b5ec670d7c5020

This commit is contained in:
Public Source Publisher committed 2026-10-01 01:36:21 +02:00
commit cb5be898ea
663 files changed
+82674

No files matched your search

+173
View File
@@ -0,0 +1,173 @@
"""WP-230D real-process E2E harness.
Starts the actual production topology entirely offline so Playwright can
drive a real browser through the real PKCE/BFF login flow, real per-role
authorization, and the real policy engine -- not a mocked frontend:
browser -> web reverse-proxy (services.runtime.web, real HTTPS)
-> control-api (services.runtime.control_api, real routes/RBAC)
-> fake OIDC provider (real HTTPS, for the /authorize redirect
the browser itself must navigate)
-> fake ContextForge (in-process MockTransport; nothing the
browser talks to directly, so no real HTTP needed here)
Each subcommand below blocks running one of these processes; Playwright's
`webServer` config (apps/web/playwright.config.ts) starts all three and
waits for each to answer its readiness probe before running any test.
Persona switching: `OIDCSettings`/the real `/auth/login` route were left
untouched (no test-only hint parameter was added to production auth code).
Instead the fake OIDC provider's real /authorize endpoint reads an
optional ?persona= query param (see fake_provider.py) to choose which
group claim to issue; Playwright supplies it by rewriting the outbound
request in the browser context (page.route), never by modifying the
server the app under test talks to.
"""
from __future__ import annotations
import argparse
import os
import sys
from pathlib import Path
ROOT = Path(__file__).resolve().parents[2]
if str(ROOT) not in sys.path:
sys.path.insert(0, str(ROOT))
import httpx # noqa: E402
import uvicorn # noqa: E402
from services.control_api.auth.config import OIDCSettings # noqa: E402
from services.control_api.auth.fake_provider import FakeOIDCProvider # noqa: E402
from services.control_api.auth.models import ManagementRole # noqa: E402
from services.control_api.contextforge.fake import FakeContextForge # noqa: E402
from services.control_api.contextforge.models import ContextForgeSettings # noqa: E402
from services.control_api.persistence import ( # noqa: E402
Base,
create_engine_from_url,
session_factory,
)
from services.runtime import web as web_runtime # noqa: E402
from services.runtime.control_api import build_app as build_control_app # noqa: E402
from services.runtime.state_sync import sync_runtime_state # noqa: E402
from tests.e2e.tls import generate_local_tls_material # noqa: E402
STATE_DIR = ROOT / ".wp230e2e"
CATALOG_ROOT = ROOT / "catalog"
ROLE_GROUPS: dict[ManagementRole, str] = {
ManagementRole.VIEWER: "hub-viewer",
ManagementRole.AUDITOR: "hub-auditor",
ManagementRole.OPERATOR: "hub-operator",
ManagementRole.ADMIN: "hub-admin",
}
def _origin(port: int) -> str:
return f"https://127.0.0.1:{port}"
def run_oidc(port: int) -> None:
tls = generate_local_tls_material(STATE_DIR / "tls-oidc")
provider = FakeOIDCProvider(
issuer=_origin(port),
user_groups=(ROLE_GROUPS[ManagementRole.VIEWER],),
persona_groups={group: (group,) for group in ROLE_GROUPS.values()},
)
uvicorn.run(
provider.asgi_app(),
host="127.0.0.1",
port=port,
log_level="warning",
ssl_certfile=str(tls.cert_path),
ssl_keyfile=str(tls.key_path),
)
def run_control_api(port: int, oidc_port: int, web_port: int) -> None:
os.environ["WP230E2E_CONTEXTFORGE_TOKEN"] = "fake-token" # noqa: S105
os.environ.setdefault("MCP_CLIENT_BINDINGS_JSON", "[]")
STATE_DIR.mkdir(parents=True, exist_ok=True)
db_path = STATE_DIR / "control-api.db"
db_path.unlink(missing_ok=True)
engine = create_engine_from_url(f"sqlite:///{db_path}")
Base.metadata.create_all(engine)
sessions = session_factory(engine)
issuer = _origin(oidc_port)
with sessions() as session, session.begin():
sync_runtime_state(session, CATALOG_ROOT, issuer)
oidc_tls = STATE_DIR / "tls-oidc"
provider_transport = httpx.HTTPTransport(verify=str(oidc_tls / "ca.pem"))
oidc = OIDCSettings(
issuer=issuer,
audience="itworx-mcp-hub",
client_id="hub-bff",
redirect_uri=f"{_origin(web_port)}/auth/callback",
secure_cookie=True,
same_site="lax",
role_group_map={group: role for role, group in ROLE_GROUPS.items()},
)
app = build_control_app(
oidc=oidc,
provider_transport=provider_transport,
contextforge_settings=ContextForgeSettings(
base_url="https://contextforge.e2e.test",
auth_token_env="WP230E2E_CONTEXTFORGE_TOKEN", # noqa: S106
),
contextforge_transport=FakeContextForge().transport(),
engine=engine,
catalog_root=CATALOG_ROOT,
)
uvicorn.run(app, host="127.0.0.1", port=port, log_level="warning")
def run_web(port: int, control_port: int, dist_root: Path) -> None:
if not (dist_root / "index.html").is_file():
raise SystemExit(
f"{dist_root} has no built frontend; "
"run `pnpm --filter @itworx-mcp-hub/web build` first"
)
os.environ["HUB_CONTROL_URL"] = f"http://127.0.0.1:{control_port}"
os.environ["HUB_WEB_ROOT"] = str(dist_root)
tls = generate_local_tls_material(STATE_DIR / "tls-web")
uvicorn.run(
web_runtime.build_app(),
host="127.0.0.1",
port=port,
log_level="warning",
ssl_certfile=str(tls.cert_path),
ssl_keyfile=str(tls.key_path),
)
def main() -> None:
parser = argparse.ArgumentParser(description=__doc__)
sub = parser.add_subparsers(dest="command", required=True)
oidc_parser = sub.add_parser("oidc")
oidc_parser.add_argument("--port", type=int, required=True)
control_parser = sub.add_parser("control-api")
control_parser.add_argument("--port", type=int, required=True)
control_parser.add_argument("--oidc-port", type=int, required=True)
control_parser.add_argument("--web-port", type=int, required=True)
web_parser = sub.add_parser("web")
web_parser.add_argument("--port", type=int, required=True)
web_parser.add_argument("--control-port", type=int, required=True)
web_parser.add_argument("--dist", type=Path, required=True)
args = parser.parse_args()
if args.command == "oidc":
run_oidc(args.port)
elif args.command == "control-api":
run_control_api(args.port, args.oidc_port, args.web_port)
elif args.command == "web":
run_web(args.port, args.control_port, args.dist)
if __name__ == "__main__":
main()
+112
View File
@@ -0,0 +1,112 @@
"""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)