lockbox

password manager
Log | Files | Refs | README | LICENSE

commit 2939d55749ec4f5a2708e5880406abf8387be6c2
parent bfce88a82fc81a85dfcee77233a8d82f284d74b3
Author: Sean Enck <sean@ttypty.com>
Date:   Wed, 14 Sep 2022 19:43:37 -0400

git repo integration

Diffstat:
MREADME.md | 8++++++++
Mcmd/main.go | 12++++++++++--
Minternal/inputs/env.go | 12++++++++++++
Minternal/store/filesystem.go | 44++++++++++++++++++++++++++++++++++++++++++++
Mtests/run.sh | 1+
5 files changed, 75 insertions(+), 2 deletions(-)

diff --git a/README.md b/README.md @@ -95,6 +95,14 @@ Setup the `.gitattributes` for the repository to include *.lb diff=lb ``` +The `insert` and `rm` actions will attempt to perform a git operation (if the +lockbox directory has a `.git/` directory), this +functionality can be disabled via + +``` +export LOCKBOX_GIT="no" +``` + ## build Requires `make` diff --git a/cmd/main.go b/cmd/main.go @@ -96,7 +96,8 @@ func main() { die("too many arguments", errors.New("insert can only perform one operation")) } isPipe := inputs.IsInputFromPipe() - entry := getEntry(store.NewFileSystemStore(), args, idx) + s := store.NewFileSystemStore() + entry := getEntry(s, args, idx) if store.PathExists(entry) { if !isPipe { if !confirm("overwrite existing") { @@ -118,10 +119,14 @@ func main() { if err := encrypt.ToFile(entry, password); err != nil { die("unable to encrypt object", err) } + if err := s.GitCommit(entry); err != nil { + die("failed to git commit changed", err) + } fmt.Println("") hooks.Run(hooks.Insert, hooks.PostStep) case "rm": - entry := getEntry(store.NewFileSystemStore(), args, 2) + s := store.NewFileSystemStore() + entry := getEntry(s, args, 2) if !store.PathExists(entry) { die("does not exists", errors.New("can not delete unknown entry")) } @@ -129,6 +134,9 @@ func main() { if err := os.Remove(entry); err != nil { die("unable to remove entry", err) } + if err := s.GitRemove(entry); err != nil { + die("failed to git remove", err) + } hooks.Run(hooks.Remove, hooks.PostStep) } case "show", "clip", "dump": diff --git a/internal/inputs/env.go b/internal/inputs/env.go @@ -12,6 +12,8 @@ const ( noClipEnv = prefixKey + "NOCLIP" noColorEnv = prefixKey + "NOCOLOR" interactiveEnv = prefixKey + "INTERACTIVE" + gitEnabledEnv = prefixKey + "GIT" + gitQuietEnv = gitEnabledEnv + "_QUIET" // TotpEnv allows for overriding of the special name for totp entries. TotpEnv = prefixKey + "TOTP" // KeyModeEnv indicates what the KEY value is (e.g. command, plaintext). @@ -58,6 +60,16 @@ func IsNoClipEnabled() (bool, error) { return isYesNoEnv(false, noClipEnv) } +// IsGitQuiet indicates if git operations should be 'quiet' (no stdout/stderr) +func IsGitQuiet() (bool, error) { + return isYesNoEnv(true, gitQuietEnv) +} + +// IsGitEnabled indicates if the filesystem store is a git repo +func IsGitEnabled() (bool, error) { + return isYesNoEnv(true, gitEnabledEnv) +} + // IsNoColorEnabled indicates if the flag is set to disable color. func IsNoColorEnabled() (bool, error) { return isYesNoEnv(false, noColorEnv) diff --git a/internal/store/filesystem.go b/internal/store/filesystem.go @@ -3,8 +3,10 @@ package store import ( "errors" + "fmt" "io/fs" "os" + "os/exec" "path/filepath" "sort" "strings" @@ -107,3 +109,45 @@ func PathExists(path string) bool { } return true } + +// GitCommit is for adding/changing entities +func (s FileSystem) GitCommit(entry string) error { + return s.gitAction("add", entry) +} + +// GitRemove is for removing entities +func (s FileSystem) GitRemove(entry string) error { + return s.gitAction("rm", entry) +} + +func (s FileSystem) gitAction(action, entry string) error { + ok, err := inputs.IsGitEnabled() + if err != nil { + return err + } + if !ok { + return nil + } + if !PathExists(filepath.Join(s.path, ".git")) { + return nil + } + if err := s.gitRun(action, entry); err != nil { + return err + } + return s.gitRun("commit", "-m", fmt.Sprintf("lb %s: %s", action, entry)) +} + +func (s FileSystem) gitRun(args ...string) error { + arguments := []string{"-C", s.path} + arguments = append(arguments, args...) + cmd := exec.Command("git", arguments...) + ok, err := inputs.IsGitQuiet() + if err != nil { + return err + } + if !ok { + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + } + return cmd.Run() +} diff --git a/tests/run.sh b/tests/run.sh @@ -8,6 +8,7 @@ export LOCKBOX_KEY="plaintextkey" export LOCKBOX_TOTP="totp" export LOCKBOX_INTERACTIVE="no" export LOCKBOX_HOOKDIR="$TESTS/hooks" +export LOCKBOX_GIT="no" rm -rf $TESTS mkdir -p $LOCKBOX_STORE