commit 38a32252d5f6a85f66edb065d2b1e1ec12b0f4e9
parent 167d2a6823b439d0b2c777c78f5712e1e04a81d0
Author: Sean Enck <sean@ttypty.com>
Date: Sat, 29 Jul 2023 08:59:48 -0400
support resolving variable cycles
Diffstat:
5 files changed, 44 insertions(+), 14 deletions(-)
diff --git a/cmd/main.go b/cmd/main.go
@@ -60,18 +60,17 @@ func run() error {
if err != nil {
return err
}
- for k, v := range found {
+ result, err := config.ExpandParsed(found)
+ if err != nil {
+ return err
+ }
+ for k, v := range result {
ok, err := config.IsUnset(k, v)
if err != nil {
return err
}
if !ok {
- if err := os.Setenv(k, os.Expand(v, func(in string) string {
- if val, ok := found[in]; ok {
- return val
- }
- return os.Getenv(in)
- })); err != nil {
+ if err := os.Setenv(k, v); err != nil {
return err
}
}
diff --git a/internal/app/core_test.go b/internal/app/core_test.go
@@ -13,7 +13,7 @@ func TestUsage(t *testing.T) {
t.Errorf("invalid usage, out of date? %d", len(u))
}
u, _ = app.Usage(true, "lb")
- if len(u) != 100 {
+ if len(u) != 101 {
t.Errorf("invalid verbose usage, out of date? %d", len(u))
}
for _, usage := range u {
diff --git a/internal/config/core.go b/internal/config/core.go
@@ -330,7 +330,7 @@ func Environ() []string {
var results []string
for _, k := range os.Environ() {
if strings.HasPrefix(k, prefixKey) {
- if strings.HasPrefix(k, EnvConfig.key) {
+ if strings.HasPrefix(k, fmt.Sprintf("%s=", EnvConfig.key)) {
continue
}
results = append(results, k)
@@ -339,3 +339,33 @@ func Environ() []string {
sort.Strings(results)
return results
}
+
+// ExpandParsed handles cycles of parsing configuration env inputs to resolve ALL variables
+func ExpandParsed(inputs map[string]string) (map[string]string, error) {
+ cycles, err := envConfigExpands.Get()
+ if err != nil {
+ return nil, err
+ }
+ if cycles == 0 {
+ return inputs, nil
+ }
+ result := inputs
+ for cycles > 0 {
+ result = expandParsed(result)
+ cycles--
+ }
+ return result, nil
+}
+
+func expandParsed(inputs map[string]string) map[string]string {
+ result := make(map[string]string)
+ for k, v := range inputs {
+ result[k] = os.Expand(v, func(in string) string {
+ if val, ok := inputs[in]; ok {
+ return val
+ }
+ return os.Getenv(in)
+ })
+ }
+ return result
+}
diff --git a/internal/config/vars.go b/internal/config/vars.go
@@ -78,10 +78,11 @@ var (
EnvJSONDataOutput = EnvironmentString{environmentBase: environmentBase{key: prefixKey + "JSON_DATA_OUTPUT", desc: fmt.Sprintf("Changes what the data field in JSON outputs will contain use\n'%s' with CAUTION.", JSONDataOutputRaw)}, canDefault: true, defaultValue: string(JSONDataOutputHash), allowed: []string{string(JSONDataOutputRaw), string(JSONDataOutputHash), string(JSONDataOutputBlank)}}
// EnvFormatTOTP supports formatting the TOTP tokens for generation of tokens
EnvFormatTOTP = EnvironmentFormatter{environmentBase: environmentBase{key: EnvTOTPToken.key + "_FORMAT", desc: "Override the otpauth url used to store totp tokens. It must have ONE format\nstring ('%s') to insert the totp base code."}, fxn: formatterTOTP, allowed: "otpauth//url/%s/args..."}
- envKeyMode = EnvironmentString{environmentBase: environmentBase{key: prefixKey + "KEYMODE", requirement: "must be set to a valid mode when using a key", desc: "How to retrieve the database store password."}, allowed: []string{commandKeyMode, plainKeyMode}, canDefault: true, defaultValue: commandKeyMode}
- envKey = EnvironmentString{environmentBase: environmentBase{requirement: requiredKeyOrKeyFile, key: prefixKey + "KEY", desc: fmt.Sprintf("The database key ('%s' mode) or command to run ('%s' mode)\nto retrieve the database password.", plainKeyMode, commandKeyMode)}, allowed: []string{commandArgsExample, "password"}, canDefault: false}
// EnvConfig is the location of the config file to read environment variables from
- EnvConfig = EnvironmentString{environmentBase: environmentBase{key: prefixKey + "ENV", desc: fmt.Sprintf("Allows setting a specific file of environment variables for lockbox\nto read and use as configuration values (an '.env' file). The keyword\n'%s' will disable this functionality the keyword '%s' will search\nfor a file in the following paths in user's home directory matching\nthe first file found.\n\ndefault search paths:\n%v\n\nNote that this setting is not output as part of the environment.", noEnvironment, detectEnvironment, detectEnvironmentPaths)}, canDefault: true, defaultValue: detectEnvironment, allowed: []string{detectEnvironment, fileExample, noEnvironment}}
+ EnvConfig = EnvironmentString{environmentBase: environmentBase{key: prefixKey + "ENV", desc: fmt.Sprintf("Allows setting a specific file of environment variables for lockbox\nto read and use as configuration values (an '.env' file). The keyword\n'%s' will disable this functionality the keyword '%s' will search\nfor a file in the following paths in user's home directory matching\nthe first file found.\n\ndefault search paths:\n%v\n\nNote that this setting is not output as part of the environment.", noEnvironment, detectEnvironment, detectEnvironmentPaths)}, canDefault: true, defaultValue: detectEnvironment, allowed: []string{detectEnvironment, fileExample, noEnvironment}}
+ envKeyMode = EnvironmentString{environmentBase: environmentBase{key: prefixKey + "KEYMODE", requirement: "must be set to a valid mode when using a key", desc: "How to retrieve the database store password."}, allowed: []string{commandKeyMode, plainKeyMode}, canDefault: true, defaultValue: commandKeyMode}
+ envKey = EnvironmentString{environmentBase: environmentBase{requirement: requiredKeyOrKeyFile, key: prefixKey + "KEY", desc: fmt.Sprintf("The database key ('%s' mode) or command to run ('%s' mode)\nto retrieve the database password.", plainKeyMode, commandKeyMode)}, allowed: []string{commandArgsExample, "password"}, canDefault: false}
+ envConfigExpands = EnvironmentInt{environmentBase: environmentBase{key: EnvConfig.key + "_EXPANDS", desc: "The number of times to expand the input configuration to resolve variables."}, shortDesc: "hash length", allowZero: true, defaultValue: 3}
)
// GetReKey will get the rekey environment settings
@@ -152,7 +153,7 @@ func GetKey() ([]byte, error) {
// ListEnvironmentVariables will print information about env variables
func ListEnvironmentVariables() []string {
var results []string
- for _, item := range []printer{EnvStore, envKeyMode, envKey, EnvNoClip, EnvNoColor, EnvInteractive, EnvReadOnly, EnvTOTPToken, EnvFormatTOTP, EnvMaxTOTP, EnvTOTPColorBetween, EnvClipPaste, EnvClipCopy, EnvClipMax, EnvPlatform, EnvNoTOTP, EnvHookDir, EnvClipOSC52, EnvKeyFile, EnvModTime, EnvJSONDataOutput, EnvHashLength, EnvConfig} {
+ for _, item := range []printer{EnvStore, envKeyMode, envKey, EnvNoClip, EnvNoColor, EnvInteractive, EnvReadOnly, EnvTOTPToken, EnvFormatTOTP, EnvMaxTOTP, EnvTOTPColorBetween, EnvClipPaste, EnvClipCopy, EnvClipMax, EnvPlatform, EnvNoTOTP, EnvHookDir, EnvClipOSC52, EnvKeyFile, EnvModTime, EnvJSONDataOutput, EnvHashLength, EnvConfig, envConfigExpands} {
env := item.self()
value, allow := item.values()
if len(value) == 0 {
diff --git a/internal/config/vars_test.go b/internal/config/vars_test.go
@@ -112,7 +112,7 @@ func TestListVariables(t *testing.T) {
known[trim] = struct{}{}
}
l := len(known)
- if l != 23 {
+ if l != 24 {
t.Errorf("invalid env count, outdated? %d", l)
}
}