forked from gopasspw/gopass
/
show.go
163 lines (141 loc) · 4.93 KB
/
show.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
package action
import (
"context"
"fmt"
"os"
"strings"
"github.com/gopasspw/gopass/pkg/clipboard"
"github.com/gopasspw/gopass/pkg/ctxutil"
"github.com/gopasspw/gopass/pkg/out"
"github.com/gopasspw/gopass/pkg/qrcon"
"github.com/gopasspw/gopass/pkg/store"
"github.com/pkg/errors"
"github.com/urfave/cli"
)
const (
// BinarySuffix is the suffix that is appended to binaries in the store
BinarySuffix = ".b64"
)
// Show the content of a secret file
func (s *Action) Show(ctx context.Context, c *cli.Context) error {
name := c.Args().First()
key := c.Args().Get(1)
ctx = s.Store.WithConfig(ctx, name)
ctx = WithClip(ctx, c.Bool("clip"))
ctx = WithForce(ctx, c.Bool("force"))
ctx = WithPrintQR(ctx, c.Bool("qr"))
ctx = WithPasswordOnly(ctx, c.Bool("password"))
ctx = WithRevision(ctx, c.String("revision"))
if c.Bool("sync") {
if err := s.sync(out.WithHidden(ctx, true), c, s.Store.MountPoint(name)); err != nil {
out.Error(ctx, "Failed to sync %s: %s", name, err)
}
}
if err := s.show(ctx, c, name, key, true); err != nil {
return ExitError(ctx, ExitDecrypt, err, "%s", err)
}
return nil
}
// show displays the given secret/key
func (s *Action) show(ctx context.Context, c *cli.Context, name, key string, recurse bool) error {
if name == "" {
return ExitError(ctx, ExitUsage, nil, "Usage: %s show [name]", s.Name)
}
if s.Store.IsDir(ctx, name) && !s.Store.Exists(ctx, name) {
return s.List(ctx, c)
}
if s.Store.IsDir(ctx, name) && ctxutil.IsTerminal(ctx) {
out.Cyan(ctx, "Warning: %s is a secret and a folder. Use 'gopass show %s' to display the secret and 'gopass list %s' to show the content of the folder", name, name, name)
}
// auto-fallback to binary files with b64 suffix, if unique
if !s.Store.Exists(ctx, name) && s.Store.Exists(ctx, name+BinarySuffix) {
name += BinarySuffix
}
if HasRevision(ctx) {
return s.showHandleRevision(ctx, c, name, key, GetRevision(ctx))
}
sec, ctx, err := s.Store.GetContext(ctx, name)
if err != nil {
return s.showHandleError(ctx, c, name, recurse, err)
}
return s.showHandleOutput(ctx, name, key, sec)
}
// showHandleRevision displays a single revision
func (s *Action) showHandleRevision(ctx context.Context, c *cli.Context, name, key, revision string) error {
sec, err := s.Store.GetRevision(ctx, name, revision)
if err != nil {
return s.showHandleError(ctx, c, name, false, err)
}
return s.showHandleOutput(ctx, name, key, sec)
}
// showHandleOutput displays a secret
func (s *Action) showHandleOutput(ctx context.Context, name, key string, sec store.Secret) error {
var content string
switch {
case key != "":
val, err := sec.Value(key)
if err != nil {
return s.showHandleYAMLError(ctx, name, key, err)
}
if IsClip(ctx) {
return clipboard.CopyTo(ctx, name, []byte(val))
}
content = val
case IsPrintQR(ctx):
return s.showPrintQR(ctx, name, sec.Password())
case IsClip(ctx):
return clipboard.CopyTo(ctx, name, []byte(sec.Password()))
default:
switch {
case IsPasswordOnly(ctx):
content = sec.Password()
case ctxutil.IsShowSafeContent(ctx) && !IsForce(ctx):
content = sec.Body()
if content == "" {
if ctxutil.IsAutoClip(ctx) {
out.Yellow(ctx, "No safe content to display, you can force display with show -f.\nCopying password instead.")
return clipboard.CopyTo(ctx, name, []byte(sec.Password()))
}
return ExitError(ctx, ExitNotFound, store.ErrNoBody, store.ErrNoBody.Error())
}
default:
buf, err := sec.Bytes()
if err != nil {
return ExitError(ctx, ExitUnknown, err, "failed to encode secret: %s", err)
}
content = string(buf)
}
}
ctx = out.WithNewline(ctx, ctxutil.IsTerminal(ctx) && !strings.HasSuffix(content, "\n"))
out.Yellow(ctx, content)
return nil
}
// showHandleError handles errors retrieving secrets
func (s *Action) showHandleError(ctx context.Context, c *cli.Context, name string, recurse bool, err error) error {
if err != store.ErrNotFound || !recurse || !ctxutil.IsTerminal(ctx) {
return ExitError(ctx, ExitUnknown, err, "failed to retrieve secret '%s': %s", name, err)
}
out.Yellow(ctx, "Entry '%s' not found. Starting search...", name)
if err := s.Find(ctx, c); err != nil {
return ExitError(ctx, ExitNotFound, err, "%s", err)
}
os.Exit(ExitNotFound)
return nil
}
func (s *Action) showHandleYAMLError(ctx context.Context, name, key string, err error) error {
if errors.Cause(err) == store.ErrYAMLValueUnsupported {
return ExitError(ctx, ExitUnsupported, err, "Can not show nested key directly. Use 'gopass show %s'", name)
}
if errors.Cause(err) == store.ErrNotFound {
return ExitError(ctx, ExitNotFound, err, "Secret '%s' not found", name)
}
return ExitError(ctx, ExitUnknown, err, "failed to retrieve key '%s' from '%s': %s", key, name, err)
}
func (s *Action) showPrintQR(ctx context.Context, name, pw string) error {
qr, err := qrcon.QRCode(pw)
if err != nil {
return ExitError(ctx, ExitUnknown, err, "failed to encode '%s' as QR: %s", name, err)
}
fmt.Fprintln(stdout, qr)
return nil
}