127 lines
3.7 KiB
Go
127 lines
3.7 KiB
Go
// Package config contains distillery configuration
|
|
package config
|
|
|
|
import (
|
|
"hash/fnv"
|
|
"math/rand"
|
|
"reflect"
|
|
"time"
|
|
|
|
"github.com/tkw1536/pkglib/reflectx"
|
|
"github.com/tkw1536/pkglib/yamlx"
|
|
"gopkg.in/yaml.v3"
|
|
|
|
_ "embed"
|
|
)
|
|
|
|
// Config represents the configuration of a WissKI Distillery.
|
|
//
|
|
// Config is read from a byte stream using [Unmarshal].
|
|
//
|
|
// Config contains many methods that do not require any interaction with any running components.
|
|
// Methods that require running components are instead store inside the [Distillery] or an appropriate [Component].
|
|
type Config struct {
|
|
Listen ListenConfig `yaml:"listen" recurse:"true"`
|
|
Paths PathsConfig `yaml:"paths" recurse:"true"`
|
|
HTTP HTTPConfig `yaml:"http" recurse:"true"`
|
|
Home HomeConfig `yaml:"home" recurse:"true"`
|
|
Docker DockerConfig `yaml:"docker" recurse:"true"`
|
|
|
|
SQL SQLConfig `yaml:"sql" recurse:"true"`
|
|
TS TSConfig `yaml:"triplestore" recurse:"true"`
|
|
|
|
// Maximum age for backup in days
|
|
MaxBackupAge time.Duration `yaml:"age" validate:"duration"`
|
|
|
|
// Various components use password-based-authentication.
|
|
// These passwords are generated automatically.
|
|
// This variable can be used to determine their length.
|
|
PasswordLength int `yaml:"password_length" default:"64" validate:"positive"`
|
|
|
|
// session secret holds the secret for login
|
|
SessionSecret string `yaml:"session_secret" validate:"nonempty" sensitive:"true"`
|
|
|
|
// interval to trigger distillery cron tasks in
|
|
CronInterval time.Duration `yaml:"cron_interval" default:"10m" validate:"duration"`
|
|
|
|
// ConfigPath is the path this configuration was loaded from (if any)
|
|
ConfigPath string `yaml:"-"`
|
|
}
|
|
|
|
func zeroSensitive(v reflect.Value) {
|
|
reflectx.IterateFields(v.Type(), func(field reflect.StructField, index int) (stop bool) {
|
|
// if we set the recurse tag, recurse into it
|
|
if _, ok := field.Tag.Lookup("recurse"); ok {
|
|
zeroSensitive(v.FieldByName(field.Name))
|
|
}
|
|
|
|
// if the field is sensitive, set the zero value!
|
|
if _, ok := field.Tag.Lookup("sensitive"); ok {
|
|
v.FieldByName(field.Name).Set(reflect.Zero(field.Type))
|
|
}
|
|
return false
|
|
})
|
|
}
|
|
|
|
func (config Config) MarshalSensitive() string {
|
|
// zero out all the sensitive fields
|
|
zeroSensitive(reflect.ValueOf(&config).Elem())
|
|
|
|
// marshal the result
|
|
result, err := Marshal(&config, nil)
|
|
if err != nil {
|
|
return ""
|
|
}
|
|
|
|
return string(result)
|
|
}
|
|
|
|
//go:embed config.yml
|
|
var configBytes []byte
|
|
|
|
// Marshal marshals this configuration in nicely formatted form.
|
|
// Where possible, this will provided yaml comments.
|
|
//
|
|
// Previous may optionally provide the bytes of a previous configuration file to replace settings in.
|
|
// The previous yaml file must be a valid configuration yaml, meaning all fields should be set.
|
|
// When previous is of length 0, the default configuration yaml will be used instead.
|
|
func Marshal(config *Config, previous []byte) ([]byte, error) {
|
|
if len(previous) == 0 {
|
|
previous = configBytes
|
|
}
|
|
|
|
// load the template yaml
|
|
template := new(yaml.Node)
|
|
if err := yaml.Unmarshal(previous, template); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// load the config yaml
|
|
cfg, err := yamlx.Marshal(config)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// transplant the configuration yaml into the template
|
|
if err := yamlx.Transplant(template, cfg); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// marshal it again as a set of bytes
|
|
return yaml.Marshal(template)
|
|
}
|
|
|
|
// CSRFSecret return the csrfSecret derived from the session secret
|
|
func (config *Config) CSRFSecret() []byte {
|
|
// take the hash of the secret
|
|
h := fnv.New32a()
|
|
h.Write([]byte(config.SessionSecret))
|
|
|
|
// seed a random number generator
|
|
rand := rand.New(rand.NewSource(int64(h.Sum32())))
|
|
|
|
// take a bunch of bytes from it
|
|
secret := make([]byte, 32)
|
|
rand.Read(secret)
|
|
return secret
|
|
}
|