From d69cc477af71aca368ff55de8b585946b59a4b0e Mon Sep 17 00:00:00 2001 From: bumi Date: Mon, 22 May 2017 21:19:15 +0200 Subject: [PATCH 1/2] Add support for YAML metadata This allows loading a specfied key from a as yaml formatted metadata part of a secret. Example: secret yaml-example: password yaml: rocks foo: bar usage: gopass show yaml-example -k foo # => bar --- action/show.go | 49 +++++++++++++++++++++++++++++------------- main.go | 4 ++++ password/root_store.go | 13 +++++++++++ 3 files changed, 51 insertions(+), 15 deletions(-) diff --git a/action/show.go b/action/show.go index 190254937b..2055039db9 100644 --- a/action/show.go +++ b/action/show.go @@ -7,6 +7,7 @@ import ( "github.com/atotto/clipboard" "github.com/fatih/color" "github.com/justwatchcom/gopass/qrcon" + "github.com/smallfish/simpleyaml" "github.com/urfave/cli" ) @@ -16,6 +17,7 @@ func (s *Action) Show(c *cli.Context) error { clip := c.Bool("clip") force := c.Bool("force") qr := c.Bool("qr") + key := c.String("key") if name == "" { return fmt.Errorf("provide a secret name") @@ -25,12 +27,42 @@ func (s *Action) Show(c *cli.Context) error { return s.List(c) } - if clip || qr { - content, err := s.Store.First(name) + content, err := s.Store.Get(name) + if err != nil { + return err + } + + // if we only want to display safe contnt or if we want parse the secret as yaml strip the first line + if (s.Store.SafeContent && !force) || key != "" { + content, err = s.Store.Metadata(name) if err != nil { return err } + } else if clip || qr { + content, err = s.Store.First(name) + if err != nil { + return err + } + } + if key != "" { + yaml, err := simpleyaml.NewYaml(content) + if err != nil { + return fmt.Errorf("failed to load secret as yaml") + } else { + value, err := yaml.Get(key).String() + if err != nil { + keys, err := yaml.GetMapKeys() + if err == nil { + return fmt.Errorf("%s not available. Available keys are: %s\n", key, keys) + } + } else { + content = []byte(value) + } + } + } + + if clip || qr { if qr { qr, err := qrcon.QRCode(string(content)) if err != nil { @@ -42,19 +74,6 @@ func (s *Action) Show(c *cli.Context) error { return s.copyToClipboard(name, content) } - content, err := s.Store.Get(name) - if err != nil { - return err - } - - if s.Store.SafeContent && !force { - lines := bytes.SplitN(content, []byte("\n"), 2) - if len(lines) < 2 || len(bytes.TrimSpace(lines[1])) == 0 { - return fmt.Errorf("no safe content to display, you can force display with show -f") - } - content = lines[1] - } - color.Yellow(string(content)) return nil diff --git a/main.go b/main.go index 38605932ac..c9f70d15eb 100644 --- a/main.go +++ b/main.go @@ -416,6 +416,10 @@ func main() { Name: "force, f", Usage: "Display the password even if safecontent is enabled", }, + cli.StringFlag{ + Name: "key, k", + Usage: "Load secret metadata as YAML and display a single key", + }, }, }, { diff --git a/password/root_store.go b/password/root_store.go index a75d7503a9..c8de551cfb 100644 --- a/password/root_store.go +++ b/password/root_store.go @@ -325,6 +325,19 @@ func (r *RootStore) First(name string) ([]byte, error) { return bytes.TrimSpace(lines[0]), nil } +// Metadata returns the content of a single entry except of the first line (the password) +func (r *RootStore) Metadata(name string) ([]byte, error) { + content, err := r.Get(name) + if err != nil { + return nil, err + } + lines := bytes.SplitN(content, []byte("\n"), 2) + if len(lines) < 2 || len(bytes.TrimSpace(lines[1])) == 0 { + return nil, fmt.Errorf("no safe content to display, you can force display with show -f") + } + return lines[1], nil +} + // Exists checks the existence of a single entry func (r *RootStore) Exists(name string) (bool, error) { store := r.getStore(name) From 0cea2b5ad324b5c956260fee499e8e8157cb94a3 Mon Sep 17 00:00:00 2001 From: bumi Date: Tue, 23 May 2017 00:43:51 +0200 Subject: [PATCH 2/2] Fix password clip/qr with safecontent config setting --- action/show.go | 5 ++--- password/root_store.go | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/action/show.go b/action/show.go index 2055039db9..e84b94fe0b 100644 --- a/action/show.go +++ b/action/show.go @@ -1,7 +1,6 @@ package action import ( - "bytes" "fmt" "github.com/atotto/clipboard" @@ -32,8 +31,8 @@ func (s *Action) Show(c *cli.Context) error { return err } - // if we only want to display safe contnt or if we want parse the secret as yaml strip the first line - if (s.Store.SafeContent && !force) || key != "" { + // load metadata if we want to parse the YAML (key is given) or we only want to display safe content (=metadata) and no clip/qr flag is given + if key != "" || (s.Store.SafeContent && !force && !(clip || qr)) { content, err = s.Store.Metadata(name) if err != nil { return err diff --git a/password/root_store.go b/password/root_store.go index c8de551cfb..0aa9b03e38 100644 --- a/password/root_store.go +++ b/password/root_store.go @@ -333,7 +333,7 @@ func (r *RootStore) Metadata(name string) ([]byte, error) { } lines := bytes.SplitN(content, []byte("\n"), 2) if len(lines) < 2 || len(bytes.TrimSpace(lines[1])) == 0 { - return nil, fmt.Errorf("no safe content to display, you can force display with show -f") + return nil, fmt.Errorf("no metadata found") } return lines[1], nil }