Skip to content

fix(db): tell apart Arc's 401, its three 403s and its 404 on the database listings - #41

Open
xe-nvdk wants to merge 1 commit into
mainfrom
fix/databases-listing-403-handling
Open

xe-nvdk wants to merge 1 commit into
mainfrom
fix/databases-listing-403-handling

Conversation

@xe-nvdk

@xe-nvdk xe-nvdk commented Oct 2, 2026

Copy link
Copy Markdown
Member

Summary

Arc's three database listing endpoints (GET /api/v1/databases, /:name, /:name/measurements) previously carried no authentication at all. They now require read permission and, where the server restricts reads per database, a grant covering the database being listed. db list, db show and measurement list can therefore receive a 401 or a 403 where they previously could not.

Those are not one condition, and 403 is not even one condition by itself — Arc answers it for three different things and distinguishes them only by message:

Arc's answer What it means What arcli now says
401 token missing, invalid or expired whether the connection has no token, or carries one Arc rejected
403 no permission for read on database 'x' / access denied: no read permission for database 'x' the token may read, but not that database the token is scoped — name one it is granted
403 token does not have 'read' permission / Permission denied: read required no read permission at all use a token with read permission
403 permission data unavailable Arc could not load the grants a server-side fault, not a problem with the token
404 no such database unchanged — stays distinguishable from "you may not read it"

The 403 on db list is a normal state, not a failure. Arc refuses to list every database for a tenant-scoped token rather than returning a filtered list, the same bar SHOW DATABASES applies. db list now says so and names the way forward (arcli db show <database>, arcli measurement list --database <database>) instead of printing a bare HTTP 403, and arcli never retries it.

Nothing changes for an admin token, for a read token on a server that does not restrict reads per database, or for a server with authentication disabled.

Notes on the implementation

  • Matching is by stable message fragment, not whole string, and both the read middleware's wording and the handler gate's are recognised — so a change to the permission word cannot silently reclassify one refusal as another.
  • An unrecognised 403 falls back to naming the token's permissions, on purpose: that is safe to say about any refusal, whereas telling an operator to pass a database they may already have passed is not.
  • New client.AccessDeniedError carries the status, the database, whether the refusal was the per-database one, and whether the connection sent a token. client.Scoped(err) reports the scoped case for callers embedding the package.

Companion to the Arc server change on fix/rbac-measurement-where-and-databases-auth; see that branch's release notes under "RBAC read and write restrictions were unreachable, and the database listings had no authorization".

Test plan

  • gofmt -l . clean, go vet ./... clean, go build ./...
  • go test -race -count=1 ./... — all 7 packages pass
  • New tests in internal/client/access_test.go pin every body Arc can send: both scoped wordings (named and empty/wildcard database), both coarse wordings, permission data unavailable, an unrecognised 403, 401 with and without a token, 404, and 500
  • Each new test verified to fail before the fix (revert database.go, run, restore): 6 failures, all on the classification
  • Built binary run against a server returning the real bodies — db list, measurement list, db show on a missing database and db show without read permission each print their own actionable message
  • Docs updated: README, db list/db show/measurement list help text, docs/releases/v26.09.5.md

…base listings

Arc's three database listing endpoints carried no authentication at all. They
now require read permission and, where the server restricts reads per
database, a grant covering the database being listed — so `db list`, `db show`
and `measurement list` can receive a 401 or a 403 where they previously could
not.

Those statuses are not one condition, and 403 is not even one condition by
itself. Arc answers it for a token that carries no read permission, for a
token whose grants do not cover the database asked for, and for its own
permission data being unreadable, and distinguishes them only by message. The
three need different responses from the operator, so arcli now reads the
message and says which one happened:

- Scoped: Arc refuses `GET /api/v1/databases` for a token restricted to
  particular databases rather than returning a filtered list, the same bar
  `SHOW DATABASES` applies. `db list` now says the token is scoped and names
  the way forward (`arcli db show <database>`, `arcli measurement list
  --database <database>`) instead of printing a bare `HTTP 403`. This is an
  expected state for a tenant-scoped token, so arcli does not retry it.
- No read permission: says so and asks for a token that has it. Telling this
  operator to name a database would send them somewhere that cannot help.
- Permission data unavailable: reported as a server-side fault, not as a
  statement about the token.

A 404 stays a 404, so "you may not read it" and "it is not there" remain
distinguishable, and a 401 says whether the connection has no token at all or
carries one Arc rejected.

Matching is by stable message fragment rather than whole string, and both the
read middleware's wording and the handler gate's are recognised, so a change
to the permission word cannot silently reclassify one refusal as another. An
unrecognised 403 falls back to naming the token's permissions, which is safe
to say about any refusal.

`client.AccessDeniedError` carries the status, the database, whether the
refusal was the per-database one and whether the connection sent a token;
`client.Scoped(err)` reports the scoped case for callers embedding the
package.

Companion to the Arc server change on
fix/rbac-measurement-where-and-databases-auth.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant