Commit Graph
7 Commits
Author SHA1 Message Date
Sharang ParnerkarandClaude Opus 4.7 b3a8a97729 feat(dashboard): UI for managing MCP tokens
CI / Check (pull_request) Successful in 8m8s
CI / Detect Changes (pull_request) Has been skipped
CI / Deploy Agent (pull_request) Has been skipped
CI / Deploy Dashboard (pull_request) Has been skipped
CI / Deploy Docs (pull_request) Has been skipped
CI / Deploy MCP (pull_request) Has been skipped
Adds /mcp-tokens page that lets a logged-in user mint, list, and
revoke bearer tokens for the MCP server. Stacks on #92 (which added
the agent endpoints + middleware) — once both land, the loop is
closed: a user can copy a token from the dashboard straight into
their Claude Desktop / Cursor / ChatGPT MCP config.

UX
- "Create Token" button → inline form with name input.
- On submit, server function calls `POST /api/v1/mcp-tokens`. The
  raw token is shown ONCE in a prominent yellow banner with a copy
  button and a "won't be shown again" warning, then the user
  dismisses it manually.
- List view: card per token with name, prefix `mcpt_xxxx…`, created
  date, last_used (or "never"). Revoked tokens render dimmed with a
  "revoked" pill. Active tokens have a trash button → confirm
  modal → soft delete.
- Toast feedback on create/revoke success/failure.

Files
- infrastructure/mcp_tokens.rs (new) — three #[server] functions:
  fetch_mcp_tokens, create_mcp_token, revoke_mcp_token. All go
  through agent_client so the Keycloak Bearer is auto-attached;
  the agent then enforces tenant scoping on every endpoint.
- pages/mcp_tokens.rs (new) — the page component itself.
- app.rs — adds Route::McpTokensPage at /mcp-tokens.
- pages/mod.rs, infrastructure/mod.rs — module + re-export wiring.

Timestamp format
- The agent serializes BSON DateTime as extended JSON
  `{"$date":{"$numberLong":"..."}}`. Page has a small helper that
  accepts that shape, plain ISO strings, or anything else
  (best-effort). Same approach used elsewhere in the dashboard so
  there's no new dependency.

Test plan
- cargo fmt --all clean
- cargo clippy -p compliance-dashboard --features server
  -- -D warnings clean
- cargo clippy -p compliance-dashboard --features web
  --no-default-features -- -D warnings clean
- cargo check on both feature sets clean

Followup
- No sidebar entry yet (matches mcp_servers — settings-style
  pages are reached via direct URL today). Worth adding a
  Settings sub-menu in a separate UX pass.
- Token expiry + per-tool scope when those land on the agent side
  will need a small UI for the create modal (extra fields).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-18 12:53:59 +02:00
Sharang ParnerkarandClaude Opus 4.7 628f346529 feat(m7.3): MCP tenant-scoped bearer tokens
CI / Check (pull_request) Successful in 8m9s
CI / Detect Changes (pull_request) Has been skipped
CI / Deploy Agent (pull_request) Has been skipped
CI / Deploy Dashboard (pull_request) Has been skipped
CI / Deploy Docs (pull_request) Has been skipped
CI / Deploy MCP (pull_request) Has been skipped
LLM clients (Claude Desktop, Cursor, ChatGPT) can't run a Keycloak
OIDC flow, so the MCP server can't use JWTs for auth. This PR
introduces opaque static bearer tokens minted per-tenant via new
agent endpoints, validated by the MCP server, and used to route
incoming MCP requests to the caller's per-tenant database.

Until now, the MCP server connected to a single shared MongoDB DB
with no auth and no tenant awareness — every tool (list_findings,
list_sbom_packages, etc.) returned data across all tenants. After
M7.2 made the agent per-tenant, MCP was the lone cross-tenant data
leak. This closes it.

Design summary
- Token format: `mcpt_<43 url-safe random chars>` (48 chars total).
  Opaque, never embeds tenant_id, never stored in plaintext.
- Storage: cross-tenant `<prefix>__admin.mcp_tokens` collection,
  keyed by SHA-256 hash. Each row carries the tenant_id, name,
  created_by, created_at, last_used_at, revoked flag.
- Agent endpoints (tenant-scoped via TenantCtx):
    POST   /api/v1/mcp-tokens    → mint (returns raw token ONCE)
    GET    /api/v1/mcp-tokens    → list (metadata + 12-char prefix,
                                   never the hash)
    DELETE /api/v1/mcp-tokens/id → soft revoke
- MCP middleware: extract `Authorization: Bearer mcpt_...`, sniff
  the prefix, SHA-256 → lookup in admin DB → reject if missing or
  revoked. Updates last_used_at fire-and-forget so it never blocks.
  Sets `tokio::task_local!` TENANT_ID for the inner service call;
  the rmcp tool handlers read it and resolve the per-tenant DB.
- task_local is scoped via TENANT_ID.scope(...) around next.run(req)
  so the rmcp tool handlers downstream see the tenant_id without
  modifying their (macro-generated) signatures.

Files
- compliance-core/src/models/mcp_token.rs (new) — McpToken +
  McpTokenView (public projection without the hash).
- compliance-agent/src/database.rs — DatabasePool::admin_db() +
  admin_db_name(): cross-tenant access for token storage.
