dyndns-server/config.go
Thomas Preisner e15da33296 config.go: improve config validation
Also check whether there are no users/records and if records have an
actual user associated with them.
2021-09-11 22:14:39 +02:00

114 lines
2.9 KiB
Go

package main
import (
"fmt"
"github.com/pelletier/go-toml"
"os"
)
type Config struct {
ServerPort uint16 `toml:"port"`
Users []User `toml:"user"`
RRConfigs []RRConfig `toml:"rrconfig"`
users map[string]*User
rrconfigs map[string]*RRConfig
}
type User struct {
Username string `toml:"username"`
Password string `toml:"password"`
Records []string `toml:"records"`
records map[string]bool
}
type RRConfig struct {
Recordname string `toml:"recordname"`
Zonename string `toml:"zonename"`
Nameserver string `toml:"nameserver"`
Tsigalgo string `toml:"tsig_algo"`
Tsigid string `toml:"tsig_id"`
Tsigkey string `toml:"tsig_key"`
Ttl int `toml:"ttl" default:"60"`
}
func LoadConfig(path string) (*Config, error) {
var cfg Config
f, err := os.Open(path)
if err != nil {
return nil, err
}
decoder := toml.NewDecoder(f).Strict(true)
err = decoder.Decode(&cfg)
if err != nil {
return nil, err
}
return prepareConfig(&cfg)
}
func prepareConfig(cfg *Config) (*Config, error) {
if len(cfg.Users) <= 0 || len(cfg.RRConfigs) <= 0 {
return nil, fmt.Errorf("No users or resource record configs present.")
}
// temporary map for detecting duplicated or orphaned entries between
// records and users
globRecords := map[string]bool{}
// populate user map
cfg.users = map[string]*User{}
for _, user := range cfg.Users {
if _, ok := cfg.users[user.Username]; ok {
return nil, fmt.Errorf("Duplicate username detected: %s", user.Username)
}
// initialize and populate user.records map
user.records = make(map[string]bool)
if len(user.Records) <= 0 {
return nil, fmt.Errorf("User without records detected: %s", user.Username)
}
for _, record := range user.Records {
// check for duplicate records
if globRecords[record] {
return nil, fmt.Errorf("Record associated with multiple users detected: %s", record)
}
// memorize record both in the user as well as in the temporary map
user.records[record] = true
globRecords[record] = true
}
cfg.users[user.Username] = &user
}
// populate record map
cfg.rrconfigs = map[string]*RRConfig{}
for _, record := range cfg.RRConfigs {
// check for duplicate record before verifying association to users
if _, ok := cfg.rrconfigs[record.Recordname]; ok {
return nil, fmt.Errorf("Duplicate record detected: %s", record.Recordname)
}
if !globRecords[record.Recordname] {
return nil, fmt.Errorf("Record without associated user detected: %s", record.Recordname)
}
// need to remove record from globRecords for detecting orphaned records
delete(globRecords, record.Recordname)
cfg.rrconfigs[record.Recordname] = &record
}
// check whether all records have been processed
if len(globRecords) > 0 {
missing := make([]string, len(globRecords))
i := 0
for k := range globRecords {
missing[i] = k
i++
}
return nil, fmt.Errorf("Records associated to user missing: %v", missing)
}
return cfg, nil
}