The vault makes node-tools' own secret ledgers as {} on every machine whose tool runner is wanted (issue 1966 V1)
This commit is contained in:
@@ -0,0 +1,69 @@
|
||||
package main
|
||||
|
||||
// The ledger accounts of a machine and the tool runner's own secret `ledgers` (novox/hq ADR 0597 §9, design 25 § 12.4,
|
||||
// issue 1966 V1 and V2). Nothing here is the custody ledger of ledger.go (ADR 0085): this file is about the bus account
|
||||
// of kind ledger, one per module assignment that asks for a ledger.
|
||||
//
|
||||
// The tool runner of a machine holds its machine's ledger accounts together as its own secret `ledgers`
|
||||
// (`own/<node>/node-tools/ledgers`): a JSON map of module to credential, `{}` when there is none, so its composition
|
||||
// never refuses a secret the vault did not make. The vault makes it as `{}` for every machine whose tool runner is
|
||||
// wanted, and rewrites it on each ledger account minted or retired, saying `secret-changed` once per rewrite.
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"sort"
|
||||
|
||||
"git.novox.be/novox/mesh-sdk/go/buscompose"
|
||||
)
|
||||
|
||||
// toolRunnerModule is the module whose own secret `ledgers` is: the tool runner (mesh-tools node-tools).
|
||||
const toolRunnerModule = "node-tools"
|
||||
|
||||
// ledgersSecret is the vault's name of node's `ledgers`.
|
||||
func ledgersSecret(node string) (string, error) {
|
||||
return OwnSecret(node, toolRunnerModule, buscompose.LedgersSecret)
|
||||
}
|
||||
|
||||
// toolRunnerNodes are the machines whose tool runner is wanted, sorted.
|
||||
func (v *Vault) toolRunnerNodes() []string {
|
||||
v.minting.mu.Lock()
|
||||
defer v.minting.mu.Unlock()
|
||||
var out []string
|
||||
for _, w := range v.minting.wants {
|
||||
if w.Kind == buscompose.KindNodeTools {
|
||||
out = append(out, w.Node)
|
||||
}
|
||||
}
|
||||
sort.Strings(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// LedgersPass makes `ledgers` as `{}` on every machine whose tool runner is wanted and for which the vault holds none,
|
||||
// at the vault's minute pass (and when the tool runner's want is heard).
|
||||
func (v *Vault) LedgersPass() {
|
||||
for _, node := range v.toolRunnerNodes() {
|
||||
v.ensureLedgers(node)
|
||||
}
|
||||
}
|
||||
|
||||
// ensureLedgers keeps node's `ledgers` as `{}` when the vault holds none: a held one is never touched here.
|
||||
func (v *Vault) ensureLedgers(node string) {
|
||||
secret, err := ledgersSecret(node)
|
||||
if err != nil {
|
||||
v.log("%s's ledgers are not made: %v", node, err)
|
||||
return
|
||||
}
|
||||
_, held, err := v.Store.Read(secret)
|
||||
if err != nil || held {
|
||||
if err != nil {
|
||||
v.log("%s is not read, so its ledgers are not made: %v", secret, err)
|
||||
}
|
||||
return
|
||||
}
|
||||
empty, _ := json.Marshal(map[string]json.RawMessage{})
|
||||
if _, err := v.keepLocked(secret, string(empty), "the vault", "", OriginMade, keepOptions{neverReplace: true}); err != nil {
|
||||
v.log("%s is not made, tried again at the next pass: %v", secret, err)
|
||||
return
|
||||
}
|
||||
v.log("made %s as {}: no ledger account is held for its machine yet", secret)
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"git.novox.be/novox/mesh-sdk/go/buscompose"
|
||||
"git.novox.be/novox/mesh-sdk/go/seal"
|
||||
)
|
||||
|
||||
// Ledger accounts and the tool runner's own secret `ledgers` (novox/hq ADR 0597 §9, issue 1966). Not the custody
|
||||
// ledger of ledger.go (ADR 0085), which is another thing.
|
||||
|
||||
// nodeToolsWant is the wanted account of a machine's tool runner, as the grants part writes it.
|
||||
func nodeToolsWant(node string) buscompose.WantedAccount {
|
||||
w := aWant("node-tools")
|
||||
w.Kind, w.Node, w.User = buscompose.KindNodeTools, node, node+".node-tools"
|
||||
return w
|
||||
}
|
||||
|
||||
// ledgerWant is the wanted ledger account of a module on a machine.
|
||||
func ledgerWant(node, module string) buscompose.WantedAccount {
|
||||
w := aWant(module)
|
||||
w.Kind, w.Node, w.User, w.Claims = buscompose.KindLedger, node, "ledger."+node+"."+module, nil
|
||||
return w
|
||||
}
|
||||
|
||||
// ledgersOf is what the vault's `own` answers the machine as node-tools' `ledgers`: the map of module to credential,
|
||||
// and whether the secret is there at all.
|
||||
func ledgersOf(t *testing.T, v testVault, priv string) (map[string]json.RawMessage, bool) {
|
||||
t.Helper()
|
||||
own, err := v.OwnOfNode("node.ace")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for _, o := range own.Own {
|
||||
if o.Module == "node-tools" && o.Name == buscompose.LedgersSecret {
|
||||
value, err := seal.Open(o.Sealed, priv)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
got := map[string]json.RawMessage{}
|
||||
if err := json.Unmarshal([]byte(value), &got); err != nil {
|
||||
t.Fatalf("ledgers is not a JSON map: %v", err)
|
||||
}
|
||||
return got, true
|
||||
}
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// changesOf counts the secret-changed events said for name since the first of events.
|
||||
func changesOf(v testVault, since int, name string) int {
|
||||
n := 0
|
||||
for _, e := range v.bus.events[since:] {
|
||||
if e["event"] == ChangedEvent && e["name"] == name {
|
||||
n++
|
||||
}
|
||||
}
|
||||
return n
|
||||
}
|
||||
@@ -350,6 +350,7 @@ func main() {
|
||||
}
|
||||
env.Vault.HashOwed()
|
||||
env.Vault.MintOwed()
|
||||
env.Vault.LedgersPass()
|
||||
env.Vault.RetirePass()
|
||||
env.Vault.RotationPass()
|
||||
env.Vault.LoginsPass()
|
||||
|
||||
@@ -193,6 +193,9 @@ func (v *Vault) WantHeard(user string, raw []byte, deleted bool) {
|
||||
v.minting.wants[user] = w
|
||||
v.minting.mu.Unlock()
|
||||
v.wantBack(user)
|
||||
if w.Kind == buscompose.KindNodeTools {
|
||||
v.ensureLedgers(w.Node)
|
||||
}
|
||||
if _, err := v.mintWanted(w, "the grants part's want of "+user); err != nil {
|
||||
v.log("%s is not minted: %v", user, err)
|
||||
}
|
||||
|
||||
@@ -99,6 +99,36 @@ func TestTheVaultMintsAWantedAccountOnceAndNothingItHolds(t *testing.T) {
|
||||
if st := v.MintState(); len(st.HeldBack) != 0 || len(st.Minted) != 1 {
|
||||
t.Errorf("vault_status says %+v: an adopted user is not held back, one account minted", st)
|
||||
}
|
||||
t.Run("ledgers", func(t *testing.T) {
|
||||
for _, c := range []struct {
|
||||
name string
|
||||
run func(t *testing.T, v testVault, priv string)
|
||||
}{
|
||||
// 1966/AC24 (issue 1966 V1): a machine carrying node-tools, with no ledger account, has `ledgers` as {}.
|
||||
{"an empty ledgers on a machine with node-tools", func(t *testing.T, v testVault, priv string) {
|
||||
if _, ok := ledgersOf(t, v, priv); ok {
|
||||
t.Fatal("ledgers is there before node-tools is wanted")
|
||||
}
|
||||
w := nodeToolsWant("ace")
|
||||
v.WantHeard(w.User, wantRaw(t, w), false)
|
||||
before := len(v.bus.events)
|
||||
v.LedgersPass()
|
||||
got, ok := ledgersOf(t, v, priv)
|
||||
if !ok || len(got) != 0 {
|
||||
t.Fatalf("ledgers is %v (held %v), want {}", got, ok)
|
||||
}
|
||||
v.LedgersPass()
|
||||
if n := changesOf(v, before, "own/ace/node-tools/ledgers"); n != 0 {
|
||||
t.Errorf("a second pass said secret-changed %d times for a value that did not change", n)
|
||||
}
|
||||
}},
|
||||
} {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
v, _, priv := mintingVault(t)
|
||||
c.run(t, v, priv)
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 774/AC8, after research 072's gap "vault mints after the lease": the vault is the one issuer, so a wanted account is
|
||||
|
||||
Reference in New Issue
Block a user