The night's backup of the bus takes each stream through JetStream's snapshot API, run by the nats module under its own account. The module holding mesh-broker is composed that account: stream names and info, the snapshot request, its flow-control acks, its own inbox — no write, which the writers table checks. A bus module declaring anything else to say on the bus is refused by module check rather than silently granted nothing. The genesis user list is unchanged: the controller's grants are.
76 lines
2.7 KiB
Go
76 lines
2.7 KiB
Go
package broker
|
|
|
|
import (
|
|
"slices"
|
|
"testing"
|
|
)
|
|
|
|
// The bus's own module copies the bus and does nothing else on it (novox/hq ADR 0235): its whole
|
|
// authority is the snapshot API and its own inbox, whatever else it declared — its tools are the
|
|
// node's runtime's to serve.
|
|
func TestTheBussOwnModuleMaySnapshotAndNothingElse(t *testing.T) {
|
|
p := Principal{Kind: KindModule, Node: "anchor", Module: "nats", SnapshotsTheBus: true,
|
|
Serves: []string{"nats_streams"}, PasswordHash: "x"}
|
|
perms, err := PermissionsFor(p)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
want := []string{"$JS.API.STREAM.INFO.*", "$JS.API.STREAM.NAMES", "$JS.API.STREAM.SNAPSHOT.*", "$JS.SNAPSHOT.ACK.>"}
|
|
if !slices.Equal(perms.Publish, want) {
|
|
t.Errorf("it may publish %v, want exactly %v", perms.Publish, want)
|
|
}
|
|
if !slices.Equal(perms.Subscribe, []string{"_INBOX.anchor.nats.>"}) {
|
|
t.Errorf("it may subscribe %v, want its own inbox alone", perms.Subscribe)
|
|
}
|
|
if perms.AllowResponses {
|
|
t.Error("nothing is asked of it, and it may answer")
|
|
}
|
|
}
|
|
|
|
// Read-only, by the writers table: no grant of it overlaps a subject a write of the mesh's state is a
|
|
// publish to — a stream's definition, a bucket, a message.
|
|
func TestTheSnapshotGrantsWriteNothing(t *testing.T) {
|
|
p := Principal{Kind: KindModule, Node: "anchor", Module: "nats"}
|
|
if err := CheckWriters(p, BusSnapshotGrants.Publish); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
for _, grant := range BusSnapshotGrants.Publish {
|
|
for _, write := range []string{"$JS.API.STREAM.CREATE.X", "$JS.API.STREAM.UPDATE.X", "$JS.API.STREAM.DELETE.X",
|
|
"$JS.API.STREAM.PURGE.X", "$JS.API.STREAM.MSG.DELETE.X", "$JS.API.STREAM.RESTORE.X",
|
|
"$JS.API.CONSUMER.CREATE.X", "$JS.API.CONSUMER.DURABLE.CREATE.X.y", "$KV.b.k", "mesh.mod.m.event.e"} {
|
|
if SubjectsOverlap(grant, write) {
|
|
t.Errorf("%s would let the bus's own module publish %s", grant, write)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// A module that is not the bus gets nothing of it, and the flag travels from the records to the user.
|
|
func TestOnlyTheBussOwnModuleIsGrantedTheSnapshot(t *testing.T) {
|
|
users, err := Users(Records{Nodes: []string{"anchor"}, Assigned: map[string][]Declared{"anchor": {
|
|
{Module: "nats", SnapshotsTheBus: true},
|
|
{Module: "shop", Emits: []string{"order.placed"}},
|
|
}}})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
for _, u := range users {
|
|
perms, err := PermissionsFor(u)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
snapshots := slices.Contains(perms.Publish, "$JS.API.STREAM.SNAPSHOT.*")
|
|
switch u.Username() {
|
|
case "anchor.nats":
|
|
if !snapshots {
|
|
t.Error("the bus's own module is not granted the snapshot")
|
|
}
|
|
case "controller":
|
|
default:
|
|
if snapshots {
|
|
t.Errorf("%s may snapshot the bus", u.Username())
|
|
}
|
|
}
|
|
}
|
|
}
|