fix: align dashboard session access with RBAC #82
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "fix/dashboard-rbac-list-get-consistency"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Verification
Adds one positive-case test per typed error code introduced in the schema.js taxonomy refactor, plus a regression guard for the ERR_SCHEMA_DRIFT case (so the "please file a bug" copy can't get accidentally repurposed for normal RBAC / not-found / invalid / tool errors). - mock-server.ts: add `state.textEnvelopes: Record<string, string>` knob that returns text envelopes with `isError: false`, mirroring the production server's `ToolResult::ok(format!("[error] ..."))` pattern. Distinct from `toolErrors` (which sets `isError: true` and causes rpc.js to throw before the envelope reaches the schema asserts — unsuitable for exercising the schema.js classifier). - dashboard.spec.ts: 4 new tests, all driving get_session through the classifier: 1. ERR_NOT_FOUND — '[error] session not found: sess-A' 2. ERR_INVALID_ARG — '[invalid] session_id must be a UUID' 3. ERR_TOOL_ERROR — '[error] storage backend unreachable' 4. ERR_SCHEMA_DRIFT — malformedGetSession (regression guard: asserts schemaFallbackText IS shown for genuine drift, so the refactor cannot collapse the wording into the typed cases). Each test asserts (a) the panel does NOT show schemaFallbackText for non-drift cases and (b) the typed copy contains a stable substring that distinguishes the class. Per-class wording is implementation-defined; the tests assert on regex substrings rather than exact copy so cosmetic changes don't break them. Full dashboard.spec.ts now: 31 passing (was 27). Full e2e suite: 45 passing.The dashboard's `list_sessions` was returning `{sessions:[], total:0, warnings:["filtered 20 session(s) the caller is not authorised to read"]}` even though 20 locally-owned sessions existed in storage. Two converged gaps: 1. `transport/sse.rs::handle_mcp_sse` and `transport/websocket.rs:: handle_connection` were minting a brand-new ephemeral `AgentSession:: new(Primary)` per connection. That fresh UUID had no role on any stdio-created session, so the post-bridge RBAC filter (from the previous commits) correctly dropped every entry. 2. `setup.rs::uses_local_owner_bootstrap` returned true only for stdio, so even threading the registry's local_owner identity through to SSE would have collapsed: SSE was generating its own random `local_owner` UUID at every startup, distinct from the stdio-persisted one. Fix: - New `AgentSession::with_id` constructor so transports can bind to a pre-existing principal instead of always minting one. - `SseTransport` and `WsTransport` now accept `local_owner_agent_id: Option<AgentId>`, threaded from `main.rs` via `run_transport`. When Some, every authenticated connection adopts that canonical identity. - Refcount-aware cleanup in both transports: only retire the `AgentRegistry` entry when the last sharer disconnects. SSE uses its existing connections map; WS gains an `Arc<Mutex<HashMap<AgentId, usize>>>` for the same purpose. Ephemeral identities have refcount 1 so legacy behaviour is preserved. - `uses_local_owner_bootstrap` now returns true whenever storage is SQLite, not only for stdio. This is what makes the persisted seed file (~/.cognix/) the single source of truth for local_owner across every transport sharing the same DB. Test coverage: - `test_sse_binds_to_local_owner_when_configured` — pins the principal-binding contract. - `test_sse_mints_ephemeral_agent_when_no_local_owner` — pins the back-compat fallback for deployments without a local owner. - `test_sse_refcount_keeps_local_owner_when_one_of_two_closes` — pins the refcount guard so closing one dashboard tab never strands a concurrent one. Existing tests updated to pass `None` for the new arg (status quo).