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) } // 1329/AC3: the runner no longer carries the teller, so it may not write the teller's answers either. if canWrite(runner, read, "k") { 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) } } } }