//go:build integration package main import ( "context" "strings" "testing" "github.com/novox/mesh-controller/internal/broker" ) // review round 3, fault B (1181): the test asserted a literal url, while issueOnTheNewBus works out the // machine's reachable address from the catalogue's network records (brokerReachableAt), not from this // process's environment — the real try found "nats://anchor.internal:5671", not the literal guessed here. // Fixed by calling issueProcess directly, with the known broker and reachable address given to it, exactly // as issued_to_vault_test.go's issue/issueWith and issueProvisionerClient are called directly; the url // asserted below is one of those two inputs, never a separate guess. // // review round 1, findings 3 and 4 (1181/AC1, AC2): module issue mints and delivers a process's own // account against the store — no bus to dial, busAddress "" — for mesh-mail's real manifest shape (review // round 1 finding 1: no own secret `broker` at all, its tools served by the tool runner), and issuing it // again is the path every bus account takes (`rollout mint`'s KindProcess case, and a re-run of `module // issue`): it rotates the password, and the old hash no longer matches. func TestModuleIssueMintsAndDeliversAProcesssAccount(t *testing.T) { open := aMesh(t) ctx := t.Context() inv := open.inventory theVaultIsInstalled(t, open) m := mailSenderWithProcess() m.Moved = true // so handIssuedToVault's accountIsCovered is true, and the hand-off is exercised whole register(t, open, m) vault := newStandInVault(t) vaultAskerForIssue = func(context.Context) vaultAsker { return vault.ask } t.Cleanup(func() { vaultAskerForIssue = func(context.Context) vaultAsker { return newStandInVault(nil).ask } }) pr, found := processPrincipalFor(m, "send") if !found { t.Fatal("mesh-mail declares no process send") } known := broker.Broker{Fingerprint: "ab"} const reachable = "10.0.0.1:4222" issue := func() { t.Helper() if err := issueProcess(ctx, inv, m, pr, "laptop", "", known, reachable); err != nil { t.Fatal(err) } } issue() user := broker.Principal{Kind: broker.KindProcess, Node: "laptop", Module: "mesh-mail", Process: "send"}.Username() if user != "laptop.mesh-mail.send" { t.Fatalf("the process's username: %q", user) } hash1, held, err := inv.BusUserHash(ctx, user) if err != nil || !held || hash1 == "" { t.Fatalf("the process's account was not minted: held %v, %v", held, err) } // 1181/AC1: accepted with the provisioner client's JSON, and handed to the vault whole — the url and // the fingerprint being exactly what issueProcess was given above, not a guess at what it computes. taken := vault.taken["accept"] for _, want := range []string{`"url":"nats://` + reachable + `"`, `"fingerprint":"ab"`, `"node":"laptop"`, `"module":"mesh-mail"`, `"user":"laptop.mesh-mail.send"`, `"password"`} { if !strings.Contains(taken, want) { t.Errorf("the vault was handed %q, without %s", taken, want) } } if !strings.Contains(vault.said(), `"name":"broker-send"`) { t.Errorf("the vault was not told the secret's name broker-send: %s", vault.said()) } // The file's own owner (mesh-mail-send, review round 1 finding 7's area) is 1170's mechanism, proven // generically there and for this fixture in TestAProcessesOwnSecretIsOwnedByItsOwnAccount. // 1181/AC2: issued again — the path `rollout mint`'s KindProcess case and a re-run of `module issue` // both take — rotates the password; the old hash no longer matches what is recorded. issue() hash2, held, err := inv.BusUserHash(ctx, user) if err != nil || !held { t.Fatalf("the process's account is gone after a re-issue: held %v, %v", held, err) } if hash1 == hash2 { t.Fatal("issuing the process's account again did not rotate its password") } }