authelia/internal/configuration/koanf_util.go
James Elliott d2f1e5d36d
feat(configuration): automatically map old keys (#3199)
This performs automatic remapping of deprecated configuration keys in most situations.
2022-06-28 13:15:50 +10:00

165 lines
3.7 KiB
Go

package configuration
import (
"fmt"
"strings"
"github.com/knadh/koanf"
"github.com/knadh/koanf/providers/confmap"
"github.com/authelia/authelia/v4/internal/configuration/schema"
"github.com/authelia/authelia/v4/internal/utils"
)
func koanfGetKeys(ko *koanf.Koanf) (keys []string) {
keys = ko.Keys()
for key, value := range ko.All() {
slc, ok := value.([]interface{})
if !ok {
continue
}
for _, item := range slc {
m, mok := item.(map[string]interface{})
if !mok {
continue
}
for k := range m {
full := fmt.Sprintf("%s[].%s", key, k)
if !utils.IsStringInSlice(full, keys) {
keys = append(keys, full)
}
}
}
}
return keys
}
func koanfRemapKeys(val *schema.StructValidator, ko *koanf.Koanf, ds map[string]Deprecation) (final *koanf.Koanf, err error) {
keys := ko.All()
keys = koanfRemapKeysStandard(keys, val, ds)
keys = koanfRemapKeysMapped(keys, val, ds)
final = koanf.New(".")
if err = final.Load(confmap.Provider(keys, "."), nil); err != nil {
return nil, err
}
return final, nil
}
func koanfRemapKeysStandard(keys map[string]interface{}, val *schema.StructValidator, ds map[string]Deprecation) (keysFinal map[string]interface{}) {
var (
ok bool
d Deprecation
key string
value interface{}
)
keysFinal = make(map[string]interface{})
for key, value = range keys {
if d, ok = ds[key]; ok {
if !d.AutoMap {
val.Push(fmt.Errorf("invalid configuration key '%s' was replaced by '%s'", d.Key, d.NewKey))
keysFinal[key] = value
continue
} else {
val.PushWarning(fmt.Errorf("configuration key '%s' is deprecated in %s and has been replaced by '%s': "+
"this has been automatically mapped for you but you will need to adjust your configuration to remove this message", d.Key, d.Version.String(), d.NewKey))
}
if !mapHasKey(d.NewKey, keys) && !mapHasKey(d.NewKey, keysFinal) {
if d.MapFunc != nil {
keysFinal[d.NewKey] = d.MapFunc(value)
} else {
keysFinal[d.NewKey] = value
}
}
continue
}
keysFinal[key] = value
}
return keysFinal
}
func koanfRemapKeysMapped(keys map[string]interface{}, val *schema.StructValidator, ds map[string]Deprecation) (keysFinal map[string]interface{}) {
var (
key string
value interface{}
slc, slcFinal []interface{}
ok bool
m map[string]interface{}
d Deprecation
)
keysFinal = make(map[string]interface{})
for key, value = range keys {
if slc, ok = value.([]interface{}); !ok {
keysFinal[key] = value
continue
}
slcFinal = make([]interface{}, len(slc))
for i, item := range slc {
if m, ok = item.(map[string]interface{}); !ok {
slcFinal[i] = item
continue
}
itemFinal := make(map[string]interface{})
for subkey, element := range m {
prefix := fmt.Sprintf("%s[].", key)
fullKey := prefix + subkey
if d, ok = ds[fullKey]; ok {
if !d.AutoMap {
val.Push(fmt.Errorf("invalid configuration key '%s' was replaced by '%s'", d.Key, d.NewKey))
itemFinal[subkey] = element
continue
} else {
val.PushWarning(fmt.Errorf("configuration key '%s' is deprecated in %s and has been replaced by '%s': "+
"this has been automatically mapped for you but you will need to adjust your configuration to remove this message", d.Key, d.Version.String(), d.NewKey))
}
newkey := strings.Replace(d.NewKey, prefix, "", 1)
if !mapHasKey(newkey, m) && !mapHasKey(newkey, itemFinal) {
if d.MapFunc != nil {
itemFinal[newkey] = d.MapFunc(element)
} else {
itemFinal[newkey] = element
}
}
} else {
itemFinal[subkey] = element
}
}
slcFinal[i] = itemFinal
}
keysFinal[key] = slcFinal
}
return keysFinal
}