122 lines
5.8 KiB
Go
122 lines
5.8 KiB
Go
package buscompose
|
|
|
|
import (
|
|
"slices"
|
|
"strings"
|
|
|
|
"git.novox.be/novox/mesh-sdk/go/delivery"
|
|
"git.novox.be/novox/mesh-sdk/go/health"
|
|
)
|
|
|
|
// **The grants part's user list, sealed to the bus node** (novox/hq ADR 0547 clause 7, issue 1945). The grants part writes
|
|
// the list it composed as one object of its own object bucket, sealed to the key of the node holding mesh-broker, and
|
|
// names it in one key of its own state `user-list`. The node-engine of that node follows the key when the switch says
|
|
// so (clause 8), opens the object and writes the bus's user-list file.
|
|
//
|
|
// Who may do what, and nothing more:
|
|
//
|
|
// - the grants part writes its object bucket (create, put, purge of an object's old chunks, delete) and its own state
|
|
// as before;
|
|
// - the node-engine of the node holding mesh-broker reads that one key of the state, watched, and an object of the
|
|
// bucket by its name: never the bucket's list of names, never a write, never another key of the grants part's state;
|
|
// - no other principal touches either.
|
|
|
|
// GrantsUserListBucket, GrantsUserListKey, GrantsUserListObjects and GrantsUserListObjectStream are delivery's names,
|
|
// here where the grants are composed.
|
|
const (
|
|
GrantsUserListBucket = delivery.GrantsUserListBucket
|
|
GrantsUserListKey = delivery.GrantsUserListKey
|
|
GrantsUserListObjects = delivery.GrantsUserListObjects
|
|
GrantsUserListObjectStream = delivery.GrantsUserListObjectStream
|
|
)
|
|
|
|
// GrantsUserListFollows is what the node-engine of the node holding mesh-broker may publish to follow the grants part's
|
|
// list: the one key `user-list.sealed` read and watched (its own ordered consumer, filtered on that key alone), and an
|
|
// object of the bucket by its name, as a node reads its declaration's files (ADR 0486 §2: never STREAM.INFO of the object
|
|
// stream, which lists every name, and never STREAM.MSG.GET).
|
|
func GrantsUserListFollows() []string {
|
|
kv := "KV_" + GrantsUserListBucket
|
|
key := "$KV." + GrantsUserListBucket + "." + GrantsUserListKey
|
|
obj, s := GrantsUserListObjects, GrantsUserListObjectStream
|
|
return []string{
|
|
"$JS.API.DIRECT.GET." + kv + "." + key,
|
|
"$JS.API.CONSUMER.CREATE." + kv + ".*." + key,
|
|
"$JS.API.CONSUMER.DELETE." + kv + ".*",
|
|
"$JS.API.STREAM.INFO." + kv,
|
|
"$JS.FC." + kv + ".>",
|
|
"$JS.API.DIRECT.GET." + s + ".$O." + obj + ".M.*",
|
|
"$JS.API.CONSUMER.CREATE." + s + ".*.$O." + obj + ".C.*",
|
|
"$JS.API.CONSUMER.DELETE." + s + ".*",
|
|
"$JS.FC." + s + ".>",
|
|
}
|
|
}
|
|
|
|
// GrantsUserListWrites is what the grants part may publish to keep its object bucket: create it and read its info back,
|
|
// put an object (its chunks and its metadata, the old chunks of a name purged), read an object's metadata, and delete
|
|
// one. Its own state `user-list` it writes already, as every module writes its own.
|
|
func GrantsUserListWrites() []string {
|
|
obj, s := GrantsUserListObjects, GrantsUserListObjectStream
|
|
return []string{
|
|
"$JS.API.STREAM.CREATE." + s,
|
|
"$JS.API.STREAM.INFO." + s,
|
|
"$JS.API.STREAM.PURGE." + s,
|
|
"$JS.API.DIRECT.GET." + s + ".>",
|
|
"$JS.API.STREAM.MSG.GET." + s,
|
|
"$O." + obj + ".>",
|
|
}
|
|
}
|
|
|
|
// grantsOwnObjects is the grants part writing its own object bucket, and nothing of another.
|
|
func grantsOwnObjects(p Principal, pattern string) bool {
|
|
return p.Kind == KindModule && p.Module == GrantsModule &&
|
|
(pattern == "$O."+GrantsUserListObjects+".>" || pattern == "$JS.API.STREAM.CREATE."+GrantsUserListObjectStream)
|
|
}
|
|
|
|
// **Named readers** (novox/hq ADR 0547 clause 9, progressive insight of 2026-10-26; issue 1945). A state named here is
|
|
// read by the modules named and by no other, whatever another module's `reads` says: the vault's `bus-accounts` holds
|
|
// every bus account's bcrypt hash, and from take-over the grants part alone may read it. A module's announcement may name
|
|
// readers too (announce.State.Readers); both are honoured.
|
|
const VaultBusAccounts = "mesh-vault.bus-accounts"
|
|
|
|
// NamedReaders are the states only the modules named may read, by `<owner>.<name>`: the grants part's wanted accounts
|
|
// are read by the vault alone (novox/hq ADR 0561 §2).
|
|
// The controller's lease is read by the parts that turn on at its write (ADR 0561 §3, §6), and by the assigner, which
|
|
// reads the catalogue's placement alone from then (ADR 0549 §5, issue 774).
|
|
//
|
|
// The health holders (novox/hq ADR 0564 Limits 2 and 8, clauses 8 and 9): the condition keeper, mesh-healers and the
|
|
// watchman read the lease; the keeper alone the controller's conditions, mesh-healers alone its hand-act log; and nobody
|
|
// the healers' `would` (health_grants.go).
|
|
var NamedReaders = map[string][]string{VaultBusAccounts: {GrantsModule}, GrantsAccounts: {VaultSeat},
|
|
ControllerLease: {VaultSeat, GrantsModule, delivery.AssignerModule, health.KeeperModule, health.HealersModule, WatchmanModule},
|
|
ControllerConditions: {health.KeeperModule}, ControllerHandActs: {health.HealersModule}, HealersWould: {}}
|
|
|
|
// ReaderMay says whether module may read the state read (`<owner>.<name>`) by the named readers: always, for a state
|
|
// that names none.
|
|
func ReaderMay(module, read string) bool {
|
|
readers, named := NamedReaders[read]
|
|
return !named || slices.Contains(readers, module)
|
|
}
|
|
|
|
// readsNamed splits a module's reads by the named readers: those it may read, and those named for others.
|
|
func readsNamed(module string, reads []string) (granted, refused []string) {
|
|
for _, r := range reads {
|
|
if ReaderMay(module, strings.TrimSpace(r)) {
|
|
granted = append(granted, r)
|
|
} else {
|
|
refused = append(refused, r)
|
|
}
|
|
}
|
|
return granted, refused
|
|
}
|
|
|
|
// withNamedReaders is d with the reads named for other modules moved to its refused reads.
|
|
func withNamedReaders(d Declared) Declared {
|
|
granted, refused := readsNamed(d.Module, d.Reads)
|
|
if len(refused) == 0 {
|
|
return d
|
|
}
|
|
d.Reads = granted
|
|
d.ReadsRefused = append(append([]string(nil), d.ReadsRefused...), refused...)
|
|
return d
|
|
}
|