commit f6727077df18eb469ff83e3d78a43b6ad37a702f
parent 4bab0442b6cf611a0b1f11921604fc953a1baa47
Author: Sean Enck <sean@ttypty.com>
Date: Sat, 1 Oct 2022 12:41:48 -0400
splitting up/renaming items
Diffstat:
5 files changed, 267 insertions(+), 257 deletions(-)
diff --git a/internal/backend/actions.go b/internal/backend/actions.go
@@ -0,0 +1,137 @@
+// Package backend handles kdbx interactions
+package backend
+
+import (
+ "errors"
+ "os"
+ "path/filepath"
+
+ "github.com/enckse/lockbox/internal/inputs"
+ "github.com/tobischo/gokeepasslib/v3"
+ "github.com/tobischo/gokeepasslib/v3/wrappers"
+)
+
+func (t *Transaction) act(cb action) error {
+ if !t.valid {
+ return errors.New("invalid transaction")
+ }
+ key, err := inputs.GetKey("", "")
+ if err != nil {
+ return err
+ }
+ k := string(key)
+ if !t.exists {
+ if err := create(t.file, k); err != nil {
+ return err
+ }
+ }
+ f, err := os.Open(t.file)
+ if err != nil {
+ return err
+ }
+ defer f.Close()
+ db := gokeepasslib.NewDatabase()
+ db.Credentials = gokeepasslib.NewPasswordCredentials(k)
+ if err := gokeepasslib.NewDecoder(f).Decode(db); err != nil {
+ return err
+ }
+ if len(db.Content.Root.Groups) != 1 {
+ return errors.New("kdbx must only have ONE root group")
+ }
+ cErr := cb(Context{db: db})
+ if t.write {
+ if err := db.LockProtectedEntries(); err != nil {
+ return err
+ }
+ if err := f.Close(); err != nil {
+ return err
+ }
+ f, err = os.Create(t.file)
+ if err != nil {
+ return err
+ }
+ defer f.Close()
+ return encode(f, db)
+ }
+ return cErr
+}
+
+func (t *Transaction) change(cb action) error {
+ return t.act(func(c Context) error {
+ if err := c.db.UnlockProtectedEntries(); err != nil {
+ return err
+ }
+ t.write = true
+ return cb(c)
+ })
+}
+
+// Insert handles inserting a new element
+func (t *Transaction) Insert(path, val string, entity *QueryEntity, multi bool) error {
+ return t.change(func(c Context) error {
+ if entity != nil {
+ if err := remove(entity, c); err != nil {
+ return err
+ }
+ }
+ e := gokeepasslib.NewEntry()
+ e.Values = append(e.Values, value(titleKey, filepath.Dir(path)))
+ e.Values = append(e.Values, value(userNameKey, filepath.Base(path)))
+ field := passKey
+ if multi {
+ field = notesKey
+ }
+
+ e.Values = append(e.Values, protectedValue(field, val))
+ c.db.Content.Root.Groups[0].Entries = append(c.db.Content.Root.Groups[0].Entries, e)
+ return nil
+ })
+}
+
+func remove(entity *QueryEntity, c Context) error {
+ entries := c.db.Content.Root.Groups[0].Entries
+ idx := -1
+ for i, e := range entries {
+ if entity.Path == getPathName(e) {
+ idx = i
+ }
+ }
+ if idx < 0 {
+ return errors.New("unable to select entity for deletion")
+ }
+ c.db.Content.Root.Groups[0].Entries = append(entries[:idx], entries[idx+1:]...)
+ return nil
+}
+
+// Remove handles remove an element
+func (t *Transaction) Remove(entity *QueryEntity) error {
+ if entity == nil {
+ return errors.New("entity is empty/invalid")
+ }
+ return t.change(func(c Context) error {
+ return remove(entity, c)
+ })
+}
+
+func getValue(e gokeepasslib.Entry, key string) string {
+ v := e.Get(key)
+ if v == nil {
+ return ""
+ }
+ return v.Value.Content
+}
+
+func value(key string, value string) gokeepasslib.ValueData {
+ return gokeepasslib.ValueData{Key: key, Value: gokeepasslib.V{Content: value}}
+}
+
+func getPathName(entry gokeepasslib.Entry) string {
+ return filepath.Join(entry.GetTitle(), getValue(entry, userNameKey))
+}
+
+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/core.go b/internal/backend/core.go
@@ -0,0 +1,65 @@
+// Package backend handles kdbx interactions
+package backend
+
+import (
+ "errors"
+ "os"
+ "strings"
+
+ "github.com/enckse/lockbox/internal/inputs"
+ "github.com/tobischo/gokeepasslib/v3"
+)
+
+// Load will load a kdbx file for transactions
+func Load(file string) (*Transaction, error) {
+ return loadFile(file, true)
+}
+
+func loadFile(file string, must bool) (*Transaction, error) {
+ if !strings.HasSuffix(file, ".kdbx") {
+ return nil, errors.New("should use a .kdbx extension")
+ }
+ exists := pathExists(file)
+ if must {
+ if !exists {
+ return nil, errors.New("invalid file, does not exists")
+ }
+ }
+ return &Transaction{valid: true, file: file, exists: exists}, nil
+}
+
+// NewTransaction will use the underlying environment data store location
+func NewTransaction() (*Transaction, error) {
+ return loadFile(os.Getenv(inputs.StoreEnv), false)
+}
+
+func create(file, key string) error {
+ root := gokeepasslib.NewGroup()
+ root.Name = "root"
+ db := gokeepasslib.NewDatabase(gokeepasslib.WithDatabaseKDBXVersion4())
+ db.Credentials = gokeepasslib.NewPasswordCredentials(key)
+ db.Content.Root =
+ &gokeepasslib.RootData{
+ Groups: []gokeepasslib.Group{root},
+ }
+ f, err := os.Create(file)
+ if err != nil {
+ return err
+ }
+ defer f.Close()
+ return encode(f, db)
+}
+
+func encode(f *os.File, db *gokeepasslib.Database) error {
+ return gokeepasslib.NewEncoder(f).Encode(db)
+}
+
+// pathExists indicates if a path exists.
+func pathExists(path string) bool {
+ if _, err := os.Stat(path); err != nil {
+ if os.IsNotExist(err) {
+ return false
+ }
+ }
+ return true
+}
diff --git a/internal/backend/query.go b/internal/backend/query.go
@@ -9,48 +9,6 @@ import (
"path/filepath"
"sort"
"strings"
-
- "github.com/tobischo/gokeepasslib/v3"
-)
-
-type (
- // QueryMode indicates HOW an entity will be found
- QueryMode int
- // ValueMode indicates what to do with the store value of the entity
- ValueMode int
- // QueryOptions indicates how to find entities
- QueryOptions struct {
- Mode QueryMode
- Values ValueMode
- Criteria string
- }
- // QueryEntity is the result of a query
- QueryEntity struct {
- Path string
- Value string
- backing gokeepasslib.Entry
- }
-)
-
-const (
- noneMode QueryMode = iota
- // ListMode indicates ALL entities will be listed
- ListMode
- // FindMode indicates a _contains_ search for an entity
- FindMode
- // ExactMode means an entity must MATCH the string exactly
- ExactMode
- // SuffixMode will look for an entity ending in a specific value
- SuffixMode
-)
-
-const (
- // BlankValue will not decrypt secrets, empty value
- BlankValue ValueMode = iota
- // HashedValue will decrypt and then hash the password
- HashedValue
- // SecretValue will have the raw secret onboard
- SecretValue
)
// Get will request a singular entity
diff --git a/internal/backend/transact.go b/internal/backend/transact.go
@@ -1,215 +0,0 @@
-// Package backend handles kdbx interactions
-package backend
-
-import (
- "errors"
- "os"
- "path/filepath"
- "strings"
-
- "github.com/enckse/lockbox/internal/inputs"
- "github.com/tobischo/gokeepasslib/v3"
- "github.com/tobischo/gokeepasslib/v3/wrappers"
-)
-
-const (
- userNameKey = "UserName"
- notesKey = "Notes"
- titleKey = "Title"
- passKey = "Password"
-)
-
-type (
- // action are transcation operations that more or less CRUD the kdbx file
- action func(t Context) error
- // Transaction handles the overall operation of the transaction
- Transaction struct {
- valid bool
- file string
- exists bool
- write bool
- }
- // Context handles operating on the underlying database
- Context struct {
- db *gokeepasslib.Database
- }
-)
-
-// Load will load a kdbx file for transactions
-func Load(file string) (*Transaction, error) {
- return loadFile(file, true)
-}
-
-func loadFile(file string, must bool) (*Transaction, error) {
- if !strings.HasSuffix(file, ".kdbx") {
- return nil, errors.New("should use a .kdbx extension")
- }
- exists := pathExists(file)
- if must {
- if !exists {
- return nil, errors.New("invalid file, does not exists")
- }
- }
- return &Transaction{valid: true, file: file, exists: exists}, nil
-}
-
-// NewTransaction will use the underlying environment data store location
-func NewTransaction() (*Transaction, error) {
- return loadFile(os.Getenv(inputs.StoreEnv), false)
-}
-
-func create(file, key string) error {
- root := gokeepasslib.NewGroup()
- root.Name = "root"
- db := gokeepasslib.NewDatabase(gokeepasslib.WithDatabaseKDBXVersion4())
- db.Credentials = gokeepasslib.NewPasswordCredentials(key)
- db.Content.Root =
- &gokeepasslib.RootData{
- Groups: []gokeepasslib.Group{root},
- }
- f, err := os.Create(file)
- if err != nil {
- return err
- }
- defer f.Close()
- return encode(f, db)
-}
-
-func encode(f *os.File, db *gokeepasslib.Database) error {
- return gokeepasslib.NewEncoder(f).Encode(db)
-}
-
-func (t *Transaction) act(cb action) error {
- if !t.valid {
- return errors.New("invalid transaction")
- }
- key, err := inputs.GetKey("", "")
- if err != nil {
- return err
- }
- k := string(key)
- if !t.exists {
- if err := create(t.file, k); err != nil {
- return err
- }
- }
- f, err := os.Open(t.file)
- if err != nil {
- return err
- }
- defer f.Close()
- db := gokeepasslib.NewDatabase()
- db.Credentials = gokeepasslib.NewPasswordCredentials(k)
- if err := gokeepasslib.NewDecoder(f).Decode(db); err != nil {
- return err
- }
- if len(db.Content.Root.Groups) != 1 {
- return errors.New("kdbx must only have ONE root group")
- }
- cErr := cb(Context{db: db})
- if t.write {
- if err := db.LockProtectedEntries(); err != nil {
- return err
- }
- if err := f.Close(); err != nil {
- return err
- }
- f, err = os.Create(t.file)
- if err != nil {
- return err
- }
- defer f.Close()
- return encode(f, db)
- }
- return cErr
-}
-
-func (t *Transaction) change(cb action) error {
- return t.act(func(c Context) error {
- if err := c.db.UnlockProtectedEntries(); err != nil {
- return err
- }
- t.write = true
- return cb(c)
- })
-}
-
-// Insert handles inserting a new element
-func (t *Transaction) Insert(path, val string, entity *QueryEntity, multi bool) error {
- return t.change(func(c Context) error {
- if entity != nil {
- if err := remove(entity, c); err != nil {
- return err
- }
- }
- e := gokeepasslib.NewEntry()
- e.Values = append(e.Values, value(titleKey, filepath.Dir(path)))
- e.Values = append(e.Values, value(userNameKey, filepath.Base(path)))
- field := passKey
- if multi {
- field = notesKey
- }
-
- e.Values = append(e.Values, protectedValue(field, val))
- c.db.Content.Root.Groups[0].Entries = append(c.db.Content.Root.Groups[0].Entries, e)
- return nil
- })
-}
-
-func remove(entity *QueryEntity, c Context) error {
- entries := c.db.Content.Root.Groups[0].Entries
- idx := -1
- for i, e := range entries {
- if entity.Path == getPathName(e) {
- idx = i
- }
- }
- if idx < 0 {
- return errors.New("unable to select entity for deletion")
- }
- c.db.Content.Root.Groups[0].Entries = append(entries[:idx], entries[idx+1:]...)
- return nil
-}
-
-// Remove handles remove an element
-func (t *Transaction) Remove(entity *QueryEntity) error {
- if entity == nil {
- return errors.New("entity is empty/invalid")
- }
- return t.change(func(c Context) error {
- return remove(entity, c)
- })
-}
-
-func getValue(e gokeepasslib.Entry, key string) string {
- v := e.Get(key)
- if v == nil {
- return ""
- }
- return v.Value.Content
-}
-
-func value(key string, value string) gokeepasslib.ValueData {
- return gokeepasslib.ValueData{Key: key, Value: gokeepasslib.V{Content: value}}
-}
-
-func getPathName(entry gokeepasslib.Entry) string {
- return filepath.Join(entry.GetTitle(), getValue(entry, userNameKey))
-}
-
-func protectedValue(key string, value string) gokeepasslib.ValueData {
- return gokeepasslib.ValueData{
- Key: key,
- Value: gokeepasslib.V{Content: value, Protected: wrappers.NewBoolWrapper(true)},
- }
-}
-
-// pathExists indicates if a path exists.
-func pathExists(path string) bool {
- if _, err := os.Stat(path); err != nil {
- if os.IsNotExist(err) {
- return false
- }
- }
- return true
-}
diff --git a/internal/backend/types.go b/internal/backend/types.go
@@ -0,0 +1,65 @@
+// Package backend has types
+package backend
+
+import (
+ "github.com/tobischo/gokeepasslib/v3"
+)
+
+type (
+ // QueryMode indicates HOW an entity will be found
+ QueryMode int
+ // ValueMode indicates what to do with the store value of the entity
+ ValueMode int
+ // QueryOptions indicates how to find entities
+ QueryOptions struct {
+ Mode QueryMode
+ Values ValueMode
+ Criteria string
+ }
+ // QueryEntity is the result of a query
+ QueryEntity struct {
+ Path string
+ Value string
+ backing gokeepasslib.Entry
+ }
+ action func(t Context) error
+ // Transaction handles the overall operation of the transaction
+ Transaction struct {
+ valid bool
+ file string
+ exists bool
+ write bool
+ }
+ // Context handles operating on the underlying database
+ Context struct {
+ db *gokeepasslib.Database
+ }
+)
+
+const (
+ noneMode QueryMode = iota
+ // ListMode indicates ALL entities will be listed
+ ListMode
+ // FindMode indicates a _contains_ search for an entity
+ FindMode
+ // ExactMode means an entity must MATCH the string exactly
+ ExactMode
+ // SuffixMode will look for an entity ending in a specific value
+ SuffixMode
+)
+
+const (
+ // BlankValue will not decrypt secrets, empty value
+ BlankValue ValueMode = iota
+ // HashedValue will decrypt and then hash the password
+ HashedValue
+ // SecretValue will have the raw secret onboard
+ SecretValue
+)
+
+const (
+ userNameKey = "UserName"
+ notesKey = "Notes"
+ titleKey = "Title"
+ passKey = "Password"
+)