mirror of
https://github.com/gopasspw/gopass.git
synced 2026-10-04 14:57:51 +02:00
* perf: batch-load GPG keys for formatting When formatting multiple recipient fingerprints, calling FormatKey once per fingerprint performs separate secret-key and public-key searches for every ID. This incurs repeated GPG process startup and keyring access costs. This patch adds `FormatKeys` so we can load the existing cached bulk secret and public key lists once, then resolve all requested IDs in memory. This reduces multi-key formatting to at most two bulk GPG lookups per backend instance while preserving the existing single-key formatting behavior. I considered narrowing the list of loaded keys, as gpg does allow specifying them, but after brief testing with a sizable keyring (several thousand public keys and dozens of private keys), I concluded the extra code wasn't buying any time. Signed-off-by: James Vasile <james@jamesvasile.com> * perf: use FormatKeys in recipients Use bulk key-formatting for recipient selection and recipient-tree rendering. These can format many recipient IDs in one command, so loading the GPG key lists once avoids repeated keyring lookups and GPG process overhead. Add the shared FormatKeys interface method and backend implementations required by the recipient paths. I did a quick test to see how much speedup I get. On my tree with my keyring (>3000 public keys, dozens of private keys), I got a 10.58X speedup. ``` equivalent results: yes command: recipients iterations: 5 gopass-fast median: 0.389s gopass-orig median: 4.120s speedup: 10.58x ``` Signed-off-by: James Vasile <james@jamesvasile.com> * feat: add recipients subtree listing This patch adds a `recipients list` subcommand with an optional prefix for displaying recipients below a selected subtree. It retains the existing `recipients` command as an all-recipient view, but narrows that command. The old `recipients` used to tolerate a subtree as a positional parameter. It didn't do anything with that param, but it didn't error. Now we error. The user should use the `list` subcommand so this is unambiguous and we can't have namespace contention between subtrees and subcommands. This patch is much faster than the old `recipients` command. That command used FormatKey. This one uses FormatKeys. Also, we now build prefixed recipient trees in two phases: first use raw recipient IDs to determine whether the requested subtree exists, then resolve and format keys only after a match is found. This avoids GPG formatting work for missing subtrees while preserving pretty and JSON output. Signed-off-by: James Vasile <james@jamesvasile.com> * perf: use FormatKeys for key selection widely Use bulk key formatting when displaying multiple private keys in the shared key-selection prompt. This avoids repeating GPG keyring lookups when the prompt has several candidates. This is not a big performance speedup, but it is a minor code optimization. Keep single-key and custom-template formatting on FormatKey. Signed-off-by: James Vasile <james@jamesvasile.com> --------- Signed-off-by: James Vasile <james@jamesvasile.com>
364 lines
15 KiB
Go
364 lines
15 KiB
Go
package action
|
|
|
|
// handler_shims.go — thin delegation methods on *Action.
|
|
//
|
|
// Every public command registered in commands.go, and every internal method
|
|
// called from test files, is available on *Action through a one-liner that
|
|
// forwards to the appropriate focused handler. This keeps commands.go and all
|
|
// existing tests unchanged while the real logic lives in the handler types.
|
|
|
|
import (
|
|
"context"
|
|
|
|
"github.com/gopasspw/gopass/internal/backend"
|
|
"github.com/urfave/cli/v3"
|
|
)
|
|
|
|
// ── secretHandler shims ────────────────────────────────────────────────────
|
|
|
|
func (s *Action) Show(ctx context.Context, cmd *cli.Command) error { return s.secrets.Show(ctx, cmd) }
|
|
|
|
func (s *Action) Insert(ctx context.Context, cmd *cli.Command) error {
|
|
return s.secrets.Insert(ctx, cmd)
|
|
}
|
|
|
|
func (s *Action) Edit(ctx context.Context, cmd *cli.Command) error { return s.secrets.Edit(ctx, cmd) }
|
|
|
|
func (s *Action) Delete(ctx context.Context, cmd *cli.Command) error {
|
|
return s.secrets.Delete(ctx, cmd)
|
|
}
|
|
|
|
func (s *Action) Copy(ctx context.Context, cmd *cli.Command) error { return s.secrets.Copy(ctx, cmd) }
|
|
|
|
func (s *Action) Move(ctx context.Context, cmd *cli.Command) error { return s.secrets.Move(ctx, cmd) }
|
|
|
|
func (s *Action) Link(ctx context.Context, cmd *cli.Command) error { return s.secrets.Link(ctx, cmd) }
|
|
|
|
func (s *Action) Merge(ctx context.Context, cmd *cli.Command) error { return s.secrets.Merge(ctx, cmd) }
|
|
|
|
// Internal methods accessed from tests.
|
|
func (s *Action) show(ctx context.Context, cmd *cli.Command, name string, recurse bool) error {
|
|
return s.secrets.show(ctx, cmd, name, recurse)
|
|
}
|
|
|
|
func (s *Action) showHandleRevision(ctx context.Context, cmd *cli.Command, name, revision string) error {
|
|
return s.secrets.showHandleRevision(ctx, cmd, name, revision)
|
|
}
|
|
|
|
func (s *Action) showHandleError(ctx context.Context, cmd *cli.Command, name string, recurse bool, err error) error {
|
|
return s.secrets.showHandleError(ctx, cmd, name, recurse, err)
|
|
}
|
|
|
|
func (s *Action) showPrintQR(name, pw string) error {
|
|
return s.secrets.showPrintQR(name, pw)
|
|
}
|
|
|
|
func (s *Action) hasAliasDomain(ctx context.Context, name string) string {
|
|
return s.secrets.hasAliasDomain(ctx, name)
|
|
}
|
|
|
|
func (s *Action) insert(ctx context.Context, cmd *cli.Command, name, key string, echo, multiline, force, appending bool, kvps map[string]string) error {
|
|
return s.secrets.insert(ctx, cmd, name, key, echo, multiline, force, appending, kvps)
|
|
}
|
|
|
|
func (s *Action) insertStdin(ctx context.Context, name string, content []byte, appendTo bool) error {
|
|
return s.secrets.insertStdin(ctx, name, content, appendTo)
|
|
}
|
|
|
|
func (s *Action) insertYAML(ctx context.Context, name, key string, content []byte, kvps map[string]string) error {
|
|
return s.secrets.insertYAML(ctx, name, key, content, kvps)
|
|
}
|
|
|
|
func (s *Action) editUpdate(ctx context.Context, name string, content, nContent []byte, changed bool, ed string) error {
|
|
return s.secrets.editUpdate(ctx, name, content, nContent, changed, ed)
|
|
}
|
|
|
|
// ── searchHandler shims ────────────────────────────────────────────────────
|
|
|
|
func (s *Action) Find(ctx context.Context, cmd *cli.Command) error { return s.search.Find(ctx, cmd) }
|
|
|
|
func (s *Action) FindFuzzy(ctx context.Context, cmd *cli.Command) error {
|
|
return s.search.FindFuzzy(ctx, cmd)
|
|
}
|
|
|
|
func (s *Action) Grep(ctx context.Context, cmd *cli.Command) error { return s.search.Grep(ctx, cmd) }
|
|
|
|
func (s *Action) List(ctx context.Context, cmd *cli.Command) error { return s.search.List(ctx, cmd) }
|
|
|
|
func (s *Action) History(ctx context.Context, cmd *cli.Command) error {
|
|
return s.search.History(ctx, cmd)
|
|
}
|
|
|
|
// ── generateHandler shims ──────────────────────────────────────────────────
|
|
|
|
func (s *Action) Generate(ctx context.Context, cmd *cli.Command) error {
|
|
return s.generate.Generate(ctx, cmd)
|
|
}
|
|
|
|
func (s *Action) Create(ctx context.Context, cmd *cli.Command) error {
|
|
return s.generate.Create(ctx, cmd)
|
|
}
|
|
|
|
func (s *Action) CompleteGenerate(ctx context.Context, cmd *cli.Command) {
|
|
s.generate.CompleteGenerate(ctx, cmd)
|
|
}
|
|
|
|
// ── mountHandler shims ─────────────────────────────────────────────────────
|
|
|
|
func (s *Action) MountRemove(ctx context.Context, cmd *cli.Command) error {
|
|
return s.mounts.MountRemove(ctx, cmd)
|
|
}
|
|
|
|
func (s *Action) MountsPrint(ctx context.Context, cmd *cli.Command) error {
|
|
return s.mounts.MountsPrint(ctx, cmd)
|
|
}
|
|
|
|
func (s *Action) MountsComplete(ctx context.Context, cmd *cli.Command) {
|
|
s.mounts.MountsComplete(ctx, cmd)
|
|
}
|
|
|
|
func (s *Action) MountAdd(ctx context.Context, cmd *cli.Command) error {
|
|
return s.mounts.MountAdd(ctx, cmd)
|
|
}
|
|
|
|
func (s *Action) MountsVersions(ctx context.Context, cmd *cli.Command) error {
|
|
return s.mounts.MountsVersions(ctx, cmd)
|
|
}
|
|
|
|
// ── recipientHandler shims ─────────────────────────────────────────────────
|
|
|
|
func (s *Action) RecipientsPrint(ctx context.Context, cmd *cli.Command) error {
|
|
return s.recipients.RecipientsPrint(ctx, cmd)
|
|
}
|
|
|
|
func (s *Action) RecipientsList(ctx context.Context, cmd *cli.Command) error {
|
|
return s.recipients.RecipientsList(ctx, cmd)
|
|
}
|
|
|
|
func (s *Action) RecipientsComplete(ctx context.Context, cmd *cli.Command) {
|
|
s.recipients.RecipientsComplete(ctx, cmd)
|
|
}
|
|
|
|
func (s *Action) RecipientsAck(ctx context.Context, cmd *cli.Command) error {
|
|
return s.recipients.RecipientsAck(ctx, cmd)
|
|
}
|
|
|
|
func (s *Action) RecipientsAdd(ctx context.Context, cmd *cli.Command) error {
|
|
return s.recipients.RecipientsAdd(ctx, cmd)
|
|
}
|
|
|
|
func (s *Action) RecipientsRemove(ctx context.Context, cmd *cli.Command) error {
|
|
return s.recipients.RecipientsRemove(ctx, cmd)
|
|
}
|
|
|
|
func (s *Action) RecipientsCanonicalize(ctx context.Context, cmd *cli.Command) error {
|
|
return s.recipients.RecipientsCanonicalize(ctx, cmd)
|
|
}
|
|
|
|
func (s *Action) RecipientsUpdate(ctx context.Context, cmd *cli.Command) error {
|
|
return s.recipients.RecipientsUpdate(ctx, cmd)
|
|
}
|
|
|
|
// ── setupHandler shims ─────────────────────────────────────────────────────
|
|
|
|
func (s *Action) IsInitialized(ctx context.Context, cmd *cli.Command) (context.Context, error) {
|
|
return s.setup.IsInitialized(ctx, cmd)
|
|
}
|
|
|
|
func (s *Action) Init(ctx context.Context, cmd *cli.Command) error { return s.setup.Init(ctx, cmd) }
|
|
|
|
func (s *Action) Setup(ctx context.Context, cmd *cli.Command) error { return s.setup.Setup(ctx, cmd) }
|
|
|
|
func (s *Action) Clone(ctx context.Context, cmd *cli.Command) error { return s.setup.Clone(ctx, cmd) }
|
|
|
|
func (s *Action) RCSInit(ctx context.Context, cmd *cli.Command) error {
|
|
return s.setup.RCSInit(ctx, cmd)
|
|
}
|
|
|
|
func (s *Action) RCSAddRemote(ctx context.Context, cmd *cli.Command) error {
|
|
return s.setup.RCSAddRemote(ctx, cmd)
|
|
}
|
|
|
|
func (s *Action) RCSRemoveRemote(ctx context.Context, cmd *cli.Command) error {
|
|
return s.setup.RCSRemoveRemote(ctx, cmd)
|
|
}
|
|
|
|
func (s *Action) RCSPull(ctx context.Context, cmd *cli.Command) error {
|
|
return s.setup.RCSPull(ctx, cmd)
|
|
}
|
|
|
|
func (s *Action) RCSPush(ctx context.Context, cmd *cli.Command) error {
|
|
return s.setup.RCSPush(ctx, cmd)
|
|
}
|
|
|
|
func (s *Action) RCSStatus(ctx context.Context, cmd *cli.Command) error {
|
|
return s.setup.RCSStatus(ctx, cmd)
|
|
}
|
|
|
|
// Internal methods accessed from tests.
|
|
func (s *Action) clone(ctx context.Context, repo, mount, path string) error {
|
|
return s.setup.clone(ctx, repo, mount, path)
|
|
}
|
|
|
|
func (s *Action) cloneGetGitConfig(ctx context.Context, name string) (string, string, error) {
|
|
return s.setup.cloneGetGitConfig(ctx, name)
|
|
}
|
|
|
|
func (s *Action) initHasUseablePrivateKeys(ctx context.Context, crypto backend.Crypto) bool {
|
|
return s.setup.initHasUseablePrivateKeys(ctx, crypto)
|
|
}
|
|
|
|
func (s *Action) initGenerateIdentity(ctx context.Context, crypto backend.Crypto, name, email string) error {
|
|
return s.setup.initGenerateIdentity(ctx, crypto, name, email)
|
|
}
|
|
|
|
func (s *Action) printRecipients(ctx context.Context, alias string) {
|
|
s.setup.printRecipients(ctx, alias)
|
|
}
|
|
|
|
func (s *Action) rcsInit(ctx context.Context, store, un, ue string) error {
|
|
return s.setup.rcsInit(ctx, store, un, ue)
|
|
}
|
|
|
|
func (s *Action) getUserData(ctx context.Context, store, name, email string) (string, string) {
|
|
return s.setup.getUserData(ctx, store, name, email)
|
|
}
|
|
|
|
// ── syncHandler shims ──────────────────────────────────────────────────────
|
|
|
|
func (s *Action) Sync(ctx context.Context, cmd *cli.Command) error { return s.syncH.Sync(ctx, cmd) }
|
|
func (s *Action) Git(ctx context.Context, cmd *cli.Command) error { return s.syncH.Git(ctx, cmd) }
|
|
|
|
// ── auditHandler shims ─────────────────────────────────────────────────────
|
|
|
|
func (s *Action) Audit(ctx context.Context, cmd *cli.Command) error { return s.audit.Audit(ctx, cmd) }
|
|
|
|
func (s *Action) Fsck(ctx context.Context, cmd *cli.Command) error { return s.audit.Fsck(ctx, cmd) }
|
|
|
|
// ── templateHandler shims ──────────────────────────────────────────────────
|
|
|
|
func (s *Action) TemplatesPrint(ctx context.Context, cmd *cli.Command) error {
|
|
return s.templates.TemplatesPrint(ctx, cmd)
|
|
}
|
|
|
|
func (s *Action) TemplatePrint(ctx context.Context, cmd *cli.Command) error {
|
|
return s.templates.TemplatePrint(ctx, cmd)
|
|
}
|
|
|
|
func (s *Action) TemplateEdit(ctx context.Context, cmd *cli.Command) error {
|
|
return s.templates.TemplateEdit(ctx, cmd)
|
|
}
|
|
|
|
func (s *Action) TemplateRemove(ctx context.Context, cmd *cli.Command) error {
|
|
return s.templates.TemplateRemove(ctx, cmd)
|
|
}
|
|
|
|
func (s *Action) TemplatesComplete(ctx context.Context, cmd *cli.Command) {
|
|
s.templates.TemplatesComplete(ctx, cmd)
|
|
}
|
|
|
|
// ── binaryHandler shims ────────────────────────────────────────────────────
|
|
|
|
func (s *Action) Cat(ctx context.Context, cmd *cli.Command) error { return s.binary.Cat(ctx, cmd) }
|
|
|
|
func (s *Action) BinaryCopy(ctx context.Context, cmd *cli.Command) error {
|
|
return s.binary.BinaryCopy(ctx, cmd)
|
|
}
|
|
|
|
func (s *Action) BinaryMove(ctx context.Context, cmd *cli.Command) error {
|
|
return s.binary.BinaryMove(ctx, cmd)
|
|
}
|
|
func (s *Action) Sum(ctx context.Context, cmd *cli.Command) error { return s.binary.Sum(ctx, cmd) }
|
|
|
|
// Internal methods accessed from tests.
|
|
func (s *Action) binaryCopy(ctx context.Context, cmd *cli.Command, from, to string, deleteSource bool) error {
|
|
return s.binary.binaryCopy(ctx, cmd, from, to, deleteSource)
|
|
}
|
|
|
|
func (s *Action) binaryGet(ctx context.Context, name string) ([]byte, error) {
|
|
return s.binary.binaryGet(ctx, name)
|
|
}
|
|
|
|
// ── envHandler shims ───────────────────────────────────────────────────────
|
|
|
|
func (s *Action) Env(ctx context.Context, cmd *cli.Command) error { return s.envH.Env(ctx, cmd) }
|
|
|
|
// ── otpHandler shims ───────────────────────────────────────────────────────
|
|
|
|
func (s *Action) OTP(ctx context.Context, cmd *cli.Command) error { return s.otpH.OTP(ctx, cmd) }
|
|
|
|
// Internal methods accessed from tests.
|
|
func (s *Action) otp(ctx context.Context, name, qrf string, clip, pw, recurse, chained, alsoClip bool) error {
|
|
return s.otpH.otp(ctx, name, qrf, clip, pw, recurse, chained, alsoClip)
|
|
}
|
|
|
|
// ── miscHandler shims ──────────────────────────────────────────────────────
|
|
|
|
func (s *Action) AliasesPrint(ctx context.Context, cmd *cli.Command) error {
|
|
return s.misc.AliasesPrint(ctx, cmd)
|
|
}
|
|
|
|
func (s *Action) Version(ctx context.Context, cmd *cli.Command) error {
|
|
return s.misc.Version(ctx, cmd)
|
|
}
|
|
|
|
func (s *Action) Convert(ctx context.Context, cmd *cli.Command) error {
|
|
return s.misc.Convert(ctx, cmd)
|
|
}
|
|
|
|
func (s *Action) Reorg(ctx context.Context, cmd *cli.Command) error { return s.misc.Reorg(ctx, cmd) }
|
|
|
|
func (s *Action) ReorgAfterEdit(ctx context.Context, initial, modified []string) error {
|
|
return s.misc.ReorgAfterEdit(ctx, initial, modified)
|
|
}
|
|
|
|
func (s *Action) Process(ctx context.Context, cmd *cli.Command) error {
|
|
return s.misc.Process(ctx, cmd)
|
|
}
|
|
|
|
func (s *Action) Unclip(ctx context.Context, cmd *cli.Command) error { return s.misc.Unclip(ctx, cmd) }
|
|
|
|
func (s *Action) Update(ctx context.Context, cmd *cli.Command) error { return s.misc.Update(ctx, cmd) }
|
|
|
|
func (s *Action) REPL(ctx context.Context, cmd *cli.Command) error { return s.misc.REPL(ctx, cmd) }
|
|
|
|
func (s *Action) Doctor(ctx context.Context, cmd *cli.Command) error { return s.misc.Doctor(ctx, cmd) }
|
|
func (s *Action) Complete(ctx context.Context, cmd *cli.Command) { s.misc.Complete(ctx, cmd) }
|
|
func (s *Action) CompletionOpenBSDKsh(a *cli.Command) error { return s.misc.CompletionOpenBSDKsh(a) }
|
|
|
|
func (s *Action) CompletionBash(ctx context.Context, cmd *cli.Command) error {
|
|
return s.misc.CompletionBash(ctx, cmd)
|
|
}
|
|
|
|
func (s *Action) CompletionFish(a *cli.Command) error { return s.misc.CompletionFish(a) }
|
|
|
|
func (s *Action) CompletionZSH(a *cli.Command) error { return s.misc.CompletionZSH(a) }
|
|
|
|
func (s *Action) Config(ctx context.Context, cmd *cli.Command) error { return s.misc.Config(ctx, cmd) }
|
|
|
|
func (s *Action) ConfigComplete(ctx context.Context, cmd *cli.Command) {
|
|
s.misc.ConfigComplete(ctx, cmd)
|
|
}
|
|
|
|
func (s *Action) newGopassCompleter(ctx context.Context, cmd *cli.Command) *gopassCompleter {
|
|
return s.misc.newGopassCompleter(ctx, cmd)
|
|
}
|
|
|
|
func (s *Action) setConfigValue(ctx context.Context, store, key, value string) error {
|
|
return s.misc.setConfigValue(ctx, store, key, value)
|
|
}
|
|
|
|
func (s *Action) printConfigValues(ctx context.Context, store string, needles ...string) {
|
|
s.misc.printConfigValues(ctx, store, needles...)
|
|
}
|
|
|
|
// ── searchHandler internal shims ───────────────────────────────────────────
|
|
|
|
func (s *Action) find(ctx context.Context, cmd *cli.Command, needle string, cb showFunc, fuzzy bool) error {
|
|
return s.search.find(ctx, cmd, needle, cb, fuzzy)
|
|
}
|
|
|
|
func (s *Action) findSelection(ctx context.Context, cmd *cli.Command, choices []string, needle string, cb showFunc) error {
|
|
return s.search.findSelection(ctx, cmd, choices, needle, cb)
|
|
}
|