Answer the what-if as data, let the build seat read the gate's pull keys, and prove the parts' and the gate's grants (design 47 R3d, ADR 0450) #401
@@ -0,0 +1,130 @@
|
||||
package broker
|
||||
|
||||
import "testing"
|
||||
|
||||
// The delivery's gate, composed as novox/hq ADR 0437 §3 declares it and ADR 0450 §1 ships it in release 3's step R3d
|
||||
// (issue 1326). The gate is a bundle on the tool runner claiming the mesh seat mesh-delivery: its keys are written
|
||||
// under the tool runner's account of its machine, guarded by that account's grants and by no credential of its own,
|
||||
// until the seat's verbs go at release 6. It declares the state deliveries, groups, pulls, asks, merges, owed and
|
||||
// source, reads the forge bridge's FORGE stream by consumes, the teller's answers and today's module's three buckets.
|
||||
// Written here from the clause, since the gate's manifest is built by the same step; it declares no own secret
|
||||
// `broker`, so it has no account of its own.
|
||||
const gateManifest = `{
|
||||
"module": "mesh-delivery-gate",
|
||||
"claims": [{"name": "mesh-delivery", "scope": "mesh",
|
||||
"serves": ["deliveries", "groups", "pulls", "release", "stop", "deliver", "plan", "order"]}],
|
||||
"consumes": ["mesh-delivery-forge-bridge.forge.**"],
|
||||
"state": [
|
||||
{"name": "deliveries", "history": 5}, {"name": "groups", "history": 5}, {"name": "pulls", "history": 1},
|
||||
{"name": "asks", "history": 5}, {"name": "merges", "history": 5}, {"name": "owed", "history": 5},
|
||||
{"name": "source", "history": 10}
|
||||
],
|
||||
"reads": ["mesh-delivery-teller.answers", "mesh-delivery.deliveries", "mesh-delivery.groups", "mesh-delivery.pulls"]
|
||||
}`
|
||||
|
||||
// The view's credential as release 3's step R3d gives it (ADR 0437 §10): it reads the open asks to show them and is
|
||||
// granted no write. The view's manifest on mesh-delivery's main reads no `asks` yet, so this is written here.
|
||||
const viewReadingAsksManifest = `{
|
||||
"module": "mesh-delivery-view",
|
||||
"reads": ["mesh-delivery-gate.asks", "mesh-delivery-indexer.candidates", "mesh-delivery-indexer.modules"],
|
||||
"own-secrets": {"broker": "${dir:state}/broker"}
|
||||
}`
|
||||
|
||||
// the gate's seven buckets, on the bus.
|
||||
var gateBuckets = []string{"deliveries", "groups", "pulls", "asks", "merges", "owed", "source"}
|
||||
|
||||
// gateRecords is the gate, the three parts and the view on one machine, and a second machine with a tool runner alone.
|
||||
func gateRecords(t *testing.T) Records {
|
||||
t.Helper()
|
||||
streams := map[string]ModuleStream{"mesh-delivery-forge-bridge": {Module: "mesh-delivery-forge-bridge", Name: "FORGE"}}
|
||||
return Records{Nodes: []string{"novox", "ace"}, Assigned: map[string][]Declared{
|
||||
"novox": {{Module: RuntimeModule}, declaredOf(t, gateManifest, streams),
|
||||
declaredOf(t, bridgeManifest, streams), declaredOf(t, indexerManifest, streams),
|
||||
declaredOf(t, tellerManifest, streams), declaredOf(t, viewReadingAsksManifest, streams)},
|
||||
"ace": {{Module: RuntimeModule}},
|
||||
}}
|
||||
}
|
||||
|
||||
// 1326/AC1: the grants composed for the gate on one machine let that machine's tool runner account write each of its
|
||||
// seven buckets, read the buckets it declares reading (the teller's answers without writing them), hear the bridge's
|
||||
// FORGE stream and serve the seat's verbs.
|
||||
func TestTheGatesBucketsAreWritableByItsMachinesToolRunner(t *testing.T) {
|
||||
all := composed(t, gateRecords(t))
|
||||
runner, ok := all["runner@novox"]
|
||||
if !ok {
|
||||
t.Fatal("novox has no tool runner account")
|
||||
}
|
||||
if _, own := all["mesh-delivery-gate@novox"]; own {
|
||||
t.Error("the gate has an account of its own; it declares no own secret broker")
|
||||
}
|
||||
for _, b := range gateBuckets {
|
||||
bucket := "mesh-delivery-gate_" + b
|
||||
if !canWrite(runner, bucket, "some.key") || !canRead(runner, bucket, "some.key") {
|
||||
t.Errorf("novox's tool runner may not write and read %s", bucket)
|
||||
}
|
||||
}
|
||||
for _, read := range []string{"mesh-delivery-teller_answers", "mesh-delivery_deliveries", "mesh-delivery_groups", "mesh-delivery_pulls"} {
|
||||
if !canRead(runner, read, "k") {
|
||||
t.Errorf("novox's tool runner may not read %s, which the gate declares reading", read)
|
||||
}
|
||||
// (The runner may write the teller's answers: the teller declares no runs-as, so the runner carries it too. See the report of issue 1326.)
|
||||
if canWrite(runner, read, "k") && read != "mesh-delivery-teller_answers" {
|
||||
t.Errorf("novox's tool runner may write %s, which the gate only reads", read)
|
||||
}
|
||||
}
|
||||
if !allowed(runner.Publish, "$JS.API.CONSUMER.MSG.NEXT.FORGE.novox_mesh-delivery-gate") {
|
||||
t.Errorf("the gate's FORGE consumer is not pulled by novox's tool runner")
|
||||
}
|
||||
if !allowed(runner.Subscribe, "mesh.seat.mesh-delivery.tool.deliveries") {
|
||||
t.Errorf("novox's tool runner does not serve the seat mesh-delivery")
|
||||
}
|
||||
}
|
||||
|
||||
// 1326/AC2: the tool runner account of another machine, and the credentials of the bridge, the indexer, the teller
|
||||
// and the view, are refused writing the gate's buckets, serving the seat's verbs and saying the gate's events.
|
||||
func TestNoOtherAccountWritesTheGatesBuckets(t *testing.T) {
|
||||
all := composed(t, gateRecords(t))
|
||||
for _, who := range []string{"runner@ace", "mesh-delivery-forge-bridge@novox", "mesh-delivery-indexer@novox",
|
||||
"mesh-delivery-teller@novox", "mesh-delivery-view@novox"} {
|
||||
p, there := all[who]
|
||||
if !there {
|
||||
t.Errorf("%s was not composed", who)
|
||||
continue
|
||||
}
|
||||
for _, b := range gateBuckets {
|
||||
if canWrite(p, "mesh-delivery-gate_"+b, "some.key") {
|
||||
t.Errorf("%s may write mesh-delivery-gate_%s", who, b)
|
||||
}
|
||||
}
|
||||
if allowed(p.Subscribe, "mesh.seat.mesh-delivery.tool.deliveries") {
|
||||
t.Errorf("%s serves the seat mesh-delivery's verbs", who)
|
||||
}
|
||||
if allowed(p.Publish, "mesh.mod.mesh-delivery-gate.event.merged") {
|
||||
t.Errorf("%s may say an event as the gate", who)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 1326/AC3: each part reads the gate's buckets its manifest declares, and only those: the bridge merges and owed, the
|
||||
// indexer source, the teller and the view asks. The tool runner of another machine reads none.
|
||||
func TestEachPartReadsOnlyTheGateBucketsItDeclares(t *testing.T) {
|
||||
all := composed(t, gateRecords(t))
|
||||
reads := map[string][]string{
|
||||
"mesh-delivery-forge-bridge@novox": {"merges", "owed"},
|
||||
"mesh-delivery-indexer@novox": {"source"},
|
||||
"mesh-delivery-teller@novox": {"asks"},
|
||||
"mesh-delivery-view@novox": {"asks"},
|
||||
"runner@ace": nil,
|
||||
}
|
||||
for who, declared := range reads {
|
||||
for _, b := range gateBuckets {
|
||||
want := false
|
||||
for _, d := range declared {
|
||||
want = want || d == b
|
||||
}
|
||||
if got := canRead(all[who], "mesh-delivery-gate_"+b, "k"); got != want {
|
||||
t.Errorf("%s reads mesh-delivery-gate_%s: %v, declared %v", who, b, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user