fix!(config): read all env variables into config store explicitly

This changes the way environment variables are read into Vikunja's config.
With this change, Vikunja loads the config from all env variables into the config store explicitly, after all config files have been processed. The breaking change here is that values from env variables now may override values from a config file when both are specified.
This allows specifying openid providers using only environment values. Previously this still required a config file to work, because viper wouldn't know about these values otherwise.

Resolves https://community.vikunja.io/t/configure-openid-via-environment/628/16
This commit is contained in:
kolaente 2025-01-24 14:42:27 +01:00
parent 9aa197b196
commit 5c02527d2d
No known key found for this signature in database
GPG Key ID: F40E70337AB24C9B
1 changed files with 40 additions and 2 deletions

View File

@ -29,6 +29,7 @@ import (
_ "time/tzdata" // Imports time zone data instead of relying on the os
"code.vikunja.io/api/pkg/log"
"github.com/spf13/viper"
)
@ -458,6 +459,40 @@ func readConfigValuesFromFiles() {
}
}
func setConfigFromEnv() error {
envKeys := os.Environ()
configMap := make(map[string]any)
for _, envKeyValue := range envKeys {
keyValue := strings.SplitN(envKeyValue, "=", 2)
if len(keyValue) != 2 {
continue
}
key, value := keyValue[0], keyValue[1]
if strings.HasPrefix(key, "VIKUNJA_") {
formattedKey := strings.ToLower(strings.TrimPrefix(key, "VIKUNJA_"))
keys := strings.Split(formattedKey, "_")
currentMap := configMap
for i, part := range keys {
if i == len(keys)-1 {
// Set the value at the final level
currentMap[part] = value
} else {
// Check if the key exists at the current level, create a new map if not
if _, exists := currentMap[part]; !exists {
currentMap[part] = make(map[string]any)
}
// Move into the nested map
currentMap = currentMap[part].(map[string]any)
}
}
}
}
return viper.MergeConfigMap(configMap)
}
// InitConfig initializes the config, sets defaults etc.
func InitConfig() {
@ -469,8 +504,6 @@ func InitConfig() {
viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
viper.AutomaticEnv()
// Just load environment variables
_ = viper.ReadInConfig()
log.ConfigureLogger(LogEnabled.GetBool(), LogStandard.GetString(), LogPath.GetString(), LogLevel.GetString())
// Load the config file
@ -502,6 +535,11 @@ func InitConfig() {
log.Info("No config file found, using default or config from environment variables.")
}
err = setConfigFromEnv()
if err != nil {
log.Warningf("Failed to set config from environment variables: %s", err.Error())
}
readConfigValuesFromFiles()
if RateLimitStore.GetString() == "keyvalue" {