mesh/delivery merged: source.main moved to novox/mesh-sdk@d6c87ee1a7ae
mesh/delivery-group group fix/1126-provision-credentials merged: every member is merged
mesh/check pass c459f757308a on 036266daff85 | unit 6028 run, 142 kept, 16 skipped | integration 6091 run, 174 kept, 25 skipped | over bound: unit, in…
After the controller, nothing makes a pair row for a new consumer, so a need whose provider offers no credential (form none) was composed with a stand-in on both ends and the node-engine held the consumer (issue 1126: mesh-cli on ace, "stands in for the grant of mcp-endpoint"). The vault never mints for form none, so such a need now composes with no credential: the consumer's need carries none, the provider's grant carries the consumer and nothing sealed, neither is named foreseen, CredentialsNeeded asks the vault for neither, and no empty grant file is written. Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
70 lines
2.6 KiB
Go
70 lines
2.6 KiB
Go
package compose
|
|
|
|
import (
|
|
"fmt"
|
|
"sort"
|
|
)
|
|
|
|
// CredentialKey names one pair credential by the ends it joins: the provision, the consumer's node and module, the
|
|
// provider's node, and the local name where a module keeps several (Credential's and Issued's key).
|
|
type CredentialKey struct {
|
|
Provision string `json:"provision"`
|
|
Consumer string `json:"consumer"`
|
|
ConsumerModule string `json:"consumer_module"`
|
|
Provider string `json:"provider"`
|
|
Local string `json:"local,omitempty"`
|
|
}
|
|
|
|
// CredentialsNeeded is every pair credential node's composition reads (novox/hq ADR 0547 §2, ADR 0550 §5, issue 1126):
|
|
// 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). 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)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
for _, n := range resolved.Needs {
|
|
if n.ByRecord || n.From == "" || credentialless(n) {
|
|
continue
|
|
}
|
|
asConsumer = append(asConsumer, CredentialKey{Provision: n.Name, Consumer: node, ConsumerModule: n.For,
|
|
Provider: n.From, Local: n.Local})
|
|
}
|
|
rows, err := c.rowsFor(node)
|
|
if err != nil {
|
|
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})
|
|
}
|
|
sortKeys(asConsumer)
|
|
sortKeys(asProvider)
|
|
return asConsumer, asProvider, nil
|
|
}
|
|
|
|
func sortKeys(ks []CredentialKey) {
|
|
sort.Slice(ks, func(i, j int) bool {
|
|
a, b := ks[i], ks[j]
|
|
switch {
|
|
case a.Provision != b.Provision:
|
|
return a.Provision < b.Provision
|
|
case a.Provider != b.Provider:
|
|
return a.Provider < b.Provider
|
|
case a.Consumer != b.Consumer:
|
|
return a.Consumer < b.Consumer
|
|
case a.ConsumerModule != b.ConsumerModule:
|
|
return a.ConsumerModule < b.ConsumerModule
|
|
}
|
|
return a.Local < b.Local
|
|
})
|
|
}
|