lockbox

password manager
Log | Files | Refs | README | LICENSE

commit e071ba30f6128511b86ee2a99ba66ad6ae0a06db
parent 25faededdb01bc44a02df10d363a6bfb699e214e
Author: Sean Enck <sean@ttypty.com>
Date:   Mon,  9 Jun 2025 14:51:33 -0400

deprecate ask mode

Diffstat:
Mcmd/lb/main_test.go | 9---------
Mcmd/lb/tests/expected.log | 1-
Minternal/app/completions/core.go | 2--
Minternal/app/completions/core_test.go | 4+---
Minternal/app/completions/shell/bash.sh | 20+++++---------------
Minternal/app/completions/shell/zsh.sh | 20+++++---------------
Minternal/config/key.go | 30+++---------------------------
Minternal/config/key_test.go | 86++++++-------------------------------------------------------------------------
Minternal/config/vars.go | 2+-
Minternal/kdbx/actions.go | 3+--
10 files changed, 22 insertions(+), 155 deletions(-)

diff --git a/cmd/lb/main_test.go b/cmd/lb/main_test.go @@ -179,15 +179,6 @@ func test(profile string) error { } r.writeConfig(c) r.run("echo testing |", "insert test1/key1/password") - if hasPass { - delete(c, "credentials.password") - c["interactive"] = "true" - c["credentials.password_mode"] = c.quoteString("ask") - r.writeConfig(c) - } else { - r.logAppend("printf \"password: \"") - } - r.raw(fmt.Sprintf("echo %s |", testPass), "ls", r.log, "/dev/null") c = r.newConf() c["interactive"] = "false" if hasPass { diff --git a/cmd/lb/tests/expected.log b/cmd/lb/tests/expected.log @@ -1,4 +1,3 @@ -password: test1/key1/password 'test3' is not an allowed field name '' is not an allowed field name 'still' is not an allowed field name diff --git a/internal/app/completions/core.go b/internal/app/completions/core.go @@ -45,7 +45,6 @@ type ( ReadOnly string CanClip string CanTOTP string - AskMode string Ever string } Exported []string @@ -89,7 +88,6 @@ func NewConditionals() Conditionals { c.Not.ReadOnly = registerIsNotEqual(config.EnvReadOnly, config.YesValue) c.Not.CanClip = registerIsNotEqual(config.EnvClipEnabled, config.NoValue) c.Not.CanTOTP = registerIsNotEqual(config.EnvTOTPEnabled, config.NoValue) - c.Not.AskMode = registerIsNotEqual(config.EnvPasswordMode, string(config.AskKeyMode)) c.Not.Ever = fmt.Sprintf(shellIsNotText, "1", "0") return c } diff --git a/internal/app/completions/core_test.go b/internal/app/completions/core_test.go @@ -23,7 +23,7 @@ func TestCompletions(t *testing.T) { func TestConditionals(t *testing.T) { c := completions.NewConditionals() sort.Strings(c.Exported) - need := []string{"LOCKBOX_CLIP_ENABLED", "LOCKBOX_CREDENTIALS_PASSWORD_MODE", "LOCKBOX_READONLY", "LOCKBOX_TOTP_ENABLED"} + need := []string{"LOCKBOX_CLIP_ENABLED", "LOCKBOX_READONLY", "LOCKBOX_TOTP_ENABLED"} if len(c.Exported) != len(need) || fmt.Sprintf("%v", c.Exported) != fmt.Sprintf("%v", need) { t.Errorf("invalid exports: %v", c.Exported) } @@ -36,8 +36,6 @@ func TestConditionals(t *testing.T) { switch n { case "LOCKBOX_READONLY": value = "true" - case "LOCKBOX_CREDENTIALS_PASSWORD_MODE": - value = "ask" } if !slices.Contains(fields, fmt.Sprintf(`[ "$%s" != "%s" ]`, n, value)) { t.Errorf("needed conditional %s not found: %v", n, fields) diff --git a/internal/app/completions/shell/bash.sh b/internal/app/completions/shell/bash.sh @@ -34,14 +34,10 @@ _{{ $.Executable }}() { opts="{{ $.HelpAdvancedCommand }} {{ $.HelpConfigCommand }}" ;; "{{ $.MoveCommand }}" | "{{ $.RemoveCommand }}" | "{{ $.InsertCommand }}") - if {{ $.Conditionals.Not.AskMode }}; then - opts="$opts $({{ $.DoGroups }})" - fi + opts="$opts $({{ $.DoGroups }})" ;; "{{ $.UnsetCommand }}") - if {{ $.Conditionals.Not.AskMode }}; then - opts="$opts $({{ $.DoList }})" - fi + opts="$opts $({{ $.DoList }})" ;; "{{ $.TOTPCommand }}") opts="{{ $.TOTPListCommand }} " @@ -52,27 +48,21 @@ _{{ $.Executable }}() { {{- end}} ;; "{{ $.ShowCommand }}" | "{{ $.JSONCommand }}" | "{{ $.ClipCommand }}") - if {{ $.Conditionals.Not.AskMode }}; then - opts=$({{ $.DoList }}) - fi + opts=$({{ $.DoList }}) ;; esac else if [ "$COMP_CWORD" -eq 3 ]; then case "$chosen" in "{{ $.MoveCommand }}") - if {{ $.Conditionals.Not.AskMode }}; then - opts=$({{ $.DoGroups }}) - fi + opts=$({{ $.DoGroups }}) ;; "{{ $.TOTPCommand }}") case "${COMP_WORDS[2]}" in {{- range $key, $value := $.TOTPSubCommands }} "{{ $value.Key }}") if {{ $value.Conditional }}; then - if {{ $.Conditionals.Not.AskMode }}; then - opts=$({{ $.DoTOTPList }}) - fi + opts=$({{ $.DoTOTPList }}) fi ;; {{- end}} diff --git a/internal/app/completions/shell/zsh.sh b/internal/app/completions/shell/zsh.sh @@ -47,24 +47,18 @@ _{{ $.Executable }}() { ;; "{{ $.RemoveCommand }}" | "{{ $.InsertCommand }}") if [ "$len" -eq 3 ]; then - if {{ $.Conditionals.Not.AskMode }}; then - compadd "$@" $({{ $.DoGroups }}) - fi + compadd "$@" $({{ $.DoGroups }}) fi ;; "{{ $.UnsetCommand }}") if [ "$len" -eq 3 ]; then - if {{ $.Conditionals.Not.AskMode }}; then - compadd "$@" $({{ $.DoList }}) - fi + compadd "$@" $({{ $.DoList }}) fi ;; "{{ $.MoveCommand }}") case "$len" in 3 | 4) - if {{ $.Conditionals.Not.AskMode }}; then - compadd "$@" $({{ $.DoGroups }}) - fi + compadd "$@" $({{ $.DoGroups }}) ;; esac ;; @@ -83,9 +77,7 @@ _{{ $.Executable }}() { {{- range $key, $value := .TOTPSubCommands }} "{{ $value.Key }}") if {{ $value.Conditional }}; then - if {{ $.Conditionals.Not.AskMode }}; then - compadd "$@" $({{ $.DoTOTPList }}) - fi + compadd "$@" $({{ $.DoTOTPList }}) fi ;; {{- end}} @@ -94,9 +86,7 @@ _{{ $.Executable }}() { ;; "{{ $.ShowCommand }}" | "{{ $.JSONCommand }}" | "{{ $.ClipCommand }}") if [ "$len" -eq 3 ]; then - if {{ $.Conditionals.Not.AskMode }}; then - compadd "$@" $({{ $.DoList }}) - fi + compadd "$@" $({{ $.DoList }}) fi ;; esac diff --git a/internal/config/key.go b/internal/config/key.go @@ -11,8 +11,6 @@ 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 { mode KeyModeType @@ -23,9 +21,7 @@ type ( const ( plainKeyMode KeyModeType = "plaintext" - // AskKeyMode is the mode in which the user is prompted for key input (each time) - AskKeyMode KeyModeType = "ask" - noKeyMode KeyModeType = "none" + noKeyMode KeyModeType = "none" // IgnoreKeyMode will ignore the value set in the key (acts like no key) IgnoreKeyMode KeyModeType = "ignore" commandKeyMode KeyModeType = "command" @@ -46,12 +42,6 @@ func NewKey(defaultKeyModeType KeyModeType) (Key, error) { case string(noKeyMode): requireEmptyKey = true case string(commandKeyMode), string(plainKeyMode): - case string(AskKeyMode): - isInteractive := EnvInteractive.Get() - if !isInteractive { - return Key{}, errors.New("ask key mode requested in non-interactive mode") - } - requireEmptyKey = true default: return Key{}, fmt.Errorf("unknown key mode: %s", keyMode) } @@ -78,20 +68,12 @@ 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") - } +func (k Key) Read() (string, error) { if !k.valid { return "", errors.New("invalid key given") } - if k.empty() && !k.Ask() { + if k.empty() { return "", nil } var useKey string @@ -99,12 +81,6 @@ func (k Key) Read(ask AskPassword) (string, error) { useKey = k.inputKey[0] } switch k.mode { - case AskKeyMode: - read, err := ask() - if err != nil { - return "", err - } - useKey = read case commandKeyMode: exe := k.inputKey[0] var args []string diff --git a/internal/config/key_test.go b/internal/config/key_test.go @@ -1,7 +1,6 @@ package config_test import ( - "errors" "strings" "testing" @@ -32,11 +31,6 @@ func TestNewKeyErrors(t *testing.T) { 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) } - store.SetString("LOCKBOX_CREDENTIALS_PASSWORD_MODE", "ask") - store.SetArray("LOCKBOX_CREDENTIALS_PASSWORD", []string{"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) - } store.SetString("LOCKBOX_CREDENTIALS_PASSWORD_MODE", "command") store.SetArray("LOCKBOX_CREDENTIALS_PASSWORD", []string{}) if _, err := config.NewKey(config.IgnoreKeyMode); err == nil || err.Error() != "key MUST be set in this key mode" { @@ -47,59 +41,6 @@ func TestNewKeyErrors(t *testing.T) { if _, err := config.NewKey(config.IgnoreKeyMode); err == nil || err.Error() != "key MUST be set in this key mode" { t.Errorf("invalid error: %v", err) } - store.SetBool("LOCKBOX_INTERACTIVE", true) - store.SetString("LOCKBOX_CREDENTIALS_PASSWORD_MODE", "ask") - store.SetArray("LOCKBOX_CREDENTIALS_PASSWORD", []string{}) - if _, err := config.NewKey(config.IgnoreKeyMode); err != nil { - t.Errorf("invalid error: %v", err) - } - store.SetBool("LOCKBOX_INTERACTIVE", false) - 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) { - store.Clear() - store.SetArray("LOCKBOX_CREDENTIALS_PASSWORD", []string{"test"}) - k, _ := config.NewKey(config.IgnoreKeyMode) - if k.Ask() { - t.Error("invalid ask key") - } - store.SetString("LOCKBOX_CREDENTIALS_PASSWORD_MODE", "ask") - store.SetArray("LOCKBOX_CREDENTIALS_PASSWORD", []string{}) - store.SetBool("LOCKBOX_INTERACTIVE", false) - k, _ = config.NewKey(config.IgnoreKeyMode) - if k.Ask() { - t.Error("invalid ask key") - } - store.SetString("LOCKBOX_CREDENTIALS_PASSWORD_MODE", "ask") - store.SetBool("LOCKBOX_INTERACTIVE", true) - 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) { @@ -119,13 +60,7 @@ func TestIgnoreKey(t *testing.T) { func TestReadErrors(t *testing.T) { store.Clear() 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" { + if _, err := k.Read(); err == nil || err.Error() != "invalid key given" { t.Errorf("invalid error: %v", err) } } @@ -138,10 +73,7 @@ func TestPlainKey(t *testing.T) { if err != nil { t.Errorf("invalid error: %v", err) } - fxn := func() (string, error) { - return "", nil - } - val, err := k.Read(fxn) + val, err := k.Read() if err != nil || val != "test" { t.Errorf("invalid error: %v", err) } @@ -155,10 +87,7 @@ func TestReadIgnoreOrNoKey(t *testing.T) { if err != nil { t.Errorf("invalid error: %v", err) } - fxn := func() (string, error) { - return "", nil - } - val, err := k.Read(fxn) + val, err := k.Read() if err != nil || val != "" { t.Errorf("invalid error: %v", err) } @@ -168,7 +97,7 @@ func TestReadIgnoreOrNoKey(t *testing.T) { if err != nil { t.Errorf("invalid error: %v", err) } - val, err = k.Read(fxn) + val, err = k.Read() if err != nil || val != "" { t.Errorf("invalid error: %v", err) } @@ -177,7 +106,7 @@ func TestReadIgnoreOrNoKey(t *testing.T) { if err != nil { t.Errorf("invalid error: %v", err) } - val, err = k.Read(fxn) + val, err = k.Read() if err != nil || val != "" { t.Errorf("invalid error: %v", err) } @@ -191,10 +120,7 @@ func TestCommandKey(t *testing.T) { if err != nil { t.Errorf("invalid error: %v", err) } - fxn := func() (string, error) { - return "", nil - } - _, err = k.Read(fxn) + _, err = k.Read() 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 @@ -199,7 +199,7 @@ must be a list of one (or more) rules where a '%s' delimits the start and end se description: fmt.Sprintf(`How to retrieve the database store password. Set to '%s' when only using a key file. Set to '%s' to ignore the set key value`, noKeyMode, IgnoreKeyMode), }), - allowed: []string{string(AskKeyMode), string(commandKeyMode), string(IgnoreKeyMode), string(noKeyMode), string(plainKeyMode)}, + allowed: []string{string(commandKeyMode), string(IgnoreKeyMode), string(noKeyMode), string(plainKeyMode)}, flags: []stringsFlags{canDefaultFlag}, }, }) diff --git a/internal/kdbx/actions.go b/internal/kdbx/actions.go @@ -9,7 +9,6 @@ import ( "time" "git.sr.ht/~enckse/lockbox/internal/config" - "git.sr.ht/~enckse/lockbox/internal/platform" "github.com/tobischo/gokeepasslib/v3" ) @@ -25,7 +24,7 @@ func (t *Transaction) act(cb action) error { if err != nil { return err } - k, err := key.Read(platform.ReadInteractivePassword) + k, err := key.Read() if err != nil { return err }