The vault takes a handing whole once, refusals and all, and names the refused users in its last adoption (review of the adoption, ADR 0547 clause 9)

This commit is contained in:
2026-10-26 12:03:14 +01:00
parent f0fd53281b
commit e2136ec0c9
2 changed files with 29 additions and 11 deletions
+11 -8
View File
@@ -54,7 +54,7 @@ type Adoption struct {
// adoptionKept is what the vault keeps of its last adoption, for its tools and to skip a have that says the same.
type adoptionKept struct {
last *Adoption
handed string // the From and To of the handing last adopted with nothing refused
handed string // the From and To of the handing last taken in a complete pass
failed string // why the last look found nothing to adopt, or ""
}
@@ -103,8 +103,8 @@ func (v *Vault) AdoptNow(caller string) (Adoption, error) {
return v.AdoptFrom(node, raw, someoneOr(caller, "a tool"), true)
}
// AdoptFrom adopts from the have of the bus module on node: unless force, nothing when it hands what the last adoption
// took whole.
// AdoptFrom adopts from the have of the bus module on node: unless force, nothing when it hands what the last complete
// adoption took.
func (v *Vault) AdoptFrom(node string, raw []byte, by string, force bool) (Adoption, error) {
a, err := v.adopt(node, raw, by, force)
v.mu.Lock()
@@ -207,11 +207,11 @@ func (v *Vault) adopt(node string, raw []byte, by string, force bool) (Adoption,
}
}
}
if len(a.Refused) == 0 {
v.mu.Lock()
v.adoption.handed = handed
v.mu.Unlock()
}
// A complete pass, refusals and all: the same handing is not opened again at the next change of the have (a
// refused user stays refused until the list changes, and the last adoption names it).
v.mu.Lock()
v.adoption.handed = handed
v.mu.Unlock()
return a, nil
}
@@ -266,6 +266,9 @@ func (v *Vault) adoptionSaid() map[string]any {
v.mu.Lock()
defer v.mu.Unlock()
out := map[string]any{"last": v.adoption.last}
if v.adoption.last != nil && len(v.adoption.last.Refused) > 0 {
out["refused"] = v.adoption.last.Refused
}
if v.adoption.failed != "" {
out["why"] = v.adoption.failed
}
@@ -235,10 +235,14 @@ func TestSecretOwnListsFingerprintsOfOwnSecretsOnly(t *testing.T) {
sealedTo := v.Own.PublicKey
have := func() []byte {
var accounts []delivery.HandedAccount
for _, u := range []string{"controller", "node.ace", "g14.dunst"} {
accounts = append(accounts, delivery.HandedAccount{User: u, Hash: handing[u]})
for _, u := range []string{"controller", "node.ace", "g14.dunst", "bad.user"} {
if hash, ok := handing[u]; ok {
accounts = append(accounts, delivery.HandedAccount{User: u, Hash: hash})
}
}
handed, err := delivery.HandAccounts(accounts, sealedTo, "sha256:"+handing["node.ace"][7:15], time.Now())
// The list's digest, as the node says it: another list (another hash or user) is another digest.
list, _ := json.Marshal(accounts)
handed, err := delivery.HandAccounts(accounts, sealedTo, Fingerprint(string(list)), time.Now())
if err != nil {
t.Fatal(err)
}
@@ -300,6 +304,17 @@ func TestSecretOwnListsFingerprintsOfOwnSecretsOnly(t *testing.T) {
if acc.keys["g14.dunst"] != dunst {
t.Errorf("a user whose value the vault holds was adopted over: %+v", acc.keys["g14.dunst"])
}
// A refused user is named in the last adoption, and the same handing is not opened again at the next change.
sealedTo, handing["bad.user"] = v.Own.PublicKey, "not-bcrypt"
if a, err := v.AdoptFrom("novox", have(), "the watch", false); err != nil || a.Refused["bad.user"] == "" || a.Handed != 4 {
t.Fatalf("a refused user: %+v, %v", a, err)
}
if a, err := v.AdoptFrom("novox", have(), "the watch", false); err != nil || !a.At.IsZero() {
t.Fatalf("the same handing was opened again after a refusal: %+v, %v", a, err)
}
if said := v.adoptionSaid(); said["refused"] == nil {
t.Errorf("the refusal is not said: %v", said)
}
if _, err := v.AdoptFrom("ace", have(), "the test", true); err == nil || !strings.Contains(err.Error(), "does not hold the bus") {
t.Errorf("a handing from a node not holding the bus: %v", err)
}