- compliance-agent/src/api/handlers/mcp_tokens.rs (new) — three
  endpoints. Token generation: 32 random bytes → URL-safe base64,
  no padding. SHA-256 hex stored.
- compliance-mcp/src/database.rs — replaced single Database with
  DatabasePool. Tenant-scoped Database constructed per request.
  Same sanitization + 63-byte cap + hash fallback as the agent.
- compliance-mcp/src/auth.rs (new) — bearer middleware + task_local.
  Includes a SHA-256 round-trip test against a known vector.
- compliance-mcp/src/main.rs — HTTP transport: bearer middleware
  layered on /mcp (not /health, so orca's container probe still
  works). stdio transport: falls back to STDIO_TENANT_ID env (defaults
  to "dev") so local development still works; logged loudly as
  not-for-production.
- compliance-mcp/src/server.rs — each of the 12 tool handlers
  resolves the per-tenant DB via task_local before calling its tool
  fn. Tool fns themselves are unchanged.

Token UX
- Generated by the dashboard (or curl + KC JWT) — user sees raw
  token exactly once, copies it into their LLM client config.
- Dashboard UI for management is a follow-up; can use curl in the
  meantime:
    curl -X POST https://comp-dev.../api/v1/mcp-tokens \
      -H "Authorization: Bearer $KC_JWT" \
      -H "Content-Type: application/json" \
      -d '{"name":"Claude Desktop"}'

Test plan
- cargo fmt --all clean
- cargo clippy --workspace --exclude compliance-dashboard
  -- -D warnings clean
- cargo test -p compliance-core --lib — 7 pass
- cargo test -p compliance-agent --lib — 230 pass (+2 new for
  token generation + sha256 stability)
- cargo test -p compliance-agent --test tenant_isolation — 6 pass
- cargo test -p compliance-mcp — 34 pass (+1 new sha256 vector)

What's deferred
- Dashboard UI for managing tokens (page + create modal + list/
  revoke). Trivial once the API is live.
- Token expiry + per-tool scope (today every token grants access
  to all 12 tools for its tenant).
- Lifting DatabasePool into compliance-core (duplicated for now
  in compliance-mcp to keep this PR focused; lift if a third
  consumer appears).

Production
- The `<prefix>__admin` DB needs to NOT collide with a tenant
  DB. Sanitized tenant_id never starts with `_admin` for any
  current tenant_id shape (UUIDs); flagged in the database.rs
  docstring so tenant provisioning can reject `_admin*` ids
  proactively.
- orca-infra MCP service block already has MONGODB_URI /
  MONGODB_DATABASE — no new env needed. No KC creds since MCP
  doesn't use Keycloak for its own auth.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-18 11:54:01 +02:00
Sharang Parnerkar 5cafd13f44 ci: log orca webhook response so deploy steps arent silent
CI / Deploy Agent (push) Has been skipped
CI / Deploy Dashboard (push) Has been skipped
CI / Deploy Docs (push) Has been skipped
CI / Deploy MCP (push) Has been skipped
CI / Check (push) Has been skipped
CI / Detect Changes (push) Successful in 5s
Nightly E2E Tests / E2E Tests (push) Failing after 2m59s
2026-04-08 15:09:27 +02:00
Sharang ParnerkarandClaude Opus 4.6 69209649a5 ci: trigger first orca build for all services
CI / Check (push) Has been skipped
CI / Deploy Agent (push) Successful in 7m5s
CI / Deploy Dashboard (push) Failing after 21m28s
CI / Detect Changes (push) Successful in 4s
CI / Deploy Docs (push) Successful in 30s
CI / Deploy MCP (push) Successful in 1m31s
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 10:10:07 +02:00
Sharang ParnerkarandClaude Opus 4.6 d5439adc0d ci: trigger build of dashboard, docs, mcp images for orca
CI / Check (push) Has been cancelled
CI / Detect Changes (push) Has been cancelled
CI / Deploy Agent (push) Has been cancelled
CI / Deploy Dashboard (push) Has been cancelled
CI / Deploy Docs (push) Has been cancelled
CI / Deploy MCP (push) Has been cancelled
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 10:09:49 +02:00
Sharang ParnerkarandClaude Opus 4.6 bc7cdd35e4 ci: replace coolify webhook with orca deploy
CI / Check (push) Has been cancelled
CI / Detect Changes (push) Has been cancelled
CI / Deploy Agent (push) Has been cancelled
CI / Deploy Dashboard (push) Has been cancelled
CI / Deploy Docs (push) Has been cancelled
CI / Deploy MCP (push) Has been cancelled
Each deploy job now builds the per-service image, pushes to the
private registry as :latest and :sha, then triggers an HMAC-signed
orca redeploy webhook. Coolify webhooks are no longer used.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 10:06:11 +02:00
Sharang ParnerkarandClaude Opus 4.6 c062d834a1 fix: downgrade dotenv missing file from FAILED to info message
CI / Check (push) Has been skipped
CI / Detect Changes (push) Successful in 3s
CI / Deploy Agent (push) Successful in 2s
CI / Deploy Dashboard (push) Has been skipped
CI / Deploy Docs (push) Has been skipped
CI / Deploy MCP (push) Has been skipped
Nightly E2E Tests / E2E Tests (push) Failing after 2m16s
Non-fatal in Docker where env vars come from container config.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 15:33:24 +02:00