Files
mesh-catalog/modules/mesh-mail-web/cmd/mesh-mail-web/config.go
T

78 lines
2.6 KiB
Go

package main
// The trusted setting `operators` (novox/hq ADR 0440 §3, settlement 5 of design 59 milestone 1's prepare step):
// the Keycloak identities (the ID token's preferred_username) let in. Nobody by default; a setting that cannot
// be read, is empty, or holds something not its own shape lets nobody in either, and says why (issue 1262/AC4).
import (
"encoding/json"
"errors"
"fmt"
"os"
"strings"
)
// Env is where the mesh puts what the module reads, as the manifest's build env names it.
type Env struct {
Operators string // the trusted setting operators
OIDC string // the oidc-client binding, rendered (issue 1262's oidc.json)
Listen string // the port the mesh gave endpoint `web` (MESH_MAIL_WEB_LISTEN)
Node string // this machine's own name (MESH_NODE), for the internal host mail.<node>.internal
}
func envFromOS() Env {
return Env{
Operators: os.Getenv("MESH_MAIL_WEB_OPERATORS"),
OIDC: os.Getenv("MESH_MAIL_WEB_OIDC"),
Listen: os.Getenv("MESH_MAIL_WEB_LISTEN"),
Node: os.Getenv("MESH_NODE"),
}
}
// OperatorsFile is the shape of the setting as the mesh writes it.
type OperatorsFile struct {
Operators []string `json:"operators"`
}
// ReadOperators reads the operators setting (issue 1262/AC4): a setting missing, empty, or holding an unknown
// field lets nobody in, and the reason is kept (never silently "everyone", never a panic).
func ReadOperators(path string) ([]string, error) {
if path == "" {
return nil, errors.New("the module was started without its operators setting (MESH_MAIL_WEB_OPERATORS)")
}
raw, err := os.ReadFile(path)
if err != nil {
return nil, fmt.Errorf("the operators setting cannot be read: %w", err)
}
dec := json.NewDecoder(strings.NewReader(string(raw)))
dec.DisallowUnknownFields()
var f OperatorsFile
if err := dec.Decode(&f); err != nil {
return nil, fmt.Errorf("the operators setting is not the module's: %w", err)
}
return f.Operators, nil
}
// Operators is the setting read fresh each time it is asked (never cached): it who may sign in, so a setting
// that cannot be read grants nobody rather than whoever it last allowed.
type Operators struct {
Path string
}
// Allows says whether identity (the ID token's preferred_username) is let in, and why not when it is refused.
func (o Operators) Allows(identity string) (bool, string) {
if identity == "" {
return false, "no identity was given"
}
names, err := ReadOperators(o.Path)
if err != nil {
return false, err.Error()
}
for _, n := range names {
if n == identity {
return true, ""
}
}
return false, fmt.Sprintf("%q is not in the setting operators", identity)
}