Add sentryctl dashboards permissions list|grant|revoke

PUT/DELETE /dashboards/{id}/permissions/{userId} (per-resource dashboard
grants, built earlier this phase) had no caller but Go tests and curl --
named as a real, disclosed gap in docs/phase-4-runbook.md. Adds a CLI
surface: sentryctl dashboards permissions list/grant/revoke, following
the existing dashboards subcommand pattern.

grant/revoke needed a new httpclient.go helper (httpMutateNoBody) since
both endpoints respond 204 No Content -- the existing helpers all expect
a JSON body to pretty-print. grant validates the role client-side
(viewer/editor only, mirroring api/dashboards.validGrantRole) before
making a request, since Admin/Owner already have tenant-wide dashboard
access and a resource-level grant can never raise someone past Editor.

Verified with real httptest.Server round trips (method, path, request
body, and error-body parsing on a 501 from a deployment with no
enterprise permission service wired in) -- the same pattern every other
sentryctl subcommand's tests already use, no fake/mock client needed
since sentryctl itself is just an HTTP client with no store of its own.
This commit is contained in:
2026-08-14 23:09:33 -07:00
parent abeee0076b
commit 3cf1320881
7 changed files with 282 additions and 9 deletions
+22 -7
View File
@@ -847,13 +847,28 @@ Full accounting: `/docs/security/threat-model.md`. Headline items:
- **Per-resource dashboard grants are now enforced** (`api/dashboards`'
handler reads `dashboard_permissions` via
`enterprise/internal/rbacstore.DashboardPermissions`, only when
`enterprise-api` -- not plain `api` -- serves traffic), but there's
still no UI or `sentryctl` command to create a grant -- `PUT
/dashboards/{id}/permissions/{userId}` has to be called directly.
Verified against a fake store; the real-Postgres integration tests
(`enterprise/internal/rbacstore/rbacstore_test.go`) haven't run
against a live database in this environment, same gap as the rest of
this phase's Postgres-backed pieces.
`enterprise-api` -- not plain `api` -- serves traffic), **and now has a
CLI surface**: `sentryctl dashboards permissions list|grant|revoke`
(`cli/cmd/sentryctl/cmd_dashboards.go`) against
`GET`/`PUT`/`DELETE /dashboards/{id}/permissions/{userId}` -- still no
`web` UI for it, just the CLI. Verified against a real `httptest.
Server` (not a fake store this time -- the CLI has no store of its
own, just an HTTP client, so this is exercising real request
construction/method/path/body/error-parsing, the same pattern every
other `sentryctl` subcommand's tests use):
```sh
cd cli
go test ./... -run TestCmdDashboardsPermissions -v
```
`api/dashboards`' own handler tests (fake `PermissionStore`) and the
real-Postgres `enterprise/internal/rbacstore/rbacstore_test.go`
integration tests are unchanged by this -- the CLI is a new caller of
an existing, already-tested endpoint, not new server-side logic. Those
Postgres-backed tests still haven't run against a live database in
this environment, same gap as the rest of this phase's Postgres-backed
pieces.
- All four adversarial ClickHouse/Tantivy probes named in
`/docs/phase-4-isolation-design.md`'s verification plan are now closed
-- see `api/queryapi/tenant_isolation_gap_test.go` for the full