Read the whole plan the controller writes: at, from, as, names and credential-tool (issue 954)

The client decodes a plan strictly, and since the controller carries these fields
(issues 923 and 928) it refused the plans of gitea, keycloak and claude-code.
This commit is contained in:
lead-57
2026-10-16 21:32:13 +02:00
parent 0bfece71da
commit 4215b40547
2 changed files with 49 additions and 0 deletions
@@ -97,6 +97,16 @@ type PlannedProvision struct {
// Contributes is what this module tells the provider about itself for this provision (a database's name, its // Contributes is what this module tells the provider about itself for this provision (a database's name, its
// extensions): the consumer manifest's `contributes`, which the provider's helper reads and the client keeps. // extensions): the consumer manifest's `contributes`, which the provider's helper reads and the client keeps.
Contributes map[string]any `json:"contributes,omitempty"` Contributes map[string]any `json:"contributes,omitempty"`
// At, From and As are where the provider is on the private network, its machine, and who the consumer is at
// its end (issue 788); Names are what the mesh calls the consumer through this provision (issue 792). The
// provider's helper reads them from the plan; the client keeps them, so the plan reads whole (issue 954).
At string `json:"at,omitempty"`
From string `json:"from,omitempty"`
As string `json:"as,omitempty"`
Names map[string]string `json:"names,omitempty"`
// CredentialTool is, for a software-issued offer, the provider's tool the vault asks for the value (issue 928);
// the vault acts on it and this client only reads it.
CredentialTool string `json:"credential-tool,omitempty"`
} }
// issuedForHolder is whether the entry names a value its provider's software issued for this holder alone (issue // issuedForHolder is whether the entry names a value its provider's software issued for this holder alone (issue
@@ -0,0 +1,39 @@
package main
import (
"encoding/json"
"fmt"
"strings"
"testing"
)
// A plan provision carries everything the controller's PlanProvision writes (issue 954): what the consumer
// contributes, where the provider is and who the consumer is at its end (issue 788), the names the mesh calls it
// (issue 792) and, for a software-issued offer, the tool the vault asks (issue 928). The client still refuses a field
// nobody writes.
func TestAPlanProvisionWithEveryFieldTheControllerWritesIsRead(t *testing.T) {
p, err := json.Marshal(contributor(t, "shell:zsh:normal", "alias nox=mesh-cli"))
if err != nil {
t.Fatal(err)
}
provision := `"provisions":[{"provision":"route","local":"web","provider":"novox","provider-module":"route-proxy",` +
`"credential-form":"pair","contributes":{"label":"git"},"at":"10.10.0.1","from":"novox","as":"gitea",` +
`"names":{"name-web":"git.example.org"}},{"name":"gitea-read","provision":"gitea-read","provider":"novox",` +
`"provider-module":"gitea","credential-form":"issued","credential-tool":"gitea_agent_token",` +
`"path":"/var/lib/claude-code/gitea-read.secret"}],"items":`
value := fmt.Sprintf(`{"node":"g14","module":"mesh-cli","provisioning":%s}`,
strings.Replace(string(p), `"items":`, provision, 1))
_, got, err := planOf("g14", "g14.mesh-cli", []byte(value))
if err != nil {
t.Fatalf("a plan with the controller's fields is refused: %v", err)
}
if len(got.Provisions) != 2 || got.Provisions[0].At != "10.10.0.1" || got.Provisions[0].From != "novox" ||
got.Provisions[0].As != "gitea" || got.Provisions[0].Names["name-web"] != "git.example.org" ||
got.Provisions[1].CredentialTool != "gitea_agent_token" {
t.Fatalf("the plan's provisions read as %+v", got.Provisions)
}
spoilt := strings.Replace(value, `"at":`, `"surprise":1,"at":`, 1)
if _, _, err := planOf("g14", "g14.mesh-cli", []byte(spoilt)); err == nil {
t.Fatalf("a provision with a field nobody writes is read: %s", spoilt)
}
}