Merge pull request 'compose: an offer of form none composes no credential and holds nothing (issue 1126)' (#159) from fix/1126-provision-credentials into main

This commit was merged in pull request #159.
This commit is contained in:
2026-11-02 15:31:34 +00:00
7 changed files with 118 additions and 26 deletions
+5
View File
@@ -846,6 +846,11 @@ func (r Resolution) compose(with Rendering, owner map[string]string,
// and nothing would say so.
continue
}
if g.Sealed == "" {
// Its offer presents no credential (form none, issue 1126): nothing to write, and an empty credential
// file would be read as one that failed. Who asked still reaches the provider in its contributions.
continue
}
first = append(first, ownedBy(r.provisionsAs(m), map[string]any{
// One file per holder — the consumer's module with its local name after it
// where it keeps several (ADR 0094); the lab found two files with one id.
+17
View File
@@ -66,6 +66,23 @@ func TestAGrantSecretIsOwnedByTheAccountThatProvisions(t *testing.T) {
"root-owned 0600 file is one it cannot read — which is silent, because the harness "+
"calls it \"not readable yet\"", fmtPath(secret), got, "operator")
}
// A grant whose offer presents no credential (form none, issue 1126) carries nothing sealed: no secret file is
// written for it, as none is for a need with no credential, and the provider still learns who asked.
out, err = r.Declaration(Rendering{Grants: []Grant{{
Provision: "mongodb-database", Consumer: "workstation", From: "photos", Slug: "photos", Values: map[string]any{}}}})
if err != nil {
t.Fatal(err)
}
asked := false
for _, res := range out {
if res["type"] == "file" && strings.HasSuffix(fmtPath(res), ".secret") {
t.Errorf("a grant with no credential writes %v", res)
}
asked = asked || fmtPath(res) == "/var/lib/mongodb/grants/mesh.json"
}
if !asked {
t.Errorf("the provider no longer learns who asked: %v", out)
}
}
// And a provider whose code still runs in a container keeps the owner it declares, so this
+1 -1
View File
@@ -372,7 +372,7 @@ func TestWhatAModuleOfItsOwnAccountReadsIsItsAccounts(t *testing.T) {
with := Rendering{ArtifactStore: "anchor.internal:5101",
Needed: map[string]map[string]string{RuntimeModule: {"broker": "sealed-credential"}, "vault": {"broker": "own"}},
Kept: &KeptExport{Export: 1, OperatorKey: "k"},
Grants: []Grant{{Provision: "secret", Consumer: "anchor", From: "telegram",
Grants: []Grant{{Provision: "secret", Consumer: "anchor", From: "telegram", Sealed: "sealed-to-anchor",
Values: map[string]any{"name": "telegram"}}}}
goRuntime := Manifest{Module: RuntimeModule, Version: "1",
OwnSecrets: OwnSecrets{"broker": {Path: "/var/lib/mesh/" + RuntimeModule + "/broker"}},
+18 -1
View File
@@ -105,7 +105,12 @@ func (c *composer) grantsFor(node string) ([]catalogue.Grant, []catalogue.Overfl
}
}
sealed := s.ForProvider
if sealed == "" {
switch {
case credentiallessRow(plan, s, node):
// Its offer presents no credential (form none): the grant carries the consumer and nothing sealed, and
// nothing stands in for it, so neither end waits for a value nobody makes (issue 1126).
sealed = ""
case sealed == "":
sealed = catalogue.ForeseenSealed
foreseen = append(foreseen, fmt.Sprintf("the grant of %s for %s on %s", s.Provision, s.ConsumerModule, s.Consumer))
}
@@ -116,6 +121,18 @@ func (c *composer) grantsFor(node string) ([]catalogue.Grant, []catalogue.Overfl
return out, withheld, unbound, foreseen, nil
}
// credentialless is whether a need's provider offers it with no credential (catalogue.CredentialFormNone): the vault
// mints nothing for it, so nothing is asked of the vault, sealed into a grant or a file, or stood in for (issue 1126).
func credentialless(n catalogue.Needed) bool { return n.CredentialForm == catalogue.CredentialFormNone }
// credentiallessRow is whether the consumer's resolution answers row's need from provider with an offer of form none.
func credentiallessRow(consumer catalogue.Resolution, row Issued, provider string) bool {
return slices.ContainsFunc(consumer.Needs, func(n catalogue.Needed) bool {
return n.Name == row.Provision && n.For == row.ConsumerModule && n.Local == row.Local && n.From == provider &&
credentialless(n)
})
}
// rowsFor is every pair credential this node's declaration must carry a grant for, in the order the controller reads them
// (SecretsFrom: by provision, consumer, consumer module and local name): the ones the Input says were made, and the ones the same send
// would make before it composes this node -- every need of a node that resolves (this one too), whose provider is this node, that is not
+7 -2
View File
@@ -19,7 +19,8 @@ type CredentialKey struct {
// as a consumer, each need of its resolution a provider's credential answers (a need answered by a record is a licence's,
// not a pair's); as a provider, each credential it must carry a grant for (rowsFor). A node that composes its own asks the
// vault for exactly these, sealed to its key, and composes with them; one it does not get is stood in for, which the
// node's own apply refuses (CompositionsApplied).
// node's own apply refuses (CompositionsApplied). A need whose provider offers it with no credential (form none) is
// in neither list: the vault mints nothing for it (issue 1126).
func CredentialsNeeded(in Input, node string) (asConsumer, asProvider []CredentialKey, err error) {
c := newComposer(in)
resolved, err := c.resolution(node)
@@ -27,7 +28,7 @@ func CredentialsNeeded(in Input, node string) (asConsumer, asProvider []Credenti
return nil, nil, err
}
for _, n := range resolved.Needs {
if n.ByRecord || n.From == "" {
if n.ByRecord || n.From == "" || credentialless(n) {
continue
}
asConsumer = append(asConsumer, CredentialKey{Provision: n.Name, Consumer: node, ConsumerModule: n.For,
@@ -38,6 +39,10 @@ func CredentialsNeeded(in Input, node string) (asConsumer, asProvider []Credenti
return nil, nil, fmt.Errorf("compose: the credentials %s provides: %w", node, err)
}
for _, r := range rows {
// A grant whose offer presents no credential (form none) has none to ask for (issue 1126).
if consumer, err := c.planFor(r.Consumer); err == nil && credentiallessRow(consumer.Plan, r, node) {
continue
}
asProvider = append(asProvider, CredentialKey{Provision: r.Provision, Consumer: r.Consumer,
ConsumerModule: r.ConsumerModule, Provider: node, Local: r.Local})
}
+66 -22
View File
@@ -384,30 +384,74 @@ func TestNoSecretValueOfTheInputIsInWhatTheCompositionSays(t *testing.T) {
}
}
// Round 1, finding 2: a consumer the same send adds has no credential in the Input yet, and the provider's declaration of that send
// carries its grant, composed with a stand-in and named foreseen.
func TestAConsumerNoCredentialWasMadeForYetIsGrantedWithAStandInAndNamedForeseen(t *testing.T) {
in := aGrantMesh(t)
in.Issued = nil
got := rendered(t, in, "anchor", Foreseeing)
if len(got.Rendering.Grants) != 1 {
t.Fatalf("grants are %+v", got.Rendering.Grants)
}
g := got.Rendering.Grants[0]
if g.Provision != "s3-bucket" || g.Consumer != "laptop" || g.From != "files" || g.Sealed != catalogue.ForeseenSealed {
t.Errorf("the grant is %+v", g)
}
if !has(got.Foreseen, "the grant of s3-bucket for files on laptop") {
t.Errorf("foreseen %v", got.Foreseen)
}
// Only the provider's own: the consumer is granted nothing, and nothing in the Input changed.
if len(rendered(t, in, "laptop", Foreseeing).Rendering.Grants) != 0 {
t.Error("the consumer is granted its own credential")
}
if len(in.Issued) != 0 {
t.Error("a row was added to the Input")
// A consumer no credential was made for yet is granted with a stand-in and named foreseen, for every form whose credential
// the vault mints or takes in; an offer of form none has no credential (issue 1126, 2026-11-02: mesh-cli on ace held for
// "the grant of mcp-endpoint", whose provider node-tools offers form none): its grant carries the consumer and no
// credential, nothing stands in on either side, and neither end asks the vault for one.
func TestAConsumerNoCredentialWasMadeForYetIsGrantedWithAStandInUnlessItsOfferHasNone(t *testing.T) {
for _, tc := range []struct {
form string
cred *catalogue.OfferCredential
}{
{"undeclared", nil},
{catalogue.CredentialFormPair, &catalogue.OfferCredential{Form: catalogue.CredentialFormPair}},
{catalogue.CredentialFormShared, &catalogue.OfferCredential{Own: "access-key"}},
{catalogue.CredentialFormNone, &catalogue.OfferCredential{Form: catalogue.CredentialFormNone}},
} {
in := aGrantMesh(t)
in.Issued = nil
provider := in.Catalogue["objects"]
provider.Provides[0].Credential = tc.cred
if tc.cred != nil && tc.cred.Own != "" {
provider.OwnSecrets = catalogue.OwnSecrets{"access-key": {Path: "/var/lib/mesh/objects/access-key"}}
}
in.Catalogue["objects"] = provider
none := tc.form == catalogue.CredentialFormNone
got := rendered(t, in, "anchor", Foreseeing)
if len(got.Rendering.Grants) != 1 {
t.Fatalf("%s: grants are %+v", tc.form, got.Rendering.Grants)
}
g, wantSealed := got.Rendering.Grants[0], catalogue.ForeseenSealed
if none {
wantSealed = ""
}
if g.Provision != "s3-bucket" || g.Consumer != "laptop" || g.From != "files" || g.Sealed != wantSealed {
t.Errorf("%s: the grant is %+v", tc.form, g)
}
if has(got.Foreseen, "the grant of s3-bucket for files on laptop") == none {
t.Errorf("%s: foreseen %v", tc.form, got.Foreseen)
}
consumer, asProvider, err := CredentialsNeeded(in, "laptop")
_, anchorProvides, err2 := CredentialsNeeded(in, "anchor")
if err != nil || err2 != nil || len(asProvider) != 0 || (len(consumer) == 0) != none || (len(anchorProvides) == 0) != none {
t.Errorf("%s: laptop asks %v, anchor provides %v (%v %v)", tc.form, consumer, anchorProvides, err, err2)
}
resolved, err := Resolve(in, "laptop")
if err != nil {
t.Fatalf("%s: %v", tc.form, err)
}
if n := theFilesNeed(t, resolved); (n.Sealed == "") != none || (len(resolved.Foreseen) == 0) != none {
t.Errorf("%s: the consumer's credential is %q, foreseen %v", tc.form, n.Sealed, resolved.Foreseen)
}
if lap := rendered(t, in, "laptop", Foreseeing); none && len(lap.Foreseen) != 0 {
t.Errorf("%s: the consumer stands in for %v", tc.form, lap.Foreseen)
}
// Only the provider's own: the consumer is granted nothing, and nothing in the Input changed.
if len(rendered(t, in, "laptop", Foreseeing).Rendering.Grants) != 0 {
t.Errorf("%s: the consumer is granted its own credential", tc.form)
}
if len(in.Issued) != 0 {
t.Errorf("%s: a row was added to the Input", tc.form)
}
// A row made long ago for an offer of form none (the controller's) carries no credential either.
in.Issued = []Issued{{Provision: "s3-bucket", Consumer: "laptop", ConsumerModule: "files", Provider: "anchor", ForProvider: "sealed-to-anchor"}}
if g := rendered(t, in, "anchor", Foreseeing).Rendering.Grants; none && (len(g) != 1 || g[0].Sealed != "") {
t.Errorf("%s: an old row's grant is %+v", tc.form, g)
}
}
// A credential only a person gives and nobody has is no grant.
in := aGrantMesh(t)
in.Issued = nil
in.Credentials = []Credential{{Provision: "s3-bucket", Consumer: "laptop", ConsumerModule: "files", Provider: "anchor", State: CredentialNotGiven}}
if got := rendered(t, in, "anchor", Foreseeing); len(got.Rendering.Grants) != 0 {
t.Errorf("a credential nobody gave is granted: %+v", got.Rendering.Grants)
+4
View File
@@ -118,6 +118,10 @@ func (c *composer) planForNow(nodeName string) (Resolved, error) {
}
continue
}
if credentialless(n) {
// Its provider's offer presents no credential (form none): nothing is sealed, made or stood in for (issue 1126).
continue
}
cred, held := c.credential(n, nodeName)
_, outside := issuedOutsideBy(shelf, n)
switch {