lockbox

password manager
Log | Files | Refs | README | LICENSE

commit 47c3363f5699ab3573a8efa2e94f91c239b14e68
parent 0fd9293a68255e1096c64e51f6866c8e611f8855
Author: Sean Enck <sean@ttypty.com>
Date:   Thu, 27 Jul 2023 18:44:09 -0400

continue refactoring backend layout

Diffstat:
Minternal/backend/actions.go | 30+++++++-----------------------
Minternal/backend/actions_test.go | 38+++++++++++++++++++++++++++++++++++++-
Minternal/backend/core.go | 56++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Minternal/backend/core_test.go | 86+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Minternal/backend/query.go | 43-------------------------------------------
Minternal/backend/query_test.go | 80-------------------------------------------------------------------------------
6 files changed, 186 insertions(+), 147 deletions(-)

diff --git a/internal/backend/actions.go b/internal/backend/actions.go @@ -9,18 +9,12 @@ import ( "github.com/enckse/lockbox/internal/inputs" "github.com/tobischo/gokeepasslib/v3" - "github.com/tobischo/gokeepasslib/v3/wrappers" ) type ( // ActionMode represents activities performed via transactions ActionMode string - removal struct { - parts []string - title string - hook Hook - } - action func(t Context) error + action func(t Context) error ) const ( @@ -98,10 +92,6 @@ func (t *Transaction) change(cb action) error { }) } -func (c Context) insertEntity(offset []string, title string, entity gokeepasslib.Entry) bool { - return c.alterEntities(true, offset, title, &entity) -} - func (c Context) alterEntities(isAdd bool, offset []string, title string, entity *gokeepasslib.Entry) bool { g, e, ok := findAndDo(isAdd, title, offset, entity, c.db.Content.Root.Groups[0].Groups, c.db.Content.Root.Groups[0].Entries) c.db.Content.Root.Groups[0].Groups = g @@ -237,7 +227,7 @@ func (t *Transaction) Move(src QueryEntity, dst string) error { } e.Values = append(e.Values, protectedValue(field, v)) e.Values = append(e.Values, value(modTimeKey, modTime.Format(time.RFC3339))) - c.insertEntity(dOffset, dTitle, e) + c.alterEntities(true, dOffset, dTitle, &e) return nil }) if err != nil { @@ -264,6 +254,11 @@ func (t *Transaction) RemoveAll(entities []QueryEntity) error { if len(entities) == 0 { return errors.New("no entities given") } + type removal struct { + parts []string + title string + hook Hook + } removals := []removal{} hasHooks := false for _, entity := range entities { @@ -303,14 +298,3 @@ func (t *Transaction) RemoveAll(entities []QueryEntity) error { } return nil } - -func value(key string, value string) gokeepasslib.ValueData { - return gokeepasslib.ValueData{Key: key, Value: gokeepasslib.V{Content: value}} -} - -func protectedValue(key string, value string) gokeepasslib.ValueData { - return gokeepasslib.ValueData{ - Key: key, - Value: gokeepasslib.V{Content: value, Protected: wrappers.NewBoolWrapper(true)}, - } -} diff --git a/internal/backend/actions_test.go b/internal/backend/actions_test.go @@ -28,7 +28,7 @@ func fullSetup(t *testing.T, keep bool) *backend.Transaction { } func TestKeyFile(t *testing.T) { - os.Remove("file.key") + os.Clearenv() os.Remove("keyfile_test.kdbx") os.Setenv("LOCKBOX_READONLY", "no") os.Setenv("LOCKBOX_STORE", "keyfile_test.kdbx") @@ -243,3 +243,39 @@ func check(t *testing.T, checks ...string) error { } return nil } + +func TestKeyAndOrKeyFile(t *testing.T) { + keyAndOrKeyFile(t, true, true) + keyAndOrKeyFile(t, false, true) + keyAndOrKeyFile(t, true, false) + keyAndOrKeyFile(t, false, false) +} + +func keyAndOrKeyFile(t *testing.T, key, keyFile bool) { + os.Clearenv() + os.Remove("keyorkeyfile_test.kdbx") + os.Setenv("LOCKBOX_STORE", "keyorkeyfile_test.kdbx") + if key { + os.Setenv("LOCKBOX_KEY", "test") + os.Setenv("LOCKBOX_KEYMODE", "plaintext") + } + if keyFile { + os.Setenv("LOCKBOX_KEYFILE", "keyfileor.key.kdbx") + os.WriteFile("keyfileor.key.kdbx", []byte("test"), 0o644) + } + tr, err := backend.NewTransaction() + if err != nil { + t.Errorf("failed: %v", err) + } + invalid := !key && !keyFile + err = tr.Insert(backend.NewPath("a", "b"), "t") + if invalid { + if err == nil || err.Error() != "key and/or keyfile must be set" { + t.Errorf("invalid error: %v", err) + } + } else { + if err != nil { + t.Errorf("no error allowed: %v", err) + } + } +} diff --git a/internal/backend/core.go b/internal/backend/core.go @@ -3,12 +3,14 @@ package backend import ( "errors" + "fmt" "os" "strings" "github.com/enckse/lockbox/internal/inputs" "github.com/enckse/lockbox/internal/platform" "github.com/tobischo/gokeepasslib/v3" + "github.com/tobischo/gokeepasslib/v3/wrappers" ) var errPath = errors.New("input paths must contain at LEAST 2 components") @@ -148,3 +150,57 @@ func isTOTP(title string) (bool, error) { func getPathName(entry gokeepasslib.Entry) string { return entry.GetTitle() } + +func value(key string, value string) gokeepasslib.ValueData { + return gokeepasslib.ValueData{Key: key, Value: gokeepasslib.V{Content: value}} +} + +func protectedValue(key string, value string) gokeepasslib.ValueData { + return gokeepasslib.ValueData{ + Key: key, + Value: gokeepasslib.V{Content: value, Protected: wrappers.NewBoolWrapper(true)}, + } +} + +// NewSuffix creates a new user 'name' suffix +func NewSuffix(name string) string { + return fmt.Sprintf("%s%s", pathSep, name) +} + +// NewPath creates a new storage location path. +func NewPath(segments ...string) string { + return strings.Join(segments, pathSep) +} + +// Directory gets the offset location of the entry without the 'name' +func (e QueryEntity) Directory() string { + return Directory(e.Path) +} + +// Base will get the base name of input path +func Base(s string) string { + parts := strings.Split(s, pathSep) + if len(parts) == 0 { + return s + } + return parts[len(parts)-1] +} + +// Directory will get the directory/group for the given path +func Directory(s string) string { + parts := strings.Split(s, pathSep) + return NewPath(parts[0 : len(parts)-1]...) +} + +func getValue(e gokeepasslib.Entry, key string) string { + v := e.Get(key) + if v == nil { + return "" + } + return v.Value.Content +} + +// IsDirectory will indicate if a path looks like a group/directory +func IsDirectory(path string) bool { + return strings.HasSuffix(path, pathSep) +} diff --git a/internal/backend/core_test.go b/internal/backend/core_test.go @@ -17,3 +17,89 @@ func TestLoad(t *testing.T) { t.Errorf("invalid error: %v", err) } } + +func TestIsDirectory(t *testing.T) { + if backend.IsDirectory("") { + t.Error("invalid directory detection") + } + if !backend.IsDirectory("/") { + t.Error("invalid directory detection") + } + if backend.IsDirectory("/a") { + t.Error("invalid directory detection") + } +} + +func TestBase(t *testing.T) { + b := backend.Base("") + if b != "" { + t.Error("invalid base") + } + b = backend.Base("aaa") + if b != "aaa" { + t.Error("invalid base") + } + b = backend.Base("aaa/") + if b != "" { + t.Error("invalid base") + } + b = backend.Base("aaa/a") + if b != "a" { + t.Error("invalid base") + } +} + +func TestDirectory(t *testing.T) { + b := backend.Directory("") + if b != "" { + t.Error("invalid directory") + } + b = backend.Directory("/") + if b != "" { + t.Error("invalid directory") + } + b = backend.Directory("/a") + if b != "" { + t.Error("invalid directory") + } + b = backend.Directory("a") + if b != "" { + t.Error("invalid directory") + } + b = backend.Directory("b/a") + if b != "b" { + t.Error("invalid directory") + } +} + +func TestEntityDir(t *testing.T) { + q := backend.QueryEntity{Path: backend.NewPath("abc", "xyz")} + if q.Directory() != "abc" { + t.Error("invalid query directory") + } + q = backend.QueryEntity{Path: backend.NewPath("abc", "xyz", "111")} + if q.Directory() != "abc/xyz" { + t.Error("invalid query directory") + } + q = backend.QueryEntity{Path: ""} + if q.Directory() != "" { + t.Error("invalid query directory") + } + q = backend.QueryEntity{Path: backend.NewPath("abc")} + if q.Directory() != "" { + t.Error("invalid query directory") + } +} + +func TestNewPath(t *testing.T) { + p := backend.NewPath("abc", "xyz") + if p != backend.NewPath("abc", "xyz") { + t.Error("invalid new path") + } +} + +func TestNewSuffix(t *testing.T) { + if backend.NewSuffix("test") != "/test" { + t.Error("invalid suffix") + } +} diff --git a/internal/backend/query.go b/internal/backend/query.go @@ -213,46 +213,3 @@ func (t *Transaction) QueryCallback(args QueryOptions) ([]QueryEntity, error) { } return results, nil } - -// NewSuffix creates a new user 'name' suffix -func NewSuffix(name string) string { - return fmt.Sprintf("%s%s", pathSep, name) -} - -// NewPath creates a new storage location path. -func NewPath(segments ...string) string { - return strings.Join(segments, pathSep) -} - -// Directory gets the offset location of the entry without the 'name' -func (e QueryEntity) Directory() string { - return Directory(e.Path) -} - -// Base will get the base name of input path -func Base(s string) string { - parts := strings.Split(s, pathSep) - if len(parts) == 0 { - return s - } - return parts[len(parts)-1] -} - -// Directory will get the directory/group for the given path -func Directory(s string) string { - parts := strings.Split(s, pathSep) - return NewPath(parts[0 : len(parts)-1]...) -} - -func getValue(e gokeepasslib.Entry, key string) string { - v := e.Get(key) - if v == nil { - return "" - } - return v.Value.Content -} - -// IsDirectory will indicate if a path looks like a group/directory -func IsDirectory(path string) bool { - return strings.HasSuffix(path, pathSep) -} diff --git a/internal/backend/query_test.go b/internal/backend/query_test.go @@ -210,32 +210,6 @@ func TestQueryCallback(t *testing.T) { } } -func TestEntityDir(t *testing.T) { - q := backend.QueryEntity{Path: backend.NewPath("abc", "xyz")} - if q.Directory() != "abc" { - t.Error("invalid query directory") - } - q = backend.QueryEntity{Path: backend.NewPath("abc", "xyz", "111")} - if q.Directory() != "abc/xyz" { - t.Error("invalid query directory") - } - q = backend.QueryEntity{Path: ""} - if q.Directory() != "" { - t.Error("invalid query directory") - } - q = backend.QueryEntity{Path: backend.NewPath("abc")} - if q.Directory() != "" { - t.Error("invalid query directory") - } -} - -func TestNewPath(t *testing.T) { - p := backend.NewPath("abc", "xyz") - if p != backend.NewPath("abc", "xyz") { - t.Error("invalid new path") - } -} - func TestSetModTime(t *testing.T) { testDateTime := "2022-12-30T12:34:56-05:00" tr := fullSetup(t, false) @@ -275,57 +249,3 @@ func TestSetModTime(t *testing.T) { t.Errorf("invalid error: %v", err) } } - -func TestIsDirectory(t *testing.T) { - if backend.IsDirectory("") { - t.Error("invalid directory detection") - } - if !backend.IsDirectory("/") { - t.Error("invalid directory detection") - } - if backend.IsDirectory("/a") { - t.Error("invalid directory detection") - } -} - -func TestBase(t *testing.T) { - b := backend.Base("") - if b != "" { - t.Error("invalid base") - } - b = backend.Base("aaa") - if b != "aaa" { - t.Error("invalid base") - } - b = backend.Base("aaa/") - if b != "" { - t.Error("invalid base") - } - b = backend.Base("aaa/a") - if b != "a" { - t.Error("invalid base") - } -} - -func TestDirectory(t *testing.T) { - b := backend.Directory("") - if b != "" { - t.Error("invalid directory") - } - b = backend.Directory("/") - if b != "" { - t.Error("invalid directory") - } - b = backend.Directory("/a") - if b != "" { - t.Error("invalid directory") - } - b = backend.Directory("a") - if b != "" { - t.Error("invalid directory") - } - b = backend.Directory("b/a") - if b != "b" { - t.Error("invalid directory") - } -}