From 5c02527d2d7300f5508f8267ed9faf1989979535 Mon Sep 17 00:00:00 2001 From: kolaente Date: Fri, 24 Jan 2025 14:42:27 +0100 Subject: [PATCH] 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 --- pkg/config/config.go | 42 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 40 insertions(+), 2 deletions(-) diff --git a/pkg/config/config.go b/pkg/config/config.go index 3c5928c1f..a0d54b0c8 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -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" {