78 lines
2.6 KiB
Go
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)
|
|
}
|