lockbox

password manager
Log | Files | Refs | README | LICENSE

commit 8c2660e464d603826a937d4c9a8b3e8fc60ec1a6
parent cf802f3d85ab3ff974ce7670ffeb15d566a03fc3
Author: Sean Enck <sean@ttypty.com>
Date:   Sat, 24 Dec 2022 10:58:43 -0500

allow for stats

Diffstat:
MMakefile | 3++-
Mcmd/main.go | 12+++++++++++-
Mcmd/vers.txt | 4++--
Minternal/backend/actions.go | 3++-
Minternal/backend/query.go | 12++++++++++--
Minternal/backend/query_test.go | 12+++++++++++-
Minternal/backend/types.go | 13++++++++-----
Minternal/cli/core.go | 2++
Mscripts/check.go | 1+
Mscripts/tests.expected.log | 1+
10 files changed, 50 insertions(+), 13 deletions(-)

diff --git a/Makefile b/Makefile @@ -6,6 +6,7 @@ DOC := $(BUILD)doc.text MAN := $(BUILD)lb.man DOCTEXT := scripts/doc.sections ACTUAL := $(BUILD)actual.log +DATE := $(shell date +%Y-%m-%d) .PHONY: $(TESTDIR) @@ -22,7 +23,7 @@ $(TESTDIR): check: $(TARGET) $(TESTDIR) rm -f $(BUILD)*.kdbx - LB_BUILD=$(TARGET) TEST_DATA=$(BUILD) SCRIPTS=$(PWD)/scripts/ go run scripts/check.go 2>&1 | sed "s#$(PWD)/$(DATA)##g" | sed 's/^[0-9][0-9][0-9][0-9][0-9][0-9]$$/XXXXXX/g' > $(ACTUAL) + LB_BUILD=$(TARGET) TEST_DATA=$(BUILD) SCRIPTS=$(PWD)/scripts/ go run scripts/check.go 2>&1 | sed "s#$(PWD)/$(DATA)##g" | sed 's/^[0-9][0-9][0-9][0-9][0-9][0-9]$$/XXXXXX/g' | sed 's/ ($(DATE).*//g' | sed 's/$(DATE).*//g' > $(ACTUAL) diff -u $(ACTUAL) scripts/tests.expected.log clean: diff --git a/cmd/main.go b/cmd/main.go @@ -241,11 +241,21 @@ func run() error { return wrapped("unable to remove entry", err) } } - case cli.ShowCommand, cli.ClipCommand: + case cli.ShowCommand, cli.ClipCommand, cli.StatsCommand: if len(args) != 3 { return errors.New("entry required") } entry := args[2] + if command == cli.StatsCommand { + v, err := t.Get(entry, backend.TimeValue) + if err != nil { + return wrapped("unable to get stats", err) + } + if v != nil { + fmt.Printf("modtime: %s\n", v.Value) + } + return nil + } clipboard := platform.Clipboard{} isShow := command == cli.ShowCommand if !isShow { diff --git a/cmd/vers.txt b/cmd/vers.txt @@ -1 +1 @@ -v22.12.02 -\ No newline at end of file +v22.12.03 +\ No newline at end of file diff --git a/internal/backend/actions.go b/internal/backend/actions.go @@ -7,6 +7,7 @@ import ( "os/exec" "path/filepath" "strings" + "time" "github.com/enckse/lockbox/internal/inputs" "github.com/tobischo/gokeepasslib/v3" @@ -262,8 +263,8 @@ func (t *Transaction) Move(src QueryEntity, dst string) error { v = inputs.FormatTOTP(v) e.Values = append(e.Values, protectedValue("otp", v)) } - e.Values = append(e.Values, protectedValue(field, v)) + e.Values = append(e.Values, value(modTimeKey, time.Now().Format(time.RFC3339))) c.insertEntity(dOffset, dTitle, e) return nil }) diff --git a/internal/backend/query.go b/internal/backend/query.go @@ -129,8 +129,16 @@ func (t *Transaction) QueryCallback(args QueryOptions) ([]QueryEntity, error) { switch args.Values { case SecretValue: entity.Value = val - case HashedValue: - entity.Value = fmt.Sprintf("%x", sha512.Sum512([]byte(val))) + case HashedValue, TimeValue: + t := getValue(e.backing, modTimeKey) + if args.Values == TimeValue { + entity.Value = t + } else { + if t != "" { + t = fmt.Sprintf(" (%s)", t) + } + entity.Value = fmt.Sprintf("%x%s", sha512.Sum512([]byte(val)), t) + } } } results = append(results, entity) diff --git a/internal/backend/query_test.go b/internal/backend/query_test.go @@ -1,6 +1,7 @@ package backend_test import ( + "strings" "testing" "github.com/enckse/lockbox/internal/backend" @@ -83,9 +84,18 @@ func TestValueModes(t *testing.T) { if err != nil { t.Errorf("no error: %v", err) } - if q.Value != "44276ba24db13df5568aa6db81e0190ab9d35d2168dce43dca61e628f5c666b1d8b091f1dda59c2359c86e7d393d59723a421d58496d279031e7f858c11d893e" { + hash := q.Value + parts := strings.Split(hash, " ") + if len(parts) != 2 { + t.Errorf("invalid hash output: %v", parts) + } + if parts[0] != "44276ba24db13df5568aa6db81e0190ab9d35d2168dce43dca61e628f5c666b1d8b091f1dda59c2359c86e7d393d59723a421d58496d279031e7f858c11d893e" { t.Errorf("invalid result value: %s", q.Value) } + dt := parts[1] + if !strings.HasPrefix(dt, "(") || !strings.HasSuffix(dt, ")") || len(dt) != 27 { + t.Errorf("invalid date/time: %s", dt) + } q, err = fullSetup(t, true).Get("test/test/ab11c", backend.SecretValue) if err != nil { t.Errorf("no error: %v", err) diff --git a/internal/backend/types.go b/internal/backend/types.go @@ -89,14 +89,17 @@ const ( HashedValue // SecretValue will have the raw secret onboard SecretValue + // TimeValue will show the last modification time + TimeValue ) const ( - notesKey = "Notes" - titleKey = "Title" - passKey = "Password" - pathSep = "/" - isGlob = pathSep + "*" + notesKey = "Notes" + titleKey = "Title" + passKey = "Password" + pathSep = "/" + isGlob = pathSep + "*" + modTimeKey = "ModTime" ) var ( diff --git a/internal/cli/core.go b/internal/cli/core.go @@ -14,6 +14,8 @@ import ( ) const ( + // StatsCommand will display additional entry stat information + StatsCommand = "stats" // TOTPCommand is the parent of totp and by defaults generates a rotating token TOTPCommand = "totp" // HashCommand handles hashing the data store diff --git a/scripts/check.go b/scripts/check.go @@ -77,6 +77,7 @@ func main() { runCommand([]string{"find", "e"}, nil) show("keys/k/one2") show("keys2/k/three") + runCommand([]string{"stats", "keys2/k/three"}, nil) for _, k := range []string{"test/k", "test/k/totp"} { runCommand([]string{"insert", "-totp", k}, []string{"5ae472abqdekjqykoyxk7hvc2leklq5n"}) } diff --git a/scripts/tests.expected.log b/scripts/tests.expected.log @@ -23,6 +23,7 @@ keys2/k/three test2 test3 test4 +modtime: test/k