Files
mesh-catalog/modules/mesh-vault/cmd/mesh-vault/ownissue.go
T

90 lines
3.5 KiB
Go

package main
// The vault asks a software-issuing provider's tool itself (novox/hq ADR 0349 clause 10, issue 928).
//
// A provider whose offer says `issued-by: software` has its value taken from its tool, sealed to the vault, "when the
// provider starts, when the software's value changes and at least hourly". Only the controller's `secret issued` made
// the vault ask until now, so a holder that bound after the last such act waited for ever. The pass (once a minute and
// when the vault starts) now asks the tool for the holders the plans name that hold nothing yet, in the vault's own
// name and through the same path as the controller's verb. A holder that holds its value is left untouched: the
// forge mints anew on every ask, and a rotation is the software's (ADR 0305 §5, §9). A failed ask is said, keeps
// nothing, and is asked again at the next pass.
import (
"fmt"
"sort"
"strings"
)
// ownCaller is who a value the vault asked for itself is recorded as taken by.
const ownCaller = "mesh-vault"
// perHolderWhy is what a pass says of a provision whose plans name a credential per holder.
const perHolderWhy = "a plan names a credential per holder for it (issue 757): there is no one shared value"
// ownIssue is the pass's part for a provision whose plans name a credential per holder: when its offer is issued by
// the provider's software and the plans name the tool, the holders that hold nothing yet are asked of it.
func (v *Vault) ownIssue(k pairKey, b *binds) PairOutcome {
name := k.name()
said := PairOutcome{Name: name, Outcome: PairPerHolder, Why: perHolderWhy}
if v.Ask == nil || len(b.forms) != 1 || !b.forms[FormIssued] {
return said
}
held := func(why string) PairOutcome {
return PairOutcome{Name: name, Outcome: PairHeld, Why: why}
}
var tools []string
for t := range b.tools {
if t != "" {
tools = append(tools, t)
}
}
sort.Strings(tools)
switch {
case len(tools) == 0:
return held(fmt.Sprintf("the plans name %s from %s as issued by its software and name no tool that answers it; "+
"the vault cannot ask for it (issue 928)", k.provision, k.provider))
case len(tools) > 1 || b.tools[""]:
return held(fmt.Sprintf("the plans naming %s from %s disagree on its tool (%s); the vault asks none", k.provision,
k.provider, strings.Join(tools, ", ")))
case len(b.providerModules) != 1:
return held(fmt.Sprintf("no one plan names the module serving %s on %s, so the vault cannot tell whose tool to ask",
k.provision, k.provider))
}
var module string
for m := range b.providerModules {
module = m
}
var missing []Holder
for h := range b.holders {
secret, err := HolderSecret(k.provider, k.provision, h)
if err != nil {
continue
}
if _, have, err := v.Store.Read(secret); err == nil && !have {
missing = append(missing, h)
}
}
if len(missing) == 0 {
return said
}
sort.Slice(missing, func(i, j int) bool { return missing[i].String() < missing[j].String() })
got, err := v.takeIssuedFor(ownCaller, k.provision, k.provider, module, tools[0], missing)
if err != nil {
return held(err.Error())
}
if len(got.NotAnswered) > 0 {
var who []string
for _, h := range got.NotAnswered {
who = append(who, h.String())
}
return held(fmt.Sprintf("its tool %s gave no value for %s", tools[0], strings.Join(who, ", ")))
}
var who []string
for _, h := range got.Holders {
who = append(who, h.Holder.String())
}
return PairOutcome{Name: name, Outcome: PairPerHolder, Why: fmt.Sprintf("the vault asked its tool %s and keeps a value "+
"for %s (issue 928)", tools[0], strings.Join(who, ", "))}
}