lockbox

password manager
Log | Files | Refs | README | LICENSE

commit 38a32252d5f6a85f66edb065d2b1e1ec12b0f4e9
parent 167d2a6823b439d0b2c777c78f5712e1e04a81d0
Author: Sean Enck <sean@ttypty.com>
Date:   Sat, 29 Jul 2023 08:59:48 -0400

support resolving variable cycles

Diffstat:
Mcmd/main.go | 13++++++-------
Minternal/app/core_test.go | 2+-
Minternal/config/core.go | 32+++++++++++++++++++++++++++++++-
Minternal/config/vars.go | 9+++++----
Minternal/config/vars_test.go | 2+-
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) } }