feat(m7.2-B): migrate API handlers to per-tenant database pool #87
Closed
sharang
wants to merge 1 commits from
feat/m7.2b-handlers-tenant-scoped into feat/m7.2a-tenant-db-pool
pull from: feat/m7.2b-handlers-tenant-scoped
merge into: :feat/m7.2a-tenant-db-pool
:main
:migrate/ci-breakpilot-tld
:fix/plc-refresh-control-refs
:fix/plc-control-mapping
:fix/refresh-control-refs
:docs/mcp-loop
:fix/mcp-tenant-context
:feat/promote-grounded-controls
:docs/control-mapping
:feat/tune-semantic-retrieval
:fix/embed-batch-chunking
:feat/b3-architectural-controls
:feat/b2-grounded-surface-checks
:feat/b1-cra-semgrep-rules
:feat/semantic-index-cache-autowire
:feat/semantic-control-mapping
:feat/oscal-emit-unmapped-mcp
:feat/grounded-control-check
:feat/oscal-assess-live
:feat/oscal-assessment
:feat/oscal-controls-provider
:feat/werkbank-runnable-loop
:feat/werkbank-exec-crate
:feat/werkbank-runner-endpoints
:feat/werkbank-job-queue
:feat/werkbank-job-contract
:feat/plc-provision-and-test
:docs/plc-runtimes
:docs/plc-soft-plc-architecture
:feat/dast-http-scheme
:feat/enable-optin-scans
:feat/plc-control-logic-scanner
:refactor/rip-out-legacy-repository
:feat/onboarding-validation-edit
:feat/real-nix-firmware-sbom
:fix/bump-tramiton-0.4.1
:feat/firmware-sbom-repro-build
:feat/firmware-sbom-tramiton
:feat/dashboard-targets-dropdowns
:fix/targets-visibility-unified-default
:feat/onboarding-scan-trigger
:fix/ci-concurrency-no-cancel-main
:fix/ci-docker-tramiton-dashboard-mcp
:feat/onboarding-scan-detection
:feat/onboarding-pipeline
:feat/onboarding-wizard
:fix/ci-docker-tramiton-fetch
:feat/onboarding-api
:feat/onboarding-migration
:feat/ci-kellnr-sccache
:feat/onboarding-ingest-classify
:feat/onboarding-foundation
:feat/dashboard-token-refresh
:fix/dashboard-bearer-token
:feat/m7.1-agent-wire
:fix/m7.1-jwks-refresh
:feat/m7.1-smoke-harness
:feat/m7.1-tenant-claims
:feat/light-mode-theme-toggle
:fix/embedding-build-progress
:fix/cve-scan-http-timeout
:fix/scan-resource-limits-and-script-error
:fix/multiple-issues
:feat/cve-alerts
:feat/e2e-tests
:feat/help-chat-widget
:fix/cascade-delete-repo
:feat/refine-llm-prompts
:fix/gitea-pr-review-error-handling
:test/dummy-bad-code
:fix/remove-code-review-from-findings
:feat/pentest-onboarding
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Summary
Builds on PR M7.2-A (#86). Every HTTP handler in
compliance-agent/src/api/now takes aTenantCtxextractor and pulls a tenant-scopedDatabasefromagent.db_pool.for_tenant(&ctx). Query bodies are unchanged —db.findings().find(doc! {...})reads from the tenant's own physical database, so the filter doc cannot leak data across tenants because the wrong tenant's data is on a different db handle.Targets
feat/m7.2a-tenant-db-pool— stacks on top of #86 so the diff shown is only B's changes. When #86 merges, this PR will auto-retarget tomain.Foundation
dto::tenant_db(&agent, &tenant) -> Result<Database, StatusCode>— every migrated handler calls it at the top of the body instead oflet db = &agent.db;. 500 on the rare pool failure; 4xx auth failures are already handled by the M7.1 status gate upstream.api::server::inject_dev_tenantmiddleware — mounted only when Keycloak is NOT configured. Synthesises aTenantContextwithtenant_id = $DEV_TENANT_ID(defaultdev) socargo runagainst a bare Mongo + no KC still serves the API. Logged loudly as "DO NOT use in any environment with real customer data".TestServermountsinject_dev_tenantso existing E2E tests reach handlers;cleanup()now drops every<db_name>_*per-tenant database, not just the legacy<db_name>.Files migrated
chat.rsinner()so vector search is per-tenantdast.rsfindings.rsgraph.rstrigger_build's spawn rewires to tenant DBhealth.rsstats_overviewmigrated; public/healthstays unscoped (correct — it's inPUBLIC_ENDPOINTS)issues.rsnotifications.rspentest_handlers/session.rspentest_handlers/export.rstenant_dbacquisitionpentest_handlers/stats.rspentest_handlers/stream.rsrepos.rssbom.rsscans.rshelp_chat.rshas no DB queries and was skipped.Grep proof:
Test plan
cargo fmt --all -- --checkcleancargo clippy --workspace --exclude compliance-dashboard -- -D warningscleancargo test -p compliance-core --lib— 7 passcargo test -p compliance-agent --lib— 228 passcargo test -p compliance-agent --test tenant_isolation— 5 pass (driver-level isolation still holds post-handler migration)cargo test -p compliance-agent --test tenant_status_middleware— 6 passWhat's NOT yet migrated
These run as the agent's service account (no JWT-derived tenant), and need a different shape — they take a
tenant_idat job-construction time. PR-C does this work.scheduler.rs(6 sites)pipeline/orchestrator.rs(14)pentest/orchestrator.rs(13)webhooks/{gitea,github,gitlab}.rs— webhooks resolve tenant from the incoming repo's ownertrackers/jira.rspipeline/dedup.rs,pipeline/graph_build.rs,pipeline/issue_creation.rs,pipeline/sbom/mod.rspentest/context.rs,pentest/report/html/*llm/triage.rsagent.dbis still in theComplianceAgentstruct as a transitional handle for those paths. PR-D removes it once PR-C migrates them, and also adds the cross-tenant admin helpers (list-all flows).🤖 Generated with Claude Code
Builds on PR M7.2-A. Every HTTP handler in compliance-agent/src/api/ now takes a TenantCtx extractor and pulls a tenant-scoped Database from agent.db_pool.for_tenant(&ctx). The query bodies are unchanged — `db.findings().find(doc! {...})` reads from the tenant's own physical database, so the filter doc cannot leak data across tenants because the wrong tenant's data is literally on a different db handle. Changes - New `dto::tenant_db(&agent, &tenant) -> Result<Database, StatusCode>` helper. Every migrated handler calls it at the top of the body instead of `let db = &agent.db;`. 500 on the rare pool failure; 4xx auth failures are already handled by the M7.1 status gate. - New `api::server::inject_dev_tenant` middleware mounted only when Keycloak is NOT configured. Synthesizes a TenantContext with tenant_id = $DEV_TENANT_ID (default `dev`) so `cargo run` against a bare Mongo + no KC still serves the API. Logged loudly as "DO NOT use in any environment with real customer data". - Test harness: TestServer mounts inject_dev_tenant so existing E2E tests reach handlers; cleanup() now drops every <db_name>_* per-tenant database, not just the legacy <db_name>. Files migrated (handler count, all pass `cargo build`): - chat.rs (3) — also rewires RagPipeline + EmbeddingStore to the tenant DB's inner() so vector search is per-tenant - dast.rs (5) - findings.rs (5) - graph.rs (7) — also rewires GraphStore inside trigger_build's spawn to the tenant DB - health.rs (1) — stats_overview migrated; public /health stays un-scoped - issues.rs (1) - notifications.rs (5) - pentest_handlers/session.rs (12) — both wizard + legacy paths, plus pause/resume/stop/get_attack_chain/get_messages/ get_session_findings/lookup_repo. PentestOrchestrator now gets the tenant DB clone in its spawn. - pentest_handlers/export.rs (1) — fans out across sessions, attack_chain_nodes, dast_findings, findings, sbom_entries, graph_nodes from a single tenant_db acquisition - pentest_handlers/stats.rs (1) - pentest_handlers/stream.rs (1) — SSE handler verifies session via the tenant DB before subscribing - repos.rs (6) - sbom.rs (5) - scans.rs (1) help_chat.rs has no DB queries and was skipped. 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 — 228 pass - cargo test -p compliance-agent --test tenant_isolation — 5 pass (driver-level isolation still holds post-handler migration) - cargo test -p compliance-agent --test tenant_status_middleware — 6 pass What's not yet migrated (PR-C / PR-D) - scheduler.rs (6 sites), pipeline/orchestrator.rs (14), pentest/orchestrator.rs (13), webhooks (gitea/github/gitlab), trackers/jira.rs, pipeline/dedup.rs etc. — background paths without a JWT-derived tenant context. - agent.db is still in the ComplianceAgent struct as a transitional handle for those paths. PR-D removes it once PR-C migrates the background paths. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>Superseded — the M7.2 stack was inadvertently included in PR #90 squash-merge (
5648291) on main. The dashboard PR was branched off this PR's descendant and its full diff swept into main as one squash commit. M7.2-A through M7.2-D are all live on main and in production. Closing without merging.Pull request closed