lockbox

password manager
Log | Files | Refs | README | LICENSE

commit cb4515e735fac2892730f96e2900256faea0629b
parent cac6456f26a5d9bd9ae7666f72b01f9a5a228e93
Author: Sean Enck <sean@ttypty.com>
Date:   Sat, 16 Jul 2022 16:07:41 -0400

splitting logic out a bit

Diffstat:
Mcmd/lb/main.go | 133++++++++++++++++---------------------------------------------------------------
Ainternal/subcommands/clear.go | 43+++++++++++++++++++++++++++++++++++++++++++
Ainternal/subcommands/display.go | 80+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ainternal/subcommands/libexec.go | 32++++++++++++++++++++++++++++++++
Ainternal/subcommands/listfind.go | 34++++++++++++++++++++++++++++++++++
Mtests/expected.log | 2+-
6 files changed, 216 insertions(+), 108 deletions(-)

diff --git a/cmd/lb/main.go b/cmd/lb/main.go @@ -4,14 +4,9 @@ import ( "errors" "fmt" "os" - "os/exec" "path/filepath" - "sort" - "strings" - "time" "github.com/enckse/lockbox/internal/cli" - "github.com/enckse/lockbox/internal/colors" "github.com/enckse/lockbox/internal/dump" "github.com/enckse/lockbox/internal/encrypt" "github.com/enckse/lockbox/internal/hooks" @@ -19,6 +14,7 @@ import ( "github.com/enckse/lockbox/internal/misc" "github.com/enckse/lockbox/internal/platform" "github.com/enckse/lockbox/internal/store" + "github.com/enckse/lockbox/internal/subcommands" ) var ( @@ -47,7 +43,6 @@ func main() { misc.Die("missing arguments", errors.New("requires subcommand")) } command := args[1] - fs := store.NewFileSystemStore() switch command { case "ls", "list", "find": isFind := command == "find" @@ -58,16 +53,7 @@ func main() { } searchTerm = args[2] } - viewOptions := store.ViewOptions{Display: true} - if isFind { - viewOptions.Filter = func(inPath string) string { - if strings.Contains(inPath, searchTerm) { - return inPath - } - return "" - } - } - files, err := fs.List(viewOptions) + files, err := subcommands.ListFindCallback(subcommands.ListFindOptions{Find: isFind, Search: searchTerm, Store: store.NewFileSystemStore()}) if err != nil { misc.Die("unable to list files", err) } @@ -93,6 +79,7 @@ func main() { misc.Die("too many arguments", errors.New("insert can only perform one operation")) } isPipe := inputs.IsInputFromPipe() + fs := store.NewFileSystemStore() entry := getEntry(fs, args, idx) if misc.PathExists(entry) { if !isPipe { @@ -131,6 +118,7 @@ func main() { fmt.Println("") hooks.Run(hooks.Insert, hooks.PostStep) case "rm": + fs := store.NewFileSystemStore() entry := getEntry(fs, args, 2) if !misc.PathExists(entry) { misc.Die("does not exists", errors.New("can not delete unknown entry")) @@ -151,73 +139,22 @@ func main() { } } } + fs := store.NewFileSystemStore() inEntry := getEntry(fs, args, startEntry) - isShow := command == "show" || isDump - entries := []string{inEntry} - if strings.Contains(inEntry, "*") { - if inEntry == getEntry(fs, []string{"***"}, 0) { - all, err := fs.List(store.ViewOptions{}) - if err != nil { - misc.Die("unable to get all files", err) - } - entries = all - } else { - matches, err := filepath.Glob(inEntry) - if err != nil { - misc.Die("bad glob", err) - } - entries = matches - } - } - isGlob := len(entries) > 1 - if isGlob { - if !isShow { - misc.Die("cannot glob to clipboard", errors.New("bad glob request")) - } - sort.Strings(entries) - } - coloring, err := colors.NewTerminal(colors.Red) - if err != nil { - misc.Die("unable to get color for terminal", err) - } - dumpData := []dump.ExportEntity{} + opts := subcommands.DisplayOptions{Dump: isDump, Entry: inEntry, Show: command == "show" || isDump, Glob: getEntry(fs, []string{"***"}, 0), Store: fs} clipboard := platform.Clipboard{} - if !isShow { + var err error + if !opts.Show { clipboard, err = platform.NewClipboard() if err != nil { misc.Die("unable to get clipboard", err) } } - for _, entry := range entries { - if !misc.PathExists(entry) { - misc.Die("invalid entry", errors.New("entry not found")) - } - decrypt, err := encrypt.FromFile(entry) - if err != nil { - misc.Die("unable to decrypt", err) - } - value := strings.TrimSpace(string(decrypt)) - entity := dump.ExportEntity{} - if isShow { - if isGlob { - fileName := fs.CleanPath(entry) - if isDump { - entity.Path = fileName - } else { - fmt.Printf("%s%s:%s\n", coloring.Start, fileName, coloring.End) - } - } - if isDump { - entity.Value = value - dumpData = append(dumpData, entity) - } else { - fmt.Println(value) - } - continue - } - clipboard.CopyTo(value, getExecutable()) + dumpData, err := subcommands.DisplayCallback(opts) + if err != nil { + misc.Die("display command failed to retrieve data", err) } - if isDump { + if opts.Dump { if !options.Yes { if !confirm("dump data to stdout as plaintext") { return @@ -228,47 +165,29 @@ func main() { misc.Die("failed to marshal items", err) } fmt.Println(string(d)) + return } - case "clear": - idx := 0 - val, err := inputs.Stdin(false) - if err != nil { - misc.Die("unable to read value to clear", err) - } - clipboard, err := platform.NewClipboard() - if err != nil { - misc.Die("unable to get paste command", err) - } - pCmd, pArgs := clipboard.Args(false) - val = strings.TrimSpace(val) - for idx < clipboard.MaxTime { - idx++ - time.Sleep(1 * time.Second) - out, err := exec.Command(pCmd, pArgs...).Output() - if err != nil { + for _, obj := range dumpData { + if opts.Show { + if obj.Path != "" { + fmt.Println(obj.Path) + } + fmt.Println(obj.Value) continue } - fmt.Println(string(out)) - fmt.Println(val) - if strings.TrimSpace(string(out)) != val { - return - } + clipboard.CopyTo(obj.Value, getExecutable()) + } + case "clear": + if err := subcommands.ClearClipboardCallback(); err != nil { + misc.Die("failed to handle clipboard clear", err) } - clipboard.CopyTo("", getExecutable()) default: lib := os.Getenv("LOCKBOX_LIBEXEC") if lib == "" { lib = libExec } - tryCommand := fmt.Sprintf(filepath.Join(lib, "lb-%s"), command) - if !misc.PathExists(tryCommand) { - misc.Die("unknown subcommand", errors.New(command)) - } - c := exec.Command(tryCommand, args[2:]...) - c.Stdout = os.Stdout - c.Stderr = os.Stderr - if err := c.Run(); err != nil { - misc.Die("bad command", err) + if err := subcommands.LibExecCallback(subcommands.LibExecOptions{Directory: lib, Command: command, Args: args[2:]}); err != nil { + misc.Die("subcommand failed", err) } } } diff --git a/internal/subcommands/clear.go b/internal/subcommands/clear.go @@ -0,0 +1,43 @@ +package subcommands + +import ( + "os" + "os/exec" + "strings" + "time" + + "github.com/enckse/lockbox/internal/inputs" + "github.com/enckse/lockbox/internal/platform" +) + +// ClearClipboardCallback handles clearing the clipboard over time. +func ClearClipboardCallback() error { + idx := 0 + val, err := inputs.Stdin(false) + if err != nil { + return err + } + clipboard, err := platform.NewClipboard() + if err != nil { + return err + } + pCmd, pArgs := clipboard.Args(false) + val = strings.TrimSpace(val) + for idx < clipboard.MaxTime { + idx++ + time.Sleep(1 * time.Second) + out, err := exec.Command(pCmd, pArgs...).Output() + if err != nil { + continue + } + if strings.TrimSpace(string(out)) != val { + return nil + } + } + exe, err := os.Executable() + if err != nil { + return err + } + clipboard.CopyTo("", exe) + return nil +} diff --git a/internal/subcommands/display.go b/internal/subcommands/display.go @@ -0,0 +1,80 @@ +package subcommands + +import ( + "errors" + "fmt" + "path/filepath" + "sort" + "strings" + + "github.com/enckse/lockbox/internal/colors" + "github.com/enckse/lockbox/internal/dump" + "github.com/enckse/lockbox/internal/encrypt" + "github.com/enckse/lockbox/internal/misc" + "github.com/enckse/lockbox/internal/store" +) + +type ( + // DisplayOptions for getting a set of items for display uses. + DisplayOptions struct { + Dump bool + Entry string + Show bool + Glob string + Store store.FileSystem + } +) + +// DisplayCallback handles getting entries for display. +func DisplayCallback(args DisplayOptions) ([]dump.ExportEntity, error) { + entries := []string{args.Entry} + if strings.Contains(args.Entry, "*") { + if args.Entry == args.Glob { + all, err := args.Store.List(store.ViewOptions{}) + if err != nil { + return nil, err + } + entries = all + } else { + matches, err := filepath.Glob(args.Entry) + if err != nil { + return nil, err + } + entries = matches + } + } + isGlob := len(entries) > 1 + if isGlob { + if !args.Show { + return nil, errors.New("bad glob request") + } + sort.Strings(entries) + } + coloring, err := colors.NewTerminal(colors.Red) + if err != nil { + return nil, err + } + dumpData := []dump.ExportEntity{} + for _, entry := range entries { + if !misc.PathExists(entry) { + return nil, errors.New("entry not found") + } + decrypt, err := encrypt.FromFile(entry) + if err != nil { + return nil, err + } + entity := dump.ExportEntity{Value: strings.TrimSpace(string(decrypt))} + if args.Show { + if isGlob { + fileName := args.Store.CleanPath(entry) + if args.Dump { + entity.Path = fileName + } else { + entity.Path = fmt.Sprintf("%s%s:%s", coloring.Start, fileName, coloring.End) + } + } + dumpData = append(dumpData, entity) + } + } + return dumpData, nil +} diff --git a/internal/subcommands/libexec.go b/internal/subcommands/libexec.go @@ -0,0 +1,32 @@ +package subcommands + +import ( + "errors" + "fmt" + "os" + "os/exec" + "path/filepath" + + "github.com/enckse/lockbox/internal/misc" +) + +type ( + // LibExecOptions for calling to subcommands that are executables. + LibExecOptions struct { + Directory string + Command string + Args []string + } +) + +// LibExecCallback will handle subcommand handling outside of known functions. +func LibExecCallback(args LibExecOptions) error { + tryCommand := fmt.Sprintf(filepath.Join(args.Directory, "lb-%s"), args.Command) + if !misc.PathExists(tryCommand) { + return errors.New(args.Command) + } + c := exec.Command(tryCommand, args.Args...) + c.Stdout = os.Stdout + c.Stderr = os.Stderr + return c.Run() +} diff --git a/internal/subcommands/listfind.go b/internal/subcommands/listfind.go @@ -0,0 +1,34 @@ +package subcommands + +import ( + "strings" + + "github.com/enckse/lockbox/internal/store" +) + +type ( + // ListFindOptions for listing/finding entries in a store. + ListFindOptions struct { + Find bool + Search string + Store store.FileSystem + } +) + +// ListFindCallback for searching/finding/listing entries. +func ListFindCallback(args ListFindOptions) ([]string, error) { + viewOptions := store.ViewOptions{Display: true} + if args.Find { + viewOptions.Filter = func(inPath string) string { + if strings.Contains(inPath, args.Search) { + return inPath + } + return "" + } + } + files, err := args.Store.List(viewOptions) + if err != nil { + return nil, err + } + return files, nil +} diff --git a/tests/expected.log b/tests/expected.log @@ -48,6 +48,6 @@ remove entry? (y/N) HOOK RAN remove post remove entry? (y/N) HOOK RAN remove post -unable to decrypt (decrypt not ok) +display command failed to retrieve data (decrypt not ok) rekeying: /keys/one2.lb test2