Files
mesh-sdk/go/callers.go
T

113 lines
3.8 KiB
Go

package stdio
import (
"bytes"
"encoding/json"
"fmt"
"slices"
"strings"
)
// Callers is a module's trusted list of who may call a verb of its (novox/hq ADR 0459 §2, ADR 0563 §8), as the mesh
// writes a list setting into a file the module reads: `{"callers": [...]}`. An entry is a module's name (that module on
// any machine), exactly <machine>.<module>, or exactly person.<name>. Each caller is the runtime's proof (CallerOf), never
// an argument; nobody beyond the list is granted, a wildcard is no entry, and an unproven caller never matches.
type Callers []string
// ParseCallers reads a callers file: the list under `callers`, and nothing else.
func ParseCallers(raw []byte) (Callers, error) {
var f struct {
Callers Callers `json:"callers"`
}
dec := json.NewDecoder(bytes.NewReader(raw))
dec.DisallowUnknownFields()
if err := dec.Decode(&f); err != nil {
return nil, fmt.Errorf("the callers setting is not a list of callers: %v", err)
}
return f.Callers, nil
}
// Allows says whether caller, as the runtime proved it, is one the list names.
func (c Callers) Allows(caller string) bool {
if caller == "" || caller == Unproven {
return false
}
for _, entry := range c {
if entry == "" || strings.ContainsAny(entry, "*> ") {
continue
}
if entry == caller {
return true
}
if !strings.Contains(entry, ".") {
if _, module, ok := strings.Cut(caller, "."); ok && module == entry && !strings.HasPrefix(caller, "person.") {
return true
}
}
}
return false
}
// VerbCallers is a holder's trusted list per verb (novox/hq ADR 0627 §2, design 51 *Who may call which verb*): for each
// verb of the seats it holds, the callers that may call it, each entry read as Callers reads it. Two entries more stand
// for a whole class: AnyCaller lets every proved caller call the verb (a read the mesh keeps open), AnyPerson every
// operator's session the runtime proved as person.<name>. It names no seat and no verb: the holder gives its verbs and
// its defaults, and keeps any rule of its own seat (who may never be on a list) beside it.
type VerbCallers map[string][]string
// The entries of a VerbCallers list that stand for a class of callers.
const (
AnyCaller = "*"
AnyPerson = "operator"
)
// ParseVerbCallers reads a holder's config value of verb to callers; an empty value is defaults. A verb not among verbs
// is refused, so a list that names a verb the holder does not serve is said at once.
func ParseVerbCallers(raw string, verbs []string, defaults VerbCallers) (VerbCallers, error) {
if strings.TrimSpace(raw) == "" {
return defaults, nil
}
var c VerbCallers
if err := json.Unmarshal([]byte(raw), &c); err != nil {
return nil, fmt.Errorf("callers is not a JSON object of verb to callers: %w", err)
}
for verb := range c {
if !slices.Contains(verbs, verb) {
return nil, fmt.Errorf("callers names the verb %q, which the holder does not serve", verb)
}
}
return c, nil
}
// Allowed answers nil when caller, as the runtime proved it, may call verb; else the refusal naming both. An unproven
// caller is never allowed, not even by AnyCaller.
func (c VerbCallers) Allowed(verb, caller string) error {
if caller == "" || caller == Unproven {
return fmt.Errorf("%s: refused: the request carries no caller the tool runner proved", verb)
}
person := strings.HasPrefix(caller, "person.")
for _, entry := range c[verb] {
switch entry {
case AnyCaller:
return nil
case AnyPerson:
if person {
return nil
}
default:
if (Callers{entry}).Allows(caller) {
return nil
}
}
}
return fmt.Errorf("%s: refused for %s: not on the holder's caller list for %s", verb, caller, verb)
}
// ModuleOf is the module of a proved module caller, which the runtime names <machine>.<module>.
func ModuleOf(proved string) string {
if _, module, ok := strings.Cut(proved, "."); ok {
return module
}
return proved
}