132 lines
4.8 KiB
Go
132 lines
4.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
|
|
}
|
|
|
|
// ConfiguredVerbCallers is the holder's list as mesh-config resolved its config value key for this machine (Config):
|
|
// a JSON object of verb to callers, or that object written as a string; an unset or empty value is defaults. A value
|
|
// that cannot be read is an error, never the defaults, so a broken list is said, not widened.
|
|
func ConfiguredVerbCallers(key string, verbs []string, defaults VerbCallers) (VerbCallers, error) {
|
|
raw, err := Config(key)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("the config value %s cannot be read: %w", key, err)
|
|
}
|
|
return parseConfiguredVerbCallers(raw, verbs, defaults)
|
|
}
|
|
|
|
func parseConfiguredVerbCallers(raw json.RawMessage, verbs []string, defaults VerbCallers) (VerbCallers, error) {
|
|
var text string
|
|
if err := json.Unmarshal(raw, &text); err == nil || len(bytes.TrimSpace(raw)) == 0 || string(bytes.TrimSpace(raw)) == "null" {
|
|
return ParseVerbCallers(text, verbs, defaults)
|
|
}
|
|
return ParseVerbCallers(string(raw), verbs, defaults)
|
|
}
|