commit e169c2834d76b50eddc556db0e111faac8ccce4c
parent b1c2db039ea5d3fef59996151bbf22405cfe9389
Author: Sean Enck <sean@ttypty.com>
Date: Sun, 6 Oct 2024 19:35:34 -0400
disable state in ask pass mode
Diffstat:
9 files changed, 105 insertions(+), 55 deletions(-)
diff --git a/internal/app/completions.go b/internal/app/completions.go
@@ -34,6 +34,7 @@ type (
ReadOnly string
NoClip string
NoTOTP string
+ AskMode string
}
}
// CompletionOption are conditional wrapped logic for options that may be disabled
@@ -41,26 +42,31 @@ type (
Conditional string
Key string
}
+ shellPreparer interface {
+ ShellIsNotConditional(string) string
+ }
+ emptyShellPreparer struct{}
)
//go:embed shell/*
var shell embed.FS
-func newConditional(left, right string) string {
- return fmt.Sprintf("[ \"%s\" != \"%s\" ]", left, right)
+func (e emptyShellPreparer) ShellIsNotConditional(s string) string {
+ return fmt.Sprintf(config.ShellIsNotConditional, "1", s)
}
-func newGenOptions(defaults []string, kv map[string]string) []CompletionOption {
- genOption := func(to []CompletionOption, command, left, right string) []CompletionOption {
- conditional := newConditional(left, right)
- return append(to, CompletionOption{conditional, command})
+func newGenOptions(defaults []string, kv map[string]shellPreparer) []CompletionOption {
+ genOption := func(to []CompletionOption, command string, prep shellPreparer, compareTo string) []CompletionOption {
+ val := prep.ShellIsNotConditional(compareTo)
+ return append(to, CompletionOption{val, command})
}
opt := []CompletionOption{}
+ emptyPrepare := emptyShellPreparer{}
for _, a := range defaults {
- opt = genOption(opt, a, "1", "0")
+ opt = genOption(opt, a, emptyPrepare, "0")
}
for key, env := range kv {
- opt = genOption(opt, key, fmt.Sprintf("$%s", env), config.YesValue)
+ opt = genOption(opt, key, env, config.YesValue)
}
return opt
}
@@ -86,24 +92,25 @@ func GenerateCompletions(completionType, exe string) ([]string, error) {
DoList: fmt.Sprintf("%s %s", exe, ListCommand),
DoTOTPList: fmt.Sprintf("%s %s %s", exe, TOTPCommand, TOTPListCommand),
}
- c.Conditionals.ReadOnly = newConditional(config.EnvReadOnly.Key(), config.YesValue)
- c.Conditionals.NoClip = newConditional(config.EnvNoClip.Key(), config.YesValue)
- c.Conditionals.NoTOTP = newConditional(config.EnvNoTOTP.Key(), config.YesValue)
+ c.Conditionals.ReadOnly = config.EnvReadOnly.ShellIsNotConditional(config.YesValue)
+ c.Conditionals.NoClip = config.EnvNoClip.ShellIsNotConditional(config.YesValue)
+ c.Conditionals.NoTOTP = config.EnvNoTOTP.ShellIsNotConditional(config.YesValue)
+ c.Conditionals.AskMode = config.KeyModeAskConditional()
c.Options = newGenOptions([]string{EnvCommand, HelpCommand, ListCommand, ShowCommand, VersionCommand, JSONCommand},
- map[string]string{
- ClipCommand: config.EnvNoClip.Key(),
- TOTPCommand: config.EnvNoTOTP.Key(),
- MoveCommand: config.EnvReadOnly.Key(),
- RemoveCommand: config.EnvReadOnly.Key(),
- InsertCommand: config.EnvReadOnly.Key(),
- MultiLineCommand: config.EnvReadOnly.Key(),
- PasswordGenerateCommand: config.EnvNoPasswordGen.Key(),
+ map[string]shellPreparer{
+ ClipCommand: config.EnvNoClip,
+ TOTPCommand: config.EnvNoTOTP,
+ MoveCommand: config.EnvReadOnly,
+ RemoveCommand: config.EnvReadOnly,
+ InsertCommand: config.EnvReadOnly,
+ MultiLineCommand: config.EnvReadOnly,
+ PasswordGenerateCommand: config.EnvNoPasswordGen,
})
c.TOTPSubCommands = newGenOptions([]string{TOTPMinimalCommand, TOTPOnceCommand, TOTPShowCommand},
- map[string]string{
- TOTPClipCommand: config.EnvNoClip.Key(),
- TOTPInsertCommand: config.EnvReadOnly.Key(),
+ map[string]shellPreparer{
+ TOTPClipCommand: config.EnvNoClip,
+ TOTPInsertCommand: config.EnvReadOnly,
})
using, err := readShell(completionType)
if err != nil {
diff --git a/internal/app/shell/bash.sh b/internal/app/shell/bash.sh
@@ -33,7 +33,9 @@ _{{ $.Executable }}() {
opts="{{ $.HelpAdvancedCommand }}"
;;
"{{ $.InsertCommand }}" | "{{ $.MultiLineCommand }}" | "{{ $.MoveCommand }}" | "{{ $.RemoveCommand }}")
- opts="$opts $({{ $.DoList }})"
+ if {{ $.Conditionals.AskMode }}; then
+ opts="$opts $({{ $.DoList }})"
+ fi
;;
"{{ $.TOTPCommand }}")
opts="{{ $.TOTPListCommand }} "
@@ -44,21 +46,27 @@ _{{ $.Executable }}() {
{{- end}}
;;
"{{ $.ShowCommand }}" | "{{ $.JSONCommand }}" | "{{ $.ClipCommand }}")
- opts=$({{ $.DoList }})
+ if {{ $.Conditionals.AskMode }}; then
+ opts=$({{ $.DoList }})
+ fi
;;
esac
else
if [ "$COMP_CWORD" -eq 3 ]; then
case "$chosen" in
"{{ $.MoveCommand }}")
- opts=$({{ $.DoList }})
+ if {{ $.Conditionals.AskMode }}; then
+ opts=$({{ $.DoList }})
+ fi
;;
"{{ $.TOTPCommand }}")
case "${COMP_WORDS[2]}" in
{{- range $key, $value := $.TOTPSubCommands }}
"{{ $value.Key }}")
if {{ $value.Conditional }}; then
- opts=$({{ $.DoTOTPList }})
+ if {{ $.Conditionals.AskMode }}; then
+ opts=$({{ $.DoTOTPList }})
+ fi
fi
;;
{{- end}}
diff --git a/internal/app/shell/fish.sh b/internal/app/shell/fish.sh
@@ -14,8 +14,10 @@ function {{ $.Executable }}-completion
complete -c {{ $.Executable }} -n "not __fish_seen_subcommand_from $commands" -a "$commands"
complete -c {{ $.Executable }} -n "__fish_seen_subcommand_from {{ $.HelpCommand }}; and test (count (commandline -opc)) -lt 3" -a "{{ $.HelpAdvancedCommand }}"
if {{ $.Conditionals.ReadOnly }}
- complete -c {{ $.Executable }} -n "__fish_seen_subcommand_from {{ $.InsertCommand }} {{ $.MultiLineCommand }} {{ $.RemoveCommand }}; and test (count (commandline -opc)) -lt 3" -a "({{ $.DoList }})"
- complete -c {{ $.Executable }} -n "__fish_seen_subcommand_from {{ $.MoveCommand }}; and test (count (commandline -opc)) -lt 4" -a "({{ $.DoList }})"
+ if {{ $.Conditionals.AskMode }}
+ complete -c {{ $.Executable }} -n "__fish_seen_subcommand_from {{ $.InsertCommand }} {{ $.MultiLineCommand }} {{ $.RemoveCommand }}; and test (count (commandline -opc)) -lt 3" -a "({{ $.DoList }})"
+ complete -c {{ $.Executable }} -n "__fish_seen_subcommand_from {{ $.MoveCommand }}; and test (count (commandline -opc)) -lt 4" -a "({{ $.DoList }})"
+ end
end
if {{ $.Conditionals.NoTOTP }}
set -f totps ""
@@ -28,12 +30,18 @@ function {{ $.Executable }}-completion
end
{{- end }}
complete -c {{ $.Executable }} -n "__fish_seen_subcommand_from {{ $.TOTPCommand }}; and not __fish_seen_subcommand_from $totps" -a "$totps"
- complete -c {{ $.Executable }} -n "__fish_seen_subcommand_from {{ $.TOTPCommand }}; and __fish_seen_subcommand_from $totps; and test (count (commandline -opc)) -lt 4" -a "({{ $.DoTOTPList }})"
+ if {{ $.Conditionals.AskMode }}
+ complete -c {{ $.Executable }} -n "__fish_seen_subcommand_from {{ $.TOTPCommand }}; and __fish_seen_subcommand_from $totps; and test (count (commandline -opc)) -lt 4" -a "({{ $.DoTOTPList }})"
+ end
end
if {{ $.Conditionals.NoClip }}
- complete -c {{ $.Executable }} -n "__fish_seen_subcommand_from {{ $.ClipCommand }}; and test (count (commandline -opc)) -lt 3" -a "({{ $.DoList}})"
+ if {{ $.Conditionals.AskMode }}
+ complete -c {{ $.Executable }} -n "__fish_seen_subcommand_from {{ $.ClipCommand }}; and test (count (commandline -opc)) -lt 3" -a "({{ $.DoList}})"
+ end
+ end
+ if {{ $.Conditionals.AskMode }}
+ complete -c {{ $.Executable }} -n "__fish_seen_subcommand_from {{ $.ShowCommand }} {{ $.JSONCommand }}; and test (count (commandline -opc)) -lt 3" -a "({{ $.DoList}})"
end
- complete -c {{ $.Executable }} -n "__fish_seen_subcommand_from {{ $.ShowCommand }} {{ $.JSONCommand }}; and test (count (commandline -opc)) -lt 3" -a "({{ $.DoList}})"
end
{{ $.Executable }}-completion
diff --git a/internal/app/shell/zsh.sh b/internal/app/shell/zsh.sh
@@ -46,13 +46,17 @@ _{{ $.Executable }}() {
;;
"{{ $.InsertCommand }}" | "{{ $.MultiLineCommand }}" | "{{ $.RemoveCommand }}")
if [ "$len" -eq 3 ]; then
- compadd "$@" $({{ $.DoList }})
+ if {{ $.Conditionals.AskMode }}; then
+ compadd "$@" $({{ $.DoList }})
+ fi
fi
;;
"{{ $.MoveCommand }}")
case "$len" in
3 | 4)
- compadd "$@" $({{ $.DoList }})
+ if {{ $.Conditionals.AskMode }}; then
+ compadd "$@" $({{ $.DoList }})
+ fi
;;
esac
;;
@@ -71,7 +75,9 @@ _{{ $.Executable }}() {
{{- range $key, $value := .TOTPSubCommands }}
"{{ $value.Key }}")
if {{ $value.Conditional }}; then
- compadd "$@" $({{ $.DoTOTPList }})
+ if {{ $.Conditionals.AskMode }}; then
+ compadd "$@" $({{ $.DoTOTPList }})
+ fi
fi
;;
{{- end}}
@@ -80,7 +86,9 @@ _{{ $.Executable }}() {
;;
"{{ $.ShowCommand }}" | "{{ $.JSONCommand }}" | "{{ $.ClipCommand }}")
if [ "$len" -eq 3 ]; then
- compadd "$@" $({{ $.DoList }})
+ if {{ $.Conditionals.AskMode }}; then
+ compadd "$@" $({{ $.DoList }})
+ fi
fi
;;
esac
diff --git a/internal/config/core.go b/internal/config/core.go
@@ -34,6 +34,8 @@ const (
YesValue = yes
// TemplateVariable is used to handle '$' in shell vars (due to expansion)
TemplateVariable = "[%]"
+ // ShellIsNotConditional is the simple shell conditional statement used for env compares in any shell
+ ShellIsNotConditional = "[ \"%s\" != \"%s\" ]"
)
var (
@@ -129,13 +131,13 @@ func environOrDefault(envKey, defaultValue string) string {
return val
}
-func (e environmentBase) Key() string {
+func (e environmentBase) key() string {
return fmt.Sprintf("LOCKBOX_%s%s", string(e.cat), e.subKey)
}
// Get will get the boolean value for the setting
func (e EnvironmentBool) Get() (bool, error) {
- read := strings.ToLower(strings.TrimSpace(getExpand(e.Key())))
+ read := strings.ToLower(strings.TrimSpace(getExpand(e.key())))
switch read {
case no:
return false, nil
@@ -145,13 +147,13 @@ func (e EnvironmentBool) Get() (bool, error) {
return e.defaultValue, nil
}
- return false, fmt.Errorf("invalid yes/no env value for %s", e.Key())
+ return false, fmt.Errorf("invalid yes/no env value for %s", e.key())
}
// Get will get the integer value for the setting
func (e EnvironmentInt) Get() (int, error) {
val := e.defaultValue
- use := getExpand(e.Key())
+ use := getExpand(e.key())
if use != "" {
i, err := strconv.Atoi(use)
if err != nil {
@@ -179,14 +181,14 @@ func (e EnvironmentInt) Get() (int, error) {
// Get will read the string from the environment
func (e EnvironmentString) Get() string {
if !e.canDefault {
- return getExpand(e.Key())
+ return getExpand(e.key())
}
- return environOrDefault(e.Key(), e.defaultValue)
+ return environOrDefault(e.key(), e.defaultValue)
}
// Get will read (and shlex) the value if set
func (e EnvironmentCommand) Get() ([]string, error) {
- value := environOrDefault(e.Key(), "")
+ value := environOrDefault(e.key(), "")
if strings.TrimSpace(value) == "" {
return nil, nil
}
@@ -195,24 +197,24 @@ func (e EnvironmentCommand) Get() ([]string, error) {
// KeyValue will get the string representation of the key+value
func (e environmentBase) KeyValue(value string) string {
- return fmt.Sprintf("%s=%s", e.Key(), value)
+ return fmt.Sprintf("%s=%s", e.key(), value)
}
// Setenv will do an environment set for the value to key
func (e environmentBase) Set(value string) error {
- unset, err := IsUnset(e.Key(), value)
+ unset, err := IsUnset(e.key(), value)
if err != nil {
return err
}
if unset {
return nil
}
- return os.Setenv(e.Key(), value)
+ return os.Setenv(e.key(), value)
}
// Get will retrieve the value with the formatted input included
func (e EnvironmentFormatter) Get(value string) string {
- return e.fxn(e.Key(), value)
+ return e.fxn(e.key(), value)
}
func (e EnvironmentString) values() (string, []string) {
@@ -346,8 +348,8 @@ func Environ() []string {
var results []string
for _, k := range os.Environ() {
for _, r := range registeredEnv {
- key := r.self().Key()
- if key == EnvConfig.Key() {
+ key := r.self().key()
+ if key == EnvConfig.key() {
continue
}
key = fmt.Sprintf("%s=", key)
@@ -371,7 +373,7 @@ func ExpandParsed(inputs map[string]string) (map[string]string, error) {
}
var err error
var cycles int
- possibleCycles, ok := inputs[envConfigExpands.Key()]
+ possibleCycles, ok := inputs[envConfigExpands.key()]
if ok {
cycles, err = strconv.Atoi(possibleCycles)
} else {
@@ -507,3 +509,8 @@ func newDefaultedEnvironment[T any](val T, base environmentBase) environmentDefa
obj.defaultValue = val
return obj
}
+
+// ShellIsNotConditional will produces a shell-ready conditional statement
+func (e environmentBase) ShellIsNotConditional(compareTo string) string {
+ return fmt.Sprintf(ShellIsNotConditional, fmt.Sprintf("$%s", e.key()), compareTo)
+}
diff --git a/internal/config/core_test.go b/internal/config/core_test.go
@@ -52,10 +52,10 @@ func TestKeyValue(t *testing.T) {
}
}
-func TestKey(t *testing.T) {
- val := config.EnvStore.Key()
- if val != "LOCKBOX_STORE" {
- t.Errorf("invalid key")
+func TestShellConditional(t *testing.T) {
+ val := config.EnvStore.ShellIsNotConditional("x")
+ if val != `[ "$LOCKBOX_STORE" != "x" ]` {
+ t.Errorf("invalid conditiona: %s", val)
}
}
diff --git a/internal/config/key.go b/internal/config/key.go
@@ -117,3 +117,8 @@ func (k Key) Read(ask AskPassword) (string, error) {
}
return key, nil
}
+
+// KeyModeAskConditional will get the key mode for 'ask' as a shell conditional
+func KeyModeAskConditional() string {
+ return envKeyMode.ShellIsNotConditional(string(askKeyMode))
+}
diff --git a/internal/config/key_test.go b/internal/config/key_test.go
@@ -202,3 +202,10 @@ func TestCommandKey(t *testing.T) {
t.Errorf("invalid error: %v", err)
}
}
+
+func TestKeyModeAskConditional(t *testing.T) {
+ val := config.KeyModeAskConditional()
+ if val != `[ "$LOCKBOX_KEYMODE" != "ask" ]` {
+ t.Errorf("invalid value: %s", val)
+ }
+}
diff --git a/internal/config/vars.go b/internal/config/vars.go
@@ -120,7 +120,7 @@ var (
}),
})
// EnvDefaultCompletionKey is the key for default completion handling
- EnvDefaultCompletionKey = EnvDefaultCompletion.Key()
+ EnvDefaultCompletionKey = EnvDefaultCompletion.key()
// EnvNoColor indicates if color outputs are disabled
EnvNoColor = environmentRegister(
EnvironmentBool{
@@ -430,7 +430,7 @@ func ListEnvironmentVariables() []string {
if r != "" {
requirement = r
}
- text := fmt.Sprintf("\n%s\n%s requirement: %s\n default: %s\n options: %s\n", env.Key(), description, requirement, value, strings.Join(allow, "|"))
+ text := fmt.Sprintf("\n%s\n%s requirement: %s\n default: %s\n options: %s\n", env.key(), description, requirement, value, strings.Join(allow, "|"))
results = append(results, text)
}
sort.Strings(results)