forked from gopasspw/gopass
-
Notifications
You must be signed in to change notification settings - Fork 0
/
recipients.go
190 lines (160 loc) · 5.26 KB
/
recipients.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
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
package action
import (
"context"
"fmt"
"strings"
"github.com/justwatchcom/gopass/pkg/ctxutil"
"github.com/justwatchcom/gopass/pkg/cui"
"github.com/justwatchcom/gopass/pkg/out"
"github.com/justwatchcom/gopass/pkg/termio"
"github.com/urfave/cli"
)
var (
removalWarning = `
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@ WARNING: REMOVING A USER WILL NOT REVOKE ACCESS FROM OLD REVISONS! @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
THE USER %s WILL STILL BE ABLE TO ACCESS ANY OLD COPY OF THE STORE AND
ANY OLD REVISION HE HAD ACCESS TO.
ANY CREDENTIALS THIS USER HAD ACCESS TO NEED TO BE CONSIDERED COMPROMISED
AND SHOULD BE REVOKED.
This feature is only meant from revoking access to any added or changed
credentials.
`
)
// RecipientsPrint prints all recipients per store
func (s *Action) RecipientsPrint(ctx context.Context, c *cli.Context) error {
out.Cyan(ctx, "Hint: run 'gopass sync' to import any missing public keys")
tree, err := s.Store.RecipientsTree(ctx, true)
if err != nil {
return ExitError(ctx, ExitList, err, "failed to list recipients: %s", err)
}
fmt.Fprintln(stdout, tree.Format(0))
return nil
}
// RecipientsComplete will print a list of recipients for bash
// completion
func (s *Action) RecipientsComplete(ctx context.Context, c *cli.Context) {
tree, err := s.Store.RecipientsTree(ctx, false)
if err != nil {
fmt.Fprintln(stdout, err)
return
}
for _, v := range tree.List(0) {
fmt.Fprintln(stdout, v)
}
}
// RecipientsAdd adds new recipients
func (s *Action) RecipientsAdd(ctx context.Context, c *cli.Context) error {
store := c.String("store")
added := 0
// select store
if store == "" {
store = cui.AskForStore(ctx, s.Store)
}
crypto := s.Store.Crypto(ctx, store)
// select recipient
recipients := []string(c.Args())
if len(recipients) < 1 {
choices := []string{}
kl, _ := crypto.FindPublicKeys(ctx)
for _, key := range kl {
choices = append(choices, crypto.FormatKey(ctx, key))
}
if len(choices) > 0 {
act, sel := cui.GetSelection(ctx, "Add Recipient -", "<↑/↓> to change the selection, <→> to add this recipient, <ESC> to quit", choices)
switch act {
case "default":
fallthrough
case "show":
recipients = []string{kl[sel]}
default:
return ExitError(ctx, ExitAborted, nil, "user aborted")
}
}
}
for _, r := range recipients {
keys, err := crypto.FindPublicKeys(ctx, r)
if err != nil {
out.Cyan(ctx, "Failed to list public key '%s': %s", r, err)
continue
}
if len(keys) < 1 {
out.Cyan(ctx, "Warning: No matching valid key found. If the key is in your keyring you may need to validate it.")
out.Cyan(ctx, "If this is your key: gpg --edit-key %s; trust (set to ultimate); quit", r)
out.Cyan(ctx, "If this is not your key: gpg --edit-key %s; lsign; trust; save; quit", r)
out.Cyan(ctx, "You may need to run 'gpg --update-trustdb' afterwards")
continue
}
if !termio.AskForConfirmation(ctx, fmt.Sprintf("Do you want to add '%s' as an recipient to the store '%s'?", crypto.FormatKey(ctx, keys[0]), store)) {
continue
}
if err := s.Store.AddRecipient(ctxutil.WithNoConfirm(ctx, true), store, keys[0]); err != nil {
return ExitError(ctx, ExitRecipients, err, "failed to add recipient '%s': %s", r, err)
}
added++
}
if added < 1 {
return ExitError(ctx, ExitUnknown, nil, "no key added")
}
out.Green(ctx, "\nAdded %d recipients", added)
out.Cyan(ctx, "You need to run 'gopass sync' to push these changes")
return nil
}
// RecipientsRemove removes recipients
func (s *Action) RecipientsRemove(ctx context.Context, c *cli.Context) error {
store := c.String("store")
// select store
if store == "" {
store = cui.AskForStore(ctx, s.Store)
}
crypto := s.Store.Crypto(ctx, store)
// select recipient
recipients := []string(c.Args())
if len(recipients) < 1 {
rs, err := s.recipientsSelectForRemoval(ctx, store)
if err != nil {
return err
}
recipients = rs
}
removed := 0
for _, r := range recipients {
kl, err := crypto.FindPrivateKeys(ctx, r)
if err == nil {
if len(kl) > 0 {
if !termio.AskForConfirmation(ctx, fmt.Sprintf("Do you want to remove yourself (%s) from the recipients?", r)) {
continue
}
}
}
if err := s.Store.RemoveRecipient(ctxutil.WithNoConfirm(ctx, true), store, strings.TrimPrefix(r, "0x")); err != nil {
return ExitError(ctx, ExitRecipients, err, "failed to remove recipient '%s': %s", r, err)
}
fmt.Fprintf(stdout, removalWarning, r)
removed++
}
out.Green(ctx, "\nRemoved %d recipients", removed)
out.Cyan(ctx, "You need to run 'gopass sync' to push these changes")
return nil
}
func (s *Action) recipientsSelectForRemoval(ctx context.Context, store string) ([]string, error) {
crypto := s.Store.Crypto(ctx, store)
ids := s.Store.ListRecipients(ctx, store)
choices := make([]string, 0, len(ids))
for _, id := range ids {
choices = append(choices, crypto.FormatKey(ctx, id))
}
if len(choices) < 1 {
return nil, nil
}
act, sel := cui.GetSelection(ctx, "Remove recipient -", "<↑/↓> to change the selection, <→> to remove this recipient, <ESC> to quit", choices)
switch act {
case "default":
fallthrough
case "show":
return []string{ids[sel]}, nil
default:
return nil, ExitError(ctx, ExitAborted, nil, "user aborted")
}
}