package broker import ( "strings" "testing" ) // A module that says it calls a tool may publish exactly that subject (novox/hq ADR 0152): the same // grant a person gets, derived the same way, so one list answers "what may this ask" for everybody. func TestAModuleMayAskOnlyTheToolsItInvokes(t *testing.T) { perms, err := PermissionsFor(Principal{Kind: KindModule, Node: "desk", Module: "mesh-console", Invokes: []string{"shop.price"}, PasswordHash: "x"}) if err != nil { t.Fatal(err) } has(t, perms.Publish, "mesh.mod.shop.tool.price") hasNot(t, perms.Publish, "mesh.mod.shop.tool.refund") hasNot(t, perms.Publish, "mesh.mod.*.tool.>") } // The console's grant: every tool, as one subject, and it reads as one. func TestAModuleInvokingEverythingMayAskAnyTool(t *testing.T) { perms, err := PermissionsFor(Principal{Kind: KindModule, Node: "desk", Module: "mesh-console", Invokes: []string{"*"}, PasswordHash: "x"}) if err != nil { t.Fatal(err) } has(t, perms.Publish, "mesh.mod.*.tool.>") } // **A grant to call widens nothing else.** A module that invokes may not publish an event it did not // declare, may not answer as another module, and subscribes nothing it did not consume — the // difference between the console and a person is that the console is on a machine, not that it may // do more. func TestInvokingGrantsNothingButTheCall(t *testing.T) { perms, err := PermissionsFor(Principal{Kind: KindModule, Node: "desk", Module: "mesh-console", Invokes: []string{"*"}, PasswordHash: "x"}) if err != nil { t.Fatal(err) } for _, p := range perms.Publish { if strings.Contains(p, ".event.") { t.Errorf("a module that only invokes may publish %q, an event it never declared", p) } if strings.HasPrefix(p, "mesh.seat.") { t.Errorf("a module that only invokes may publish %q, a seat it neither holds nor uses", p) } } for _, s := range perms.Subscribe { if strings.Contains(s, ".tool.") && !strings.HasPrefix(s, "mesh.mod.mesh-console.") { t.Errorf("a module that invokes may subscribe %q, another module's tools", s) } } } // A module that declares no invokes calls nothing, which is every module but the console. func TestAModuleThatInvokesNothingCallsNothing(t *testing.T) { perms, err := PermissionsFor(Principal{Kind: KindModule, Node: "one", Module: "shop", Emits: []string{"order.placed"}, PasswordHash: "x"}) if err != nil { t.Fatal(err) } for _, p := range perms.Publish { if strings.Contains(p, ".tool.") { t.Errorf("a module with no invokes may publish %q", p) } } } // The malformed entry is refused for a module as it is for a person, and in the same words. func TestAModulesToolGrantThatNamesNoToolIsRefused(t *testing.T) { if _, err := PermissionsFor(Principal{Kind: KindModule, Node: "one", Module: "shop", Invokes: []string{"telegram"}, PasswordHash: "x"}); err == nil { t.Fatal("a grant naming a module but no tool was accepted") } } // What a declaration says reaches the composed user, so a manifest's `invokes` is the grant. func TestADeclaredInvokeReachesTheComposedUser(t *testing.T) { users, err := Users(Records{ Nodes: []string{"desk"}, Assigned: map[string][]Declared{"desk": {{Module: "mesh-console", Invokes: []string{"*"}}}}, }) if err != nil { t.Fatal(err) } perms, err := PermissionsFor(users[len(users)-1]) if err != nil { t.Fatal(err) } has(t, perms.Publish, "mesh.mod.*.tool.>") }