/* Copyright © 2024 Brian Buller */ package models import ( "errors" "os" "strings" kp "github.com/tobischo/gokeepasslib" ) type KeePassDB struct { path string db *kp.Database } func NewKeePassDB(filename string, auth string) (*KeePassDB, error) { file, err := os.Open(filename) if err != nil { return nil, err } ret := KeePassDB{ path: filename, db: kp.NewDatabase(), } ret.db.Credentials = kp.NewPasswordCredentials(auth) err = kp.NewDecoder(file).Decode(ret.db) if err != nil { return nil, err } ret.db.UnlockProtectedEntries() return &ret, nil } func (db *KeePassDB) GetAllEntriesFromRoot() [][]string { var res [][]string groups := db.db.Content.Root.Groups for _, g := range groups { grpRes := db.GetAllEntriesInGroup(g) res = append(res, grpRes...) } return res } func (db *KeePassDB) GetAllEntriesInGroup(group kp.Group) [][]string { var res [][]string for _, g := range group.Groups { grpRes := db.GetAllEntriesInGroup(g) for _, r := range grpRes { res = append(res, append([]string{group.Name}, r...)) } } for _, e := range group.Entries { res = append(res, []string{group.Name, e.GetTitle()}) } return res } func (db *KeePassDB) GetGroupsAndEntriesFromRoot(path []string) [][]string { var res [][]string groups := db.db.Content.Root.Groups if len(path) == 0 { // Return all groups/entries at this level for _, g := range groups { res = append(res, []string{g.Name}) } return res } else { useCase := strings.ToLower(path[0]) != path[0] for _, g := range groups { var match bool if useCase { match = strings.HasPrefix(g.Name, path[0]) } else { match = strings.HasPrefix(strings.ToLower(g.Name), path[0]) } if match { grpRes := db.GetGroupsAndEntries(g, path[1:]) for _, r := range grpRes { res = append(res, append([]string{path[0]}, r...)) } } } } return res } func (db *KeePassDB) GetGroupsAndEntries(group kp.Group, path []string) [][]string { var res [][]string if len(path) == 0 { // Return all groups/entries at this level for _, g := range group.Groups { res = append(res, []string{g.Name}) } for _, e := range group.Entries { res = append(res, []string{e.GetTitle()}) } } else { useCase := strings.ToLower(path[0]) != path[0] for _, g := range group.Groups { var match bool if useCase { match = strings.HasPrefix(g.Name, path[0]) } else { match = strings.HasPrefix(strings.ToLower(g.Name), path[0]) } if match { grpRes := db.GetGroupsAndEntries(g, path[1:]) for _, r := range grpRes { res = append(res, append([]string{path[0]}, r...)) } } } for _, e := range group.Entries { var match bool if useCase { match = strings.HasPrefix(e.GetTitle(), path[0]) } else { match = strings.HasPrefix(strings.ToLower(e.GetTitle()), path[0]) } if match { res = append(res, []string{e.GetTitle()}) } } } return res } func (db *KeePassDB) GetEntryPassword(path []string) (string, error) { entry, err := db.FindEntryFromRoot(path) if err != nil { return "", err } return entry.GetPassword(), nil } func (db *KeePassDB) FindEntryFromRoot(path []string) (*kp.Entry, error) { if len(path) < 2 { return nil, errors.New("invalid path") } groups := db.db.Content.Root.Groups for _, g := range groups { if g.Name == path[0] { return db.FindEntryInGroup(g, path[1:]) } } return nil, errors.New("invalid path") } func (db *KeePassDB) FindEntryInGroup(group kp.Group, path []string) (*kp.Entry, error) { if len(path) == 0 { return nil, errors.New("invalid path") } else if len(path) == 1 { // Looking for an entry in this group for _, e := range group.Entries { if e.GetTitle() == path[0] { return &e, nil } } return nil, errors.New("invalid path") } for _, g := range group.Groups { if g.Name == path[0] { return db.FindEntryInGroup(g, path[1:]) } } return nil, errors.New("invalid path") }