The copied bus composer grants mesh-grants' principal $JS.API.STREAM.CREATE.<stream> and $JS.API.STREAM.INFO.<stream> for each bucket (KV_<module>_<state>), log (LOG_<module>_<log>) and module stream a placed module's manifest declares, each by its own name: NATS wildcards are whole tokens, so nothing narrower than an exact name exists and nothing wider is granted. The writers table lets the grants part create, never update or delete. The operator allowed this one grant on 2026-10-26 08:54, beside the memberships grant of 22:31 (ADR 0547 clause 1 and 5). The expected lists were written again by the controller's own internal/broker with the same change (mesh-control fix/1790-grants-create-buckets); a fixture now places mesh-grants beside the builder's state, log and stream. Issue: 1790
78 lines
3.5 KiB
Go
78 lines
3.5 KiB
Go
package buscompose
|
|
|
|
import "testing"
|
|
|
|
// Defends issue 1790 and novox/hq ADR 0547 §5 (the operator, 2026-10-26 08:54): the grants part may create the stream
|
|
// of each bucket, log and stream a placed module's manifest declares, by its own name, and read it back; nothing
|
|
// wider, nothing updated or deleted, and no other principal so. Held byte-equal in the controller's copy.
|
|
func TestTheGrantsPartCreatesOnlyDeclaredStreams(t *testing.T) {
|
|
records := Records{
|
|
Nodes: []string{"novox"},
|
|
Assigned: map[string][]Declared{"novox": {
|
|
{Module: "mesh-delivery-builder",
|
|
State: []Bucket{{Module: "mesh-delivery-builder", Name: "steps", History: 5}},
|
|
Logs: []Log{{Module: "mesh-delivery-builder", Name: "builds"}},
|
|
Streams: []ModuleStream{{Module: "mesh-delivery-builder", Name: "BUILDASKS", Events: []string{"ask"}}}},
|
|
{Module: GrantsModule, State: []Bucket{{Module: GrantsModule, Name: "assertions"}}},
|
|
}},
|
|
}
|
|
users, err := Users(records)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
perms := map[string]Permissions{}
|
|
for _, u := range users {
|
|
if u.Kind != KindModule {
|
|
continue
|
|
}
|
|
p, err := PermissionsFor(u)
|
|
if err != nil {
|
|
t.Fatalf("%s: %v", u.Username(), err)
|
|
}
|
|
perms[u.Module] = p
|
|
}
|
|
for _, row := range []struct {
|
|
who, subject string
|
|
may bool
|
|
}{
|
|
{GrantsModule, "$JS.API.STREAM.CREATE.KV_mesh-delivery-builder_steps", true},
|
|
{GrantsModule, "$JS.API.STREAM.INFO.KV_mesh-delivery-builder_steps", true},
|
|
{GrantsModule, "$JS.API.STREAM.CREATE.LOG_mesh-delivery-builder_builds", true},
|
|
{GrantsModule, "$JS.API.STREAM.CREATE.BUILDASKS", true},
|
|
{GrantsModule, "$JS.API.STREAM.CREATE.KV_mesh-grants_assertions", true},
|
|
{GrantsModule, "$JS.API.STREAM.UPDATE.KV_mesh-delivery-builder_steps", false},
|
|
{GrantsModule, "$JS.API.STREAM.DELETE.KV_mesh-delivery-builder_steps", false},
|
|
{GrantsModule, "$JS.API.STREAM.PURGE.KV_mesh-delivery-builder_steps", false},
|
|
{GrantsModule, "$JS.API.STREAM.CREATE.KV_mesh-delivery-builder_undeclared", false},
|
|
{GrantsModule, "$JS.API.STREAM.CREATE.EVENTS", false},
|
|
{GrantsModule, "$JS.API.STREAM.CREATE.ASSIGNMENTS", false},
|
|
{GrantsModule, "$JS.API.STREAM.INFO.EVENTS", false},
|
|
{"mesh-delivery-builder", "$JS.API.STREAM.CREATE.KV_mesh-delivery-builder_steps", false},
|
|
} {
|
|
if got := MayPublish(perms[row.who], row.subject); got != row.may {
|
|
t.Errorf("%s may publish %s: %v, want %v", row.who, row.subject, got, row.may)
|
|
}
|
|
}
|
|
// A name that is no plain token is never granted, and a module other than the grants part is never granted one.
|
|
if got := CreateStreamGrants([]string{"*", ">", "KV_a.b", "KV_a_b"}); len(got) != 2 {
|
|
t.Errorf("grants for wildcard names: %v", got)
|
|
}
|
|
other, err := PermissionsFor(Principal{Kind: KindModule, Node: "novox", Module: "shop", CreatesStreams: []string{"KV_shop_x"}})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if MayPublish(other, "$JS.API.STREAM.CREATE.KV_shop_x") {
|
|
t.Error("a module other than the grants part was granted a create")
|
|
}
|
|
// The writers table refuses the grants part an update, and a create of a stream it was not given.
|
|
g := Principal{Kind: KindModule, Node: "novox", Module: GrantsModule, CreatesStreams: []string{"KV_a_b"}}
|
|
if err := CheckWriters(g, []string{"$JS.API.STREAM.CREATE.KV_a_b"}); err != nil {
|
|
t.Errorf("the declared create refused: %v", err)
|
|
}
|
|
for _, refused := range []string{"$JS.API.STREAM.UPDATE.KV_a_b", "$JS.API.STREAM.CREATE.KV_a_c", "$JS.API.STREAM.CREATE.*"} {
|
|
if CheckWriters(g, []string{refused}) == nil {
|
|
t.Errorf("%s was not refused", refused)
|
|
}
|
|
}
|
|
}
|