Initial curated ITWorx_MCP_Hub source from 368fba691ea8d23403b4d56b53b5ec670d7c5020
This commit is contained in:
commit
cb5be898ea
663 files changed
+82674
No files matched your search
@@ -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()
|
||||
Reference in new issue
Block a user