Artifact 66a7634fa7336128de6edbea7e62cfe23ccfbe020d812219f6f2599f8ea57040:
- File
internal/web/fleet_test.go
— part of check-in
[86880118d5]
at
2026-08-18 02:09:54
on branch trunk
— account: manage identity through the API, and stop lying about why
Six ops (account.list/add/passwd/delete/grant/revoke, admin) plus quarry accounts on top of them. Creating a login or granting someone read on a repository no longer needs shell access on the server.
quarry account still opens OLTP directly and has to: the FIRST account must exist before any server runs or any admin exists to authenticate as. Everything after that is the API, so the CLI has no privileged path.
The verifier never crosses the seam in either direction, and account.add /passwd scrub the password out of any error before it travels — an error is the one value that reaches a log, an HTTP body and a terminal at once.
Verify returns a three-valued Decision instead of a bool. Denied and Insufficient are not the same thing and the wire has separate codes for them: an authenticated client short of a capability was being told 401 with a challenge, which invites it to retry the same password forever. Now 403, and deliberately without a challenge. A harness test pinned the old behaviour and was updated with the reason. (user: dmestas, size: 3951) [annotate] [blame] [check-ins using]
package web import ( "net/http" "net/http/httptest" "testing" "time" "craftdesign.group/quarry/internal/api" "craftdesign.group/quarry/internal/ops" ) // fakeSessions resolves one cookie to one user and answers Allow from a // role table keyed by repo. The empty repo is the site-admin question. type fakeSessions struct { user string roles map[string]ops.Auth } func (f fakeSessions) Login(string, string) (string, time.Time, bool) { return "", time.Time{}, false } func (f fakeSessions) Resolve(tok string) (string, bool) { return f.user, tok == "good" } func (f fakeSessions) Logout(string) {} func (f fakeSessions) Allow(user, repo string, need ops.Auth) bool { if user != f.user { return false } return f.roles[repo] >= need } func withCookie(path string) *http.Request { req := httptest.NewRequest("GET", path, nil) req.AddCookie(&http.Cookie{Name: sessionCookie, Value: "good"}) return req } // The fleet pages are gated at Admin with an empty repo. A user with // write — even ADMIN — on a repository is not thereby a site admin, and // the fleet surface exposes every store's operational detail. Getting // this wrong turns "can edit one wiki" into "can read the whole fleet's // error strings and force syncs across every store". func TestFleetGateRefusesNonSiteAdmins(t *testing.T) { verify := Verify(func(string, string, string, ops.Auth) api.Decision { return api.Denied }) for _, tc := range []struct { name string roles map[string]ops.Auth want bool }{ {"write on one repo", map[string]ops.Auth{"alpha": ops.Token}, false}, {"admin on one repo", map[string]ops.Auth{"alpha": ops.Admin}, false}, {"no roles at all", map[string]ops.Auth{}, false}, {"site admin", map[string]ops.Auth{"": ops.Admin}, true}, } { t.Run(tc.name, func(t *testing.T) { sess := fakeSessions{user: "someone", roles: tc.roles} w := httptest.NewRecorder() _, ok := gateAt(verify, sess, "", ops.Admin, w, withCookie("/fleet")) if ok != tc.want { t.Fatalf("gateAt admitted=%v, want %v (status %d)", ok, tc.want, w.Code) } if !tc.want && w.Code != http.StatusForbidden { t.Fatalf("refusal status = %d, want 403", w.Code) } }) } } // A site admin must still be refused the per-repo write gate on a repo // they hold no role for — gateAt must not have widened gate's meaning. // (Site admins DO get every repo in the real role table; this asserts // the level is what is consulted, not that admins are special-cased in // the web layer.) func TestGateStillAsksForTokenNotAdmin(t *testing.T) { verify := Verify(func(string, string, string, ops.Auth) api.Decision { return api.Denied }) sess := fakeSessions{user: "someone", roles: map[string]ops.Auth{"alpha": ops.Token}} if _, ok := gate(verify, sess, "alpha", httptest.NewRecorder(), withCookie("/r/alpha/wiki/x/edit")); !ok { t.Fatal("token-level user refused a write page") } w := httptest.NewRecorder() if _, ok := gate(verify, sess, "beta", w, withCookie("/r/beta/wiki/x/edit")); ok { t.Fatal("user with no role on beta admitted to beta's write page") } if w.Code != http.StatusForbidden { t.Fatalf("refusal status = %d, want 403", w.Code) } } // The refusal message must not name a repository when the gate was not // scoped to one: "someone has no write access to ”" is the shape that // sends an operator hunting for a repo with an empty name. func TestForbidWordsMatchTheGate(t *testing.T) { for _, tc := range []struct { repo string level ops.Auth want string }{ {"", ops.Admin, "someone is not a site admin"}, {"alpha", ops.Admin, "someone has no admin access to alpha"}, {"alpha", ops.Token, "someone has no write access to alpha"}, } { w := httptest.NewRecorder() forbid(w, "someone", tc.repo, tc.level) if got := w.Body.String(); got != tc.want { t.Errorf("forbid(%q, %v) = %q, want %q", tc.repo, tc.level, got, tc.want) } } }