gopass/internal/store/root/recipients.go
Dominik Schulz 5d5e83d789
Check existing recipients before trying to add a new one (#2487)
* Check existing recipients before trying to add a new one

Fixes #1918

RELEASE_NOTES=[ENHANCEMENT] Check recipients before adding a new one.

Signed-off-by: Dominik Schulz <dominik.schulz@gauner.org>

* Add test for CheckRecipients with an invalid key.

Signed-off-by: Dominik Schulz <dominik.schulz@gauner.org>

* Add custom error type and a better error message.

Signed-off-by: Dominik Schulz <dominik.schulz@gauner.org>

* Initialize InvalidRecipientsError

Signed-off-by: Dominik Schulz <dominik.schulz@gauner.org>

* Skip CheckRecipients tests on Windows

Signed-off-by: Dominik Schulz <dominik.schulz@gauner.org>
2022-12-24 19:16:41 +01:00

142 lines
3.7 KiB
Go

package root
import (
"context"
"fmt"
"sort"
"strings"
"github.com/fatih/color"
"github.com/gopasspw/gopass/internal/out"
"github.com/gopasspw/gopass/internal/store"
"github.com/gopasspw/gopass/internal/tree"
"github.com/gopasspw/gopass/pkg/debug"
)
// ListRecipients lists all recipients for the given store.
func (r *Store) ListRecipients(ctx context.Context, store string) []string {
sub, _ := r.getStore(store)
return sub.Recipients(ctx)
}
// CheckRecipients checks all current recipients to make sure that they are
// valid, e.g. not expired.
func (r *Store) CheckRecipients(ctx context.Context, store string) error {
sub, _ := r.getStore(store)
return sub.CheckRecipients(ctx)
}
// AddRecipient adds a single recipient to the given store.
func (r *Store) AddRecipient(ctx context.Context, store, rec string) error {
sub, _ := r.getStore(store)
return sub.AddRecipient(ctx, rec)
}
// RemoveRecipient removes a single recipient from the given store.
func (r *Store) RemoveRecipient(ctx context.Context, store, rec string) error {
sub, _ := r.getStore(store)
return sub.RemoveRecipient(ctx, rec)
}
func (r *Store) addRecipient(ctx context.Context, prefix string, root *tree.Root, recp string, pretty bool) error {
sub, _ := r.getStore(prefix)
key := recp
if pretty {
key = fmt.Sprintf("%s (missing public key)", recp)
if v := sub.Crypto().FormatKey(ctx, recp, ""); v != "" {
key = v
if !strings.HasPrefix(v, recp) {
key = recp + " => " + v
}
debug.Log("formated (FormatKey) %s as %s", recp, key)
}
}
// workaround to keep key names from breaking the folder structure.
// A proper fix should change tree.AddFile to take a path and file name
// (which could then contain slashes).
key = strings.ReplaceAll(key, "/", "")
debug.Log("adding %q to the tree", key)
return root.AddFile(prefix+key, "gopass/recipient")
}
// ImportMissingPublicKeys import missing public keys in any substore.
func (r *Store) ImportMissingPublicKeys(ctx context.Context) error {
for alias, sub := range r.mounts {
if err := sub.ImportMissingPublicKeys(ctx); err != nil {
out.Errorf(ctx, "[%s] Failed to import missing public keys: %s", alias, err)
}
}
return r.store.ImportMissingPublicKeys(ctx)
}
// SaveRecipients persists the recipients to disk. Only useful if persist keys is
// enabled.
func (r *Store) SaveRecipients(ctx context.Context, ack bool) error {
for alias, sub := range r.mounts {
if err := sub.SaveRecipients(ctx, ack); err != nil {
out.Errorf(ctx, "[%s] Failed to save recipients: %s", alias, err)
}
}
return r.store.SaveRecipients(ctx, ack)
}
// RecipientsTree returns a tree view of all stores' recipients.
func (r *Store) RecipientsTree(ctx context.Context, pretty bool) (*tree.Root, error) {
root := tree.New("gopass")
for name, recps := range r.store.RecipientsTree(ctx) {
if name != "" {
name += "/"
}
debug.Log("Store/Secret: %q -> Recipients: %v", name, recps)
for _, recp := range recps {
if err := r.addRecipient(ctx, name, root, recp, pretty); err != nil {
color.Yellow("Failed to add recipient to tree %s: %s", recp, err)
}
}
}
mps := r.MountPoints()
sort.Sort(store.ByPathLen(mps))
for _, alias := range mps {
substore := r.mounts[alias]
// ignore invalid entries
if substore == nil {
continue
}
if err := root.AddMount(alias, substore.Path()); err != nil {
return nil, fmt.Errorf("failed to add mount: %w", err)
}
for name, recps := range substore.RecipientsTree(ctx) {
if name != "" {
name += "/"
}
for _, recp := range recps {
if err := r.addRecipient(ctx, alias+"/"+name, root, recp, pretty); err != nil {
debug.Log("Failed to add recipient to tree %s: %s", recp, err)
}
}
}
}
return root, nil
}