commit f19188da18af16b76d851e6c2b7b68bf4e7f1100
parent c5c20832eff040d7e905e19e6267063977af760d
Author: Sean Enck <sean@ttypty.com>
Date: Fri, 31 Mar 2023 19:17:00 -0400
allow json to do plaintext outputs
Diffstat:
10 files changed, 63 insertions(+), 39 deletions(-)
diff --git a/internal/app/rekey.go b/internal/app/rekey.go
@@ -11,13 +11,13 @@ import (
"github.com/enckse/lockbox/internal/backend"
"github.com/enckse/lockbox/internal/cli"
"github.com/enckse/lockbox/internal/inputs"
+ "github.com/enckse/pgl/os/env"
)
type (
// Keyer defines how rekeying happens
Keyer interface {
JSON() (map[string]backend.JSON, error)
- Show(string) ([]byte, error)
Insert(ReKeyEntry) error
}
// ReKeyEntry is an entry that is being rekeyed
@@ -41,11 +41,6 @@ func NewDefaultKeyer() (DefaultKeyer, error) {
return DefaultKeyer{exe: exe}, nil
}
-// Show will get entry payload
-func (r DefaultKeyer) Show(entry string) ([]byte, error) {
- return exec.Command(r.exe, cli.ShowCommand, entry).Output()
-}
-
// JSON will get the JSON backing entries
func (r DefaultKeyer) JSON() (map[string]backend.JSON, error) {
out, err := exec.Command(r.exe, cli.JSONCommand).Output()
@@ -79,13 +74,15 @@ func (r DefaultKeyer) Insert(entry ReKeyEntry) error {
// ReKey handles entry rekeying
func ReKey(cmd CommandOptions, r Keyer) error {
args := cmd.Args()
- env, err := inputs.GetReKey(args)
+ vars, err := inputs.GetReKey(args)
if err != nil {
return err
}
if !cmd.Confirm("proceed with rekey") {
return nil
}
+
+ os.Setenv(inputs.JSONPlainTextEnv, env.Yes)
entries, err := r.JSON()
if err != nil {
return err
@@ -99,14 +96,10 @@ func ReKey(cmd CommandOptions, r Keyer) error {
if modTime == "" {
return errors.New("did not read modtime")
}
- data, err := r.Show(path)
- if err != nil {
- return err
- }
var insertEnv []string
- insertEnv = append(insertEnv, env...)
+ insertEnv = append(insertEnv, vars...)
insertEnv = append(insertEnv, fmt.Sprintf("%s=%s", inputs.ModTimeEnv, modTime))
- if err := r.Insert(ReKeyEntry{Path: path, Env: insertEnv, Data: data}); err != nil {
+ if err := r.Insert(ReKeyEntry{Path: path, Env: insertEnv, Data: []byte(entry.Data)}); err != nil {
return err
}
}
diff --git a/internal/app/rekey_test.go b/internal/app/rekey_test.go
@@ -25,14 +25,6 @@ func (m *mockKeyer) JSON() (map[string]backend.JSON, error) {
return m.items, nil
}
-func (m *mockKeyer) Show(entry string) ([]byte, error) {
- val, ok := m.data[entry]
- if !ok {
- return nil, errors.New("no data")
- }
- return val, nil
-}
-
func (m *mockKeyer) Insert(entry app.ReKeyEntry) error {
m.rekeys++
if entry.Path == "error" {
@@ -60,10 +52,6 @@ func TestErrors(t *testing.T) {
if err := app.ReKey(cmd, m); err == nil || err.Error() != "did not read modtime" {
t.Errorf("invalid error: %v", err)
}
- m.items = map[string]backend.JSON{"test1": {ModTime: "2"}}
- if err := app.ReKey(cmd, m); err == nil || err.Error() != "no data" {
- t.Errorf("invalid error: %v", err)
- }
m.data = make(map[string][]byte)
m.data["test1"] = []byte{1}
m.data["error"] = []byte{2}
diff --git a/internal/backend/query.go b/internal/backend/query.go
@@ -9,6 +9,7 @@ import (
"sort"
"strings"
+ "github.com/enckse/lockbox/internal/inputs"
"github.com/enckse/pgl/types/collections"
"github.com/tobischo/gokeepasslib/v3"
)
@@ -117,6 +118,11 @@ func (t *Transaction) QueryCallback(args QueryOptions) ([]QueryEntity, error) {
if isSort {
sort.Strings(keys)
}
+ plain, err := inputs.IsJSONPlainText()
+ if err != nil {
+ return nil, err
+ }
+ jsonHash := !plain
var results []QueryEntity
for _, k := range keys {
entity := QueryEntity{Path: k}
@@ -132,7 +138,11 @@ func (t *Transaction) QueryCallback(args QueryOptions) ([]QueryEntity, error) {
switch args.Values {
case JSONValue:
t := getValue(e.backing, modTimeKey)
- s := JSON{ModTime: t, Hash: fmt.Sprintf("%x", sha512.Sum512([]byte(val)))}
+ data := val
+ if jsonHash {
+ data = fmt.Sprintf("%x", sha512.Sum512([]byte(val)))
+ }
+ s := JSON{ModTime: t, Data: data}
m, err := json.Marshal(s)
if err != nil {
return nil, err
diff --git a/internal/backend/query_test.go b/internal/backend/query_test.go
@@ -90,7 +90,7 @@ func TestValueModes(t *testing.T) {
if err := json.Unmarshal([]byte(q.Value), &m); err != nil {
t.Errorf("no error: %v", err)
}
- if m.Hash != "44276ba24db13df5568aa6db81e0190ab9d35d2168dce43dca61e628f5c666b1d8b091f1dda59c2359c86e7d393d59723a421d58496d279031e7f858c11d893e" {
+ if m.Data != "44276ba24db13df5568aa6db81e0190ab9d35d2168dce43dca61e628f5c666b1d8b091f1dda59c2359c86e7d393d59723a421d58496d279031e7f858c11d893e" {
t.Errorf("invalid result value: %s", q.Value)
}
if len(m.ModTime) < 20 {
@@ -111,7 +111,20 @@ func TestValueModes(t *testing.T) {
if err := json.Unmarshal([]byte(q.Value), &m); err != nil {
t.Errorf("no error: %v", err)
}
- if len(m.ModTime) < 20 || m.Hash == "" {
+ if len(m.ModTime) < 20 || m.Data == "" {
+ t.Errorf("invalid json: %v", m)
+ }
+ os.Setenv("LOCKBOX_JSON_PLAINTEXT", "yes")
+ defer os.Clearenv()
+ q, err = fullSetup(t, true).Get("test/test/abc", backend.JSONValue)
+ if err != nil {
+ t.Errorf("no error: %v", err)
+ }
+ m = backend.JSON{}
+ if err := json.Unmarshal([]byte(q.Value), &m); err != nil {
+ t.Errorf("no error: %v", err)
+ }
+ if len(m.ModTime) < 20 || m.Data != "tedst" {
t.Errorf("invalid json: %v", m)
}
}
diff --git a/internal/backend/types.go b/internal/backend/types.go
@@ -56,7 +56,7 @@ type (
// JSON is an entry as a JSON string
JSON struct {
ModTime string `json:"modtime"`
- Hash string `json:"hash"`
+ Data string `json:"data"`
}
)
diff --git a/internal/cli/core_test.go b/internal/cli/core_test.go
@@ -14,7 +14,7 @@ func TestUsage(t *testing.T) {
t.Errorf("invalid usage, out of date? %d", len(u))
}
u, _ = cli.Usage(true)
- if len(u) != 82 {
+ if len(u) != 83 {
t.Errorf("invalid verbose usage, out of date? %d", len(u))
}
for _, usage := range u {
diff --git a/internal/inputs/env.go b/internal/inputs/env.go
@@ -66,6 +66,8 @@ const (
ModTimeFormat = time.RFC3339
// MaxTOTPTimeDefault is the max TOTP time to run (default)
MaxTOTPTimeDefault = "120"
+ // JSONPlainTextEnv toggles plain text on for JSON outputs
+ JSONPlainTextEnv = prefixKey + "JSON_PLAINTEXT"
)
var isYesNoArgs = []string{env.Yes, env.No}
@@ -226,6 +228,11 @@ func IsInteractive() (bool, error) {
return isYesNoEnv(true, interactiveEnv)
}
+// IsJSONPlainText indicates if JSON should plaintext values (not hashed)
+func IsJSONPlainText() (bool, error) {
+ return isYesNoEnv(false, JSONPlainTextEnv)
+}
+
// TOTPToken gets the name of the totp special case tokens
func TOTPToken() string {
return env.GetOrDefault(fieldTOTPEnv, defaultTOTPField)
@@ -277,5 +284,6 @@ func ListEnvironmentVariables(showValues bool) []string {
results = append(results, e.formatEnvironmentVariable(false, clipOSC52Env, env.No, "enable OSC52 clipboard mode", isYesNoArgs))
results = append(results, e.formatEnvironmentVariable(false, KeyFileEnv, "", "additional keyfile to access/protect the database", []string{"keyfile"}))
results = append(results, e.formatEnvironmentVariable(false, ModTimeEnv, ModTimeFormat, fmt.Sprintf("input modification time to set for the entry\n(expected format: %s)", ModTimeFormat), []string{"modtime"}))
+ results = append(results, e.formatEnvironmentVariable(false, JSONPlainTextEnv, env.No, "JSON output will show values as plaintext (not hashed)", isYesNoArgs))
return results
}
diff --git a/internal/inputs/env_test.go b/internal/inputs/env_test.go
@@ -53,6 +53,10 @@ func TestIsReadOnly(t *testing.T) {
checkYesNo("LOCKBOX_READONLY", t, inputs.IsReadOnly, false)
}
+func TestIsJSONPlaintext(t *testing.T) {
+ checkYesNo("LOCKBOX_JSON_PLAINTEXT", t, inputs.IsJSONPlainText, false)
+}
+
func TestIsOSC52(t *testing.T) {
checkYesNo("LOCKBOX_CLIP_OSC52", t, inputs.IsClipOSC52, false)
}
@@ -112,7 +116,7 @@ func TestListVariables(t *testing.T) {
known[trim] = struct{}{}
}
l := len(known)
- if l != 20 {
+ if l != 21 {
t.Errorf("invalid env count, outdated? %d", l)
}
}
diff --git a/tests/expected.log b/tests/expected.log
@@ -15,15 +15,15 @@ keys2/k/three
{
"key/a/one": {
"modtime": "XXXX-XX-XX",
- "hash": "ee26b0dd4af7e749aa1a8ee3c10ae9923f618980772e473f8819a5d4940e0db27ac185f8a0e1d5f84f88bc887fd67b143732c304cc5fa9ad8e6f57f50028a8ff"
+ "data": "ee26b0dd4af7e749aa1a8ee3c10ae9923f618980772e473f8819a5d4940e0db27ac185f8a0e1d5f84f88bc887fd67b143732c304cc5fa9ad8e6f57f50028a8ff"
},
"keys/k/one2": {
"modtime": "XXXX-XX-XX",
- "hash": "6d201beeefb589b08ef0672dac82353d0cbd9ad99e1642c83a1601f3d647bcca003257b5e8f31bdc1d73fbec84fb085c79d6e2677b7ff927e823a54e789140d9"
+ "data": "6d201beeefb589b08ef0672dac82353d0cbd9ad99e1642c83a1601f3d647bcca003257b5e8f31bdc1d73fbec84fb085c79d6e2677b7ff927e823a54e789140d9"
},
"keys2/k/three": {
"modtime": "XXXX-XX-XX",
- "hash": "132ab0244293c495a027cec12d0050598616daca888449920fc652719be0987830827d069ef78cc613e348de37c9b592d3406e2fb8d99a6961bf0c58da8a334f"
+ "data": "132ab0244293c495a027cec12d0050598616daca888449920fc652719be0987830827d069ef78cc613e348de37c9b592d3406e2fb8d99a6961bf0c58da8a334f"
}
}
@@ -33,7 +33,7 @@ test4
{
"keys2/k/three": {
"modtime": "XXXX-XX-XX",
- "hash": "132ab0244293c495a027cec12d0050598616daca888449920fc652719be0987830827d069ef78cc613e348de37c9b592d3406e2fb8d99a6961bf0c58da8a334f"
+ "data": "132ab0244293c495a027cec12d0050598616daca888449920fc652719be0987830827d069ef78cc613e348de37c9b592d3406e2fb8d99a6961bf0c58da8a334f"
}
}
@@ -44,19 +44,19 @@ XXXXXX
{
"key/a/one": {
"modtime": "XXXX-XX-XX",
- "hash": "ee26b0dd4af7e749aa1a8ee3c10ae9923f618980772e473f8819a5d4940e0db27ac185f8a0e1d5f84f88bc887fd67b143732c304cc5fa9ad8e6f57f50028a8ff"
+ "data": "ee26b0dd4af7e749aa1a8ee3c10ae9923f618980772e473f8819a5d4940e0db27ac185f8a0e1d5f84f88bc887fd67b143732c304cc5fa9ad8e6f57f50028a8ff"
},
"keys/k/one2": {
"modtime": "XXXX-XX-XX",
- "hash": "6d201beeefb589b08ef0672dac82353d0cbd9ad99e1642c83a1601f3d647bcca003257b5e8f31bdc1d73fbec84fb085c79d6e2677b7ff927e823a54e789140d9"
+ "data": "6d201beeefb589b08ef0672dac82353d0cbd9ad99e1642c83a1601f3d647bcca003257b5e8f31bdc1d73fbec84fb085c79d6e2677b7ff927e823a54e789140d9"
},
"keys2/k/three": {
"modtime": "XXXX-XX-XX",
- "hash": "132ab0244293c495a027cec12d0050598616daca888449920fc652719be0987830827d069ef78cc613e348de37c9b592d3406e2fb8d99a6961bf0c58da8a334f"
+ "data": "132ab0244293c495a027cec12d0050598616daca888449920fc652719be0987830827d069ef78cc613e348de37c9b592d3406e2fb8d99a6961bf0c58da8a334f"
},
"test/k/totp": {
"modtime": "XXXX-XX-XX",
- "hash": "7ef183065ba70aaa417b87ea0a96b7e550a938a52440c640a07537f7794d8a89e50078eca6a7cbcfacabd97a2db06d11e82ddf7556ca909c4df9fc0d006013b1"
+ "data": "7ef183065ba70aaa417b87ea0a96b7e550a938a52440c640a07537f7794d8a89e50078eca6a7cbcfacabd97a2db06d11e82ddf7556ca909c4df9fc0d006013b1"
}
}
delete entry? (y/N)
@@ -127,5 +127,11 @@ proceed with rekey? (y/N) rekeying: keys/k/one2
keys/k/one2
test2
+{
+ "keys/k/one2": {
+ "modtime": "XXXX-XX-XX",
+ "data": "test2"
+ }
+}
clipboard will clear in 5 seconds
Wrong password? HMAC-SHA256 of header mismatching
diff --git a/tests/run.sh b/tests/run.sh
@@ -99,6 +99,8 @@ _rekey() {
echo
${LB_BINARY} ls
${LB_BINARY} show keys/k/one2
+ export LOCKBOX_JSON_PLAINTEXT=yes
+ ${LB_BINARY} json k
}
_clipboard() {