mesh/delivery closed: a newer head of the same pull request
mesh/check checking 1faf31706919 on 5b75233406fe | novox/mesh-catalog#505 in group feat/1126-nodes-compose
122 lines
5.9 KiB
Go
122 lines
5.9 KiB
Go
package main
|
|
|
|
import (
|
|
"errors"
|
|
"strings"
|
|
"testing"
|
|
|
|
"git.novox.be/novox/mesh-sdk/go/seal"
|
|
)
|
|
|
|
// Issue 1126 (ADR 0547 §2, ADR 0550 §5): `own` answers a machine's node-engine the own secrets of its modules, sealed to
|
|
// the key that machine's engine stated, about that machine alone; another caller, another machine's secrets and a
|
|
// machine with no stated key get nothing.
|
|
func TestOwnAnswersAMachineItsModulesSecretsSealedToItsKey(t *testing.T) {
|
|
plans := assignments{"anchor.web": `{"node":"anchor","module":"web"}`, "laptop.web": `{"node":"laptop","module":"web"}`}
|
|
v := newVault(t, nil)
|
|
v.Askers = Declared{Read: plans.read, Store: v.Store}
|
|
anchorPub, anchorPriv := aSealingKey(t)
|
|
laptopPub, _ := aSealingKey(t)
|
|
for _, m := range []struct {
|
|
caller string
|
|
of MoveOf
|
|
value string
|
|
}{
|
|
{"node.anchor", MoveOf{Node: "anchor", Module: "web", Name: "password", Origin: "made", Generation: "g-1", SealingKey: anchorPub}, "anchor-web-pw"},
|
|
{"node.anchor", MoveOf{Node: "anchor", Module: "web", Name: "api-key", Origin: "accepted", Generation: "g-1", SealingKey: anchorPub}, "anchor-web-key"},
|
|
{"node.laptop", MoveOf{Node: "laptop", Module: "web", Name: "password", Origin: "made", Generation: "g-1", SealingKey: laptopPub}, "laptop-web-pw"},
|
|
} {
|
|
if _, err := moveIn(t, v, m.caller, m.of, m.value); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
got, err := v.OwnOfNode("node.anchor")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if got.Node != "anchor" || got.Key != anchorPub || len(got.Own) != 2 {
|
|
t.Fatalf("anchor was answered %+v", got)
|
|
}
|
|
want := map[string][2]string{"api-key": {"accepted", "anchor-web-key"}, "password": {"made", "anchor-web-pw"}}
|
|
for _, s := range got.Own {
|
|
w, ok := want[s.Name]
|
|
if !ok || s.Module != "web" || s.Origin != w[0] || s.Generation < 1 {
|
|
t.Errorf("anchor's %s: %+v", s.Name, s)
|
|
continue
|
|
}
|
|
value, err := seal.Open(s.Sealed, anchorPriv)
|
|
if err != nil || string(value) != w[1] {
|
|
t.Errorf("anchor's %s does not open to its value with anchor's key: %q, %v", s.Name, value, err)
|
|
}
|
|
}
|
|
for caller, says := range map[string]string{
|
|
"controller": "is not a node-engine",
|
|
"anchor.web": "is not a node-engine",
|
|
"": "an unproven caller",
|
|
"node.unknown": "no secret named own/unknown yet",
|
|
} {
|
|
if _, err := v.OwnOfNode(caller); err == nil || !strings.Contains(err.Error(), says) {
|
|
t.Errorf("%q: %v, want a refusal saying %q", caller, err, says)
|
|
}
|
|
}
|
|
if laptop, err := v.OwnOfNode("node.laptop"); err != nil || len(laptop.Own) != 1 || laptop.Own[0].Name != "password" {
|
|
t.Errorf("laptop was answered %+v, %v; want its one secret alone", laptop, err)
|
|
}
|
|
|
|
// A pair credential: laptop's web consumes anchor's postgres. Each end is answered the one value sealed to its own key;
|
|
// a credential the vault holds none of is named missing; a machine at neither end is refused.
|
|
if _, err := moveIn(t, v, "node.laptop", MoveOf{Provider: "anchor", Provision: "postgres", Consumer: "laptop",
|
|
ConsumerModule: "web", Origin: "made", Generation: "g-1", SealingKey: laptopPub}, "pg-pair"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
pair := CredentialAsked{Provision: "postgres", Consumer: "laptop", ConsumerModule: "web", Provider: "anchor"}
|
|
asConsumer, asProvider := pair, pair
|
|
asConsumer.Role, asProvider.Role = "consumer", "provider"
|
|
none := CredentialAsked{Role: "consumer", Provision: "redis", Consumer: "laptop", ConsumerModule: "web", Provider: "anchor"}
|
|
laptop, err := v.OwnOfNode("node.laptop", asConsumer, none)
|
|
if err != nil || len(laptop.Credentials) != 1 || len(laptop.Missing) != 1 || laptop.Missing[0] != none {
|
|
t.Fatalf("laptop as consumer: %+v, %v", laptop, err)
|
|
}
|
|
if !strings.HasPrefix(laptop.Credentials[0].Name, "pair/anchor/postgres/laptop/web") {
|
|
t.Errorf("laptop's credential is kept as %s", laptop.Credentials[0].Name)
|
|
}
|
|
anchor, err := v.OwnOfNode("node.anchor", asProvider)
|
|
if err != nil || len(anchor.Credentials) != 1 {
|
|
t.Fatalf("anchor as provider: %+v, %v", anchor, err)
|
|
}
|
|
if value, err := seal.Open(anchor.Credentials[0].Sealed, anchorPriv); err != nil || string(value) != "pg-pair" {
|
|
t.Errorf("anchor's copy does not open to the value with its key: %q, %v", value, err)
|
|
}
|
|
for who, c := range map[string]CredentialAsked{"node.anchor": asConsumer, "node.laptop": asProvider} {
|
|
if _, err := v.OwnOfNode(who, c); err == nil || !strings.Contains(err.Error(), "is not that machine") {
|
|
t.Errorf("%s asking %+v: %v", who, c, err)
|
|
}
|
|
}
|
|
|
|
// Review of L2 (C1, C2): `own` fails closed. A provision's one shared value is handed only to a consumer the record
|
|
// of its holders names, never to a machine that names its provider; assignments that cannot be read, or a vault
|
|
// that reads none, refuse rather than hand.
|
|
if _, err := v.keep("provision/anchor/redis", "shared-redis", "test", "", OriginMade); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := v.Store.WriteShared(Shared{Name: "provision/anchor/redis", Provider: "anchor", Provision: "redis",
|
|
Consumers: []string{"anchor/web"}}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if got, err := v.OwnOfNode("node.laptop", none); err != nil || len(got.Credentials) != 0 || len(got.Missing) != 1 {
|
|
t.Errorf("laptop, whose web the shared value's record does not name, got %+v, %v", got, err)
|
|
}
|
|
anchorRedis := CredentialAsked{Role: "consumer", Provision: "redis", Consumer: "anchor", ConsumerModule: "web", Provider: "anchor"}
|
|
if got, err := v.OwnOfNode("node.anchor", anchorRedis); err != nil || len(got.Credentials) != 1 {
|
|
t.Errorf("anchor's web, a holder the record names, got %+v, %v", got, err)
|
|
}
|
|
v.Askers = Declared{Read: func(string) ([]byte, error) { return nil, errors.New("the bucket did not answer") }, Store: v.Store}
|
|
if _, err := v.OwnOfNode("node.anchor"); err == nil || !strings.Contains(err.Error(), "could not be read") {
|
|
t.Errorf("own with the assignments unread: %v", err)
|
|
}
|
|
v.Askers = nil
|
|
if _, err := v.OwnOfNode("node.anchor", anchorRedis); err == nil || !strings.Contains(err.Error(), "cannot read the assignments") {
|
|
t.Errorf("own with no reader of the assignments: %v", err)
|
|
}
|
|
}
|