Make every offer say its credential's form, pair or none where it names no own secret or issuer, and read no default, so the vault never mints a login for a provider that takes none (issue 1954, ADR 0570)
This commit is contained in:
@@ -42,9 +42,10 @@ func consumerOn(t *testing.T, m Manifest, w World) Resolution {
|
||||
node := workstation()
|
||||
node.At = "workstation.internal"
|
||||
stores := shelf(m,
|
||||
Manifest{Module: "store", Version: "1", Provides: []Offer{{Name: "postgres-database", Scope: ScopeMesh}},
|
||||
Manifest{Module: "store", Version: "1", Provides: []Offer{{Name: "postgres-database", Scope: ScopeMesh,
|
||||
Credential: &OfferCredential{Form: CredentialFormPair}}},
|
||||
Serves: map[string]map[string]any{"postgres-database": {"port": 5432}}},
|
||||
Manifest{Module: "store-two", Version: "1", Provides: []Offer{{Name: "postgres-database", Scope: ScopeMesh}},
|
||||
Manifest{Module: "store-two", Version: "1", Provides: []Offer{{Name: "postgres-database", Scope: ScopeMesh, Credential: &OfferCredential{Form: CredentialFormPair}}},
|
||||
Serves: map[string]map[string]any{"postgres-database": {"port": 5434}}})
|
||||
r, err := Resolve(stores, []string{m.Module}, node, w)
|
||||
if err != nil {
|
||||
@@ -130,7 +131,10 @@ func TestAHandedModulesPlanNamesTheCredentialFormOfItsProvidersOffer(t *testing.
|
||||
return mustParse(t, raw+`}`)
|
||||
}
|
||||
for credential, want := range map[string]string{
|
||||
``: CredentialFormPair,
|
||||
// No default (the lead, 2026-10-29): an offer that says nothing has no form, and the catalogue refuses it.
|
||||
``: "",
|
||||
`,"credential":{"form":"pair"}`: CredentialFormPair,
|
||||
`,"credential":{"form":"none"}`: CredentialFormNone,
|
||||
`,"credential":{"own":"admin"}`: CredentialFormShared,
|
||||
`,"credential":{"own":"token"}`: CredentialFormGiven,
|
||||
`,"credential":{"issued-by":"outside"}`: CredentialFormGiven,
|
||||
@@ -153,6 +157,18 @@ func TestAHandedModulesPlanNamesTheCredentialFormOfItsProvidersOffer(t *testing.
|
||||
if got := (Manifest{Module: "x"}).CredentialFormOf("postgres-database"); got != "" {
|
||||
t.Errorf("a module offering nothing has the credential form %q", got)
|
||||
}
|
||||
// The catalogue refuses an offer saying no form, and a form beside an own secret or not pair or none.
|
||||
for credential, refused := range map[string]string{``: "says no credential form", `,"credential":{}`: "says no credential form",
|
||||
`,"credential":{"form":"pair","own":"admin"}`: "beside an own secret", `,"credential":{"form":"maybe"}`: `not "pair" or "none"`,
|
||||
`,"credential":{"form":"none"}`: ""} {
|
||||
raw := `{"module":"store","version":"1","provides":[{"name":"postgres-database","scope":"mesh"` + credential + `}]}`
|
||||
var m Manifest
|
||||
_ = json.Unmarshal([]byte(raw), &m)
|
||||
problems := strings.Join(CredentialFormProblems(m), "; ")
|
||||
if (refused == "") != (problems == "") || !strings.Contains(problems, refused) {
|
||||
t.Errorf("%q: %q, want %q", credential, problems, refused)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// A consumer's plan entry carries what its manifest contributes to that provision (issue 776), so the provider's
|
||||
|
||||
@@ -287,6 +287,10 @@ type OfferCredential struct {
|
||||
Own string `json:"own,omitempty"`
|
||||
IssuedBy string `json:"issued-by,omitempty"`
|
||||
Tool string `json:"tool,omitempty"`
|
||||
// Form says, for an offer that names no own secret and no issuer, which it is: CredentialFormPair, a login of
|
||||
// each consumer's own, or CredentialFormNone, no credential at all (the lead, 2026-10-29, for novox/hq ADR 0570):
|
||||
// never left to a default.
|
||||
Form string `json:"form,omitempty"`
|
||||
}
|
||||
|
||||
// IssuedBySoftware says a provision's credential is issued by its provider's software (novox/hq ADR 0305).
|
||||
@@ -362,6 +366,9 @@ const (
|
||||
// CredentialFormGiven is given by a person (novox/hq ADR 0337): the offer's credential, or the own secret it
|
||||
// shares, is issued outside the mesh; the mesh never makes it.
|
||||
CredentialFormGiven = "given"
|
||||
// CredentialFormNone is an offer whose consumers present no credential: its outcome is the provider's have, and
|
||||
// the vault mints nothing for it.
|
||||
CredentialFormNone = "none"
|
||||
)
|
||||
|
||||
// CredentialFormOf is the credential form this module's offer of a provision declares, read from the offer's
|
||||
@@ -375,7 +382,7 @@ func (m Manifest) CredentialFormOf(provision string) string {
|
||||
}
|
||||
switch c := o.Credential; {
|
||||
case c == nil:
|
||||
return CredentialFormPair
|
||||
return "" // undeclared: refused at the catalogue (CredentialFormProblems), never read as a pair
|
||||
case c.IssuedBy == IssuedOutside:
|
||||
return CredentialFormGiven
|
||||
case c.IssuedBy == IssuedBySoftware:
|
||||
@@ -385,11 +392,33 @@ func (m Manifest) CredentialFormOf(provision string) string {
|
||||
case c.Own != "":
|
||||
return CredentialFormShared
|
||||
}
|
||||
return CredentialFormPair
|
||||
return o.Credential.Form
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// CredentialFormProblems refuses an offer that does not say its credential's form (the lead, 2026-10-29): an own
|
||||
// secret, an issuer, or `form` pair or none, and `form` only where neither of the others is said.
|
||||
func CredentialFormProblems(m Manifest) []string {
|
||||
var problems []string
|
||||
for _, o := range m.Provides {
|
||||
c := o.Credential
|
||||
switch {
|
||||
case c == nil || (c.Own == "" && c.IssuedBy == "" && c.Form == ""):
|
||||
problems = append(problems, fmt.Sprintf("%s provides %q and says no credential form: say `credential` with "+
|
||||
"`form` pair (a login of each consumer's own) or none (no credential), or the own secret or issuer it is",
|
||||
m.Module, o.Name))
|
||||
case c.Form != "" && (c.Own != "" || c.IssuedBy != ""):
|
||||
problems = append(problems, fmt.Sprintf("%s provides %q with `form` beside an own secret or an issuer; `form` "+
|
||||
"says only pair or none", m.Module, o.Name))
|
||||
case c.Form != "" && c.Form != CredentialFormPair && c.Form != CredentialFormNone:
|
||||
problems = append(problems, fmt.Sprintf("%s provides %q with the form %q, not %q or %q", m.Module, o.Name,
|
||||
c.Form, CredentialFormPair, CredentialFormNone))
|
||||
}
|
||||
}
|
||||
return problems
|
||||
}
|
||||
|
||||
// ProvisionsSharing is every provision of this module whose credential is the named own secret.
|
||||
func (m Manifest) ProvisionsSharing(own string) []string {
|
||||
var out []string
|
||||
@@ -1995,6 +2024,8 @@ func parseManifest(raw []byte, predefined PredefinedList) (Manifest, error) {
|
||||
} else if offer.Credential != nil {
|
||||
own, declared := m.OwnSecrets[offer.Credential.Own]
|
||||
switch {
|
||||
case offer.Credential.Own == "" && offer.Credential.Form != "":
|
||||
// The form alone, pair or none (CredentialFormProblems judges it).
|
||||
case offer.Credential.Own == "":
|
||||
problems = append(problems, fmt.Sprintf(
|
||||
"%s provides %q with a credential that names no own secret", m.Module, p))
|
||||
|
||||
@@ -282,6 +282,10 @@ func CatalogueProblems(shelf Shelf) []string {
|
||||
problems = append(problems, RestrictedHearingProblems(shelf)...)
|
||||
// A work-queue stream is never asked for by another module's consumes (novox/hq ADR 0436 §6).
|
||||
problems = append(problems, WorkQueueConsumedElsewhereProblems(shelf)...)
|
||||
// Every offer says its credential's form, never left to a default (the lead, 2026-10-29, for novox/hq ADR 0570).
|
||||
for _, module := range shelfOrder(shelf) {
|
||||
problems = append(problems, CredentialFormProblems(shelf[module])...)
|
||||
}
|
||||
|
||||
// Who declares what, and who declared it first.
|
||||
declaredBy := map[string]string{}
|
||||
|
||||
@@ -109,7 +109,7 @@ func TestAClaimMustMatchTheDeclaredScope(t *testing.T) {
|
||||
func TestTheMeshsOwnSeatsAreStillClaimable(t *testing.T) {
|
||||
// It delivers the bus, so its holder provides the bus — the rule this check now enforces.
|
||||
m := Manifest{Module: "nats",
|
||||
Provides: []Offer{{Name: "mesh-bus", Scope: ScopeMesh}},
|
||||
Provides: []Offer{{Name: "mesh-bus", Scope: ScopeMesh, Credential: &OfferCredential{Form: CredentialFormNone}}},
|
||||
Claims: []Claim{{Name: "mesh-broker", Scope: ScopeMesh}}}
|
||||
if got := problemsFor(t, Shelf{"nats": m}); got != "" {
|
||||
t.Fatalf("a mesh seat was refused by the derived check: %s", got)
|
||||
@@ -186,7 +186,7 @@ func TestTheParserDoesNotJudgeWhatOnlyTheStoreKnows(t *testing.T) {
|
||||
// A store whose bus seat delivers something this module does provide.
|
||||
UseSeats([]Seat{{Name: "mesh-broker", Scope: ScopeMesh, Delivers: "mesh-bus", Decision: "test"}})
|
||||
raw := []byte(`{"module":"a-bus","version":"1",` +
|
||||
`"provides":[{"name":"mesh-bus","scope":"mesh"}],` +
|
||||
`"provides":[{"name":"mesh-bus","scope":"mesh","credential":{"form":"none"}}],` +
|
||||
`"claims":[{"name":"mesh-broker","scope":"mesh"}]}`)
|
||||
|
||||
m, err := ParseManifest(raw)
|
||||
|
||||
@@ -148,7 +148,7 @@ func TestTheUplinkIsANodeSeatThatDeliversNothing(t *testing.T) {
|
||||
}
|
||||
|
||||
func claimed(claims string) []byte {
|
||||
return []byte(`{"module":"thing","version":"1","provides":[{"name":"npm-package-registry","scope":"mesh"}],"claims":` + claims + `}`)
|
||||
return []byte(`{"module":"thing","version":"1","provides":[{"name":"npm-package-registry","scope":"mesh","credential":{"form":"none"}}],"claims":` + claims + `}`)
|
||||
}
|
||||
|
||||
// **The refusal moved, it did not go** (novox/hq ADR 0118, superseding 0110). A module may now
|
||||
|
||||
@@ -99,6 +99,9 @@ const (
|
||||
FormShared = "shared"
|
||||
FormIssued = "issued"
|
||||
FormGiven = "given"
|
||||
// FormNone is an offer whose consumers present no credential (catalogue.CredentialFormNone): the vault mints nothing,
|
||||
// and the provision depends on no credential.
|
||||
FormNone = "none"
|
||||
)
|
||||
|
||||
// VaultName is the vault's name of a provision's credential in form, as its verb `own` names it and the provider asks
|
||||
|
||||
@@ -74,7 +74,7 @@ func TestAProvisionIsAReferenceUnderItsConsumersKey(t *testing.T) {
|
||||
}
|
||||
// A reference's id is the vault's name as its verb own names it, in each form (the host agent, 2026-10-29).
|
||||
for form, want := range map[string]string{FormPair: "pair/novox/postgres-database/novox/keycloak", FormShared: "provision/novox/postgres-database",
|
||||
FormIssued: "provision/novox/postgres-database/novox/keycloak", "none": ""} {
|
||||
FormIssued: "provision/novox/postgres-database/novox/keycloak", FormNone: ""} {
|
||||
if got := VaultName(form, db); got != want {
|
||||
t.Errorf("the %s name is %q", form, got)
|
||||
}
|
||||
@@ -95,6 +95,8 @@ func TestAProvisionIsAReferenceUnderItsConsumersKey(t *testing.T) {
|
||||
{FormShared, nil, "ace.postgres.secret-postgres-database"},
|
||||
{FormPair, providerPlan, "ace.postgres.backup-target,novox.keycloak.secret-postgres-database"},
|
||||
{"", nil, ""},
|
||||
// An offer with no credential (the lead, 2026-10-29): nothing minted, nothing to wait for.
|
||||
{FormNone, nil, ""},
|
||||
} {
|
||||
entry := catalogue.PlanProvision{Provision: "postgres-database", Provider: postgres.Node, ProviderModule: postgres.Module,
|
||||
CredentialForm: c.form}
|
||||
|
||||
Reference in New Issue
Block a user