package ops
import (
"context"
"errors"
"testing"
)
// A token limited to repositories does nothing at forge scope but read
// its own identity, on every path that runs an op: the API router and
// the pages, which call ops in process, share Invoke.
func TestALimitedTokenIsRefusedAtForgeScopeOnEveryPath(t *testing.T) {
r := NewRegistry()
called := map[string]bool{}
reg := func(name, route string, kind Kind, scope Scope) {
t.Helper()
if err := Register(r, Op[struct{}, struct{}]{Name: name, Route: route, Kind: kind, Auth: Token, Scope: scope, Doc: "test",
Run: func(context.Context, struct{}) (struct{}, error) { called[name] = true; return struct{}{}, nil }}); err != nil {
t.Fatal(err)
}
}
reg("repo.add", "POST /api/v1/repos", Command, ScopeUnset)
reg("fleet.thing", "POST /api/v1/fleet-thing", Command, ScopeFleet)
reg("my.thing", "GET /api/v1/my-thing", Query, ScopeCaller)
reg("me.account", "GET /api/v1/me", Query, ScopeCaller)
reg("repo.thing", "POST /api/v1/r/{repo}/thing", Command, ScopeUnset)
reg("repos.update", "POST /api/v1/repos/update", Command, ScopeRepo)
limited := WithToken(WithUser(context.Background(), "jan"),
TokenAuth{ID: "T", Caps: func(string) Caps { return noCaps{} }, Limited: true})
for _, name := range []string{"repo.add", "fleet.thing", "my.thing"} {
if _, err := r.Invoke(limited, name, struct{}{}); !errors.Is(err, ErrForbidden) || called[name] {
t.Errorf("FAIL: %s by a limited token: err=%v called=%v", name, err, called[name])
}
}
for _, name := range []string{"me.account", "repo.thing", "repos.update"} {
if _, err := r.Invoke(limited, name, struct{}{}); err != nil {
t.Errorf("FAIL: %s by a limited token: %v", name, err)
}
}
unlimited := WithToken(WithUser(context.Background(), "jan"), TokenAuth{ID: "U", Caps: func(string) Caps { return noCaps{} }})
if _, err := r.Invoke(unlimited, "repo.add", struct{}{}); err != nil {
t.Errorf("FAIL: an unlimited token at forge scope: %v", err)
} else {
t.Log("ok: forge scope refuses a limited token and only that")
}
}