Files
mesh-catalog/modules/mesh-vault/cmd/mesh-vault/rotate_one_test.go
T

120 lines
5.4 KiB
Go

package main
// The controller's `rotate <provision> [--consumer <machine>] [--module <module>]` reaches the vault (issue 767): a
// provider's one shared value is rotated for every holder together, and it is never rotated for one consumer alone;
// a per-login provider (ADR 0158 §11) rotates the named consumer's own login and no other.
import (
"strings"
"testing"
)
// postgresLogins is a vault holding a login each for gitea on g14 and umami on ace, from postgres on novox.
func postgresLogins(t *testing.T) (testVault, string, string) {
t.Helper()
v := pairVault(t, assignments{
"novox.postgres": handedWith("novox", "postgres"),
"g14.gitea": handedWith("g14", "gitea", "postgres-database@novox/postgres"),
"ace.umami": handedWith("ace", "umami", "postgres-database@novox/postgres"),
})
gitea, umami := "pair/novox/postgres-database/g14/gitea", "pair/novox/postgres-database/ace/umami"
for name, value := range map[string]string{gitea: "login-of-gitea", umami: "login-of-umami"} {
if _, err := v.keepWith(name, value, "node.novox", "", OriginMade, keepOptions{neverReplace: true,
movedFrom: &MovedFrom{By: "node.novox"}}); err != nil {
t.Fatal(err)
}
}
return v, gitea, umami
}
func TestARotationNamingOneConsumerRemakesThatConsumersLoginAlone(t *testing.T) {
for _, one := range []Narrow{{Consumer: "g14"}, {ConsumerModule: "gitea"}, {Consumer: "g14", ConsumerModule: "gitea"}} {
v, gitea, umami := postgresLogins(t)
before := changedSaid(t, v, umami, "")
changed, err := v.RotateFor(ControllerCaller, "provision/novox/postgres-database", one, "person.jochen",
"suspected on g14")
if err != nil || len(changed.Each) != 1 || changed.Each[0].Name != gitea {
t.Fatalf("%+v: the rotation %+v (%v)", one, changed, err)
}
if got, _, err := asks(v, "provisioner.g14", gitea); err != nil || got == "login-of-gitea" {
t.Fatalf("%+v: gitea's login was not remade: %q (%v)", one, got, err)
}
if got, gen, err := asks(v, "provisioner.ace", umami); err != nil || got != "login-of-umami" || gen != 1 {
t.Fatalf("%+v: umami's login was touched: %q generation %d (%v)", one, got, gen, err)
}
if changedSaid(t, v, umami, "") != before {
t.Fatalf("%+v: secret-changed was said for umami's login", one)
}
}
}
func TestARotationNamingAConsumerThatHoldsNoLoginChangesNothing(t *testing.T) {
v, gitea, umami := postgresLogins(t)
_, err := v.RotateFor(ControllerCaller, "provision/novox/postgres-database", Narrow{Consumer: "g14",
ConsumerModule: "umami"}, "person.jochen", "suspected")
if err == nil || !strings.Contains(err.Error(), "umami on g14") {
t.Fatalf("a consumer holding no login was not refused by name: %v", err)
}
for name, want := range map[string]string{gitea: "login-of-gitea", umami: "login-of-umami"} {
if got, _, _ := asks(v, "provisioner.novox", name); got != want {
t.Fatalf("%s changed on a refused rotation", name)
}
}
}
// A shared value is one for every holder (ADR 0158): rotated, every holder gets the new one after secret-changed; it
// is never rotated for one consumer alone, which would change the value for all of them.
func TestASharedValueIsRotatedForEveryHolderAndNeverForOneConsumer(t *testing.T) {
v := pairVault(t, assignments{
"anchor.stand-in": handedWith("anchor", "stand-in"),
"g14.app": handedWith("g14", "app", "stand-in-api@anchor/stand-in"),
"ace.other": handedWith("ace", "other", "stand-in-api@anchor/stand-in"),
})
movedRow(t, v, "g14", "app", "the-value-they-share", "20260901T000000-1", OriginMade)
movedRow(t, v, "ace", "other", "the-value-they-share", "20260901T000000-1", OriginMade)
if o := pass(t, v, standIn); o.Outcome != PairAdopted {
t.Fatalf("stamped rows: %+v", o)
}
if _, err := v.RotateFor(ControllerCaller, standIn, Narrow{Consumer: "g14"}, "person.jochen", "suspected"); err == nil ||
!strings.Contains(err.Error(), "every holder") {
t.Fatalf("a shared value was rotated for one consumer: %v", err)
}
if got, gen, _ := asks(v, "provisioner.g14", standIn); got != "the-value-they-share" || gen != 1 {
t.Fatal("a refused rotation changed the shared value")
}
changed, err := v.RotateFor(ControllerCaller, standIn, Narrow{}, "person.jochen", "yearly")
if err != nil || changed.Generation != 2 {
t.Fatalf("the rotation: %+v (%v)", changed, err)
}
var seen string
for _, c := range []string{"provisioner.anchor", "provisioner.g14", "provisioner.ace"} {
got, gen, err := asks(v, c, standIn)
if err != nil || got == "the-value-they-share" || gen != 2 || (seen != "" && got != seen) {
t.Fatalf("%s got %q generation %d (%v): not the one new value", c, got, gen, err)
}
seen = got
}
if changedSaid(t, v, standIn, seen) == 0 {
t.Fatal("secret-changed was not said for the shared value")
}
}
func TestARotationOfAModulesOwnSecretNamesNoConsumer(t *testing.T) {
v := newVault(t, nil)
if _, err := v.RotateFor(ControllerCaller, "own/g14/app/key", Narrow{Consumer: "g14"}, "person.jochen", "x"); err == nil {
t.Fatal("a module's own secret was rotated with a consumer named")
}
}
func TestTheRotateToolTakesTheConsumerAndItsModule(t *testing.T) {
n, err := narrowNamed(map[string]any{"consumer": "g14", "consumer_module": "gitea"})
if err != nil || n != (Narrow{Consumer: "g14", ConsumerModule: "gitea"}) {
t.Fatalf("%+v (%v)", n, err)
}
if _, err := narrowNamed(map[string]any{"consumer": "g14/x"}); err == nil {
t.Fatal("a consumer that is not a name was taken")
}
}