Name the vault's credential under vault-name so the state guard accepts the bound entry (issue 945)

This commit is contained in:
fix-sdk-pas
2026-10-16 19:26:58 +02:00
parent 219bedbdd2
commit 40f3658557
2 changed files with 72 additions and 11 deletions
+9 -7
View File
@@ -105,8 +105,10 @@ type Entry struct {
User string `json:"user,omitempty"`
// Values are the served values, composed for this consumer: what `${bound:<provision>:<key>}` reads.
Values map[string]any `json:"values,omitempty"`
// Credential is the name of the credential in the vault, which the consumer's client asks the vault for.
Credential string `json:"credential,omitempty"`
// VaultName is the name under which the vault keeps this consumer's credential, which the consumer's client asks
// the vault for: a name, never the credential. Its key is not "credential" because the runtime's state guard
// refuses any key ending in that word, whatever the value (issue 945).
VaultName string `json:"vault-name,omitempty"`
// Refused, when set, is why the consumer is not served, naming both ends.
Refused string `json:"refused,omitempty"`
// Contributes is what the consumer's module tells its provider about this provision (a database's name, its
@@ -419,15 +421,15 @@ func (p *Provider) answer(ctx context.Context, e Entry, have []byte) Outcome {
switch offer.Credential {
case Shared:
e.User = offer.User
e.Credential = SharedName(e.Provider, e.Provision)
e.VaultName = SharedName(e.Provider, e.Provision)
case PerConsumer:
e.User = e.As
e.Credential = PairName(e.Provider, e.Provision, e.Consumer, e.Module, e.Local)
e.VaultName = PairName(e.Provider, e.Provision, e.Consumer, e.Module, e.Local)
}
if e.Credential != "" {
credential, err = p.opts.Vault.Value(ctx, e.Credential)
if e.VaultName != "" {
credential, err = p.opts.Vault.Value(ctx, e.VaultName)
if err != nil {
return Outcome{Key: key, Status: Waiting, Why: fmt.Sprintf("the vault's %s: %v", e.Credential, err)}
return Outcome{Key: key, Status: Waiting, Why: fmt.Sprintf("the vault's %s: %v", e.VaultName, err)}
}
}
if p.opts.Make != nil {
+63 -4
View File
@@ -224,7 +224,7 @@ func TestEachConsumerGetsAnEntryOfItsOwnAndTheVaultsValue(t *testing.T) {
e := entryOf(t, bus, "ace.letta.postgres-database")
if e.Provider != "novox" || e.ProviderModule != "postgres" || e.Consumer != "ace" || e.Module != "letta" ||
e.As != "mesh_ace_letta" || e.User != "mesh_ace_letta" ||
e.Credential != "pair/novox/postgres-database/ace/letta" || e.Refused != "" {
e.VaultName != "pair/novox/postgres-database/ace/letta" || e.Refused != "" {
t.Fatalf("entry %+v", e)
}
if e.Values["database"] != "mesh_ace_letta" || e.Values["host"] != "postgres.novox.mesh" ||
@@ -263,7 +263,7 @@ func TestASharedCredentialIsTheVaultsOneValueAndTheProvidersOneUser(t *testing.T
if err := json.Unmarshal(bus.get(BucketOf("sonarr"), key), &e); err != nil {
t.Fatalf("%s: %v", key, err)
}
if e.Credential != "provision/novox/sonarr-api" || e.User != "sonarr" || e.Values["url"] == nil {
if e.VaultName != "provision/novox/sonarr-api" || e.User != "sonarr" || e.Values["url"] == nil {
t.Fatalf("%s: %+v", key, e)
}
if m.made[key] != "one-value" {
@@ -291,7 +291,7 @@ func TestAMultiHolderConsumerIsRefusedADerivedValueNamingBothEnds(t *testing.T)
t.Fatalf("the refusal does not name %q: %s", says, e.Refused)
}
}
if e.Values != nil || e.Credential != "" {
if e.Values != nil || e.VaultName != "" {
t.Fatalf("a refused entry carries values: %+v", e)
}
if m.calls != 0 {
@@ -317,7 +317,7 @@ func TestALocalNameIsServedWhenNothingIsDerived(t *testing.T) {
if err := json.Unmarshal(bus.get(BucketOf("redis"), "ace.letta.redis-cache.queue"), &e); err != nil {
t.Fatal(err)
}
if e.Refused != "" || e.Credential != "pair/novox/redis-cache/ace/letta/queue" || e.Local != "queue" {
if e.Refused != "" || e.VaultName != "pair/novox/redis-cache/ace/letta/queue" || e.Local != "queue" {
t.Fatalf("entry %+v", e)
}
if m.made["ace.letta.redis-cache.queue"] != "q-secret" {
@@ -588,3 +588,62 @@ func TestAConsumersEntryCarriesTheNamesOfItsPlan(t *testing.T) {
t.Fatalf("an entry of a plan without names changed: %+v", plain)
}
}
// refusedByTheStateGuard mirrors the key rule of the runtime's state guard (mesh-tools node-tools internal/bus
// state.go): a JSON key whose lowercased name, with `-`, `_` and spaces taken out, ends in one of these is refused.
func refusedByTheStateGuard(key string) bool {
k := strings.NewReplacer("-", "", "_", "", " ", "").Replace(strings.ToLower(key))
for _, end := range []string{"password", "passwd", "secret", "token", "credential", "credentials",
"authorization", "apikey", "privatekey", "accesskey", "cookie"} {
if strings.HasSuffix(k, end) {
return true
}
}
return false
}
func keysOf(v any, into *[]string) {
switch x := v.(type) {
case map[string]any:
for k, c := range x {
*into = append(*into, k)
keysOf(c, into)
}
case []any:
for _, c := range x {
keysOf(c, into)
}
}
}
// Every entry the helper writes passes the state guard's key rule (issue 945), and names where the vault keeps the
// credential under `vault-name`, for a login per consumer and for a shared one.
func TestAnEntryHasNoKeyTheStateGuardRefuses(t *testing.T) {
for name, form := range map[string]CredentialForm{"pair": PerConsumer, "shared": Shared} {
bus, m := newStandIn(), &maker{}
v := &vault{values: map[string]string{"pair/novox/postgres-database/ace/letta": "s1",
"provision/novox/postgres-database": "s2"}}
assign(t, bus, "ace", "letta", bind("postgres-database", "", "novox", "postgres"))
p := New(Options{Node: "novox", Module: "postgres", Bus: bus, Vault: v, Make: m.Make, Logf: func(string, ...any) {},
Offers: map[string]Offer{"postgres-database": {Credential: form, User: "shared-user"}}})
if _, err := p.Pass(context.Background()); err != nil {
t.Fatal(err)
}
var doc map[string]any
if err := json.Unmarshal(bus.get(BucketOf("postgres"), "ace.letta.postgres-database"), &doc); err != nil {
t.Fatal(err)
}
var keys []string
keysOf(doc, &keys)
for _, k := range keys {
if refusedByTheStateGuard(k) {
t.Fatalf("%s: the entry carries the key %q, which the state guard refuses", name, k)
}
}
want := map[string]string{"pair": "pair/novox/postgres-database/ace/letta",
"shared": "provision/novox/postgres-database"}[name]
if doc["vault-name"] != want {
t.Fatalf("%s: vault-name is %v, want %s", name, doc["vault-name"], want)
}
}
}