Review: one local name is still a local name; a local name is unique; recovery knows it; recipes read as instructions; a tag before a digest; ask fails at once when nothing serves
A secrets object with one local name delivered no file. Two requirements could share a local name. secret recover and the export could not tell two locals apart. The recipe check missed continued lines and read heredoc bodies as bases. repo:tag@digest kept the tag in the repository. ask now publishes mandatory, so a tool nothing serves is said at once rather than after the wait.
This commit is contained in:
@@ -651,7 +651,10 @@ func (r Resolution) contributions(settings SettingsBy, grants []Grant,
|
||||
if sorted[i].Consumer != sorted[j].Consumer {
|
||||
return sorted[i].Consumer < sorted[j].Consumer
|
||||
}
|
||||
return sorted[i].From < sorted[j].From
|
||||
if sorted[i].From != sorted[j].From {
|
||||
return sorted[i].From < sorted[j].From
|
||||
}
|
||||
return sorted[i].Local < sorted[j].Local
|
||||
})
|
||||
// A consumer already carried by the grants loop, keyed (provision, module). When provider and
|
||||
// consumer are co-located, `grantsFor` enumerates the same-node consumer too, so without this the
|
||||
@@ -788,6 +791,8 @@ type Kept struct {
|
||||
Sealed string `json:"sealed"`
|
||||
Key string `json:"key"`
|
||||
MadeAt time.Time `json:"made-at"`
|
||||
// Local is the credential's name inside the consumer where it holds several (ADR 0094).
|
||||
Local string `json:"local,omitempty"`
|
||||
}
|
||||
|
||||
// KeptExport is what a person keeps beside the operator key, and what a vault keeps on its disk:
|
||||
|
||||
@@ -1050,6 +1050,7 @@ func ParseManifest(raw []byte) (Manifest, error) {
|
||||
problems = append(problems, m.Module+" needs a secret with no name")
|
||||
}
|
||||
}
|
||||
localOf := map[string]string{}
|
||||
for _, to := range m.SecretRequirements() {
|
||||
if _, plain := m.Secrets[to]; plain {
|
||||
if _, also := m.SecretsMany[to]; also {
|
||||
@@ -1069,9 +1070,15 @@ func ParseManifest(raw []byte) (Manifest, error) {
|
||||
m.Module, to, f.Local))
|
||||
}
|
||||
// A local name is what `${secret:<name>}` says, so it may not be another requirement's
|
||||
// name or one of the module's own secrets — the file would hold the wrong credential
|
||||
// while every check passed.
|
||||
// name, another requirement's local name, or one of the module's own secrets — the
|
||||
// file would hold the wrong credential while every check passed.
|
||||
if f.Local != "" {
|
||||
if other, taken := localOf[f.Local]; taken && other != to {
|
||||
problems = append(problems, fmt.Sprintf(
|
||||
"%s keeps credentials for %q and %q both under %q — a local name names one",
|
||||
m.Module, other, to, f.Local))
|
||||
}
|
||||
localOf[f.Local] = to
|
||||
if _, own := m.OwnSecrets[f.Local]; own {
|
||||
problems = append(problems, fmt.Sprintf(
|
||||
"%s keeps a credential for %q under %q, which is also one of its own secrets",
|
||||
|
||||
@@ -881,7 +881,9 @@ func perConsumer(needs []Needed, order []string, catalogue map[string]Manifest)
|
||||
// from one provider (ADR 0094). Each is its own pair credential downstream.
|
||||
func eachLocal(needs []Needed, catalogue map[string]Manifest, n Needed) []Needed {
|
||||
files := catalogue[n.For].SecretFiles(n.Name)
|
||||
if len(files) <= 1 {
|
||||
// One file under a local name is still a local name: the review found a module keeping ONE
|
||||
// named secret given a need with no local, and so no file, while everything reported success.
|
||||
if len(files) == 0 || (len(files) == 1 && files[0].Local == "") {
|
||||
return append(needs, n)
|
||||
}
|
||||
for _, f := range files {
|
||||
|
||||
@@ -159,3 +159,47 @@ func TestAProviderKeepsOneFilePerHolder(t *testing.T) {
|
||||
t.Fatalf("two holders are two grant files: %v", ids)
|
||||
}
|
||||
}
|
||||
|
||||
// One file under a local name is still a local name (review C1): the need carries it, the file
|
||||
// is written, and ${secret:<name>} is filled.
|
||||
func TestOneLocalNameIsStillALocalName(t *testing.T) {
|
||||
only, _ := ParseManifest([]byte(`{"module":"one","version":"1","requires":["secret"],
|
||||
"secrets":{"secret":{"only":"/var/lib/one/only"}}}`))
|
||||
vault := vaultAndCA()["mesh-vault"]
|
||||
got, err := Resolve(shelf(vault, only), []string{"mesh-vault", "one"}, workstation(), World{})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var found *Needed
|
||||
for i, n := range got.Needs {
|
||||
if n.For == "one" && n.Name == "secret" {
|
||||
found = &got.Needs[i]
|
||||
}
|
||||
}
|
||||
if found == nil || found.Local != "only" {
|
||||
t.Fatalf("the one named file did not become a need under its name: %v", got.Needs)
|
||||
}
|
||||
found.Sealed = "sealed-only"
|
||||
out, err := got.Declaration(Rendering{})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var written bool
|
||||
for _, r := range out {
|
||||
if r["path"] == "/var/lib/one/only" && r["sealed"] == "sealed-only" {
|
||||
written = true
|
||||
}
|
||||
}
|
||||
if !written {
|
||||
t.Fatal("the file under the one local name was not written")
|
||||
}
|
||||
}
|
||||
|
||||
// A local name names one credential: two requirements may not share it (review C2).
|
||||
func TestALocalNameIsUniqueAcrossRequirements(t *testing.T) {
|
||||
_, err := ParseManifest([]byte(`{"module":"x","version":"1","requires":["secret","postgres-database"],
|
||||
"secrets":{"secret":{"x":"/var/lib/x/a"},"postgres-database":{"x":"/var/lib/x/b"}}}`))
|
||||
if err == nil || !strings.Contains(err.Error(), "both under") {
|
||||
t.Fatalf("two requirements under one local name were accepted: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user