85 lines
3.2 KiB
Go
85 lines
3.2 KiB
Go
package main
|
|
|
|
// Callers is the trusted setting `callers` (lead-m2@g14's decision at this milestone's prepare step, said to
|
|
// the operator; novox/hq ADR 0459 §2: "each answers only a caller the operator grants it"): who may call one
|
|
// of mail-reading's six verbs. An entry is a bare module name (granted wherever it runs, on any machine),
|
|
// exactly <machine>.<module>, or exactly person.<name> — the same shapes mesh-mail's own trusted setting
|
|
// `grants` matches (modules/mesh-mail/cmd/mesh-mail/config.go, Grants.Allows), copied here in miniature (one
|
|
// flat list, not one per account) so mesh-mail-reader never imports mesh-mail. Nobody beyond the list is
|
|
// granted, a wildcard in the list is no entry, and an unproven caller never matches. The setting's default is
|
|
// ["mesh-mail-web"].
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"os"
|
|
"strings"
|
|
)
|
|
|
|
// Unproven is the caller the runtime proved nobody to be (stdio.Unproven; copied so this file reads alone).
|
|
const Unproven = "unproven"
|
|
|
|
// Callers is the setting's shape once read: a flat list of entries.
|
|
type Callers []string
|
|
|
|
// callersFile is the shape of the setting as the mesh writes it.
|
|
type callersFile struct {
|
|
Callers Callers `json:"callers"`
|
|
}
|
|
|
|
// ReadCallers reads the callers setting. A setting that cannot be read allows nobody, and says why: a caller
|
|
// refused for a reason it can read is better than one let through because the setting was unreadable.
|
|
func ReadCallers(path string) (Callers, error) {
|
|
if path == "" {
|
|
return nil, errors.New("the module was started without its callers setting (MESH_MAIL_READER_CALLERS)")
|
|
}
|
|
raw, err := os.ReadFile(path)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("the callers setting cannot be read: %w", err)
|
|
}
|
|
return ParseCallers(raw)
|
|
}
|
|
|
|
// ParseCallers reads the callers setting's content.
|
|
func ParseCallers(raw []byte) (Callers, error) {
|
|
var f callersFile
|
|
dec := json.NewDecoder(strings.NewReader(string(raw)))
|
|
dec.DisallowUnknownFields()
|
|
if err := dec.Decode(&f); err != nil {
|
|
return nil, fmt.Errorf("the callers setting is not the module's: %v", err)
|
|
}
|
|
return f.Callers, nil
|
|
}
|
|
|
|
// Allows says whether the caller may call a mail-reading verb: a bare module name (on any machine, as long as
|
|
// the caller is not person.<name>), exactly <machine>.<module>, or exactly person.<name>. Nobody is granted by
|
|
// default, an unproven caller never, and a wildcard entry is no entry (mesh-mail's own Grants.Allows mirrored).
|
|
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
|
|
}
|
|
|
|
// refuseCaller says why the caller is refused a mail-reading verb: the setting unread, or simply not named in it.
|
|
func refuseCaller(readErr error, caller string) error {
|
|
if readErr != nil {
|
|
return fmt.Errorf("refused: the setting callers cannot be read, so it allows nobody: %w", readErr)
|
|
}
|
|
return fmt.Errorf("refused: %s is not named in the setting callers; the operator grants it there", caller)
|
|
}
|