package inventory import ( "context" "strings" "testing" "github.com/novox/mesh-controller/internal/broker" ) // Somebody who may call the mesh's tools, and what the bus makes of them. // A person's authority is a list of tools, and it becomes exactly that on the bus — nothing on // control, nothing on nodes, nothing they could publish as a module. func TestAPersonMayCallToolsAndNothingElse(t *testing.T) { inv := ForTest(t) ctx := context.Background() if err := inv.RecordPerson(ctx, Person{Name: "ada", Invokes: []string{"mesh-catalog.catalog_tools"}}); err != nil { t.Fatal(err) } records, err := inv.BusRecords(ctx) if err != nil { t.Fatal(err) } if got := records.People["ada"]; len(got) != 1 || got[0] != "mesh-catalog.catalog_tools" { t.Fatalf("ada may call %v", got) } users, err := broker.Users(records) if err != nil { t.Fatal(err) } var found bool for _, u := range users { if u.Username() != "person.ada" { continue } found = true perms, err := broker.PermissionsFor(u) if err != nil { t.Fatal(err) } if len(perms.Publish) != 1 || perms.Publish[0] != "mesh.mod.mesh-catalog.tool.catalog_tools" { t.Errorf("ada may publish %v, which should be the one tool and nothing else", perms.Publish) } for _, s := range perms.Publish { if strings.HasPrefix(s, "mesh.control") || strings.HasPrefix(s, "mesh.node") || strings.Contains(s, ".event.") { t.Errorf("a person may publish %s — an event would let them claim a module said "+ "something, and control is not theirs", s) } } if perms.AllowResponses { t.Error("a person may answer a request, which is impersonating a module on a bus where " + "anyone may serve a tool") } } if !found { t.Fatal("no bus user was derived for a recorded person") } } // Stating what somebody may call replaces what was there. A list that could only grow is a permission // nobody can take back. func TestChangingWhatAPersonMayCallRemovesWhatIsNotNamed(t *testing.T) { inv := ForTest(t) ctx := context.Background() if err := inv.RecordPerson(ctx, Person{Name: "ada", Invokes: []string{"a.one", "b.two"}}); err != nil { t.Fatal(err) } if err := inv.RecordPerson(ctx, Person{Name: "ada", Invokes: []string{"a.one"}}); err != nil { t.Fatal(err) } people, err := inv.People(ctx) if err != nil { t.Fatal(err) } if len(people) != 1 || len(people[0].Invokes) != 1 || people[0].Invokes[0] != "a.one" { t.Fatalf("ada may call %v; the removed tool is still there", people) } } // Somebody who may call nothing is refused: there is no reason for them to reach the mesh, and an // empty list is more likely a mistake than an intention. func TestSomebodyWhoMayCallNothingIsRefused(t *testing.T) { inv := ForTest(t) if err := inv.RecordPerson(context.Background(), Person{Name: "ada"}); err == nil { t.Fatal("somebody who may call nothing was recorded") } } // Forgetting somebody takes their credential with them. **Both, or it is not a revocation**: a // person's row gone and their bus user left behind is a credential that still works and that nothing // derives. func TestForgettingAPersonTakesTheirCredential(t *testing.T) { inv := ForTest(t) ctx := context.Background() if err := inv.RecordPerson(ctx, Person{Name: "ada", Invokes: []string{"a.one"}}); err != nil { t.Fatal(err) } if _, err := inv.MintBusPassword(ctx, BusUser{Username: "person.ada", Kind: BusPerson}); err != nil { t.Fatal(err) } if err := inv.ForgetPerson(ctx, "ada"); err != nil { t.Fatal(err) } if _, known, err := inv.BusUserHash(ctx, "person.ada"); err != nil || known { t.Fatalf("a forgotten person's credential still works: %v %v", known, err) } records, err := inv.BusRecords(ctx) if err != nil { t.Fatal(err) } if _, still := records.People["ada"]; still { t.Fatal("a forgotten person is still composed into the bus") } }