commit fcfdef653168425889c1b9388a442a5cb19107c3
parent bf3c7236c2700bb1f429582c5fb6c3fac615565d
Author: Sean Enck <sean@ttypty.com>
Date: Sun, 3 Sep 2023 10:08:43 -0400
improved key read/handling
Diffstat:
12 files changed, 339 insertions(+), 167 deletions(-)
diff --git a/internal/app/completions.go b/internal/app/completions.go
@@ -79,11 +79,11 @@ func GenerateCompletions(isBash, defaults bool, exe string) ([]string, error) {
if noTOTP {
isTOTP = false
}
- k, err := config.GetKey(true)
+ k, err := config.NewKey(config.IgnoreKeyMode)
if err != nil {
return nil, err
}
- if k != nil && k.Interactive() {
+ if k.Ask() {
c.CanList = false
}
}
diff --git a/internal/app/completions_test.go b/internal/app/completions_test.go
@@ -1,7 +1,9 @@
package app_test
import (
+ "fmt"
"os"
+ "strings"
"testing"
"github.com/enckse/lockbox/internal/app"
@@ -12,42 +14,80 @@ func TestGenerateCompletions(t *testing.T) {
testCompletions(t, false)
}
-func testCompletions(t *testing.T, bash bool) {
- os.Setenv("LOCKBOX_NOTOTP", "yes")
- os.Setenv("LOCKBOX_READONLY", "yes")
- os.Setenv("LOCKBOX_NOCLIP", "yes")
- defaults, _ := app.GenerateCompletions(bash, true, "lb")
- roNoTOTPClip, _ := app.GenerateCompletions(bash, false, "lb")
- if roNoTOTPClip[0] == defaults[0] {
- t.Error("should not match defaults")
- }
+func generate(keys []string, bash bool, t *testing.T) (string, string) {
os.Setenv("LOCKBOX_NOTOTP", "")
- roNoClip, _ := app.GenerateCompletions(bash, false, "lb")
- if roNoClip[0] == defaults[0] || roNoClip[0] == roNoTOTPClip[0] {
- t.Error("should not equal defaults nor no totp/clip")
- }
os.Setenv("LOCKBOX_READONLY", "")
- os.Setenv("LOCKBOX_NOCLIP", "yes")
- noClip, _ := app.GenerateCompletions(bash, false, "lb")
- if roNoClip[0] == noClip[0] || noClip[0] == defaults[0] || noClip[0] == roNoTOTPClip[0] {
- t.Error("readonly/noclip != noclip (nor defaults, nor ro/no totp/clip)")
- }
- os.Setenv("LOCKBOX_READONLY", "yes")
os.Setenv("LOCKBOX_NOCLIP", "")
- ro, _ := app.GenerateCompletions(bash, false, "lb")
- if roNoClip[0] == ro[0] || noClip[0] == ro[0] || ro[0] == defaults[0] || ro[0] == roNoTOTPClip[0] {
- t.Error("readonly/noclip != ro (nor ro == noclip, nor ro == defaults)")
+ os.Setenv("LOCKBOX_KEYMODE", "")
+ key := "bash"
+ if !bash {
+ key = "zsh"
+ }
+ for _, k := range keys {
+ use := "yes"
+ if k == "KEYMODE" {
+ use = "ask"
+ }
+ os.Setenv(fmt.Sprintf("LOCKBOX_%s", k), use)
+ key = fmt.Sprintf("%s-%s", key, strings.ToLower(k))
+ }
+ v, err := app.GenerateCompletions(bash, false, "lb")
+ if err != nil {
+ t.Errorf("invalid error: %v", err)
+ }
+ if len(v) != 1 {
+ t.Errorf("invalid result")
+ }
+ return key, v[0]
+}
+
+func generateTest(keys []string, bash bool, t *testing.T) map[string]string {
+ r := make(map[string]string)
+ if len(keys) == 0 {
+ return r
+ }
+ k, v := generate(keys, bash, t)
+ r[k] = v
+ for _, cur := range keys {
+ var subset []string
+ for _, key := range keys {
+ if key == cur {
+ continue
+ }
+ subset = append(subset, key)
+ }
+
+ for k, v := range generateTest(subset, bash, t) {
+ r[k] = v
+ }
+ }
+ return r
+}
+
+func testCompletions(t *testing.T, bash bool) {
+ m := make(map[string]string)
+ defaults, _ := app.GenerateCompletions(bash, true, "lb")
+ m["defaults"] = defaults[0]
+ for k, v := range generateTest([]string{"NOTOTP", "READONLY", "NOCLIP", "KEYMODE"}, true, t) {
+ m[k] = v
}
+ os.Setenv("LOCKBOX_KEYMODE", "")
os.Setenv("LOCKBOX_READONLY", "")
os.Setenv("LOCKBOX_NOCLIP", "")
os.Setenv("LOCKBOX_NOTOTP", "")
- isDefaultsToo, _ := app.GenerateCompletions(bash, false, "lb")
- if isDefaultsToo[0] != defaults[0] {
- t.Error("defaults should match env defaults")
+ defaultsToo, _ := app.GenerateCompletions(bash, false, "lb")
+ if defaultsToo[0] != defaults[0] || len(defaultsToo) != 1 || len(defaults) != 1 {
+ t.Error("defaults should match env defaults/invalid defaults detected")
}
- for _, confirm := range [][]string{defaults, roNoClip, noClip, ro, isDefaultsToo} {
- if len(confirm) != 1 {
- t.Error("completions returned an invalid array")
+ for k, v := range m {
+ fmt.Println(k)
+ for kOther, vOther := range m {
+ if kOther == k {
+ continue
+ }
+ if vOther == v {
+ t.Errorf("found overlapping completion: %s == %s", k, kOther)
+ }
}
}
}
diff --git a/internal/backend/actions.go b/internal/backend/actions.go
@@ -31,15 +31,14 @@ func (t *Transaction) act(cb action) error {
if !t.valid {
return errors.New("invalid transaction")
}
- key, err := config.GetKey(false)
+ key, err := config.NewKey(config.DefaultKeyMode)
if err != nil {
return err
}
- useKey, err := platform.ReadKey(key, platform.ReadInteractivePassword)
+ k, err := key.Read(platform.ReadInteractivePassword)
if err != nil {
return err
}
- k := string(useKey)
file := config.EnvKeyFile.Get()
if !t.exists {
if err := create(t.file, k, file); err != nil {
diff --git a/internal/backend/actions_test.go b/internal/backend/actions_test.go
@@ -276,6 +276,8 @@ func keyAndOrKeyFile(t *testing.T, key, keyFile bool) {
if key {
os.Setenv("LOCKBOX_KEY", "test")
os.Setenv("LOCKBOX_KEYMODE", "plaintext")
+ } else {
+ os.Setenv("LOCKBOX_KEYMODE", "none")
}
if keyFile {
key := testFile("keyfileor.key")
diff --git a/internal/config/core.go b/internal/config/core.go
@@ -52,6 +52,7 @@ type (
key string
desc string
requirement string
+ whenUnset string
}
// EnvironmentInt are environment settings that are integers
EnvironmentInt struct {
diff --git a/internal/config/core_test.go b/internal/config/core_test.go
@@ -227,10 +227,3 @@ func TestExpandParsed(t *testing.T) {
t.Errorf("invalid expand: %v", r)
}
}
-
-func TestKey(t *testing.T) {
- k := config.Key{}
- if !k.Interactive() || k.Key() != nil {
- t.Error("should be interactive without data")
- }
-}
diff --git a/internal/config/key.go b/internal/config/key.go
@@ -9,66 +9,110 @@ import (
)
type (
+ // KeyModeType are valid ways to get the key
+ KeyModeType string
+ // AskPassword is a function to prompt for passwords (when required)
+ AskPassword func() (string, error)
// Key is a wrapper to help manage the returned key
Key struct {
- key []byte
+ inputKey string
+ mode KeyModeType
+ valid bool
}
)
-// Interactive indicates if the key requires interactive input
-func (e *Key) Interactive() bool {
- return e.key == nil
-}
-
-// Key returns the key data
-func (e *Key) Key() []byte {
- return e.key
-}
+const (
+ plainKeyMode KeyModeType = "plaintext"
+ askKeyMode KeyModeType = "ask"
+ noKeyMode KeyModeType = "none"
+ // IgnoreKeyMode will ignore the value set in the key (acts like no key)
+ IgnoreKeyMode KeyModeType = "ignore"
+ commandKeyMode KeyModeType = "command"
+ // DefaultKeyMode is the default operating keymode if NOT set
+ DefaultKeyMode = commandKeyMode
+)
-// GetKey will get the encryption key setup for lb
-func GetKey(dryrun bool) (*Key, error) {
+// NewKey will create a new key
+func NewKey(defaultKeyModeType KeyModeType) (Key, error) {
useKey := envKey.Get()
keyMode := envKeyMode.Get()
- if keyMode == askKeyMode {
+ if keyMode == "" {
+ keyMode = string(defaultKeyModeType)
+ }
+ requireEmptyKey := false
+ switch keyMode {
+ case string(IgnoreKeyMode):
+ return Key{mode: IgnoreKeyMode, inputKey: "", valid: true}, nil
+ case string(noKeyMode):
+ requireEmptyKey = true
+ case string(commandKeyMode), string(plainKeyMode):
+ case string(askKeyMode):
isInteractive, err := EnvInteractive.Get()
if err != nil {
- return nil, err
+ return Key{}, err
}
if !isInteractive {
- return nil, errors.New("ask key mode requested in non-interactive mode")
+ return Key{}, errors.New("ask key mode requested in non-interactive mode")
}
- if useKey != "" {
- return nil, errors.New("key can NOT be set in ask key mode")
+ requireEmptyKey = true
+ default:
+ return Key{}, fmt.Errorf("unknown key mode: %s", keyMode)
+ }
+ isEmpty := strings.TrimSpace(useKey) == ""
+ if requireEmptyKey {
+ if !isEmpty {
+ return Key{}, errors.New("key can NOT be set in this key mode")
}
- return &Key{}, nil
+ } else {
+ if isEmpty {
+ return Key{}, errors.New("key MUST be set in this key mode")
+ }
+ }
+ return Key{mode: KeyModeType(keyMode), inputKey: useKey, valid: true}, nil
+}
+
+func (k Key) empty() bool {
+ return k.valid && len(k.inputKey) == 0
+}
+
+// Ask will indicate if prompting is required to get the key
+func (k Key) Ask() bool {
+ return k.valid && k.mode == askKeyMode
+}
+
+// Read will read the key as configured by the mode
+func (k Key) Read(ask AskPassword) (string, error) {
+ if ask == nil {
+ return "", errors.New("invalid function given")
}
- if useKey == "" {
- return nil, nil
+ if !k.valid {
+ return "", errors.New("invalid key given")
}
- if dryrun {
- return &Key{key: []byte{0}}, nil
+ if k.empty() && !k.Ask() {
+ return "", nil
}
- var data []byte
- switch keyMode {
+ useKey := k.inputKey
+ switch k.mode {
+ case askKeyMode:
+ read, err := ask()
+ if err != nil {
+ return "", err
+ }
+ useKey = read
case commandKeyMode:
parts, err := shlex(useKey)
if err != nil {
- return nil, err
+ return "", err
}
cmd := exec.Command(parts[0], parts[1:]...)
b, err := cmd.Output()
if err != nil {
- return nil, fmt.Errorf("key command failed: %w", err)
+ return "", fmt.Errorf("key command failed: %w", err)
}
- data = b
- case plainKeyMode:
- data = []byte(useKey)
- default:
- return nil, errors.New("unknown keymode")
+ useKey = string(b)
}
- b := []byte(strings.TrimSpace(string(data)))
- if len(b) == 0 {
- return nil, errors.New("key is empty")
+ if strings.TrimSpace(useKey) == "" {
+ return "", errors.New("key is empty")
}
- return &Key{key: b}, nil
+ return useKey, nil
}
diff --git a/internal/config/key_test.go b/internal/config/key_test.go
@@ -1,60 +1,204 @@
package config_test
import (
+ "errors"
"os"
+ "strings"
"testing"
"github.com/enckse/lockbox/internal/config"
)
-func TestGetKey(t *testing.T) {
- os.Setenv("LOCKBOX_KEY", "aaa")
- os.Setenv("LOCKBOX_KEYMODE", "lak;jfea")
- if k, err := config.GetKey(false); err.Error() != "unknown keymode" || k != nil {
+func TestDefaultKey(t *testing.T) {
+ defer os.Clearenv()
+ os.Setenv("LOCKBOX_KEYMODE", "")
+ os.Setenv("LOCKBOX_KEY", "test")
+ if _, err := config.NewKey(config.IgnoreKeyMode); err != nil {
t.Errorf("invalid error: %v", err)
}
- os.Setenv("LOCKBOX_KEYMODE", "plaintext")
+ os.Setenv("LOCKBOX_KEYMODE", "")
os.Setenv("LOCKBOX_KEY", "")
- if k, err := config.GetKey(false); err != nil || k != nil {
+ if _, err := config.NewKey(config.DefaultKeyMode); err == nil || err.Error() != "key MUST be set in this key mode" {
+ t.Errorf("invalid error: %v", err)
+ }
+}
+
+func TestNewKeyErrors(t *testing.T) {
+ defer os.Clearenv()
+ os.Setenv("LOCKBOX_KEYMODE", "invalid")
+ if _, err := config.NewKey(config.IgnoreKeyMode); err == nil || err.Error() != "unknown key mode: invalid" {
t.Errorf("invalid error: %v", err)
}
- os.Setenv("LOCKBOX_KEY", "key")
- k, err := config.GetKey(false)
- if err != nil || k == nil || string(k.Key()) != "key" || k.Interactive() {
- t.Error("invalid key retrieval")
+ os.Setenv("LOCKBOX_KEYMODE", "none")
+ os.Setenv("LOCKBOX_KEY", " test")
+ if _, err := config.NewKey(config.IgnoreKeyMode); err == nil || err.Error() != "key can NOT be set in this key mode" {
+ t.Errorf("invalid error: %v", err)
}
- os.Setenv("LOCKBOX_KEY", "key")
- k, err = config.GetKey(true)
- if err != nil || k == nil || len(k.Key()) != 1 || k.Key()[0] != 0 || k.Interactive() {
- t.Error("invalid key retrieval")
+ os.Setenv("LOCKBOX_KEYMODE", "ask")
+ os.Setenv("LOCKBOX_KEY", "test")
+ if _, err := config.NewKey(config.IgnoreKeyMode); err == nil || err.Error() != "key can NOT be set in this key mode" {
+ t.Errorf("invalid error: %v", err)
}
os.Setenv("LOCKBOX_KEYMODE", "command")
- os.Setenv("LOCKBOX_KEY", "invalid command text is long and invalid via shlex")
- if k, err := config.GetKey(false); err == nil || k != nil {
- t.Error("should have failed")
+ os.Setenv("LOCKBOX_KEY", " ")
+ if _, err := config.NewKey(config.IgnoreKeyMode); err == nil || err.Error() != "key MUST be set in this key mode" {
+ t.Errorf("invalid error: %v", err)
}
- os.Setenv("LOCKBOX_INTERACTIVE", "yes")
- os.Setenv("LOCKBOX_KEYMODE", "ask")
+ os.Setenv("LOCKBOX_KEYMODE", "plaintext")
os.Setenv("LOCKBOX_KEY", "")
- if k, err := config.GetKey(false); err != nil || k == nil || !k.Interactive() {
+ if _, err := config.NewKey(config.IgnoreKeyMode); err == nil || err.Error() != "key MUST be set in this key mode" {
t.Errorf("invalid error: %v", err)
}
os.Setenv("LOCKBOX_INTERACTIVE", "yes")
os.Setenv("LOCKBOX_KEYMODE", "ask")
os.Setenv("LOCKBOX_KEY", "")
- if k, err := config.GetKey(true); err != nil || k == nil || !k.Interactive() {
+ if _, err := config.NewKey(config.IgnoreKeyMode); err != nil {
t.Errorf("invalid error: %v", err)
}
os.Setenv("LOCKBOX_INTERACTIVE", "no")
+ if _, err := config.NewKey(config.IgnoreKeyMode); err == nil || err.Error() != "ask key mode requested in non-interactive mode" {
+ t.Errorf("invalid error: %v", err)
+ }
+}
+
+func TestAskKey(t *testing.T) {
+ defer os.Clearenv()
+ os.Setenv("LOCKBOX_KEYMODE", "")
+ os.Setenv("LOCKBOX_KEY", "test")
+ k, _ := config.NewKey(config.IgnoreKeyMode)
+ if k.Ask() {
+ t.Error("invalid ask key")
+ }
os.Setenv("LOCKBOX_KEYMODE", "ask")
os.Setenv("LOCKBOX_KEY", "")
- if k, err := config.GetKey(false); err == nil || err.Error() != "ask key mode requested in non-interactive mode" || k != nil {
- t.Errorf("invalid error: %v", err)
+ os.Setenv("LOCKBOX_INTERACTIVE", "no")
+ k, _ = config.NewKey(config.IgnoreKeyMode)
+ if k.Ask() {
+ t.Error("invalid ask key")
}
- os.Setenv("LOCKBOX_INTERACTIVE", "yes")
os.Setenv("LOCKBOX_KEYMODE", "ask")
- os.Setenv("LOCKBOX_KEY", "aaa")
- if k, err := config.GetKey(false); err == nil || err.Error() != "key can NOT be set in ask key mode" || k != nil {
+ os.Setenv("LOCKBOX_KEY", "")
+ os.Setenv("LOCKBOX_INTERACTIVE", "yes")
+ k, _ = config.NewKey(config.IgnoreKeyMode)
+ if !k.Ask() {
+ t.Error("invalid ask key")
+ }
+ fxn := func() (string, error) {
+ return "", errors.New("TEST")
+ }
+ _, err := k.Read(fxn)
+ if err == nil || err.Error() != "TEST" {
+ t.Errorf("invalid error: %v", err)
+ }
+ fxn = func() (string, error) {
+ return "", nil
+ }
+ _, err = k.Read(fxn)
+ if err == nil || err.Error() != "key is empty" {
+ t.Errorf("invalid error: %v", err)
+ }
+ fxn = func() (string, error) {
+ return "abc", nil
+ }
+ val, err := k.Read(fxn)
+ if err != nil || val != "abc" {
+ t.Errorf("invalid error: %v", err)
+ }
+}
+
+func TestIgnoreKey(t *testing.T) {
+ defer os.Clearenv()
+ os.Setenv("LOCKBOX_KEYMODE", "ignore")
+ os.Setenv("LOCKBOX_KEY", "test")
+ if _, err := config.NewKey(config.IgnoreKeyMode); err != nil {
+ t.Errorf("invalid error: %v", err)
+ }
+ os.Setenv("LOCKBOX_KEYMODE", "ignore")
+ os.Setenv("LOCKBOX_KEY", "")
+ if _, err := config.NewKey(config.IgnoreKeyMode); err != nil {
+ t.Errorf("invalid error: %v", err)
+ }
+}
+
+func TestReadErrors(t *testing.T) {
+ k := config.Key{}
+ if _, err := k.Read(nil); err == nil || err.Error() != "invalid function given" {
+ t.Errorf("invalid error: %v", err)
+ }
+ fxn := func() (string, error) {
+ return "", nil
+ }
+ if _, err := k.Read(fxn); err == nil || err.Error() != "invalid key given" {
+ t.Errorf("invalid error: %v", err)
+ }
+}
+
+func TestPlainKey(t *testing.T) {
+ defer os.Clearenv()
+ os.Setenv("LOCKBOX_KEYMODE", "plaintext")
+ os.Setenv("LOCKBOX_KEY", " test ")
+ k, err := config.NewKey(config.IgnoreKeyMode)
+ if err != nil {
+ t.Errorf("invalid error: %v", err)
+ }
+ fxn := func() (string, error) {
+ return "", nil
+ }
+ val, err := k.Read(fxn)
+ if err != nil || val != " test " {
+ t.Errorf("invalid error: %v", err)
+ }
+}
+
+func TestReadIgnoreOrNoKey(t *testing.T) {
+ defer os.Clearenv()
+ os.Setenv("LOCKBOX_KEYMODE", "ignore")
+ os.Setenv("LOCKBOX_KEY", "test")
+ k, err := config.NewKey(config.IgnoreKeyMode)
+ if err != nil {
+ t.Errorf("invalid error: %v", err)
+ }
+ fxn := func() (string, error) {
+ return "", nil
+ }
+ val, err := k.Read(fxn)
+ if err != nil || val != "" {
+ t.Errorf("invalid error: %v", err)
+ }
+ os.Setenv("LOCKBOX_KEYMODE", "ignore")
+ os.Setenv("LOCKBOX_KEY", "")
+ k, err = config.NewKey(config.IgnoreKeyMode)
+ if err != nil {
+ t.Errorf("invalid error: %v", err)
+ }
+ val, err = k.Read(fxn)
+ if err != nil || val != "" {
+ t.Errorf("invalid error: %v", err)
+ }
+ os.Setenv("LOCKBOX_KEYMODE", "none")
+ k, err = config.NewKey(config.IgnoreKeyMode)
+ if err != nil {
+ t.Errorf("invalid error: %v", err)
+ }
+ val, err = k.Read(fxn)
+ if err != nil || val != "" {
+ t.Errorf("invalid error: %v", err)
+ }
+}
+
+func TestCommandKey(t *testing.T) {
+ defer os.Clearenv()
+ os.Setenv("LOCKBOX_KEYMODE", "command")
+ os.Setenv("LOCKBOX_KEY", "thisisagarbagekey")
+ k, err := config.NewKey(config.IgnoreKeyMode)
+ if err != nil {
+ t.Errorf("invalid error: %v", err)
+ }
+ fxn := func() (string, error) {
+ return "", nil
+ }
+ _, err = k.Read(fxn)
+ if err == nil || !strings.HasPrefix(err.Error(), "key command failed:") {
t.Errorf("invalid error: %v", err)
}
}
diff --git a/internal/config/vars.go b/internal/config/vars.go
@@ -13,9 +13,6 @@ import (
const (
prefixKey = "LOCKBOX_"
clipBaseEnv = prefixKey + "CLIP_"
- plainKeyMode = "plaintext"
- askKeyMode = "ask"
- commandKeyMode = "command"
commandArgsExample = "[cmd args...]"
fileExample = "<file>"
detectedValue = "<detected>"
@@ -80,7 +77,7 @@ var (
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..."}
// 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}}
- 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{askKeyMode, commandKeyMode, plainKeyMode}, canDefault: true, defaultValue: commandKeyMode}
+ envKeyMode = EnvironmentString{environmentBase: environmentBase{key: prefixKey + "KEYMODE", requirement: "must be set to a valid mode when using a key", desc: fmt.Sprintf("How to retrieve the database store password.\nSet to '%s' when only using a key file\nSet to '%s' to ignore the set key value", noKeyMode, IgnoreKeyMode), whenUnset: string(DefaultKeyMode)}, allowed: []string{string(askKeyMode), string(commandKeyMode), string(IgnoreKeyMode), string(noKeyMode), string(plainKeyMode)}, canDefault: true, defaultValue: ""}
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 maximum number of times to expand the input env to resolve variables,\nset to 0 to disable expansion. This value can NOT be an expansion itself\nif set in the env config file."}, shortDesc: "max expands", allowZero: true, defaultValue: 20}
)
@@ -127,6 +124,9 @@ func ListEnvironmentVariables() []string {
value, allow := item.values()
if len(value) == 0 {
value = "(unset)"
+ if env.whenUnset != "" {
+ value = env.whenUnset
+ }
}
description := strings.ReplaceAll(env.desc, "\n", "\n ")
requirement := "optional/default"
diff --git a/internal/platform/os.go b/internal/platform/os.go
@@ -9,14 +9,10 @@ import (
"os"
"strings"
"syscall"
-
- "github.com/enckse/lockbox/internal/config"
)
type (
stdinReaderFunc func(string) (bool, error)
- // PasswordReader is for input password handling (stdin) as a db key
- PasswordReader func() (string, error)
)
func termEcho(on bool) {
@@ -71,28 +67,6 @@ func GetUserInputPassword(piping, multiLine bool) ([]byte, error) {
return []byte(password), nil
}
-// ReadKey will attempt to resolve a key (if interactive) for the platform
-func ReadKey(key *config.Key, fxn PasswordReader) (string, error) {
- if fxn == nil {
- return "", errors.New("invalid function given")
- }
- if key == nil {
- return "", nil
- }
- useKey := string(key.Key())
- if key.Interactive() {
- read, err := fxn()
- if err != nil {
- return "", err
- }
- if len(read) == 0 {
- return "", errors.New("interactive password can NOT be empty")
- }
- useKey = read
- }
- return useKey, nil
-}
-
// ReadInteractivePassword will prompt for a single password for unlocking
func ReadInteractivePassword() (string, error) {
termEcho(false)
diff --git a/internal/platform/os_test.go b/internal/platform/os_test.go
@@ -1,12 +1,10 @@
package platform_test
import (
- "errors"
"os"
"path/filepath"
"testing"
- "github.com/enckse/lockbox/internal/config"
"github.com/enckse/lockbox/internal/platform"
)
@@ -21,35 +19,3 @@ func TestPathExist(t *testing.T) {
t.Error("test dir SHOULD exist")
}
}
-
-func TestReadKey(t *testing.T) {
- o, err := platform.ReadKey(nil, nil)
- if o != "" || err == nil || err.Error() != "invalid function given" {
- t.Errorf("invalid error: %v", err)
- }
- fxn := func() (string, error) {
- return "", nil
- }
- o, err = platform.ReadKey(nil, fxn)
- if o != "" || err != nil {
- t.Errorf("invalid error: %v", err)
- }
- o, err = platform.ReadKey(&config.Key{}, fxn)
- if o != "" || err == nil || err.Error() != "interactive password can NOT be empty" {
- t.Errorf("invalid error: %v", err)
- }
- fxn = func() (string, error) {
- return "abc", errors.New("test error")
- }
- o, err = platform.ReadKey(&config.Key{}, fxn)
- if o != "" || err == nil || err.Error() != "test error" {
- t.Errorf("invalid error: %v", err)
- }
- fxn = func() (string, error) {
- return "abc", nil
- }
- o, err = platform.ReadKey(&config.Key{}, fxn)
- if o != "abc" || err != nil {
- t.Errorf("invalid error: %v", err)
- }
-}
diff --git a/tests/run.sh b/tests/run.sh
@@ -14,7 +14,11 @@ _execute() {
export LOCKBOX_TOTP=totp
export LOCKBOX_INTERACTIVE=no
export LOCKBOX_READONLY=no
- export LOCKBOX_KEYMODE=plaintext
+ if [ "$LOCKBOX_KEY" == "" ]; then
+ export LOCKBOX_KEYMODE=none
+ else
+ export LOCKBOX_KEYMODE=plaintext
+ fi
export LOCKBOX_JSON_DATA_HASH_LENGTH=0
echo test2 |${LB_BINARY} insert keys/k/one2
oldmode="$LOCKBOX_KEYMODE"
@@ -125,8 +129,9 @@ _config() {
}
_invalid() {
- local keyfile oldkey oldkeyfile
+ local keyfile oldkey oldkeyfile oldmode
oldkey="$LOCKBOX_KEY"
+ oldmode="$LOCKBOX_KEYMODE"
oldkeyfile="$LOCKBOX_KEYFILE"
if [ -n "$LOCKBOX_KEYFILE" ]; then
export LOCKBOX_KEYFILE=""
@@ -138,9 +143,13 @@ _invalid() {
echo "invalid" > "$keyfile"
export LOCKBOX_KEYFILE="$keyfile"
fi
+ if [ "$oldmode" == "none" ]; then
+ export LOCKBOX_KEYMODE="plaintext"
+ fi
${LB_BINARY} ls
export LOCKBOX_KEYFILE="$oldkeyfile"
export LOCKBOX_KEY="$oldkey"
+ export LOCKBOX_KEYMODE="$oldmode"
}
_rekey() {