lockbox

password manager
Log | Files | Refs | README | LICENSE

commit 28ee0359ea885cec6d882f86f343de7c6a9c63cd
parent 63e8c229fcafad3564f515d4ac8d183c2495b59a
Author: Sean Enck <sean@ttypty.com>
Date:   Fri, 16 Aug 2024 18:41:14 -0400

switch from QueryEntity to a TransactionEntity for transactions

Diffstat:
Minternal/app/move.go | 2+-
Minternal/app/remove.go | 11++++++++++-
Minternal/backend/actions.go | 28++++++++++++++--------------
Minternal/backend/actions_test.go | 34+++++++++++++++++++++-------------
Minternal/backend/core.go | 10++++++++++
Minternal/backend/core_test.go | 9+++++++++
6 files changed, 65 insertions(+), 29 deletions(-)

diff --git a/internal/app/move.go b/internal/app/move.go @@ -98,5 +98,5 @@ func (r moveRequest) do(dryRun bool) error { if dryRun { return nil } - return tx.Move(*srcExists, r.dst) + return tx.Move(srcExists.Transaction(), r.dst) } diff --git a/internal/app/remove.go b/internal/app/remove.go @@ -4,6 +4,8 @@ package app import ( "errors" "fmt" + + "github.com/seanenck/lockbox/internal/backend" ) // Remove will remove an entry @@ -32,7 +34,14 @@ func Remove(cmd CommandOptions) error { fmt.Fprintln(w, "") } if cmd.Confirm(fmt.Sprintf("delete entr%s", postfixRemove)) { - if err := t.RemoveAll(existings); err != nil { + removals := func() []backend.TransactionEntity { + var tx []backend.TransactionEntity + for _, e := range existings { + tx = append(tx, e.Transaction()) + } + return tx + }() + if err := t.RemoveAll(removals); err != nil { return fmt.Errorf("unable to remove: %w", err) } } diff --git a/internal/backend/actions.go b/internal/backend/actions.go @@ -182,11 +182,11 @@ func findAndDo(isAdd bool, entityName string, offset []string, opEntity *gokeepa } // Move will move a src object to a dst location -func (t *Transaction) Move(src QueryEntity, dst string) error { - if strings.TrimSpace(src.Path) == "" { +func (t *Transaction) Move(src TransactionEntity, dst string) error { + if strings.TrimSpace(src.path) == "" { return errors.New("empty path not allowed") } - if strings.TrimSpace(src.Value) == "" { + if strings.TrimSpace(src.value) == "" { return errors.New("empty secret not allowed") } mod := config.EnvModTime.Get() @@ -202,22 +202,22 @@ func (t *Transaction) Move(src QueryEntity, dst string) error { if err != nil { return err } - sOffset, sTitle, err := splitComponents(src.Path) + sOffset, sTitle, err := splitComponents(src.path) if err != nil { return err } action := MoveAction - if dst == src.Path { + if dst == src.path { action = InsertAction } - hook, err := NewHook(src.Path, action) + hook, err := NewHook(src.path, action) if err != nil { return err } if err := hook.Run(HookPre); err != nil { return err } - multi := len(strings.Split(strings.TrimSpace(src.Value), "\n")) > 1 + multi := len(strings.Split(strings.TrimSpace(src.value), "\n")) > 1 err = t.change(func(c Context) error { c.removeEntity(sOffset, sTitle) if action == MoveAction { @@ -229,7 +229,7 @@ func (t *Transaction) Move(src QueryEntity, dst string) error { if multi { field = notesKey } - v := src.Value + v := src.value ok, err := isTOTP(dTitle) if err != nil { return err @@ -254,19 +254,19 @@ func (t *Transaction) Move(src QueryEntity, dst string) error { // Insert is a move to the same location func (t *Transaction) Insert(path, val string) error { - return t.Move(QueryEntity{Path: path, Value: val}, path) + return t.Move(TransactionEntity{path: path, value: val}, path) } // Remove will remove a single entity -func (t *Transaction) Remove(entity *QueryEntity) error { +func (t *Transaction) Remove(entity *TransactionEntity) error { if entity == nil { return errors.New("entity is empty/invalid") } - return t.RemoveAll([]QueryEntity{*entity}) + return t.RemoveAll([]TransactionEntity{*entity}) } // RemoveAll handles removing elements -func (t *Transaction) RemoveAll(entities []QueryEntity) error { +func (t *Transaction) RemoveAll(entities []TransactionEntity) error { if len(entities) == 0 { return errors.New("no entities given") } @@ -278,11 +278,11 @@ func (t *Transaction) RemoveAll(entities []QueryEntity) error { removals := []removal{} hasHooks := false for _, entity := range entities { - offset, title, err := splitComponents(entity.Path) + offset, title, err := splitComponents(entity.path) if err != nil { return err } - hook, err := NewHook(entity.Path, RemoveAction) + hook, err := NewHook(entity.path, RemoveAction) if err != nil { return err } diff --git a/internal/backend/actions_test.go b/internal/backend/actions_test.go @@ -97,7 +97,7 @@ func TestMove(t *testing.T) { setup(t) fullSetup(t, true).Insert(backend.NewPath("test", "test2", "test1"), "pass") fullSetup(t, true).Insert(backend.NewPath("test", "test2", "test3"), "pass") - if err := fullSetup(t, true).Move(backend.QueryEntity{Path: backend.NewPath("test", "test2", "test3"), Value: "abc"}, backend.NewPath("test1", "test2", "test3")); err != nil { + if err := fullSetup(t, true).Move(backend.QueryEntity{Path: backend.NewPath("test", "test2", "test3"), Value: "abc"}.Transaction(), backend.NewPath("test1", "test2", "test3")); err != nil { t.Errorf("no error: %v", err) } q, err := fullSetup(t, true).Get(backend.NewPath("test1", "test2", "test3"), backend.SecretValue) @@ -107,7 +107,7 @@ func TestMove(t *testing.T) { if q.Value != "abc" { t.Errorf("invalid retrieval") } - if err := fullSetup(t, true).Move(backend.QueryEntity{Path: backend.NewPath("test", "test2", "test1"), Value: "test"}, backend.NewPath("test1", "test2", "test3")); err != nil { + if err := fullSetup(t, true).Move(backend.QueryEntity{Path: backend.NewPath("test", "test2", "test1"), Value: "test"}.Transaction(), backend.NewPath("test1", "test2", "test3")); err != nil { t.Errorf("no error: %v", err) } q, err = fullSetup(t, true).Get(backend.NewPath("test1", "test2", "test3"), backend.SecretValue) @@ -176,54 +176,62 @@ func TestRemoves(t *testing.T) { if err := setup(t).Remove(nil); err.Error() != "entity is empty/invalid" { t.Errorf("wrong error: %v", err) } - if err := setup(t).Remove(&backend.QueryEntity{}); err.Error() != "input paths must contain at LEAST 2 components" { + if err := setup(t).Remove(&backend.TransactionEntity{}); err.Error() != "input paths must contain at LEAST 2 components" { t.Errorf("wrong error: %v", err) } - if err := setup(t).Remove(&backend.QueryEntity{Path: backend.NewPath("test1", "test2", "test3")}); err.Error() != "failed to remove entity" { + tx := backend.QueryEntity{Path: backend.NewPath("test1", "test2", "test3")}.Transaction() + if err := setup(t).Remove(&tx); err.Error() != "failed to remove entity" { t.Errorf("wrong error: %v", err) } setup(t) for _, i := range []string{"test1", "test2"} { fullSetup(t, true).Insert(backend.NewPath(i, i, i), "pass") } - if err := fullSetup(t, true).Remove(&backend.QueryEntity{Path: backend.NewPath("test1", "test1", "test1")}); err != nil { + tx = backend.QueryEntity{Path: backend.NewPath("test1", "test1", "test1")}.Transaction() + if err := fullSetup(t, true).Remove(&tx); err != nil { t.Errorf("wrong error: %v", err) } if err := check(t, backend.NewPath("test2", "test2", "test2")); err != nil { t.Errorf("invalid check: %v", err) } - if err := fullSetup(t, true).Remove(&backend.QueryEntity{Path: backend.NewPath("test2", "test2", "test2")}); err != nil { + tx = backend.QueryEntity{Path: backend.NewPath("test2", "test2", "test2")}.Transaction() + if err := fullSetup(t, true).Remove(&tx); err != nil { t.Errorf("wrong error: %v", err) } setup(t) for _, i := range []string{backend.NewPath("test", "test", "test1"), backend.NewPath("test", "test", "test2"), backend.NewPath("test", "test", "test3"), backend.NewPath("test", "test1", "test2"), backend.NewPath("test", "test1", "test5")} { fullSetup(t, true).Insert(i, "pass") } - if err := fullSetup(t, true).Remove(&backend.QueryEntity{Path: "test/test/test3"}); err != nil { + tx = backend.QueryEntity{Path: "test/test/test3"}.Transaction() + if err := fullSetup(t, true).Remove(&tx); err != nil { t.Errorf("wrong error: %v", err) } if err := check(t, backend.NewPath("test", "test", "test2"), backend.NewPath("test", "test", "test1"), backend.NewPath("test", "test1", "test2"), backend.NewPath("test", "test1", "test5")); err != nil { t.Errorf("invalid check: %v", err) } - if err := fullSetup(t, true).Remove(&backend.QueryEntity{Path: "test/test/test1"}); err != nil { + tx = backend.QueryEntity{Path: "test/test/test1"}.Transaction() + if err := fullSetup(t, true).Remove(&tx); err != nil { t.Errorf("wrong error: %v", err) } if err := check(t, backend.NewPath("test", "test", "test2"), backend.NewPath("test", "test1", "test2"), backend.NewPath("test", "test1", "test5")); err != nil { t.Errorf("invalid check: %v", err) } - if err := fullSetup(t, true).Remove(&backend.QueryEntity{Path: "test/test1/test5"}); err != nil { + tx = backend.QueryEntity{Path: "test/test1/test5"}.Transaction() + if err := fullSetup(t, true).Remove(&tx); err != nil { t.Errorf("wrong error: %v", err) } if err := check(t, backend.NewPath("test", "test", "test2"), backend.NewPath("test", "test1", "test2")); err != nil { t.Errorf("invalid check: %v", err) } - if err := fullSetup(t, true).Remove(&backend.QueryEntity{Path: "test/test1/test2"}); err != nil { + tx = backend.QueryEntity{Path: "test/test1/test2"}.Transaction() + if err := fullSetup(t, true).Remove(&tx); err != nil { t.Errorf("wrong error: %v", err) } if err := check(t, backend.NewPath("test", "test", "test2")); err != nil { t.Errorf("invalid check: %v", err) } - if err := fullSetup(t, true).Remove(&backend.QueryEntity{Path: "test/test/test2"}); err != nil { + tx = backend.QueryEntity{Path: "test/test/test2"}.Transaction() + if err := fullSetup(t, true).Remove(&tx); err != nil { t.Errorf("wrong error: %v", err) } } @@ -232,14 +240,14 @@ func TestRemoveAlls(t *testing.T) { if err := setup(t).RemoveAll(nil); err.Error() != "no entities given" { t.Errorf("wrong error: %v", err) } - if err := setup(t).RemoveAll([]backend.QueryEntity{}); err.Error() != "no entities given" { + if err := setup(t).RemoveAll([]backend.TransactionEntity{}); err.Error() != "no entities given" { t.Errorf("wrong error: %v", err) } setup(t) for _, i := range []string{backend.NewPath("test", "test", "test1"), backend.NewPath("test", "test", "test2"), backend.NewPath("test", "test", "test3"), backend.NewPath("test", "test1", "test2"), backend.NewPath("test", "test1", "test5")} { fullSetup(t, true).Insert(i, "pass") } - if err := fullSetup(t, true).RemoveAll([]backend.QueryEntity{{Path: "test/test/test3"}, {Path: "test/test/test1"}}); err != nil { + if err := fullSetup(t, true).RemoveAll([]backend.TransactionEntity{backend.QueryEntity{Path: "test/test/test3"}.Transaction(), backend.QueryEntity{Path: "test/test/test1"}.Transaction()}); err != nil { t.Errorf("wrong error: %v", err) } if err := check(t, backend.NewPath("test", "test", "test2"), backend.NewPath("test", "test1", "test2"), backend.NewPath("test", "test1", "test5")); err != nil { diff --git a/internal/backend/core.go b/internal/backend/core.go @@ -43,8 +43,18 @@ type ( Value string backing gokeepasslib.Entry } + // TransactionEntity objects alter the database entities + TransactionEntity struct { + path string + value string + } ) +// Transaction will convert a query entity to a transaction entity +func (e QueryEntity) Transaction() TransactionEntity { + return TransactionEntity{path: e.Path, value: e.Value} +} + // Load will load a kdbx file for transactions func Load(file string) (*Transaction, error) { return loadFile(file, true) diff --git a/internal/backend/core_test.go b/internal/backend/core_test.go @@ -1,6 +1,7 @@ package backend_test import ( + "fmt" "testing" "github.com/seanenck/lockbox/internal/backend" @@ -30,6 +31,14 @@ func TestIsDirectory(t *testing.T) { } } +func TestQueryToTransaction(t *testing.T) { + q := backend.QueryEntity{Path: "abc", Value: "xyz"} + tx := q.Transaction() + if fmt.Sprintf("%v", tx) != "{abc xyz}" { + t.Errorf("invalid transaction: %v", tx) + } +} + func TestBase(t *testing.T) { b := backend.Base("") if b != "" {