package main import ( "encoding/json" "errors" "strings" "testing" "github.com/novox/mesh-controller/internal/catalogue" "github.com/novox/mesh-controller/internal/inventory" "github.com/novox/mesh-controller/internal/link" "github.com/novox/mesh-controller/internal/secrets" ) // The `issued` verb runs `secret issued ` and nothing else, and that line is the one `secret` line // beside rotate and the desk's that a verb may run (novox/hq ADR 0305 §3, §10, issue 526). func TestTheIssuedVerbRunsTheOneLineThatCarriesNoValue(t *testing.T) { argv, err := argvFor("issued", map[string]any{"provision": "plex-api"}) if err != nil { t.Fatal(err) } if strings.Join(argv, " ") != "secret issued plex-api" { t.Fatalf("the verb composed %q", argv) } if _, err := argvFor("issued", map[string]any{}); err == nil { t.Fatal("the verb ran with no provision") } if _, err := argvFor("issued", map[string]any{"provision": "plex-api", "secret": "a value"}); err == nil { t.Fatal("the verb took a value beside the provision") } for _, line := range [][]string{ {"secret", "issued", "plex-api", "--from", "/tmp/x"}, {"secret", "issued", "--from"}, {"secret", "issued"}, {"secret", "accept", "ace", "kometa", "plex-api", "--provider", "ace"}, {"secret", "recover", "ace", "kometa", "plex-api"}, } { if err := terminalOnly(line); err == nil { t.Errorf("%q ran through a verb", line) } } if err := terminalOnly([]string{"secret", "issued", "plex-api"}); err != nil { t.Fatalf("the issued line was refused through its verb: %v", err) } found := false for _, v := range catalogue.ControllerVerbs { if v.Name == "issued" { found = true } } if !found { t.Fatal("the controller's seat does not declare the issued verb") } } // A provider's tool answering the value sealed to the call's key: what a provider does (ADR 0305 §2). func issuedSealed(t *testing.T, value string) func(issuedProvider, map[string]any) (link.Answer, error) { return func(_ issuedProvider, args map[string]any) (link.Answer, error) { key, _ := args["seal_to"].(string) if key == "" { t.Fatal("the provider was asked without a key to seal to") } if _, carried := args["value"]; carried { t.Fatal("the ask carried a value") } sealed, err := secrets.Seal(key, []byte(value)) if err != nil { t.Fatal(err) } raw, _ := json.Marshal(map[string]string{"sealed": sealed}) return link.Answer{Result: raw}, nil } } type issuedRecorder struct { kept []string acts []link.HandAct sent [][]string again bool // the value is held by every holder already } func (r *issuedRecorder) taker(t *testing.T, ask func(issuedProvider, map[string]any) (link.Answer, error)) issuedTaker { return issuedTaker{ providers: func(provision string) ([]issuedProvider, bool, error) { return []issuedProvider{{Node: "ace", Module: "plex", Tool: "plex_issued_credential"}}, true, nil }, ask: ask, keep: func(provision, provider, value string) (inventory.IssuedKept, error) { r.kept = append(r.kept, value) holders := []inventory.Holder{{Provision: provision, Consumer: "ace", ConsumerModule: "kometa", Provider: provider}, {Provision: provision, Consumer: "ace", ConsumerModule: "tautulli", Provider: provider}} if r.again { return inventory.IssuedKept{Holders: holders}, nil } return inventory.IssuedKept{Changed: true, Holders: holders, Sealed: holders, Machines: []string{"ace"}}, nil }, record: func(act link.HandAct) error { r.acts = append(r.acts, act); return nil }, send: func(machines []string) error { r.sent = append(r.sent, machines); return nil }, } } // The value the provider's tool sealed to the call is kept, recorded and sent, and the answer never says it. func TestAnIssuedValueIsTakenSealedFromTheProviderAndSent(t *testing.T) { var r issuedRecorder words, err := r.taker(t, issuedSealed(t, "the-servers-own-token")).take("plex-api") if err != nil { t.Fatal(err) } if len(r.kept) != 1 || r.kept[0] != "the-servers-own-token" { t.Fatalf("the provider's value was not kept: %d kept", len(r.kept)) } if len(r.sent) != 1 || strings.Join(r.sent[0], ",") != "ace" { t.Fatalf("the holders' machines were not sent: %v", r.sent) } if len(r.acts) != 1 || r.acts[0].Verb != handActIssued { t.Fatalf("the change was not recorded in the hand-act log: %+v", r.acts) } for _, said := range []string{words, r.acts[0].Why, strings.Join(r.acts[0].Args, " ")} { if strings.Contains(said, "the-servers-own-token") || strings.Contains(said, inventory.IssuedStamp("the-servers-own-token")) { t.Fatalf("the value or its fingerprint was said: %q", said) } } if !strings.Contains(words, "kometa on ace") || !strings.Contains(words, "tautulli on ace") { t.Fatalf("the answer does not name the holders: %q", words) } } // The same value again writes nothing, records nothing and sends nobody (ADR 0305 §5). func TestAnIssuedValueEveryHolderHoldsSendsNobody(t *testing.T) { r := issuedRecorder{again: true} words, err := r.taker(t, issuedSealed(t, "the-servers-own-token")).take("plex-api") if err != nil { t.Fatal(err) } if len(r.sent) != 0 || len(r.acts) != 0 { t.Fatalf("an unchanged value was sent or recorded: %v %v", r.sent, r.acts) } if !strings.Contains(words, "nothing was sent") { t.Fatalf("the answer does not say nothing was sent: %q", words) } } // A value not sealed to the call's key is not taken, nor is a refusal, nor an empty answer (ADR 0305 §2). func TestOnlyAValueSealedToTheCallIsTaken(t *testing.T) { other, _, _ := secrets.Keypair() elsewhere, _ := secrets.Seal(other, []byte("somebody's value")) raw, _ := json.Marshal(map[string]string{"sealed": elsewhere}) plain, _ := json.Marshal(map[string]string{"value": "the-servers-own-token"}) for name, ask := range map[string]func(issuedProvider, map[string]any) (link.Answer, error){ "sealed to another key": func(issuedProvider, map[string]any) (link.Answer, error) { return link.Answer{Result: raw}, nil }, "answered in the clear": func(issuedProvider, map[string]any) (link.Answer, error) { return link.Answer{Result: plain}, nil }, "refused": func(issuedProvider, map[string]any) (link.Answer, error) { return link.Answer{Error: "the controller alone"}, nil }, "not reached": func(issuedProvider, map[string]any) (link.Answer, error) { return link.Answer{}, errors.New("503 no responders") }, "sealed empty": issuedSealed(t, " "), } { var r issuedRecorder if _, err := r.taker(t, ask).take("plex-api"); err == nil { t.Errorf("%s: taken", name) } if len(r.kept) != 0 || len(r.sent) != 0 { t.Errorf("%s: something was kept or sent", name) } } } // A provision no offer says its software issues is refused before anything is asked. func TestAProvisionNoOfferSaysIsIssuedIsRefused(t *testing.T) { asked := false taker := issuedTaker{ providers: func(string) ([]issuedProvider, bool, error) { return nil, false, nil }, ask: func(issuedProvider, map[string]any) (link.Answer, error) { asked = true return link.Answer{}, nil }, } if _, err := taker.take("postgres-database"); err == nil || !strings.Contains(err.Error(), "ADR 0305") { t.Fatalf("expected a refusal naming the record, got %v", err) } if asked { t.Fatal("a provider was asked for a provision no offer says its software issues") } } // Which assignments are asked: the modules on the providing machine whose offer says so, with their tool. func TestTheProvidersAskedAreTheOffersThatSaySo(t *testing.T) { shelf := map[string]catalogue.Manifest{ "plex": {Module: "plex", Provides: []catalogue.Offer{{Name: "plex-api", Credential: &catalogue.OfferCredential{ IssuedBy: catalogue.IssuedBySoftware, Tool: "plex_issued_credential"}}}}, "jellyfin": {Module: "jellyfin", Provides: []catalogue.Offer{{Name: "plex-api"}}}, } got := issuedOn(shelf, "ace", []string{"jellyfin", "plex", "kometa"}, "plex-api") if len(got) != 1 || got[0] != (issuedProvider{Node: "ace", Module: "plex", Tool: "plex_issued_credential"}) { t.Fatalf("the providers asked: %+v", got) } if err := issuedRefusesRotation(shelf, "plex-api"); err == nil || !strings.Contains(err.Error(), "secret issued plex-api") { t.Fatalf("rotate of an issued provision was not refused with the way to take it: %v", err) } if err := issuedRefusesRotation(shelf, "postgres-database"); err != nil { t.Fatalf("rotate of an ordinary provision was refused: %v", err) } } // A change of an issued value is the mesh working as decided, never a repair (ADR 0305 §8). func TestAnIssuedValueIsADecisionInTheHandActLog(t *testing.T) { for _, v := range handActVerbs { if v.Verb == handActIssued { if v.Decision == "" { t.Fatal("secret issued counts as a repair") } return } } t.Fatal("secret issued is not in the hand-act table") }