diff --git a/docs/backends.md b/docs/backends.md index 9a14e63e1b..a3f6ea607a 100644 --- a/docs/backends.md +++ b/docs/backends.md @@ -1,28 +1,27 @@ # Backends -gopass supports pluggable backends for Storage (`store`), Encryption (`crypto`) and Source-Control-Management (`sync`). +gopass supports pluggable backends for Storage and Revision Control System (`storage`) and Encryption (`crypto`). As of today, the names and responsibilities of these backends are still unstable and will probably change. -By providing suitable backends, gopass can use different kinds of encryption (see XC below) or storage. -For example, it is pretty straightforward to add mercurial or bazaar as an SCM backend or -implement a Vault storage. +By providing suitable backends, gopass can use different kinds of encryption or storage. +For example, it is pretty straightforward to add mercurial or bazaar as an SCM backend. All backends are in their own packages below `backend/`. They need to implement the interfaces defined in the backend package and have their identification added to the context handlers in the same package. -## Storage Backends (storage) +## Storage and RCS Backends (storage) ### Filesystem (fs) -This is the only stable storage backend. It stores the encrypted (see "Crypto Backends") data directly in the filesystem. +This is the simplest storage backend. It stores the encrypted (see "Crypto Backends") data directly in the filesystem. It does not use version control and the relevant operations are no ops. -### In Memory (inmem) +### Filesystem with Git (gitfs) -This is a volatile in-memory backend for tests. - -**WARNING**: All data is lost when gopass stops! +This is the default storage backend. It stores the encrypted data directly in the +filesystem. It uses an external git binary to provide history and remote sync +operations. ### On Disk (ondisk) @@ -43,21 +42,22 @@ The age keyring itself is also age encrypted serialized JSON. TODO: Add commands to decrypt an ondisk/age store without gopass. -## RCS Backends (rcs) - -These are revision control backends talking to various source control -management systems. - -### CLI-based git (gitcli) +#### Background: How do access ondisk secrets without gopass -The CLI-based git backend requires a properly configured git binary. It has the -most features of all SCM backends and is pretty stable. One major drawback is that -it sometimes fails if commit signing is enabled and the interaction with GPG -fails. +This section assumes `age` and `jq` are properly installed. -### Noop (noop) - -This is a no-op backend for testing SCM-less support. +``` +# Decrypt the gopass-age keyring +age -d -o /tmp/keyring ~/.config/gopass/age-keyring.age +# Extract the private key +cat /tmp/keyring | jq ".[1].identity" | cut -d'"' -f2 > /tmp/private-key +# Decrypt the index +# TODO +# Locate the latest secrets +# TODO +# Decrypt it +age -d -i /tmp/private-key -o /tmp/plaintext ~/.local/share/gopass/stores/root/foo.age +``` ## Crypto Backends (crypto) @@ -103,3 +103,4 @@ encrypted keyring on top (using age in scrypt password mode). It also has use their ssh public keys for age encryption. This backend might very well become the new default backend. + diff --git a/internal/action/action_test.go b/internal/action/action_test.go index f343ee9e45..a5d170fb26 100644 --- a/internal/action/action_test.go +++ b/internal/action/action_test.go @@ -24,9 +24,8 @@ func newMock(ctx context.Context, u *gptest.Unit) (*Action, error) { cfg := config.Load() cfg.Path = u.StoreDir("") - ctx = backend.WithRCSBackend(ctx, backend.Noop) ctx = backend.WithCryptoBackend(ctx, backend.Plain) - ctx = backend.WithStorageBackend(ctx, backend.FS) + ctx = backend.WithStorageBackend(ctx, backend.GitFS) act, err := newAction(cfg, semver.Version{}) if err != nil { return nil, err diff --git a/internal/action/clone.go b/internal/action/clone.go index 96369c3e37..1e6635619d 100644 --- a/internal/action/clone.go +++ b/internal/action/clone.go @@ -24,9 +24,6 @@ func (s *Action) Clone(c *cli.Context) error { if c.IsSet("crypto") { ctx = backend.WithCryptoBackendString(ctx, c.String("crypto")) } - if c.IsSet("sync") { - ctx = backend.WithRCSBackendString(ctx, c.String("sync")) - } if c.Args().Len() < 1 { return ExitError(ExitUsage, nil, "Usage: %s clone repo [mount]", s.Name) @@ -43,11 +40,11 @@ func (s *Action) Clone(c *cli.Context) error { return s.clone(ctx, repo, mount, path) } -func rcsBackendOrDefault(ctx context.Context) backend.RCSBackend { - if be := backend.GetRCSBackend(ctx); be != backend.Noop { +func storageBackendOrDefault(ctx context.Context) backend.StorageBackend { + if be := backend.GetStorageBackend(ctx); be != backend.FS { return be } - return backend.GitCLI + return backend.GitFS } func (s *Action) clone(ctx context.Context, repo, mount, path string) error { @@ -71,7 +68,7 @@ func (s *Action) clone(ctx context.Context, repo, mount, path string) error { // clone repo debug.Log("Cloning repo '%s' to '%s'", repo, path) - if _, err := backend.CloneRCS(ctx, rcsBackendOrDefault(ctx), repo, path); err != nil { + if _, err := backend.Clone(ctx, storageBackendOrDefault(ctx), repo, path); err != nil { return ExitError(ExitGit, err, "failed to clone repo '%s' to '%s': %s", repo, path, err) } diff --git a/internal/action/clone_test.go b/internal/action/clone_test.go index b5e1ae7cf4..461971084e 100644 --- a/internal/action/clone_test.go +++ b/internal/action/clone_test.go @@ -9,7 +9,7 @@ import ( "testing" "github.com/gopasspw/gopass/internal/backend" - git "github.com/gopasspw/gopass/internal/backend/rcs/git/cli" + git "github.com/gopasspw/gopass/internal/backend/storage/gitfs" "github.com/gopasspw/gopass/internal/config" "github.com/gopasspw/gopass/internal/gptest" "github.com/gopasspw/gopass/internal/out" @@ -26,7 +26,7 @@ func aGitRepo(ctx context.Context, u *gptest.Unit, t *testing.T, name string) st gd := filepath.Join(u.Dir, name) assert.NoError(t, os.MkdirAll(gd, 0700)) - _, err := git.Open(gd) + _, err := git.New(gd) assert.Error(t, err) idf := filepath.Join(gd, ".gpg-id") @@ -46,7 +46,7 @@ func TestClone(t *testing.T) { ctx := context.Background() ctx = ctxutil.WithAlwaysYes(ctx, true) ctx = ctxutil.WithInteractive(ctx, false) - ctx = backend.WithRCSBackend(ctx, backend.GitCLI) + ctx = backend.WithStorageBackend(ctx, backend.GitFS) act, err := newMock(ctx, u) require.NoError(t, err) diff --git a/internal/action/commands.go b/internal/action/commands.go index 1b8d108d66..85a2d441f7 100644 --- a/internal/action/commands.go +++ b/internal/action/commands.go @@ -82,11 +82,7 @@ func (s *Action) GetCommands() []*cli.Command { }, &cli.StringFlag{ Name: "crypto", - Usage: "Select crypto backend (gpg, gpgcli, plain, xc)", - }, - &cli.StringFlag{ - Name: "sync", - Usage: "Select sync backend (git, gitcli, noop)", + Usage: "Select crypto backend (gpgcli, age, plain, xc)", }, }, }, @@ -268,7 +264,6 @@ func (s *Action) GetCommands() []*cli.Command { "must be a file and the other one a secret. If you want the source to " + "be securely removed after copying, use 'gopass binary move'", Before: s.Initialized, - Aliases: []string{"cp"}, Action: s.BinaryCopy, BashComplete: s.Complete, Hidden: true, @@ -292,7 +287,6 @@ func (s *Action) GetCommands() []*cli.Command { "and validated. If you don't want the source to be removed use " + "'gopass binary copy'", Before: s.Initialized, - Aliases: []string{"mv"}, Action: s.BinaryMove, BashComplete: s.Complete, Hidden: true, diff --git a/internal/action/config.go b/internal/action/config.go index 86bf1c97c5..ca33e513f1 100644 --- a/internal/action/config.go +++ b/internal/action/config.go @@ -5,7 +5,7 @@ import ( "fmt" "sort" - "github.com/gopasspw/gopass/internal/backend/storage/kv/ondisk" + "github.com/gopasspw/gopass/internal/backend/storage/ondisk" "github.com/gopasspw/gopass/internal/debug" "github.com/gopasspw/gopass/internal/out" "github.com/gopasspw/gopass/pkg/ctxutil" diff --git a/internal/action/convert.go b/internal/action/convert.go index 5cf99c64d0..6f24c81aaa 100644 --- a/internal/action/convert.go +++ b/internal/action/convert.go @@ -14,7 +14,6 @@ func (s *Action) Convert(c *cli.Context) error { move := c.Bool("move") storage := backend.StorageBackendFromName(c.String("storage")) crypto := backend.CryptoBackendFromName(c.String("crypto")) - rcs := backend.RcsBackendFromName(c.String("rcs")) - return s.Store.Convert(ctx, store, crypto, rcs, storage, move) + return s.Store.Convert(ctx, store, crypto, storage, move) } diff --git a/internal/action/history_test.go b/internal/action/history_test.go index b9e1b9f658..90718a31cf 100644 --- a/internal/action/history_test.go +++ b/internal/action/history_test.go @@ -29,9 +29,8 @@ func TestHistory(t *testing.T) { ctx := context.Background() ctx = ctxutil.WithAlwaysYes(ctx, true) - ctx = backend.WithRCSBackend(ctx, backend.GitCLI) ctx = backend.WithCryptoBackend(ctx, backend.Plain) - ctx = backend.WithStorageBackend(ctx, backend.FS) + ctx = backend.WithStorageBackend(ctx, backend.GitFS) cfg := config.New() cfg.Path = u.StoreDir("") diff --git a/internal/action/init.go b/internal/action/init.go index 1949426735..be80f2f00b 100644 --- a/internal/action/init.go +++ b/internal/action/init.go @@ -81,9 +81,6 @@ func initParseContext(ctx context.Context, c *cli.Context) context.Context { if c.IsSet("crypto") { ctx = backend.WithCryptoBackendString(ctx, c.String("crypto")) } - if c.IsSet("rcs") { - ctx = backend.WithRCSBackendString(ctx, c.String("rcs")) - } if c.IsSet("storage") { ctx = backend.WithStorageBackendString(ctx, c.String("storage")) } @@ -92,13 +89,9 @@ func initParseContext(ctx context.Context, c *cli.Context) context.Context { debug.Log("Using default Crypto Backend (GPGCLI)") ctx = backend.WithCryptoBackend(ctx, backend.GPGCLI) } - if !backend.HasRCSBackend(ctx) { - debug.Log("Using default RCS backend (GitCLI)") - ctx = backend.WithRCSBackend(ctx, backend.GitCLI) - } if !backend.HasStorageBackend(ctx) { - debug.Log("Using default storage backend (FS)") - ctx = backend.WithStorageBackend(ctx, backend.FS) + debug.Log("Using default storage backend (GitFS)") + ctx = backend.WithStorageBackend(ctx, backend.GitFS) } ctx = out.WithPrefix(ctx, "[init] ") @@ -144,8 +137,8 @@ func (s *Action) init(ctx context.Context, alias, path string, keys ...string) e } } - if backend.HasRCSBackend(ctx) { - bn := backend.RCSBackendName(backend.GetRCSBackend(ctx)) + if backend.HasStorageBackend(ctx) { + bn := backend.StorageBackendName(backend.GetStorageBackend(ctx)) debug.Log("Initializing RCS (%s) ...", bn) if err := s.rcsInit(ctx, alias, ctxutil.GetUsername(ctx), ctxutil.GetEmail(ctx)); err != nil { debug.Log("Stacktrace: %+v\n", err) diff --git a/internal/action/init_test.go b/internal/action/init_test.go index 037dfa80a4..c732f29afc 100644 --- a/internal/action/init_test.go +++ b/internal/action/init_test.go @@ -25,7 +25,6 @@ func TestInit(t *testing.T) { ctx := context.Background() ctx = ctxutil.WithAlwaysYes(ctx, true) ctx = ctxutil.WithInteractive(ctx, false) - ctx = backend.WithRCSBackend(ctx, backend.Noop) ctx = backend.WithCryptoBackend(ctx, backend.Plain) ctx = backend.WithStorageBackend(ctx, backend.FS) @@ -87,20 +86,10 @@ func TestInitParseContext(t *testing.T) { return nil }, }, - { - name: "rcs noop", - flags: map[string]string{"rcs": "noop"}, - check: func(ctx context.Context) error { - if be := backend.GetRCSBackend(ctx); be != backend.Noop { - return fmt.Errorf("wrong backend: %d", be) - } - return nil - }, - }, { name: "default", check: func(ctx context.Context) error { - if backend.GetRCSBackend(ctx) != backend.GitCLI { + if backend.GetStorageBackend(ctx) != backend.GitFS { return fmt.Errorf("wrong backend") } return nil diff --git a/internal/action/rcs.go b/internal/action/rcs.go index 45301eb0f5..642e0d9d9f 100644 --- a/internal/action/rcs.go +++ b/internal/action/rcs.go @@ -22,11 +22,11 @@ func (s *Action) RCSInit(c *cli.Context) error { store := c.String("store") un := termio.DetectName(c.Context, c) ue := termio.DetectEmail(c.Context, c) - ctx = backend.WithRCSBackendString(ctx, c.String("rcs")) + ctx = backend.WithStorageBackendString(ctx, c.String("storage")) // default to git - if !backend.HasRCSBackend(ctx) { - ctx = backend.WithRCSBackend(ctx, backend.GitCLI) + if !backend.HasStorageBackend(ctx) { + ctx = backend.WithStorageBackend(ctx, backend.GitFS) } if err := s.rcsInit(ctx, store, un, ue); err != nil { @@ -36,7 +36,11 @@ func (s *Action) RCSInit(c *cli.Context) error { } func (s *Action) rcsInit(ctx context.Context, store, un, ue string) error { - bn := backend.RCSBackendName(backend.GetRCSBackend(ctx)) + // TODO this is a hack + if backend.GetStorageBackend(ctx) == backend.FS { + return nil + } + bn := backend.StorageBackendName(backend.GetStorageBackend(ctx)) out.Green(ctx, "Initializing git repository (%s) for %s / %s...", bn, un, ue) userName, userEmail := s.getUserData(ctx, store, un, ue) diff --git a/internal/action/recipients.go b/internal/action/recipients.go index 520227f8bf..30dc6f0e69 100644 --- a/internal/action/recipients.go +++ b/internal/action/recipients.go @@ -100,7 +100,7 @@ func (s *Action) RecipientsAdd(c *cli.Context) error { } keys = []string{r} } - if len(keys) < 1 && !force { + if len(keys) < 1 && !force && crypto.Name() == "gpgcli" { 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) diff --git a/internal/action/recipients_test.go b/internal/action/recipients_test.go index 31ae0ae47a..0446966143 100644 --- a/internal/action/recipients_test.go +++ b/internal/action/recipients_test.go @@ -72,7 +72,7 @@ gopass t.Run("add recipient 0xBEEFFEED", func(t *testing.T) { defer buf.Reset() - assert.Error(t, act.RecipientsAdd(gptest.CliCtx(ctx, t, "0xBEEFFEED"))) + assert.NoError(t, act.RecipientsAdd(gptest.CliCtx(ctx, t, "0xBEEFFEED"))) }) t.Run("remove recipient 0xDEADBEEF", func(t *testing.T) { diff --git a/internal/action/sync.go b/internal/action/sync.go index 8e055ffb16..c1ed71f62a 100644 --- a/internal/action/sync.go +++ b/internal/action/sync.go @@ -4,7 +4,6 @@ import ( "context" "fmt" - "github.com/gopasspw/gopass/internal/backend/rcs/noop" "github.com/gopasspw/gopass/internal/debug" "github.com/gopasspw/gopass/internal/notify" "github.com/gopasspw/gopass/internal/out" @@ -89,11 +88,11 @@ func (s *Action) syncMount(ctx context.Context, mp string) error { numMP = len(l) } - if sub.RCS().Name() == noop.New().Name() { - out.Error(ctxno, "\n WARNING: Mount uses RCS backend 'noop'. Not syncing!\n") + if sub.Storage().Name() == "fs" { + out.Yellow(ctxno, "\n WARNING: Mount uses Storage backend 'fs'. Not syncing!\n") } else { out.Print(ctxno, "\n "+color.GreenString("git pull and push ... ")) - if err := sub.RCS().Push(ctx, "", ""); err != nil { + if err := sub.Storage().Push(ctx, "", ""); err != nil { if errors.Cause(err) == store.ErrGitNoRemote { out.Yellow(ctx, "Skipped (no remote)") debug.Log("Failed to push '%s' to its remote: %s", name, err) @@ -144,7 +143,7 @@ func (s *Action) syncMount(ctx context.Context, mp string) error { // only run second push if we did export any keys if exported { - if err := sub.RCS().Push(ctx, "", ""); err != nil { + if err := sub.Storage().Push(ctx, "", ""); err != nil { out.Error(ctx, "Failed to push '%s' to its remote: %s", name, err) return err } diff --git a/internal/action/templates_test.go b/internal/action/templates_test.go index 8157b727f1..4a8461a0df 100644 --- a/internal/action/templates_test.go +++ b/internal/action/templates_test.go @@ -11,7 +11,6 @@ import ( "github.com/gopasspw/gopass/pkg/ctxutil" _ "github.com/gopasspw/gopass/internal/backend/crypto" - _ "github.com/gopasspw/gopass/internal/backend/rcs" _ "github.com/gopasspw/gopass/internal/backend/storage" "github.com/fatih/color" diff --git a/internal/action/unclip_test.go b/internal/action/unclip_test.go index 9d15b6bf55..3fb20b80fe 100644 --- a/internal/action/unclip_test.go +++ b/internal/action/unclip_test.go @@ -12,7 +12,6 @@ import ( "github.com/stretchr/testify/require" _ "github.com/gopasspw/gopass/internal/backend/crypto" - _ "github.com/gopasspw/gopass/internal/backend/rcs" _ "github.com/gopasspw/gopass/internal/backend/storage" ) diff --git a/internal/action/update_test.go b/internal/action/update_test.go index a39e74e75a..a834b072e6 100644 --- a/internal/action/update_test.go +++ b/internal/action/update_test.go @@ -18,7 +18,6 @@ import ( "github.com/gopasspw/gopass/pkg/ctxutil" _ "github.com/gopasspw/gopass/internal/backend/crypto" - _ "github.com/gopasspw/gopass/internal/backend/rcs" _ "github.com/gopasspw/gopass/internal/backend/storage" "github.com/dominikschulz/github-releases/ghrel" diff --git a/internal/action/version.go b/internal/action/version.go index da46952b7f..0da786f97d 100644 --- a/internal/action/version.go +++ b/internal/action/version.go @@ -29,25 +29,22 @@ func (s *Action) Version(c *cli.Context) error { cli.VersionPrinter(c) cryptoVer := versionInfo(ctx, s.Store.Crypto(ctx, "")) - rcsVer := versionInfo(ctx, s.Store.RCS(ctx, "")) storageVer := versionInfo(ctx, s.Store.Storage(ctx, "")) - tpl := "%-10s - %10s - %10s - %10s\n" - fmt.Fprintf(stdout, tpl, "", cryptoVer, rcsVer, storageVer) + tpl := "%-10s - %10s - %10s\n" + fmt.Fprintf(stdout, tpl, "", cryptoVer, storageVer) // report all used crypto, sync and fs backends for _, mp := range s.Store.MountPoints() { cv := versionInfo(ctx, s.Store.Crypto(ctx, mp)) - rv := versionInfo(ctx, s.Store.RCS(ctx, mp)) sv := versionInfo(ctx, s.Store.Storage(ctx, mp)) - if cv != cryptoVer || rv != rcsVer || sv != storageVer { - fmt.Fprintf(stdout, tpl, mp, cv, rv, sv) + if cv != cryptoVer || sv != storageVer { + fmt.Fprintf(stdout, tpl, mp, cv, sv) } } fmt.Fprintf(stdout, "Available Crypto Backends: %s\n", strings.Join(backend.CryptoBackends(), ", ")) - fmt.Fprintf(stdout, "Available RCS Backends: %s\n", strings.Join(backend.RCSBackends(), ", ")) fmt.Fprintf(stdout, "Available Storage Backends: %s\n", strings.Join(backend.StorageBackends(), ", ")) select { diff --git a/internal/action/version_test.go b/internal/action/version_test.go index b55a4d4b4a..6264cad351 100644 --- a/internal/action/version_test.go +++ b/internal/action/version_test.go @@ -11,7 +11,6 @@ import ( "github.com/gopasspw/gopass/pkg/ctxutil" _ "github.com/gopasspw/gopass/internal/backend/crypto" - _ "github.com/gopasspw/gopass/internal/backend/rcs" _ "github.com/gopasspw/gopass/internal/backend/storage" "github.com/stretchr/testify/assert" diff --git a/internal/backend/context.go b/internal/backend/context.go index 47e8f72239..90e7955d4d 100644 --- a/internal/backend/context.go +++ b/internal/backend/context.go @@ -43,39 +43,6 @@ func GetCryptoBackend(ctx context.Context) CryptoBackend { return be } -// RCSBackendName returns the name of the given backend -func RCSBackendName(sb RCSBackend) string { - return RcsNameFromBackend(sb) -} - -// WithRCSBackendString returns a context with the given sync backend set -func WithRCSBackendString(ctx context.Context, sb string) context.Context { - if be := RcsBackendFromName(sb); be >= 0 { - return WithRCSBackend(ctx, be) - } - return WithRCSBackend(ctx, Noop) -} - -// WithRCSBackend returns a context with the given sync backend set -func WithRCSBackend(ctx context.Context, sb RCSBackend) context.Context { - return context.WithValue(ctx, ctxKeyRCSBackend, sb) -} - -// HasRCSBackend returns true if a value for sync backend has been set in the context -func HasRCSBackend(ctx context.Context) bool { - _, ok := ctx.Value(ctxKeyRCSBackend).(RCSBackend) - return ok -} - -// GetRCSBackend returns the sync backend or the default (Git Mock) -func GetRCSBackend(ctx context.Context) RCSBackend { - be, ok := ctx.Value(ctxKeyRCSBackend).(RCSBackend) - if !ok { - return Noop - } - return be -} - // WithStorageBackendString returns a context with the given store backend set func WithStorageBackendString(ctx context.Context, sb string) context.Context { return WithStorageBackend(ctx, StorageBackendFromName(sb)) diff --git a/internal/backend/context_test.go b/internal/backend/context_test.go index 6b86d8ee8a..82d0f80bac 100644 --- a/internal/backend/context_test.go +++ b/internal/backend/context_test.go @@ -16,17 +16,6 @@ func TestCryptoBackend(t *testing.T) { assert.Equal(t, true, HasCryptoBackend(WithCryptoBackend(ctx, GPGCLI))) } -func TestRCSBackend(t *testing.T) { - ctx := context.Background() - - assert.Equal(t, "gitcli", RCSBackendName(GitCLI)) - assert.Equal(t, Noop, GetRCSBackend(ctx)) - assert.Equal(t, GitCLI, GetRCSBackend(WithRCSBackendString(ctx, "gitcli"))) - assert.Equal(t, GitCLI, GetRCSBackend(WithRCSBackend(ctx, GitCLI))) - assert.Equal(t, Noop, GetRCSBackend(WithRCSBackendString(ctx, "foobar"))) - assert.Equal(t, true, HasRCSBackend(WithRCSBackend(ctx, GitCLI))) -} - func TestStorageBackend(t *testing.T) { ctx := context.Background() diff --git a/internal/backend/crypto/age/age.go b/internal/backend/crypto/age/age.go index c19a868f2c..714d36b131 100644 --- a/internal/backend/crypto/age/age.go +++ b/internal/backend/crypto/age/age.go @@ -1,11 +1,8 @@ package age import ( - "bytes" "context" "fmt" - "io" - "io/ioutil" "path/filepath" "sort" "strings" @@ -17,7 +14,6 @@ import ( "github.com/gopasspw/gopass/internal/cache" "github.com/gopasspw/gopass/internal/debug" "github.com/gopasspw/gopass/pkg/appdir" - "github.com/gopasspw/gopass/pkg/ctxutil" ) const ( @@ -51,7 +47,7 @@ func New() (*Age, error) { ghc: github.NewClient(nil), ghCache: cDir, keyring: filepath.Join(appdir.UserConfig(), "age-keyring.age"), - askPass: defaultAskPass, + askPass: DefaultAskPass, }, nil } @@ -125,98 +121,7 @@ func (a *Age) parseRecipients(ctx context.Context, recipients []string) ([]age.R return out, nil } -// Encrypt will encrypt the given payload -func (a *Age) Encrypt(ctx context.Context, plaintext []byte, recipients []string) ([]byte, error) { - // add our own public key - pks, err := a.pkself(ctx) - if err != nil { - return nil, err - } - recp, err := a.parseRecipients(ctx, recipients) - if err != nil { - return nil, err - } - recp = append(recp, pks) - return a.encrypt(plaintext, recp...) -} - -func (a *Age) encrypt(plaintext []byte, recp ...age.Recipient) ([]byte, error) { - out := &bytes.Buffer{} - w, err := age.Encrypt(out, recp...) - if err != nil { - return nil, err - } - n, err := w.Write(plaintext) - if err != nil { - return nil, err - } - if err := w.Close(); err != nil { - return nil, err - } - debug.Log("Wrote %d bytes of plaintext for %+v", n, recp) - return out.Bytes(), nil -} - -func (a *Age) encryptFile(filename string, plaintext []byte) error { - pw, err := a.askPass.Passphrase(filename, "index") - if err != nil { - return err - } - id, err := age.NewScryptRecipient(pw) - if err != nil { - return err - } - buf, err := a.encrypt(plaintext, id) - if err != nil { - return err - } - - return ioutil.WriteFile(filename, buf, 0600) -} - -// Decrypt will attempt to decrypt the given payload -func (a *Age) Decrypt(ctx context.Context, ciphertext []byte) ([]byte, error) { - ctx = ctxutil.WithPasswordCallback(ctx, func(prompt string) ([]byte, error) { - pw, err := a.askPass.Passphrase(prompt, "Decrypting") - return []byte(pw), err - }) - ids, err := a.getAllIds(ctx) - if err != nil { - return nil, err - } - return a.decrypt(ciphertext, ids...) -} - -func (a *Age) decrypt(ciphertext []byte, ids ...age.Identity) ([]byte, error) { - out := &bytes.Buffer{} - f := bytes.NewReader(ciphertext) - r, err := age.Decrypt(f, ids...) - if err != nil { - return nil, err - } - if _, err := io.Copy(out, r); err != nil { - return nil, err - } - return out.Bytes(), nil -} - -func (a *Age) decryptFile(filename string) ([]byte, error) { - pw, err := a.askPass.Passphrase(filename, "index") - if err != nil { - return nil, err - } - id, err := age.NewScryptIdentity(pw) - if err != nil { - return nil, err - } - ciphertext, err := ioutil.ReadFile(filename) - if err != nil { - return nil, err - } - return a.decrypt(ciphertext, id) -} - -// ListIdentities is TODO +// ListIdentities lists all identities func (a *Age) ListIdentities(ctx context.Context) ([]string, error) { ids, err := a.getAllIdentities(ctx) if err != nil { @@ -263,7 +168,7 @@ func (a *Age) getNativeIdentities(ctx context.Context) (map[string]age.Identity, if krCache != nil { return krCache, nil } - kr, err := a.loadKeyring() + kr, err := a.loadKeyring(ctx) if len(kr) < 1 || err != nil { id, err := a.genKey(ctx) if err != nil { diff --git a/internal/backend/crypto/age/askpass.go b/internal/backend/crypto/age/askpass.go index 9ebbfed4ab..c6a3c579bd 100644 --- a/internal/backend/crypto/age/askpass.go +++ b/internal/backend/crypto/age/askpass.go @@ -30,7 +30,8 @@ type askPass struct { } var ( - defaultAskPass = newAskPass() + // DefaultAskPass is the default password cache + DefaultAskPass = newAskPass() ) func newAskPass() *askPass { diff --git a/internal/backend/crypto/age/decrypt.go b/internal/backend/crypto/age/decrypt.go new file mode 100644 index 0000000000..dda090ad32 --- /dev/null +++ b/internal/backend/crypto/age/decrypt.go @@ -0,0 +1,57 @@ +package age + +import ( + "bytes" + "context" + "io" + "io/ioutil" + + "filippo.io/age" + "github.com/gopasspw/gopass/internal/debug" + "github.com/gopasspw/gopass/pkg/ctxutil" +) + +// Decrypt will attempt to decrypt the given payload +func (a *Age) Decrypt(ctx context.Context, ciphertext []byte) ([]byte, error) { + if !ctxutil.HasPasswordCallback(ctx) { + debug.Log("no password callback found, redirecting to askPass") + ctx = ctxutil.WithPasswordCallback(ctx, func(prompt string) ([]byte, error) { + pw, err := a.askPass.Passphrase(prompt, "Decrypting") + return []byte(pw), err + }) + } + ids, err := a.getAllIds(ctx) + if err != nil { + return nil, err + } + return a.decrypt(ciphertext, ids...) +} + +func (a *Age) decrypt(ciphertext []byte, ids ...age.Identity) ([]byte, error) { + out := &bytes.Buffer{} + f := bytes.NewReader(ciphertext) + r, err := age.Decrypt(f, ids...) + if err != nil { + return nil, err + } + if _, err := io.Copy(out, r); err != nil { + return nil, err + } + return out.Bytes(), nil +} + +func (a *Age) decryptFile(ctx context.Context, filename string) ([]byte, error) { + pw, err := ctxutil.GetPasswordCallback(ctx)("index") // TODO should pass filename? + if err != nil { + return nil, err + } + id, err := age.NewScryptIdentity(string(pw)) + if err != nil { + return nil, err + } + ciphertext, err := ioutil.ReadFile(filename) + if err != nil { + return nil, err + } + return a.decrypt(ciphertext, id) +} diff --git a/internal/backend/crypto/age/encrypt.go b/internal/backend/crypto/age/encrypt.go new file mode 100644 index 0000000000..9b27836c21 --- /dev/null +++ b/internal/backend/crypto/age/encrypt.go @@ -0,0 +1,81 @@ +package age + +import ( + "bytes" + "context" + "fmt" + "io/ioutil" + + "filippo.io/age" + "github.com/gopasspw/gopass/internal/debug" + "github.com/gopasspw/gopass/pkg/ctxutil" +) + +// Encrypt will encrypt the given payload +func (a *Age) Encrypt(ctx context.Context, plaintext []byte, recipients []string) ([]byte, error) { + // add our own public key + pks, err := a.pkself(ctx) + if err != nil { + return nil, err + } + recp, err := a.parseRecipients(ctx, recipients) + if err != nil { + return nil, err + } + recp = append(recp, pks) + recp = dedupe(recp) + return a.encrypt(plaintext, recp...) +} + +// dedupe the recipients, only works for native age recipients +func dedupe(recp []age.Recipient) []age.Recipient { + out := make([]age.Recipient, 0, len(recp)) + set := make(map[string]age.Recipient, len(recp)) + for _, r := range recp { + k, ok := r.(fmt.Stringer) + if !ok { + out = append(out, r) + continue + } + set[k.String()] = r + } + for _, r := range set { + out = append(out, r) + } + debug.Log("in: %+v - out: %+v", recp, out) + return out +} + +func (a *Age) encrypt(plaintext []byte, recp ...age.Recipient) ([]byte, error) { + out := &bytes.Buffer{} + w, err := age.Encrypt(out, recp...) + if err != nil { + return nil, err + } + n, err := w.Write(plaintext) + if err != nil { + return nil, err + } + if err := w.Close(); err != nil { + return nil, err + } + debug.Log("Wrote %d bytes of plaintext for %+v", n, recp) + return out.Bytes(), nil +} + +func (a *Age) encryptFile(ctx context.Context, filename string, plaintext []byte) error { + pw, err := ctxutil.GetPasswordCallback(ctx)("index") // TODO should pass filename? + if err != nil { + return err + } + id, err := age.NewScryptRecipient(string(pw)) + if err != nil { + return err + } + buf, err := a.encrypt(plaintext, id) + if err != nil { + return err + } + + return ioutil.WriteFile(filename, buf, 0600) +} diff --git a/internal/backend/crypto/age/encrypt_test.go b/internal/backend/crypto/age/encrypt_test.go new file mode 100644 index 0000000000..63b6c14d58 --- /dev/null +++ b/internal/backend/crypto/age/encrypt_test.go @@ -0,0 +1,51 @@ +package age + +import ( + "crypto/ed25519" + "fmt" + "sort" + "testing" + + "filippo.io/age" + "filippo.io/age/agessh" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "golang.org/x/crypto/ssh" +) + +func TestDedupe(t *testing.T) { + i1, err := age.GenerateX25519Identity() + require.NoError(t, err) + + i2, err := age.GenerateX25519Identity() + require.NoError(t, err) + + i3pub, _, err := ed25519.GenerateKey(nil) + require.NoError(t, err) + i3ssh, err := ssh.NewPublicKey(i3pub) + require.NoError(t, err) + i3, err := agessh.NewEd25519Recipient(i3ssh) + require.NoError(t, err) + + in := []age.Recipient{i1.Recipient(), i2.Recipient(), i2.Recipient(), i3, i3} + out := dedupe(in) + want := []age.Recipient{i3, i3, i1.Recipient(), i2.Recipient()} + + sort.Sort(Recipients(out)) + sort.Sort(Recipients(want)) + assert.Equal(t, want, out) +} + +type Recipients []age.Recipient + +func (r Recipients) Len() int { + return len(r) +} + +func (r Recipients) Swap(i, j int) { + r[i], r[j] = r[j], r[i] +} + +func (r Recipients) Less(i, j int) bool { + return fmt.Sprintf("%s", r[i]) < fmt.Sprintf("%s", r[j]) +} diff --git a/internal/backend/crypto/age/github.go b/internal/backend/crypto/age/github.go index 288104a50a..dba2c70894 100644 --- a/internal/backend/crypto/age/github.go +++ b/internal/backend/crypto/age/github.go @@ -7,13 +7,15 @@ import ( "net/http" "strings" "time" + + "github.com/gopasspw/gopass/internal/debug" ) func (a *Age) getPublicKeysGithub(ctx context.Context, user string) ([]string, error) { // TODO: recheck SoT if cache is too old pk, err := a.ghCache.Get(user) if err != nil { - return nil, err + debug.Log("failed to fetch %s from cache: %s", user, err) } if len(pk) > 0 { return pk, nil @@ -34,7 +36,9 @@ func githubListKeys(ctx context.Context, user string) ([]string, error) { ctx, cancel := context.WithTimeout(ctx, 30*time.Second) defer cancel() - req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("https://github.com/%s.keys", user), nil) + url := fmt.Sprintf("https://github.com/%s.keys", user) + debug.Log("fetching public keys for %s from github: %s", user, url) + req, err := http.NewRequestWithContext(ctx, "GET", url, nil) if err != nil { return nil, err } diff --git a/internal/backend/crypto/age/keyring.go b/internal/backend/crypto/age/keyring.go index 86c4ad6571..ea5683eb5f 100644 --- a/internal/backend/crypto/age/keyring.go +++ b/internal/backend/crypto/age/keyring.go @@ -9,6 +9,7 @@ import ( "filippo.io/age" "github.com/gopasspw/gopass/internal/debug" "github.com/gopasspw/gopass/internal/termio" + "github.com/gopasspw/gopass/pkg/ctxutil" ) // Keyring is an age keyring @@ -22,7 +23,7 @@ type Keypair struct { } func (a *Age) pkself(ctx context.Context) (age.Recipient, error) { - kr, err := a.loadKeyring() + kr, err := a.loadKeyring(ctx) var id *age.X25519Identity if err != nil || len(kr) < 1 { @@ -38,7 +39,7 @@ func (a *Age) pkself(ctx context.Context) (age.Recipient, error) { func (a *Age) genKey(ctx context.Context) (*age.X25519Identity, error) { debug.Log("No native age key found. Generating ...") - id, err := a.generateIdentity(termio.DetectName(ctx, nil), termio.DetectEmail(ctx, nil)) + id, err := a.generateIdentity(ctx, termio.DetectName(ctx, nil), termio.DetectEmail(ctx, nil)) if err != nil { return nil, err } @@ -47,17 +48,17 @@ func (a *Age) genKey(ctx context.Context) (*age.X25519Identity, error) { // GenerateIdentity will create a new native private key func (a *Age) GenerateIdentity(ctx context.Context, name, email, _ string) error { - _, err := a.generateIdentity(name, email) + _, err := a.generateIdentity(ctx, name, email) return err } -func (a *Age) generateIdentity(name, email string) (*age.X25519Identity, error) { +func (a *Age) generateIdentity(ctx context.Context, name, email string) (*age.X25519Identity, error) { id, err := age.GenerateX25519Identity() if err != nil { return id, err } - kr, err := a.loadKeyring() + kr, err := a.loadKeyring(ctx) if err != nil { debug.Log("Warning: Failed to load keyring from %s: %s", a.keyring, err) } @@ -68,12 +69,19 @@ func (a *Age) generateIdentity(name, email string) (*age.X25519Identity, error) Identity: id.String(), }) - return id, a.saveKeyring(kr) + return id, a.saveKeyring(ctx, kr) } -func (a *Age) loadKeyring() (Keyring, error) { +func (a *Age) loadKeyring(ctx context.Context) (Keyring, error) { + if !ctxutil.HasPasswordCallback(ctx) { + debug.Log("no password callback found, redirecting to askPass") + ctx = ctxutil.WithPasswordCallback(ctx, func(prompt string) ([]byte, error) { + pw, err := a.askPass.Passphrase(prompt, "to unlock the age keyring") + return []byte(pw), err + }) + } kr := make(Keyring, 1) - buf, err := a.decryptFile(a.keyring) + buf, err := a.decryptFile(ctx, a.keyring) if err != nil { debug.Log("can't decrypt keyring at %s: %s", a.keyring, err) return kr, err @@ -94,7 +102,15 @@ func (a *Age) loadKeyring() (Keyring, error) { return valid, nil } -func (a *Age) saveKeyring(k Keyring) error { +func (a *Age) saveKeyring(ctx context.Context, k Keyring) error { + if !ctxutil.HasPasswordCallback(ctx) { + debug.Log("no password callback found, redirecting to askPass") + ctx = ctxutil.WithPasswordCallback(ctx, func(prompt string) ([]byte, error) { + pw, err := a.askPass.Passphrase(prompt, "to unlock the age keyring") + return []byte(pw), err + }) + } + if err := os.MkdirAll(filepath.Dir(a.keyring), 0700); err != nil { return err } @@ -104,7 +120,7 @@ func (a *Age) saveKeyring(k Keyring) error { if err != nil { return err } - if err := a.encryptFile(a.keyring, buf); err != nil { + if err := a.encryptFile(ctx, a.keyring, buf); err != nil { return err } debug.Log("saved encrypted keyring to %s", a.keyring) diff --git a/internal/backend/crypto/age/unsupported.go b/internal/backend/crypto/age/unsupported.go index 9edcab5766..78f10c6f6f 100644 --- a/internal/backend/crypto/age/unsupported.go +++ b/internal/backend/crypto/age/unsupported.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "sort" + "strings" "github.com/gopasspw/gopass/internal/debug" ) @@ -13,8 +14,8 @@ func (a *Age) ExportPublicKey(ctx context.Context, id string) ([]byte, error) { return []byte(id), nil } -// FindRecipients it TODO -func (a *Age) FindRecipients(ctx context.Context, keys ...string) ([]string, error) { +// FindIdentities it TODO +func (a *Age) FindIdentities(ctx context.Context, keys ...string) ([]string, error) { nk, err := a.getAllIdentities(ctx) if err != nil { return nil, err @@ -33,9 +34,28 @@ func (a *Age) FindRecipients(ctx context.Context, keys ...string) ([]string, err return matches, nil } -// FindIdentities is TODO -func (a *Age) FindIdentities(ctx context.Context, keys ...string) ([]string, error) { - return a.FindRecipients(ctx, keys...) +// FindRecipients is TODO +func (a *Age) FindRecipients(ctx context.Context, keys ...string) ([]string, error) { + // TODO should not need to decrypt keyring + remote := make([]string, 0, len(keys)) + local := make([]string, 0, len(keys)) + for _, key := range keys { + if !strings.HasPrefix(key, "github:") { + local = append(local, key) + continue + } + pks, err := a.getPublicKeysGithub(ctx, strings.TrimPrefix(key, "github:")) + if err != nil { + debug.Log("Failed to get key %s from github: %s", key, err) + continue + } + remote = append(remote, pks...) + } + ids, err := a.FindIdentities(ctx, local...) + if err != nil { + return nil, err + } + return append(ids, remote...), nil } // FormatKey is TODO diff --git a/internal/backend/rcs.go b/internal/backend/rcs.go index a6bc4851bb..a92dd69500 100644 --- a/internal/backend/rcs.go +++ b/internal/backend/rcs.go @@ -2,40 +2,19 @@ package backend import ( "context" - "sort" "time" - "github.com/blang/semver" "github.com/gopasspw/gopass/internal/debug" "github.com/pkg/errors" ) -// RCSBackend is a remote-sync backend -type RCSBackend int - -const ( - // Noop is a no-op mock backend - Noop RCSBackend = iota - // GitCLI is a git-cli based sync backend - GitCLI - // OnDiskRCS is the OnDisk storage backend in disguise as an RCS backend - OnDiskRCS -) - -func (s RCSBackend) String() string { - return RcsNameFromBackend(s) -} - -// RCS is a revision control backend -type RCS interface { +// rcs is a revision control backend +type rcs interface { Add(ctx context.Context, args ...string) error Commit(ctx context.Context, msg string) error Push(ctx context.Context, remote, location string) error Pull(ctx context.Context, remote, location string) error - Name() string - Version(ctx context.Context) semver.Version - InitConfig(ctx context.Context, name, email string) error AddRemote(ctx context.Context, remote, location string) error RemoveRemote(ctx context.Context, remote string) error @@ -72,62 +51,11 @@ func (r Revisions) Swap(i, j int) { r[i], r[j] = r[j], r[i] } -// RegisterRCS registers a new RCS backend with the backend registry. -func RegisterRCS(id RCSBackend, name string, loader RCSLoader) { - rcsRegistry[id] = loader - rcsNameToBackendMap[name] = id - rcsBackendToNameMap[id] = name -} - -// DetectRCS tried to detect the RCS backend being used -func DetectRCS(ctx context.Context, path string) (RCS, error) { - if HasRCSBackend(ctx) { - if be, found := rcsRegistry[GetRCSBackend(ctx)]; found { - rcs, err := be.Open(ctx, path) - if err == nil { - return rcs, nil - } - rcs, err = be.InitRCS(ctx, path) - if err == nil { - return rcs, nil - } - return rcsRegistry[Noop].InitRCS(ctx, path) - } - } - bes := make([]RCSBackend, 0, len(rcsRegistry)) - for id := range rcsRegistry { - bes = append(bes, id) - } - sort.Slice(bes, func(i, j int) bool { - return rcsRegistry[bes[i]].Priority() < rcsRegistry[bes[j]].Priority() - }) - for _, id := range bes { - be := rcsRegistry[id] - debug.Log("Trying %s for %s", be, path) - if err := be.Handles(path); err != nil { - debug.Log("failed to use RCS %s for %s", id, path) - continue - } - debug.Log("Using %s for %s", be, path) - return be.Open(ctx, path) - } - debug.Log("No supported RCS found for %s. using NOOP", path) - return rcsRegistry[Noop].InitRCS(ctx, path) -} - -// CloneRCS clones an existing repository from a remote. -func CloneRCS(ctx context.Context, id RCSBackend, repo, path string) (RCS, error) { - if be, found := rcsRegistry[id]; found { +// Clone clones an existing repository from a remote. +func Clone(ctx context.Context, id StorageBackend, repo, path string) (Storage, error) { + if be, found := storageRegistry[id]; found { debug.Log("Cloning with %s", be.String()) return be.Clone(ctx, repo, path) } return nil, errors.Wrapf(ErrNotFound, "unknown backend: %d", id) } - -// InitRCS initializes a new repository. -func InitRCS(ctx context.Context, id RCSBackend, path string) (RCS, error) { - if be, found := rcsRegistry[id]; found { - return be.InitRCS(ctx, path) - } - return nil, errors.Wrapf(ErrNotFound, "unknown backend: %d", id) -} diff --git a/internal/backend/rcs/git/cli/git_test.go b/internal/backend/rcs/git/cli/git_test.go deleted file mode 100644 index 0aa1478dcb..0000000000 --- a/internal/backend/rcs/git/cli/git_test.go +++ /dev/null @@ -1,83 +0,0 @@ -package cli - -import ( - "bytes" - "context" - "io/ioutil" - "os" - "path/filepath" - "testing" - - "github.com/gopasspw/gopass/internal/out" - "github.com/gopasspw/gopass/pkg/ctxutil" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestGit(t *testing.T) { - td, err := ioutil.TempDir("", "gopass-") - require.NoError(t, err) - defer func() { - _ = os.RemoveAll(td) - }() - - gitdir := filepath.Join(td, "git") - require.NoError(t, os.Mkdir(gitdir, 0755)) - - ctx := context.Background() - ctx = ctxutil.WithAlwaysYes(ctx, true) - - buf := &bytes.Buffer{} - out.Stdout = buf - defer func() { - out.Stdout = os.Stdout - }() - - git, err := Init(ctx, gitdir, "Dead Beef", "dead.beef@example.org") - require.NoError(t, err) - require.NotNil(t, git) - - sv := git.Version(ctx) - assert.NotEqual(t, "", sv.String()) - - assert.Equal(t, true, git.IsInitialized()) - tf := filepath.Join(gitdir, "some-file") - require.NoError(t, ioutil.WriteFile(tf, []byte("foobar"), 0644)) - assert.NoError(t, git.Add(ctx, "some-file")) - assert.Equal(t, true, git.HasStagedChanges(ctx)) - assert.NoError(t, git.Commit(ctx, "added some-file")) - assert.Equal(t, false, git.HasStagedChanges(ctx)) - - assert.Error(t, git.Push(ctx, "origin", "master")) - assert.Error(t, git.Pull(ctx, "origin", "master")) - - git, err = Open(gitdir) - require.NoError(t, err) - require.NotNil(t, git) - assert.Equal(t, "git", git.Name()) - assert.NoError(t, git.AddRemote(ctx, "foo", "file:///tmp/foo")) - assert.NoError(t, git.RemoveRemote(ctx, "foo")) - assert.Error(t, git.RemoveRemote(ctx, "foo")) - - gitdir2 := filepath.Join(td, "git2") - require.NoError(t, os.Mkdir(gitdir2, 0755)) - - git, err = Clone(ctx, gitdir, gitdir2) - require.NoError(t, err) - require.NotNil(t, git) - assert.Equal(t, "git", git.Name()) - - tf = filepath.Join(gitdir2, "some-other-file") - require.NoError(t, ioutil.WriteFile(tf, []byte("foobar"), 0644)) - assert.NoError(t, git.Add(ctx, "some-other-file")) - assert.NoError(t, git.Commit(ctx, "added some-other-file")) - - revs, err := git.Revisions(ctx, "some-other-file") - require.NoError(t, err) - assert.Equal(t, true, len(revs) == 1) - - content, err := git.GetRevision(ctx, "some-other-file", revs[0].Hash) - require.NoError(t, err) - assert.Equal(t, "foobar", string(content)) -} diff --git a/internal/backend/rcs/gitcli.go b/internal/backend/rcs/gitcli.go deleted file mode 100644 index 22dd3239ea..0000000000 --- a/internal/backend/rcs/gitcli.go +++ /dev/null @@ -1,3 +0,0 @@ -package rcs - -import _ "github.com/gopasspw/gopass/internal/backend/rcs/git/cli" // register git cli backend diff --git a/internal/backend/rcs/noop.go b/internal/backend/rcs/noop.go deleted file mode 100644 index fae215a204..0000000000 --- a/internal/backend/rcs/noop.go +++ /dev/null @@ -1,3 +0,0 @@ -package rcs - -import _ "github.com/gopasspw/gopass/internal/backend/rcs/noop" // register noop backend diff --git a/internal/backend/rcs/noop/backend.go b/internal/backend/rcs/noop/backend.go deleted file mode 100644 index f74ee2f743..0000000000 --- a/internal/backend/rcs/noop/backend.go +++ /dev/null @@ -1,100 +0,0 @@ -// Package noop implements a ineffective RCS backend for use with external -// synchronization solutions. -// TODO(2.x) DEPRECATED and slated for removal in the 2.0.0 release. -package noop - -import ( - "context" - "time" - - "github.com/gopasspw/gopass/internal/backend" - - "github.com/blang/semver" -) - -// Noop is a no-op git backend -type Noop struct{} - -// New creates a new Noop object -func New() *Noop { - return &Noop{} -} - -// Add does nothing -func (g *Noop) Add(ctx context.Context, args ...string) error { - return nil -} - -// Commit does nothing -func (g *Noop) Commit(ctx context.Context, msg string) error { - return nil -} - -// Push does nothing -func (g *Noop) Push(ctx context.Context, origin, branch string) error { - return nil -} - -// Pull does nothing -func (g *Noop) Pull(ctx context.Context, origin, branch string) error { - return nil -} - -// Cmd does nothing -func (g *Noop) Cmd(ctx context.Context, name string, args ...string) error { - return nil -} - -// Init does nothing -func (g *Noop) Init(context.Context, string, string) error { - return nil -} - -// InitConfig does nothing -func (g *Noop) InitConfig(context.Context, string, string) error { - return nil -} - -// Version returns an empty version -func (g *Noop) Version(context.Context) semver.Version { - return semver.Version{} -} - -// Name returns noop -func (g *Noop) Name() string { - return "noop" -} - -// AddRemote does nothing -func (g *Noop) AddRemote(ctx context.Context, remote, url string) error { - return nil -} - -// RemoveRemote does nothing -func (g *Noop) RemoveRemote(ctx context.Context, remote string) error { - return nil -} - -// Revisions is not implemented -func (g *Noop) Revisions(context.Context, string) ([]backend.Revision, error) { - return []backend.Revision{ - { - Hash: "latest", - Date: time.Now(), - }}, nil -} - -// GetRevision is not implemented -func (g *Noop) GetRevision(context.Context, string, string) ([]byte, error) { - return []byte("foo\nbar"), nil -} - -// Status is not implemented -func (g *Noop) Status(context.Context) ([]byte, error) { - return []byte(""), nil -} - -// Compact is not implemented -func (g *Noop) Compact(context.Context) error { - return nil -} diff --git a/internal/backend/rcs/noop/loader.go b/internal/backend/rcs/noop/loader.go deleted file mode 100644 index 10fef28c46..0000000000 --- a/internal/backend/rcs/noop/loader.go +++ /dev/null @@ -1,43 +0,0 @@ -package noop - -import ( - "context" - - "github.com/gopasspw/gopass/internal/backend" -) - -const ( - name = "noop" -) - -func init() { - backend.RegisterRCS(backend.Noop, name, &loader{}) -} - -type loader struct{} - -// Open implements backend.RCSLoader -func (l loader) Open(ctx context.Context, _ string) (backend.RCS, error) { - return New(), nil -} - -// Clone implements backend.RCSLoader -func (l loader) Clone(ctx context.Context, repo, path string) (backend.RCS, error) { - return New(), nil -} - -// Init implements backend.RCSLoader -func (l loader) InitRCS(ctx context.Context, path string) (backend.RCS, error) { - return New(), nil -} - -func (l loader) Handles(_ string) error { - return nil -} - -func (l loader) Priority() int { - return 1000 -} -func (l loader) String() string { - return name -} diff --git a/internal/backend/rcs_test.go b/internal/backend/rcs_test.go index 31d4c33061..eedc9b621d 100644 --- a/internal/backend/rcs_test.go +++ b/internal/backend/rcs_test.go @@ -14,33 +14,7 @@ import ( "github.com/stretchr/testify/require" ) -func TestDetectRCS(t *testing.T) { - ctx := context.Background() - - td, err := ioutil.TempDir("", "gopass-") - require.NoError(t, err) - defer func() { - _ = os.RemoveAll(td) - }() - - noopDir := filepath.Join(td, "noop") - assert.NoError(t, os.MkdirAll(noopDir, 0700)) - - gitDir := filepath.Join(td, "git") - assert.NoError(t, os.MkdirAll(filepath.Join(gitDir, ".git"), 0700)) - - r, err := DetectRCS(ctx, noopDir) - assert.NoError(t, err) - assert.NotNil(t, r) - assert.Equal(t, "noop", r.Name()) - - r, err = DetectRCS(ctx, gitDir) - assert.NoError(t, err) - assert.NotNil(t, r) - assert.Equal(t, "git", r.Name()) -} - -func TestCloneRCS(t *testing.T) { +func TestClone(t *testing.T) { ctx := context.Background() td, err := ioutil.TempDir("", "gopass-") @@ -58,7 +32,7 @@ func TestCloneRCS(t *testing.T) { cmd := exec.Command("git", "init", repo) assert.NoError(t, cmd.Run()) - r, err := CloneRCS(ctx, GitCLI, repo, store) + r, err := Clone(ctx, GitFS, repo, store) assert.NoError(t, err) assert.NotNil(t, r) } @@ -83,7 +57,7 @@ func TestInitRCS(t *testing.T) { gitDir := filepath.Join(td, "git") assert.NoError(t, os.MkdirAll(filepath.Join(gitDir, ".git"), 0700)) - r, err := InitRCS(ctx, GitCLI, gitDir) + r, err := InitStorage(ctx, GitFS, gitDir) assert.NoError(t, err) assert.NotNil(t, r) } diff --git a/internal/backend/registry.go b/internal/backend/registry.go index 99eb3ff773..60212bfb9c 100644 --- a/internal/backend/registry.go +++ b/internal/backend/registry.go @@ -7,7 +7,6 @@ import ( var ( cryptoRegistry = map[CryptoBackend]CryptoLoader{} - rcsRegistry = map[RCSBackend]RCSLoader{} storageRegistry = map[StorageBackend]StorageLoader{} // ErrNotFound is returned if the requested backend was not found. @@ -22,21 +21,12 @@ type CryptoLoader interface { Priority() int } -// RCSLoader is the interface for creating a new RCS backend. -type RCSLoader interface { - fmt.Stringer - Open(context.Context, string) (RCS, error) - Clone(context.Context, string, string) (RCS, error) - InitRCS(context.Context, string) (RCS, error) - Handles(string) error - Priority() int -} - // StorageLoader is the interface for creating a new storage backend. type StorageLoader interface { fmt.Stringer New(context.Context, string) (Storage, error) Init(context.Context, string) (Storage, error) + Clone(context.Context, string, string) (Storage, error) Handles(string) error Priority() int } diff --git a/internal/backend/registry_test.go b/internal/backend/registry_test.go index 365799d0c9..f4401e4fbc 100644 --- a/internal/backend/registry_test.go +++ b/internal/backend/registry_test.go @@ -7,7 +7,6 @@ import ( "github.com/gopasspw/gopass/internal/backend" _ "github.com/gopasspw/gopass/internal/backend/crypto" "github.com/gopasspw/gopass/internal/backend/crypto/plain" - _ "github.com/gopasspw/gopass/internal/backend/rcs" _ "github.com/gopasspw/gopass/internal/backend/storage" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" diff --git a/internal/backend/storage.go b/internal/backend/storage.go index b08d21ff44..a0d5efe4bb 100644 --- a/internal/backend/storage.go +++ b/internal/backend/storage.go @@ -14,10 +14,10 @@ import ( type StorageBackend int const ( - // FS is a filesystem-backend storage + // FS is a filesystem-backed storage FS StorageBackend = iota - // InMem is an in-memory mock store for tests - InMem + // GitFS is a filesystem-backed storage with Git + GitFS // OnDisk is an on-disk store OnDisk ) @@ -29,6 +29,7 @@ func (s StorageBackend) String() string { // Storage is an storage backend type Storage interface { fmt.Stringer + rcs Get(ctx context.Context, name string) ([]byte, error) Set(ctx context.Context, name string, value []byte) error Delete(ctx context.Context, name string) error diff --git a/internal/backend/storage/fs/loader.go b/internal/backend/storage/fs/loader.go index e4227ca57b..07cfd73c31 100644 --- a/internal/backend/storage/fs/loader.go +++ b/internal/backend/storage/fs/loader.go @@ -37,6 +37,10 @@ func (l loader) Init(ctx context.Context, path string) (backend.Storage, error) return l.New(ctx, path) } +func (l loader) Clone(ctx context.Context, repo, path string) (backend.Storage, error) { + return l.New(ctx, path) +} + func (l loader) Handles(path string) error { if fsutil.IsDir(path) { return nil diff --git a/internal/backend/storage/fs/rcs.go b/internal/backend/storage/fs/rcs.go new file mode 100644 index 0000000000..7d4d93839d --- /dev/null +++ b/internal/backend/storage/fs/rcs.go @@ -0,0 +1,77 @@ +package fs + +import ( + "context" + "time" + + "github.com/gopasspw/gopass/internal/backend" +) + +// Add does nothing +func (s *Store) Add(ctx context.Context, args ...string) error { + return nil +} + +// Commit does nothing +func (s *Store) Commit(ctx context.Context, msg string) error { + return nil +} + +// Push does nothing +func (s *Store) Push(ctx context.Context, origin, branch string) error { + return nil +} + +// Pull does nothing +func (s *Store) Pull(ctx context.Context, origin, branch string) error { + return nil +} + +// Cmd does nothing +func (s *Store) Cmd(ctx context.Context, name string, args ...string) error { + return nil +} + +// Init does nothing +func (s *Store) Init(context.Context, string, string) error { + return nil +} + +// InitConfig does nothing +func (s *Store) InitConfig(context.Context, string, string) error { + return nil +} + +// AddRemote does nothing +func (s *Store) AddRemote(ctx context.Context, remote, url string) error { + return nil +} + +// RemoveRemote does nothing +func (s *Store) RemoveRemote(ctx context.Context, remote string) error { + return nil +} + +// Revisions is not implemented +func (s *Store) Revisions(context.Context, string) ([]backend.Revision, error) { + return []backend.Revision{ + { + Hash: "latest", + Date: time.Now(), + }}, nil +} + +// GetRevision is not implemented +func (s *Store) GetRevision(context.Context, string, string) ([]byte, error) { + return []byte("foo\nbar"), nil +} + +// Status is not implemented +func (s *Store) Status(context.Context) ([]byte, error) { + return []byte(""), nil +} + +// Compact is not implemented +func (s *Store) Compact(context.Context) error { + return nil +} diff --git a/internal/backend/rcs/noop/backend_test.go b/internal/backend/storage/fs/rcs_test.go similarity index 80% rename from internal/backend/rcs/noop/backend_test.go rename to internal/backend/storage/fs/rcs_test.go index e79eb2fc68..12f8796511 100644 --- a/internal/backend/rcs/noop/backend_test.go +++ b/internal/backend/storage/fs/rcs_test.go @@ -1,4 +1,4 @@ -package noop +package fs import ( "context" @@ -9,10 +9,12 @@ import ( "github.com/stretchr/testify/require" ) -func TestNoop(t *testing.T) { +func TestRCS(t *testing.T) { ctx := context.Background() + path, cleanup := newTempDir(t) + defer cleanup() - g := New() + g := New(path) assert.NoError(t, g.Add(ctx, "foo", "bar")) assert.NoError(t, g.Commit(ctx, "foobar")) assert.NoError(t, g.Push(ctx, "foo", "bar")) @@ -20,8 +22,8 @@ func TestNoop(t *testing.T) { assert.NoError(t, g.Cmd(ctx, "foo", "bar")) assert.NoError(t, g.Init(ctx, "foo", "bar")) assert.NoError(t, g.InitConfig(ctx, "foo", "bar")) - assert.Equal(t, g.Version(ctx), semver.Version{}) - assert.Equal(t, "noop", g.Name()) + assert.Equal(t, g.Version(ctx), semver.Version{Minor: 1}) + assert.Equal(t, "fs", g.Name()) assert.NoError(t, g.AddRemote(ctx, "foo", "bar")) revs, err := g.Revisions(ctx, "foo") assert.NoError(t, err) diff --git a/internal/backend/storage/gitfs.go b/internal/backend/storage/gitfs.go new file mode 100644 index 0000000000..45c9aa4b4a --- /dev/null +++ b/internal/backend/storage/gitfs.go @@ -0,0 +1,3 @@ +package storage + +import _ "github.com/gopasspw/gopass/internal/backend/storage/gitfs" // register gitfs backend diff --git a/internal/backend/rcs/git/cli/config.go b/internal/backend/storage/gitfs/config.go similarity index 93% rename from internal/backend/rcs/git/cli/config.go rename to internal/backend/storage/gitfs/config.go index e3d9d3cdef..575584dcf0 100644 --- a/internal/backend/rcs/git/cli/config.go +++ b/internal/backend/storage/gitfs/config.go @@ -1,4 +1,4 @@ -package cli +package gitfs import ( "context" @@ -61,10 +61,10 @@ func (g *Git) InitConfig(ctx context.Context, userName, userEmail string) error return errors.Wrapf(err, "failed to fix git config") } - if err := ioutil.WriteFile(filepath.Join(g.path, ".gitattributes"), []byte("*.gpg diff=gpg\n"), fileMode); err != nil { + if err := ioutil.WriteFile(filepath.Join(g.fs.Path(), ".gitattributes"), []byte("*.gpg diff=gpg\n"), fileMode); err != nil { return errors.Errorf("Failed to initialize git: %s", err) } - if err := g.Add(ctx, g.path+"/.gitattributes"); err != nil { + if err := g.Add(ctx, g.fs.Path()+"/.gitattributes"); err != nil { out.Yellow(ctx, "Warning: Failed to add .gitattributes to git") } if err := g.Commit(ctx, "Configure git repository for gpg file diff."); err != nil { @@ -88,7 +88,7 @@ func (g *Git) ConfigGet(ctx context.Context, key string) (string, error) { buf := &strings.Builder{} cmd := exec.CommandContext(ctx, "git", "config", "--get", key) - cmd.Dir = g.path + cmd.Dir = g.fs.Path() cmd.Stdout = buf cmd.Stderr = os.Stderr @@ -109,7 +109,7 @@ func (g *Git) ConfigList(ctx context.Context) (map[string]string, error) { buf := &strings.Builder{} cmd := exec.CommandContext(ctx, "git", "config", "--list") - cmd.Dir = g.path + cmd.Dir = g.fs.Path() cmd.Stdout = buf cmd.Stderr = os.Stderr diff --git a/internal/backend/rcs/git/cli/config_test.go b/internal/backend/storage/gitfs/config_test.go similarity index 98% rename from internal/backend/rcs/git/cli/config_test.go rename to internal/backend/storage/gitfs/config_test.go index b9d3f3545f..04d35d959c 100644 --- a/internal/backend/rcs/git/cli/config_test.go +++ b/internal/backend/storage/gitfs/config_test.go @@ -1,4 +1,4 @@ -package cli +package gitfs import ( "bytes" diff --git a/internal/backend/rcs/git/cli/git.go b/internal/backend/storage/gitfs/git.go similarity index 88% rename from internal/backend/rcs/git/cli/git.go rename to internal/backend/storage/gitfs/git.go index 52ab22c4f6..d9061bc028 100644 --- a/internal/backend/rcs/git/cli/git.go +++ b/internal/backend/storage/gitfs/git.go @@ -1,6 +1,6 @@ -// Package cli implements a git cli based RCS backend. +// Package gitfs implements a git cli based RCS backend. // TODO(2.x) DEPRECATED and slated for removal in the 2.0.0 release. -package cli +package gitfs import ( "bytes" @@ -15,6 +15,7 @@ import ( "time" "github.com/gopasspw/gopass/internal/backend" + "github.com/gopasspw/gopass/internal/backend/storage/fs" "github.com/gopasspw/gopass/internal/debug" "github.com/gopasspw/gopass/internal/out" "github.com/gopasspw/gopass/internal/store" @@ -25,18 +26,35 @@ import ( "github.com/pkg/errors" ) +type contextKey int + +const ( + ctxKeyPathOverride contextKey = iota +) + +func withPathOverride(ctx context.Context, path string) context.Context { + return context.WithValue(ctx, ctxKeyPathOverride, path) +} + +func getPathOverride(ctx context.Context, def string) string { + if sv, ok := ctx.Value(ctxKeyPathOverride).(string); ok && sv != "" { + return sv + } + return def +} + // Git is a cli based git backend type Git struct { - path string + fs *fs.Store } -// Open creates a new git cli based git backend -func Open(path string) (*Git, error) { +// New creates a new git cli based git backend +func New(path string) (*Git, error) { if !fsutil.IsDir(filepath.Join(path, ".git")) { return nil, fmt.Errorf("git repo does not exist") } return &Git{ - path: path, + fs: fs.New(path), }, nil } @@ -44,19 +62,18 @@ func Open(path string) (*Git, error) { // configured for this clone repo func Clone(ctx context.Context, repo, path string) (*Git, error) { g := &Git{ - path: filepath.Dir(path), + fs: fs.New(path), } - if err := g.Cmd(ctx, "Clone", "clone", repo, path); err != nil { + if err := g.Cmd(withPathOverride(ctx, filepath.Dir(path)), "Clone", "clone", repo, path); err != nil { return nil, err } - g.path = path return g, nil } // Init initializes this store's git repo func Init(ctx context.Context, path, userName, userEmail string) (*Git, error) { g := &Git{ - path: path, + fs: fs.New(path), } // the git repo may be empty (i.e. no branches, cloned from a fresh remote) // or already initialized. Only run git init if the folder is completely empty @@ -64,7 +81,7 @@ func Init(ctx context.Context, path, userName, userEmail string) (*Git, error) { if err := g.Cmd(ctx, "Init", "init"); err != nil { return nil, errors.Errorf("failed to initialize git: %s", err) } - out.Green(ctx, "git initialized at %s", g.path) + out.Green(ctx, "git initialized at %s", g.fs.Path()) } if !ctxutil.IsGitInit(ctx) { @@ -75,11 +92,11 @@ func Init(ctx context.Context, path, userName, userEmail string) (*Git, error) { if err := g.InitConfig(ctx, userName, userEmail); err != nil { return g, errors.Errorf("failed to configure git: %s", err) } - out.Green(ctx, "git configured at %s", g.path) + out.Green(ctx, "git configured at %s", g.fs.Path()) // add current content of the store - if err := g.Add(ctx, g.path); err != nil { - return g, errors.Wrapf(err, "failed to add '%s' to git", g.path) + if err := g.Add(ctx, g.fs.Path()); err != nil { + return g, errors.Wrapf(err, "failed to add '%s' to git", g.fs.Path()) } // commit if there is something to commit @@ -100,7 +117,7 @@ func (g *Git) captureCmd(ctx context.Context, name string, args ...string) ([]by bufErr := &bytes.Buffer{} cmd := exec.CommandContext(ctx, "git", args[0:]...) - cmd.Dir = g.path + cmd.Dir = getPathOverride(ctx, g.fs.Path()) cmd.Stdout = bufOut cmd.Stderr = bufErr @@ -109,7 +126,7 @@ func (g *Git) captureCmd(ctx context.Context, name string, args ...string) ([]by cmd.Stderr = io.MultiWriter(bufErr, os.Stderr) } - debug.Log("store.%s: %s %+v (%s)", name, cmd.Path, cmd.Args, g.path) + debug.Log("store.%s: %s %+v (%s)", name, cmd.Path, cmd.Args, g.fs.Path()) err := cmd.Run() return bufOut.Bytes(), bufErr.Bytes(), err } @@ -156,7 +173,7 @@ func (g *Git) Version(ctx context.Context) semver.Version { // IsInitialized returns true if this stores has an (probably) initialized .git folder func (g *Git) IsInitialized() bool { - return fsutil.IsFile(filepath.Join(g.path, ".git", "config")) + return fsutil.IsFile(filepath.Join(g.fs.Path(), ".git", "config")) } // Add adds the listed files to the git index @@ -166,7 +183,7 @@ func (g *Git) Add(ctx context.Context, files ...string) error { } for i := range files { - files[i] = strings.TrimPrefix(files[i], g.path+"/") + files[i] = strings.TrimPrefix(files[i], g.fs.Path()+"/") } args := []string{"add", "--all", "--force"} diff --git a/internal/backend/storage/gitfs/git_test.go b/internal/backend/storage/gitfs/git_test.go new file mode 100644 index 0000000000..23cb009672 --- /dev/null +++ b/internal/backend/storage/gitfs/git_test.go @@ -0,0 +1,88 @@ +package gitfs + +import ( + "bytes" + "context" + "io/ioutil" + "os" + "path/filepath" + "testing" + + "github.com/gopasspw/gopass/internal/out" + "github.com/gopasspw/gopass/pkg/ctxutil" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestGit(t *testing.T) { + td, err := ioutil.TempDir("", "gopass-") + require.NoError(t, err) + defer func() { + _ = os.RemoveAll(td) + }() + + gitdir := filepath.Join(td, "git") + require.NoError(t, os.Mkdir(gitdir, 0755)) + gitdir2 := filepath.Join(td, "git2") + require.NoError(t, os.Mkdir(gitdir2, 0755)) + + ctx := context.Background() + ctx = ctxutil.WithAlwaysYes(ctx, true) + + buf := &bytes.Buffer{} + out.Stdout = buf + defer func() { + out.Stdout = os.Stdout + }() + + t.Run("init new repo", func(t *testing.T) { + git, err := Init(ctx, gitdir, "Dead Beef", "dead.beef@example.org") + require.NoError(t, err) + require.NotNil(t, git) + + sv := git.Version(ctx) + assert.NotEqual(t, "", sv.String()) + + assert.Equal(t, true, git.IsInitialized()) + tf := filepath.Join(gitdir, "some-file") + require.NoError(t, ioutil.WriteFile(tf, []byte("foobar"), 0644)) + assert.NoError(t, git.Add(ctx, "some-file")) + assert.Equal(t, true, git.HasStagedChanges(ctx)) + assert.NoError(t, git.Commit(ctx, "added some-file")) + assert.Equal(t, false, git.HasStagedChanges(ctx)) + + assert.Error(t, git.Push(ctx, "origin", "master")) + assert.Error(t, git.Pull(ctx, "origin", "master")) + }) + + t.Run("open existing repo", func(t *testing.T) { + git, err := New(gitdir) + require.NoError(t, err) + require.NotNil(t, git) + assert.Equal(t, "git", git.Name()) + assert.NoError(t, git.AddRemote(ctx, "foo", "file:///tmp/foo")) + assert.NoError(t, git.RemoveRemote(ctx, "foo")) + assert.Error(t, git.RemoveRemote(ctx, "foo")) + }) + + t.Run("clone existing repo", func(t *testing.T) { + git, err := Clone(ctx, gitdir, gitdir2) + require.NoError(t, err) + require.NotNil(t, git) + assert.Equal(t, "git", git.Name()) + + tf := filepath.Join(gitdir2, "some-other-file") + require.NoError(t, ioutil.WriteFile(tf, []byte("foobar"), 0644)) + assert.NoError(t, git.Add(ctx, "some-other-file")) + assert.NoError(t, git.Commit(ctx, "added some-other-file")) + + revs, err := git.Revisions(ctx, "some-other-file") + require.NoError(t, err) + assert.Equal(t, true, len(revs) == 1) + + content, err := git.GetRevision(ctx, "some-other-file", revs[0].Hash) + require.NoError(t, err) + assert.Equal(t, "foobar", string(content)) + }) +} diff --git a/internal/backend/rcs/git/cli/loader.go b/internal/backend/storage/gitfs/loader.go similarity index 71% rename from internal/backend/rcs/git/cli/loader.go rename to internal/backend/storage/gitfs/loader.go index 83669753f7..473411c3db 100644 --- a/internal/backend/rcs/git/cli/loader.go +++ b/internal/backend/storage/gitfs/loader.go @@ -1,4 +1,4 @@ -package cli +package gitfs import ( "context" @@ -11,27 +11,31 @@ import ( ) const ( - name = "gitcli" + name = "gitfs" ) func init() { - backend.RegisterRCS(backend.GitCLI, name, &loader{}) + backend.RegisterStorage(backend.GitFS, name, &loader{}) } type loader struct{} +func (l loader) New(ctx context.Context, path string) (backend.Storage, error) { + return New(path) +} + // Open implements backend.RCSLoader -func (l loader) Open(ctx context.Context, path string) (backend.RCS, error) { - return Open(path) +func (l loader) Open(ctx context.Context, path string) (backend.Storage, error) { + return New(path) } // Clone implements backend.RCSLoader -func (l loader) Clone(ctx context.Context, repo, path string) (backend.RCS, error) { +func (l loader) Clone(ctx context.Context, repo, path string) (backend.Storage, error) { return Clone(ctx, repo, path) } // Init implements backend.RCSLoader -func (l loader) InitRCS(ctx context.Context, path string) (backend.RCS, error) { +func (l loader) Init(ctx context.Context, path string) (backend.Storage, error) { return Init(ctx, path, termio.DetectName(ctx, nil), termio.DetectEmail(ctx, nil)) } diff --git a/internal/backend/storage/gitfs/storage.go b/internal/backend/storage/gitfs/storage.go new file mode 100644 index 0000000000..e90643b002 --- /dev/null +++ b/internal/backend/storage/gitfs/storage.go @@ -0,0 +1,58 @@ +package gitfs + +import ( + "context" + "fmt" +) + +// Get retrieves the named content +func (g *Git) Get(ctx context.Context, name string) ([]byte, error) { + return g.fs.Get(ctx, name) +} + +// Set writes the given content +func (g *Git) Set(ctx context.Context, name string, value []byte) error { + return g.fs.Set(ctx, name, value) +} + +// Delete removes the named entity +func (g *Git) Delete(ctx context.Context, name string) error { + return g.fs.Delete(ctx, name) +} + +// Exists checks if the named entity exists +func (g *Git) Exists(ctx context.Context, name string) bool { + return g.fs.Exists(ctx, name) +} + +// List returns a list of all entities +// e.g. foo, far/bar baz/.bang +// directory separator are normalized using `/` +func (g *Git) List(ctx context.Context, prefix string) ([]string, error) { + return g.fs.List(ctx, prefix) +} + +// IsDir returns true if the named entity is a directory +func (g *Git) IsDir(ctx context.Context, name string) bool { + return g.fs.IsDir(ctx, name) +} + +// Prune removes a named directory +func (g *Git) Prune(ctx context.Context, prefix string) error { + return g.fs.Prune(ctx, prefix) +} + +// String implements fmt.Stringer +func (g *Git) String() string { + return fmt.Sprintf("gitfs(v0.1.0,path:%s)", g.fs.Path()) +} + +// Path returns the ondisk path +func (g *Git) Path() string { + return g.fs.Path() +} + +// Fsck checks the storage integrity +func (g *Git) Fsck(ctx context.Context) error { + return g.fs.Fsck(ctx) +} diff --git a/internal/backend/storage/inmem.go b/internal/backend/storage/inmem.go deleted file mode 100644 index 93e843c481..0000000000 --- a/internal/backend/storage/inmem.go +++ /dev/null @@ -1,3 +0,0 @@ -package storage - -import _ "github.com/gopasspw/gopass/internal/backend/storage/kv/inmem" // register in-memory backend diff --git a/internal/backend/storage/kv/inmem/loader.go b/internal/backend/storage/kv/inmem/loader.go deleted file mode 100644 index 84e874ddf4..0000000000 --- a/internal/backend/storage/kv/inmem/loader.go +++ /dev/null @@ -1,41 +0,0 @@ -package inmem - -import ( - "context" - "fmt" - - "github.com/gopasspw/gopass/internal/backend" -) - -const ( - name = "inmem" -) - -func init() { - backend.RegisterStorage(backend.InMem, name, &loader{}) -} - -type loader struct{} - -// New implements backend.StorageLoader -func (l loader) New(ctx context.Context, _ string) (backend.Storage, error) { - return New(), nil -} - -func (l loader) Init(ctx context.Context, path string) (backend.Storage, error) { - return l.New(ctx, path) -} - -func (l loader) Handles(path string) error { - if path == "//gopass/inmem" { - return nil - } - return fmt.Errorf("not supported") -} - -func (l loader) Priority() int { - return 1000 -} -func (l loader) String() string { - return name -} diff --git a/internal/backend/storage/ondisk.go b/internal/backend/storage/ondisk.go index 09b3b9fe0b..434694e571 100644 --- a/internal/backend/storage/ondisk.go +++ b/internal/backend/storage/ondisk.go @@ -1,3 +1,3 @@ package storage -import _ "github.com/gopasspw/gopass/internal/backend/storage/kv/ondisk" // register on-disk backend +import _ "github.com/gopasspw/gopass/internal/backend/storage/ondisk" // register on-disk backend diff --git a/internal/backend/storage/kv/ondisk/fsck.go b/internal/backend/storage/ondisk/fsck.go similarity index 100% rename from internal/backend/storage/kv/ondisk/fsck.go rename to internal/backend/storage/ondisk/fsck.go diff --git a/internal/backend/storage/kv/ondisk/gjs/entry.go b/internal/backend/storage/ondisk/gjs/entry.go similarity index 100% rename from internal/backend/storage/kv/ondisk/gjs/entry.go rename to internal/backend/storage/ondisk/gjs/entry.go diff --git a/internal/backend/storage/kv/ondisk/gjs/gjs.go b/internal/backend/storage/ondisk/gjs/gjs.go similarity index 100% rename from internal/backend/storage/kv/ondisk/gjs/gjs.go rename to internal/backend/storage/ondisk/gjs/gjs.go diff --git a/internal/backend/storage/kv/ondisk/gjs/merge.go b/internal/backend/storage/ondisk/gjs/merge.go similarity index 100% rename from internal/backend/storage/kv/ondisk/gjs/merge.go rename to internal/backend/storage/ondisk/gjs/merge.go diff --git a/internal/backend/storage/kv/ondisk/gjs/merge_test.go b/internal/backend/storage/ondisk/gjs/merge_test.go similarity index 100% rename from internal/backend/storage/kv/ondisk/gjs/merge_test.go rename to internal/backend/storage/ondisk/gjs/merge_test.go diff --git a/internal/backend/storage/kv/ondisk/gjs/revision.go b/internal/backend/storage/ondisk/gjs/revision.go similarity index 100% rename from internal/backend/storage/kv/ondisk/gjs/revision.go rename to internal/backend/storage/ondisk/gjs/revision.go diff --git a/internal/backend/storage/kv/ondisk/gjs/sort.go b/internal/backend/storage/ondisk/gjs/sort.go similarity index 100% rename from internal/backend/storage/kv/ondisk/gjs/sort.go rename to internal/backend/storage/ondisk/gjs/sort.go diff --git a/internal/backend/storage/kv/ondisk/gjs/store.go b/internal/backend/storage/ondisk/gjs/store.go similarity index 100% rename from internal/backend/storage/kv/ondisk/gjs/store.go rename to internal/backend/storage/ondisk/gjs/store.go diff --git a/internal/backend/storage/kv/ondisk/gpb/Makefile b/internal/backend/storage/ondisk/gpb/Makefile similarity index 100% rename from internal/backend/storage/kv/ondisk/gpb/Makefile rename to internal/backend/storage/ondisk/gpb/Makefile diff --git a/internal/backend/storage/kv/ondisk/gpb/entry.go b/internal/backend/storage/ondisk/gpb/entry.go similarity index 100% rename from internal/backend/storage/kv/ondisk/gpb/entry.go rename to internal/backend/storage/ondisk/gpb/entry.go diff --git a/internal/backend/storage/kv/ondisk/gpb/gpb.pb.go b/internal/backend/storage/ondisk/gpb/gpb.pb.go similarity index 100% rename from internal/backend/storage/kv/ondisk/gpb/gpb.pb.go rename to internal/backend/storage/ondisk/gpb/gpb.pb.go diff --git a/internal/backend/storage/kv/ondisk/gpb/gpb.proto b/internal/backend/storage/ondisk/gpb/gpb.proto similarity index 100% rename from internal/backend/storage/kv/ondisk/gpb/gpb.proto rename to internal/backend/storage/ondisk/gpb/gpb.proto diff --git a/internal/backend/storage/kv/ondisk/gpb/merge.go b/internal/backend/storage/ondisk/gpb/merge.go similarity index 100% rename from internal/backend/storage/kv/ondisk/gpb/merge.go rename to internal/backend/storage/ondisk/gpb/merge.go diff --git a/internal/backend/storage/kv/ondisk/gpb/merge_test.go b/internal/backend/storage/ondisk/gpb/merge_test.go similarity index 100% rename from internal/backend/storage/kv/ondisk/gpb/merge_test.go rename to internal/backend/storage/ondisk/gpb/merge_test.go diff --git a/internal/backend/storage/kv/ondisk/gpb/revision.go b/internal/backend/storage/ondisk/gpb/revision.go similarity index 100% rename from internal/backend/storage/kv/ondisk/gpb/revision.go rename to internal/backend/storage/ondisk/gpb/revision.go diff --git a/internal/backend/storage/kv/ondisk/gpb/sort.go b/internal/backend/storage/ondisk/gpb/sort.go similarity index 100% rename from internal/backend/storage/kv/ondisk/gpb/sort.go rename to internal/backend/storage/ondisk/gpb/sort.go diff --git a/internal/backend/storage/kv/ondisk/gpb/store.go b/internal/backend/storage/ondisk/gpb/store.go similarity index 100% rename from internal/backend/storage/kv/ondisk/gpb/store.go rename to internal/backend/storage/ondisk/gpb/store.go diff --git a/internal/backend/storage/kv/ondisk/loader.go b/internal/backend/storage/ondisk/loader.go similarity index 77% rename from internal/backend/storage/kv/ondisk/loader.go rename to internal/backend/storage/ondisk/loader.go index 83074968b3..4d5f6fa880 100644 --- a/internal/backend/storage/kv/ondisk/loader.go +++ b/internal/backend/storage/ondisk/loader.go @@ -17,28 +17,27 @@ const ( func init() { backend.RegisterStorage(backend.OnDisk, name, &loader{}) - backend.RegisterRCS(backend.OnDiskRCS, name, &loader{}) } type loader struct{} // New creates a new ondisk loader func (l loader) New(ctx context.Context, path string) (backend.Storage, error) { - be, err := New(path) + be, err := New(ctx, path) debug.Log("Using Storage Backend %p: %s", be, path) return be, err } // Open loads an existing ondisk repo -func (l loader) Open(ctx context.Context, path string) (backend.RCS, error) { - be, err := New(path) +func (l loader) Open(ctx context.Context, path string) (backend.Storage, error) { + be, err := New(ctx, path) debug.Log("Using RCS Backend: %s", be.String()) return be, err } // Clone loads an existing ondisk repo -func (l loader) Clone(ctx context.Context, repo, path string) (backend.RCS, error) { - be, err := New(path) +func (l loader) Clone(ctx context.Context, repo, path string) (backend.Storage, error) { + be, err := New(ctx, path) debug.Log("Using RCS Backend %p: %s", be, be.String()) if err := be.SetRemote(ctx, repo); err != nil { return nil, err @@ -49,24 +48,15 @@ func (l loader) Clone(ctx context.Context, repo, path string) (backend.RCS, erro return be, err } -// InitRCS creates a new ondisk repo -func (l loader) InitRCS(ctx context.Context, path string) (backend.RCS, error) { - return l.init(ctx, path) -} - -func (l loader) init(ctx context.Context, path string) (*OnDisk, error) { +func (l loader) Init(ctx context.Context, path string) (backend.Storage, error) { if err := os.MkdirAll(path, 0700); err != nil { return nil, err } - be, err := New(path) + be, err := New(ctx, path) debug.Log("Using RCS Backend %p: %s", be, be.String()) return be, err } -func (l loader) Init(ctx context.Context, path string) (backend.Storage, error) { - return l.init(ctx, path) -} - func (l loader) Handles(path string) error { if fsutil.IsFile(filepath.Join(path, idxFile)) { return nil diff --git a/internal/backend/storage/kv/ondisk/rcs.go b/internal/backend/storage/ondisk/rcs.go similarity index 100% rename from internal/backend/storage/kv/ondisk/rcs.go rename to internal/backend/storage/ondisk/rcs.go diff --git a/internal/backend/storage/kv/ondisk/remote.go b/internal/backend/storage/ondisk/remote.go similarity index 99% rename from internal/backend/storage/kv/ondisk/remote.go rename to internal/backend/storage/ondisk/remote.go index 2d39382a01..2f82b5e2ea 100644 --- a/internal/backend/storage/kv/ondisk/remote.go +++ b/internal/backend/storage/ondisk/remote.go @@ -88,6 +88,7 @@ func (o *OnDisk) loadRemoteConfig(ctx context.Context) (*RemoteConfig, error) { } return nil, err } + debug.Log("loading remote config from %s", path) plain, err := o.age.Decrypt(ctx, buf) if err != nil { return nil, err diff --git a/internal/backend/storage/kv/ondisk/store.go b/internal/backend/storage/ondisk/store.go similarity index 93% rename from internal/backend/storage/kv/ondisk/store.go rename to internal/backend/storage/ondisk/store.go index 4c1434fb18..fc53524044 100644 --- a/internal/backend/storage/kv/ondisk/store.go +++ b/internal/backend/storage/ondisk/store.go @@ -17,7 +17,7 @@ import ( "github.com/blang/semver" "github.com/gopasspw/gopass/internal/backend/crypto/age" - "github.com/gopasspw/gopass/internal/backend/storage/kv/ondisk/gjs" + "github.com/gopasspw/gopass/internal/backend/storage/ondisk/gjs" "github.com/gopasspw/gopass/internal/debug" "github.com/gopasspw/gopass/pkg/ctxutil" "github.com/minio/minio-go/v6" @@ -46,7 +46,7 @@ type OnDisk struct { } // New creates a new ondisk store -func New(baseDir string) (*OnDisk, error) { +func New(ctx context.Context, baseDir string) (*OnDisk, error) { a, err := age.New() if err != nil { return nil, err @@ -55,12 +55,12 @@ func New(baseDir string) (*OnDisk, error) { dir: baseDir, age: a, } - idx, err := o.loadOrCreate(baseDir) + idx, err := o.loadOrCreate(ctx, baseDir) if err != nil { return nil, err } o.idx = idx - if err := o.initRemote(); err != nil { + if err := o.initRemote(ctx); err != nil { return nil, err } return o, nil @@ -71,8 +71,8 @@ func (o *OnDisk) Path() string { return o.dir } -func (o *OnDisk) initRemote() error { - cfg, err := o.loadRemoteConfig(context.TODO()) +func (o *OnDisk) initRemote(ctx context.Context) error { + cfg, err := o.loadRemoteConfig(ctx) if err != nil { return err } @@ -103,7 +103,7 @@ func (o *OnDisk) initRemote() error { return nil } -func (o *OnDisk) loadOrCreate(path string) (*gjs.Store, error) { +func (o *OnDisk) loadOrCreate(ctx context.Context, path string) (*gjs.Store, error) { path = filepath.Join(path, idxFile) buf, err := ioutil.ReadFile(path) if err != nil { @@ -115,7 +115,8 @@ func (o *OnDisk) loadOrCreate(path string) (*gjs.Store, error) { } return nil, err } - return o.loadIndex(context.TODO(), buf) + debug.Log("loading index from %s", path) + return o.loadIndex(ctx, buf) } func (o *OnDisk) loadIndex(ctx context.Context, buf []byte) (*gjs.Store, error) { diff --git a/internal/backend/storage/kv/ondisk/sync.go b/internal/backend/storage/ondisk/sync.go similarity index 98% rename from internal/backend/storage/kv/ondisk/sync.go rename to internal/backend/storage/ondisk/sync.go index e3d6aa19fe..1f5d159734 100644 --- a/internal/backend/storage/kv/ondisk/sync.go +++ b/internal/backend/storage/ondisk/sync.go @@ -7,7 +7,7 @@ import ( "github.com/cenkalti/backoff" "github.com/gopasspw/gopass/internal/backend/crypto/age" - "github.com/gopasspw/gopass/internal/backend/storage/kv/ondisk/gjs" + "github.com/gopasspw/gopass/internal/backend/storage/ondisk/gjs" "github.com/gopasspw/gopass/internal/debug" "github.com/gopasspw/gopass/internal/recipients" "github.com/minio/minio-go/v6" diff --git a/internal/backend/storage_test.go b/internal/backend/storage_test.go index 224716c9d7..685158fdf4 100644 --- a/internal/backend/storage_test.go +++ b/internal/backend/storage_test.go @@ -7,6 +7,9 @@ import ( "path/filepath" "testing" + "github.com/gopasspw/gopass/internal/debug" + "github.com/gopasspw/gopass/internal/gptest" + "github.com/gopasspw/gopass/pkg/ctxutil" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -14,35 +17,41 @@ import ( func TestDetectStorage(t *testing.T) { ctx := context.Background() + uv := gptest.UnsetVars("GOPASS_HOMEDIR") + defer uv() + td, err := ioutil.TempDir("", "gopass-") require.NoError(t, err) defer func() { _ = os.RemoveAll(td) }() + // all tests involving ondisk/age should set GOPASS_HOMEDIR + os.Setenv("GOPASS_HOMEDIR", td) + ctx = ctxutil.WithPasswordCallback(ctx, func(_ string) ([]byte, error) { + debug.Log("static test password callback") + return []byte("gopass"), nil + }) + fsDir := filepath.Join(td, "fs") assert.NoError(t, os.MkdirAll(fsDir, 0700)) - inmemDir := "//gopass/inmem" - ondiskDir := filepath.Join(td, "ondisk") assert.NoError(t, os.MkdirAll(ondiskDir, 0700)) - assert.NoError(t, ioutil.WriteFile(filepath.Join(ondiskDir, "index.pb"), []byte("null"), 0600)) - - r, err := DetectStorage(ctx, fsDir) - assert.NoError(t, err) - assert.NotNil(t, r) - assert.Equal(t, "fs", r.Name()) - - r, err = DetectStorage(ctx, inmemDir) - assert.NoError(t, err) - assert.NotNil(t, r) - assert.Equal(t, "inmem", r.Name()) - - t.Skip("WIP") - - r, err = DetectStorage(ctx, ondiskDir) - assert.NoError(t, err) - assert.NotNil(t, r) - assert.Equal(t, "ondisk", r.Name()) + assert.NoError(t, ioutil.WriteFile(filepath.Join(ondiskDir, "index.gp1"), []byte("null"), 0600)) + + t.Run("detect fs", func(t *testing.T) { + r, err := DetectStorage(ctx, fsDir) + assert.NoError(t, err) + assert.NotNil(t, r) + assert.Equal(t, "fs", r.Name()) + }) + + t.Run("detect ondisk", func(t *testing.T) { + r, err := DetectStorage(ctx, ondiskDir) + // the "fake" index can't be decoded, so it must fail + assert.Error(t, err) + assert.Nil(t, r) + assert.Equal(t, "ondisk", r.Name()) + }) } diff --git a/internal/backend/strings.go b/internal/backend/strings.go index 31303f9a9e..d6a1d1767a 100644 --- a/internal/backend/strings.go +++ b/internal/backend/strings.go @@ -5,8 +5,6 @@ import "sort" var ( cryptoNameToBackendMap = map[string]CryptoBackend{} cryptoBackendToNameMap = map[CryptoBackend]string{} - rcsNameToBackendMap = map[string]RCSBackend{} - rcsBackendToNameMap = map[RCSBackend]string{} storageNameToBackendMap = map[string]StorageBackend{} storageBackendToNameMap = map[StorageBackend]string{} ) @@ -15,9 +13,6 @@ func init() { for k, v := range cryptoNameToBackendMap { cryptoBackendToNameMap[v] = k } - for k, v := range rcsNameToBackendMap { - rcsBackendToNameMap[v] = k - } for k, v := range storageNameToBackendMap { storageBackendToNameMap[v] = k } @@ -33,16 +28,6 @@ func CryptoBackends() []string { return bes } -// RCSBackends returns the list of registered RCS backends. -func RCSBackends() []string { - bes := make([]string, 0, len(rcsNameToBackendMap)) - for k := range rcsNameToBackendMap { - bes = append(bes, k) - } - sort.Strings(bes) - return bes -} - // StorageBackends returns the list of registered storage backends. func StorageBackends() []string { bes := make([]string, 0, len(storageNameToBackendMap)) @@ -72,25 +57,6 @@ func CryptoNameFromBackend(be CryptoBackend) string { return "" } -// RcsBackendFromName parses the identifier into a rcs backend -func RcsBackendFromName(name string) RCSBackend { - if name == "git" { - name = "gitcli" - } - if b, found := rcsNameToBackendMap[name]; found { - return b - } - return -1 -} - -// RcsNameFromBackend returns the name of a given RCS backend -func RcsNameFromBackend(be RCSBackend) string { - if b, found := rcsBackendToNameMap[be]; found { - return b - } - return "" -} - // StorageBackendFromName parses the identifier into a storage backend func StorageBackendFromName(name string) StorageBackend { if b, found := storageNameToBackendMap[name]; found { diff --git a/internal/config/config_test.go b/internal/config/config_test.go index 90feb829b0..597928281b 100644 --- a/internal/config/config_test.go +++ b/internal/config/config_test.go @@ -6,7 +6,6 @@ import ( "testing" _ "github.com/gopasspw/gopass/internal/backend/crypto" - _ "github.com/gopasspw/gopass/internal/backend/rcs" _ "github.com/gopasspw/gopass/internal/backend/storage" "github.com/gopasspw/gopass/internal/config" diff --git a/internal/gptest/integration.go b/internal/gptest/integration.go deleted file mode 100644 index 229f59cf1b..0000000000 --- a/internal/gptest/integration.go +++ /dev/null @@ -1 +0,0 @@ -package gptest diff --git a/internal/gptest/unit.go b/internal/gptest/unit.go index 771f71fed6..04b8b23f34 100644 --- a/internal/gptest/unit.go +++ b/internal/gptest/unit.go @@ -8,7 +8,6 @@ import ( "testing" aclip "github.com/atotto/clipboard" - "github.com/gopasspw/gopass/internal/backend/crypto/plain" "github.com/stretchr/testify/assert" ) @@ -102,13 +101,13 @@ func (u Unit) InitStore(name string) error { if err := os.MkdirAll(dir, 0700); err != nil { return err } - fn := filepath.Join(dir, plain.IDFile) + fn := filepath.Join(dir, ".plain-id") // plain.IDFile _ = os.Remove(fn) if err := ioutil.WriteFile(fn, u.recipients(), 0600); err != nil { return err } for _, p := range AllPathsToSlash(u.Entries) { - fn := filepath.Join(dir, p+"."+plain.Ext) + fn := filepath.Join(dir, p+".txt") // plain.Ext _ = os.Remove(fn) if err := os.MkdirAll(filepath.Dir(fn), 0700); err != nil { return err diff --git a/internal/store/leaf/convert.go b/internal/store/leaf/convert.go index 279424cc8c..152d581189 100644 --- a/internal/store/leaf/convert.go +++ b/internal/store/leaf/convert.go @@ -17,9 +17,9 @@ import ( ) // Convert will convert an existing store to a new store with possibly -// different set of crypto, RCS and storage backend. Please note that it -// will happily convert to the same set of backend if requested. -func (s *Store) Convert(ctx context.Context, cryptoBe backend.CryptoBackend, rcsBe backend.RCSBackend, storageBe backend.StorageBackend, move bool) error { +// different set of crypto and storage backends. Please note that it +// will happily convert to the same set of backends if requested. +func (s *Store) Convert(ctx context.Context, cryptoBe backend.CryptoBackend, storageBe backend.StorageBackend, move bool) error { // create temp path tmpPath := s.path + "-autoconvert" @@ -35,12 +35,6 @@ func (s *Store) Convert(ctx context.Context, cryptoBe backend.CryptoBackend, rcs } debug.Log("initialized storage %s at %s", st, tmpPath) - rcs, err := backend.InitRCS(ctx, rcsBe, tmpPath) - if err != nil { - return err - } - debug.Log("initialized RCS %s at %s", rcs, tmpPath) - crypto, err := backend.NewCrypto(ctx, cryptoBe) if err != nil { return err @@ -52,7 +46,6 @@ func (s *Store) Convert(ctx context.Context, cryptoBe backend.CryptoBackend, rcs alias: s.alias, path: tmpPath, crypto: crypto, - rcs: rcs, storage: st, } diff --git a/internal/store/leaf/fsck.go b/internal/store/leaf/fsck.go index 5dbc9dbb61..9c517acc50 100644 --- a/internal/store/leaf/fsck.go +++ b/internal/store/leaf/fsck.go @@ -22,9 +22,9 @@ func (s *Store) Fsck(ctx context.Context, path string) error { return errors.Wrapf(err, "storage backend found errors: %s", err) } - // second give a chance to the RCS backend - if err := s.rcs.Compact(ctx); err != nil { - return errors.Wrapf(err, "rcs backend compaction failed: %s", err) + // then try to compact storage / rcs + if err := s.storage.Compact(ctx); err != nil { + return errors.Wrapf(err, "storage backend compaction failed: %s", err) } pcb := ctxutil.GetProgressCallback(ctx) @@ -48,7 +48,7 @@ func (s *Store) Fsck(ctx context.Context, path string) error { } } - if err := s.rcs.Push(ctx, "", ""); err != nil { + if err := s.storage.Push(ctx, "", ""); err != nil { out.Red(ctx, "RCS Push failed: %s", err) } diff --git a/internal/store/leaf/fsck_test.go b/internal/store/leaf/fsck_test.go index c2b15ce787..7059391451 100644 --- a/internal/store/leaf/fsck_test.go +++ b/internal/store/leaf/fsck_test.go @@ -8,7 +8,6 @@ import ( "testing" "github.com/gopasspw/gopass/internal/backend/crypto/plain" - "github.com/gopasspw/gopass/internal/backend/rcs/noop" "github.com/gopasspw/gopass/internal/backend/storage/fs" "github.com/gopasspw/gopass/internal/out" "github.com/gopasspw/gopass/pkg/ctxutil" @@ -36,7 +35,6 @@ func TestFsck(t *testing.T) { alias: "", path: tempdir, crypto: plain.New(), - rcs: noop.New(), storage: fs.New(tempdir), } assert.NoError(t, s.saveRecipients(ctx, []string{"john.doe"}, "test")) @@ -53,6 +51,7 @@ func TestFsck(t *testing.T) { // common tear down _ = os.RemoveAll(tempdir) } + func TestCompareStringSlices(t *testing.T) { want := []string{"foo", "bar"} have := []string{"baz", "bar"} diff --git a/internal/store/leaf/list_test.go b/internal/store/leaf/list_test.go index a253487f90..18ca368074 100644 --- a/internal/store/leaf/list_test.go +++ b/internal/store/leaf/list_test.go @@ -8,7 +8,6 @@ import ( "testing" plain "github.com/gopasspw/gopass/internal/backend/crypto/plain" - noop "github.com/gopasspw/gopass/internal/backend/rcs/noop" "github.com/gopasspw/gopass/internal/backend/storage/fs" "github.com/gopasspw/gopass/internal/out" "github.com/gopasspw/gopass/pkg/ctxutil" @@ -84,7 +83,6 @@ func TestList(t *testing.T) { alias: "", path: tempdir, crypto: plain.New(), - rcs: noop.New(), storage: fs.New(tempdir), } diff --git a/internal/store/leaf/move.go b/internal/store/leaf/move.go index dc28cee095..f17bc46bfc 100644 --- a/internal/store/leaf/move.go +++ b/internal/store/leaf/move.go @@ -85,7 +85,7 @@ func (s *Store) delete(ctx context.Context, name string, recurse bool) error { return nil } - if err := s.rcs.Commit(ctx, fmt.Sprintf("Remove %s from store.", name)); err != nil { + if err := s.storage.Commit(ctx, fmt.Sprintf("Remove %s from store.", name)); err != nil { switch errors.Cause(err) { case store.ErrGitNotInit: debug.Log("skipping git commit - git not initialized") @@ -96,7 +96,7 @@ func (s *Store) delete(ctx context.Context, name string, recurse bool) error { } } - if err := s.rcs.Push(ctx, "", ""); err != nil { + if err := s.storage.Push(ctx, "", ""); err != nil { if errors.Cause(err) == store.ErrGitNotInit || errors.Cause(err) == store.ErrGitNoRemote { return nil } @@ -118,7 +118,7 @@ func (s *Store) deleteRecurse(ctx context.Context, name, path string) error { return err } - if err := s.rcs.Add(ctx, name); err != nil { + if err := s.storage.Add(ctx, name); err != nil { if errors.Cause(err) == store.ErrGitNotInit { return nil } @@ -137,7 +137,7 @@ func (s *Store) deleteSingle(ctx context.Context, path string) error { return err } - if err := s.rcs.Add(ctx, path); err != nil { + if err := s.storage.Add(ctx, path); err != nil { if errors.Cause(err) == store.ErrGitNotInit { return nil } diff --git a/internal/store/leaf/move_test.go b/internal/store/leaf/move_test.go index 724775c475..dbb334a62d 100644 --- a/internal/store/leaf/move_test.go +++ b/internal/store/leaf/move_test.go @@ -8,7 +8,6 @@ import ( "testing" plain "github.com/gopasspw/gopass/internal/backend/crypto/plain" - noop "github.com/gopasspw/gopass/internal/backend/rcs/noop" "github.com/gopasspw/gopass/internal/backend/storage/fs" "github.com/gopasspw/gopass/internal/out" "github.com/gopasspw/gopass/pkg/ctxutil" @@ -79,7 +78,6 @@ func TestCopy(t *testing.T) { alias: "", path: tempdir, crypto: plain.New(), - rcs: noop.New(), storage: fs.New(tempdir), } @@ -155,7 +153,6 @@ func TestMove(t *testing.T) { alias: "", path: tempdir, crypto: plain.New(), - rcs: noop.New(), storage: fs.New(tempdir), } @@ -215,7 +212,6 @@ func TestDelete(t *testing.T) { alias: "", path: tempdir, crypto: plain.New(), - rcs: noop.New(), storage: fs.New(tempdir), } @@ -298,7 +294,6 @@ func TestPrune(t *testing.T) { alias: "", path: tempdir, crypto: plain.New(), - rcs: noop.New(), storage: fs.New(tempdir), } diff --git a/internal/store/leaf/rcs.go b/internal/store/leaf/rcs.go index ba46a27b87..1428040d87 100644 --- a/internal/store/leaf/rcs.go +++ b/internal/store/leaf/rcs.go @@ -4,8 +4,6 @@ import ( "context" "github.com/gopasspw/gopass/internal/backend" - _ "github.com/gopasspw/gopass/internal/backend/rcs" // register RCS backends - "github.com/gopasspw/gopass/internal/backend/storage/kv/ondisk" "github.com/gopasspw/gopass/internal/debug" "github.com/gopasspw/gopass/internal/out" "github.com/gopasspw/gopass/internal/store" @@ -14,43 +12,26 @@ import ( "github.com/pkg/errors" ) -func (s *Store) initRCSBackend(ctx context.Context) { - if rcs, ok := s.storage.(*ondisk.OnDisk); ok { - s.rcs = rcs - return - } - rcs, err := backend.DetectRCS(ctx, s.path) - if err != nil { - debug.Log("Failed to initialized RCS backend: %s", err) - } - s.rcs = rcs -} - -// RCS returns the sync backend -func (s *Store) RCS() backend.RCS { - return s.rcs -} - -// GitInit initializes the the git repo in the store +// GitInit initializes the git storage func (s *Store) GitInit(ctx context.Context) error { - rcs, err := backend.InitRCS(ctx, backend.GetRCSBackend(ctx), s.path) + storage, err := backend.InitStorage(ctx, backend.GetStorageBackend(ctx), s.path) if err != nil { return err } - s.rcs = rcs + s.storage = storage return nil } // ListRevisions will list all revisions for a secret func (s *Store) ListRevisions(ctx context.Context, name string) ([]backend.Revision, error) { p := s.passfile(name) - return s.rcs.Revisions(ctx, p) + return s.storage.Revisions(ctx, p) } // GetRevision will retrieve a single revision from the backend func (s *Store) GetRevision(ctx context.Context, name, revision string) (gopass.Secret, error) { p := s.passfile(name) - ciphertext, err := s.rcs.GetRevision(ctx, p, revision) + ciphertext, err := s.storage.GetRevision(ctx, p, revision) if err != nil { return nil, errors.Wrapf(err, "failed to get ciphertext of '%s'@'%s'", name, revision) } @@ -70,7 +51,7 @@ func (s *Store) GetRevision(ctx context.Context, name, revision string) (gopass. // GitStatus shows the git status output func (s *Store) GitStatus(ctx context.Context, _ string) error { - buf, err := s.rcs.Status(ctx) + buf, err := s.storage.Status(ctx) if err != nil { return err } diff --git a/internal/store/leaf/rcs_test.go b/internal/store/leaf/rcs_test.go index 76cc26f9bf..cfaedf3f56 100644 --- a/internal/store/leaf/rcs_test.go +++ b/internal/store/leaf/rcs_test.go @@ -26,21 +26,21 @@ func TestGit(t *testing.T) { s, err := createSubStore(tempdir) require.NoError(t, err) - assert.NotNil(t, s.RCS()) - assert.Equal(t, "noop", s.RCS().Name()) - assert.NoError(t, s.RCS().InitConfig(ctx, "foo", "bar@baz.com")) - assert.Equal(t, semver.Version{}, s.RCS().Version(ctx)) - assert.NoError(t, s.RCS().AddRemote(ctx, "foo", "bar")) - assert.NoError(t, s.RCS().Pull(ctx, "origin", "master")) - assert.NoError(t, s.RCS().Push(ctx, "origin", "master")) + require.NotNil(t, s.Storage()) + require.Equal(t, "fs", s.Storage().Name()) + assert.NoError(t, s.Storage().InitConfig(ctx, "foo", "bar@baz.com")) + assert.Equal(t, semver.Version{Minor: 1}, s.Storage().Version(ctx)) + assert.NoError(t, s.Storage().AddRemote(ctx, "foo", "bar")) + assert.NoError(t, s.Storage().Pull(ctx, "origin", "master")) + assert.NoError(t, s.Storage().Push(ctx, "origin", "master")) assert.NoError(t, s.GitInit(ctx)) - assert.NoError(t, s.GitInit(backend.WithRCSBackend(ctx, backend.Noop))) - assert.Error(t, s.GitInit(backend.WithRCSBackend(ctx, -1))) + assert.NoError(t, s.GitInit(backend.WithStorageBackend(ctx, backend.FS))) + assert.Error(t, s.GitInit(backend.WithStorageBackend(ctx, -1))) ctx = ctxutil.WithUsername(ctx, "foo") ctx = ctxutil.WithEmail(ctx, "foo@baz.com") - assert.NoError(t, s.GitInit(backend.WithRCSBackend(ctx, backend.GitCLI))) + assert.NoError(t, s.GitInit(backend.WithStorageBackend(ctx, backend.GitFS))) } func TestGitRevisions(t *testing.T) { @@ -55,9 +55,9 @@ func TestGitRevisions(t *testing.T) { s, err := createSubStore(tempdir) require.NoError(t, err) - assert.NotNil(t, s.RCS()) - assert.Equal(t, "noop", s.RCS().Name()) - assert.NoError(t, s.RCS().InitConfig(ctx, "foo", "bar@baz.com")) + require.NotNil(t, s.Storage()) + require.Equal(t, "fs", s.Storage().Name()) + assert.NoError(t, s.Storage().InitConfig(ctx, "foo", "bar@baz.com")) revs, err := s.ListRevisions(ctx, "foo") assert.NoError(t, err) diff --git a/internal/store/leaf/recipients.go b/internal/store/leaf/recipients.go index 267fba09da..7bc4f7b09a 100644 --- a/internal/store/leaf/recipients.go +++ b/internal/store/leaf/recipients.go @@ -213,7 +213,7 @@ func (s *Store) ExportMissingPublicKeys(ctx context.Context, rs []string) (bool, } // at least one key has been exported exported = true - if err := s.rcs.Add(ctx, path); err != nil { + if err := s.storage.Add(ctx, path); err != nil { if errors.Cause(err) == store.ErrGitNotInit { continue } @@ -221,7 +221,7 @@ func (s *Store) ExportMissingPublicKeys(ctx context.Context, rs []string) (bool, out.Error(ctx, "failed to add public key for '%s' to git: %s", r, err) continue } - if err := s.rcs.Commit(ctx, fmt.Sprintf("Exported Public Keys %s", r)); err != nil && err != store.ErrGitNothingToCommit { + if err := s.storage.Commit(ctx, fmt.Sprintf("Exported Public Keys %s", r)); err != nil && err != store.ErrGitNothingToCommit { ok = false out.Error(ctx, "Failed to git commit: %s", err) continue @@ -245,13 +245,13 @@ func (s *Store) saveRecipients(ctx context.Context, rs []string, msg string) err return errors.Wrapf(err, "failed to write recipients file") } - if err := s.rcs.Add(ctx, idf); err != nil { + if err := s.storage.Add(ctx, idf); err != nil { if err != store.ErrGitNotInit { return errors.Wrapf(err, "failed to add file '%s' to git", idf) } } - if err := s.rcs.Commit(ctx, msg); err != nil { + if err := s.storage.Commit(ctx, msg); err != nil { if err != store.ErrGitNotInit && err != store.ErrGitNothingToCommit { return errors.Wrapf(err, "failed to commit changes to git") } @@ -265,7 +265,7 @@ func (s *Store) saveRecipients(ctx context.Context, rs []string, msg string) err } // push to remote repo - if err := s.rcs.Push(ctx, "", ""); err != nil { + if err := s.storage.Push(ctx, "", ""); err != nil { if errors.Cause(err) == store.ErrGitNotInit { return nil } diff --git a/internal/store/leaf/recipients_test.go b/internal/store/leaf/recipients_test.go index 81b83fddf4..02ea19044a 100644 --- a/internal/store/leaf/recipients_test.go +++ b/internal/store/leaf/recipients_test.go @@ -13,7 +13,6 @@ import ( "github.com/gopasspw/gopass/internal/backend" plain "github.com/gopasspw/gopass/internal/backend/crypto/plain" - noop "github.com/gopasspw/gopass/internal/backend/rcs/noop" "github.com/gopasspw/gopass/internal/backend/storage/fs" "github.com/gopasspw/gopass/internal/out" "github.com/gopasspw/gopass/pkg/ctxutil" @@ -44,7 +43,6 @@ func TestGetRecipientsDefault(t *testing.T) { alias: "", path: tempdir, crypto: plain.New(), - rcs: noop.New(), storage: fs.New(tempdir), } @@ -76,7 +74,6 @@ func TestGetRecipientsSubID(t *testing.T) { alias: "", path: tempdir, crypto: plain.New(), - rcs: noop.New(), storage: fs.New(tempdir), } @@ -115,7 +112,6 @@ func TestSaveRecipients(t *testing.T) { alias: "", path: tempdir, crypto: plain.New(), - rcs: noop.New(), storage: fs.New(tempdir), } @@ -169,7 +165,6 @@ func TestAddRecipient(t *testing.T) { alias: "", path: tempdir, crypto: plain.New(), - rcs: noop.New(), storage: fs.New(tempdir), } @@ -208,7 +203,6 @@ func TestRemoveRecipient(t *testing.T) { alias: "", path: tempdir, crypto: plain.New(), - rcs: noop.New(), storage: fs.New(tempdir), } @@ -239,7 +233,6 @@ func TestListRecipients(t *testing.T) { }() ctx = backend.WithCryptoBackendString(ctx, "plain") - ctx = backend.WithRCSBackendString(ctx, "noop") s, err := New( ctx, "", diff --git a/internal/store/leaf/reencrypt.go b/internal/store/leaf/reencrypt.go index 26761a1333..c27d69289c 100644 --- a/internal/store/leaf/reencrypt.go +++ b/internal/store/leaf/reencrypt.go @@ -89,7 +89,7 @@ func (s *Store) reencrypt(ctx context.Context) error { if ctxutil.HasConcurrency(ctx) { for _, name := range entries { p := s.passfile(name) - if err := s.rcs.Add(ctx, p); err != nil { + if err := s.storage.Add(ctx, p); err != nil { switch errors.Cause(err) { case store.ErrGitNotInit: debug.Log("skipping git add - git not initialized") @@ -102,7 +102,7 @@ func (s *Store) reencrypt(ctx context.Context) error { } } - if err := s.rcs.Commit(ctx, ctxutil.GetCommitMessage(ctx)); err != nil { + if err := s.storage.Commit(ctx, ctxutil.GetCommitMessage(ctx)); err != nil { switch errors.Cause(err) { case store.ErrGitNotInit: debug.Log("skipping git commit - git not initialized") @@ -117,7 +117,7 @@ func (s *Store) reencrypt(ctx context.Context) error { } func (s *Store) reencryptGitPush(ctx context.Context) error { - if err := s.rcs.Push(ctx, "", ""); err != nil { + if err := s.storage.Push(ctx, "", ""); err != nil { if errors.Cause(err) == store.ErrGitNotInit { msg := "Warning: git is not initialized for this.storage. Ignoring auto-push option\n" + "Run: gopass git init" diff --git a/internal/store/leaf/store.go b/internal/store/leaf/store.go index 1dde770989..95f367acad 100644 --- a/internal/store/leaf/store.go +++ b/internal/store/leaf/store.go @@ -18,7 +18,6 @@ type Store struct { alias string path string crypto backend.Crypto - rcs backend.RCS storage backend.Storage } @@ -36,12 +35,6 @@ func Init(ctx context.Context, alias, path string) (*Store, error) { } s.storage = st - rcs, err := backend.InitRCS(ctx, backend.GetRCSBackend(ctx), path) - if err != nil { - return nil, err - } - s.rcs = rcs - crypto, err := backend.NewCrypto(ctx, backend.GetCryptoBackend(ctx)) if err != nil { return nil, err @@ -60,20 +53,17 @@ func New(ctx context.Context, alias, path string) (*Store, error) { path: path, } - // init store backend + // init storage and rcs backend if err := s.initStorageBackend(ctx); err != nil { return nil, errors.Wrapf(err, "failed to init storage backend: %s", err) } - // init sync backend - s.initRCSBackend(ctx) - // init crypto backend if err := s.initCryptoBackend(ctx); err != nil { return nil, errors.Wrapf(err, "failed to init crypto backend: %s", err) } - debug.Log("Instantiated %s at %s - storage: %+#v - rcs: %+#v - crypto: %+#v", alias, path, s.storage, s.rcs, s.crypto) + debug.Log("Instantiated %s at %s - storage: %+#v - crypto: %+#v", alias, path, s.storage, s.crypto) return s, nil } diff --git a/internal/store/leaf/store_test.go b/internal/store/leaf/store_test.go index b9e19f0acb..ce36a6330d 100644 --- a/internal/store/leaf/store_test.go +++ b/internal/store/leaf/store_test.go @@ -14,7 +14,6 @@ import ( _ "github.com/gopasspw/gopass/internal/backend/crypto" "github.com/gopasspw/gopass/internal/backend/crypto/plain" - _ "github.com/gopasspw/gopass/internal/backend/rcs" _ "github.com/gopasspw/gopass/internal/backend/storage" "github.com/stretchr/testify/assert" @@ -54,7 +53,7 @@ func createSubStore(dir string) (*Store, error) { ctx := context.Background() ctx = backend.WithCryptoBackendString(ctx, "plain") - ctx = backend.WithRCSBackendString(ctx, "noop") + ctx = backend.WithStorageBackendString(ctx, "fs") return New( ctx, "", @@ -154,12 +153,6 @@ func TestNew(t *testing.T) { ctx context.Context ok bool }{ - { - dsc: "InMem Storage", - dir: "//gopass/inmem", - ctx: backend.WithCryptoBackend(backend.WithStorageBackend(ctx, backend.InMem), backend.Plain), - ok: true, - }, { dsc: "Invalid Storage", ctx: backend.WithStorageBackend(ctx, -1), @@ -167,24 +160,17 @@ func TestNew(t *testing.T) { ok: true, }, { - dsc: "GitCLI RCS", + dsc: "GitFS Storage", dir: tempdir, - ctx: backend.WithCryptoBackend(backend.WithRCSBackend(ctx, backend.GitCLI), backend.Plain), + ctx: backend.WithCryptoBackend(backend.WithStorageBackend(ctx, backend.GitFS), backend.Plain), ok: true, }, { - dsc: "Noop RCS", + dsc: "FS Storage", dir: tempdir, - ctx: backend.WithCryptoBackend(backend.WithRCSBackend(ctx, backend.Noop), backend.Plain), + ctx: backend.WithCryptoBackend(backend.WithStorageBackend(ctx, backend.FS), backend.Plain), ok: true, }, - { - dsc: "Invalid RCS", - dir: tempdir, - ctx: backend.WithRCSBackend(ctx, -1), - // ok: false, // TODO clarify - ok: true, - }, { dsc: "GPG Crypto", dir: tempdir, diff --git a/internal/store/leaf/templates.go b/internal/store/leaf/templates.go index ee43386d1e..ebb17d761e 100644 --- a/internal/store/leaf/templates.go +++ b/internal/store/leaf/templates.go @@ -103,7 +103,7 @@ func (s *Store) SetTemplate(ctx context.Context, name string, content []byte) er return errors.Wrapf(err, "failed to write template") } - if err := s.rcs.Add(ctx, p); err != nil { + if err := s.storage.Add(ctx, p); err != nil { if errors.Cause(err) == store.ErrGitNotInit { return nil } @@ -125,7 +125,7 @@ func (s *Store) RemoveTemplate(ctx context.Context, name string) error { return errors.Wrapf(err, "failed to remote template") } - if err := s.rcs.Add(ctx, p); err != nil { + if err := s.storage.Add(ctx, p); err != nil { if errors.Cause(err) == store.ErrGitNotInit { return nil } diff --git a/internal/store/leaf/templates_test.go b/internal/store/leaf/templates_test.go index 5518e1b7cd..9bb1d92aaf 100644 --- a/internal/store/leaf/templates_test.go +++ b/internal/store/leaf/templates_test.go @@ -28,7 +28,7 @@ func TestTemplates(t *testing.T) { require.NoError(t, err) ctx = backend.WithCryptoBackendString(ctx, "plain") - ctx = backend.WithRCSBackendString(ctx, "noop") + ctx = backend.WithStorageBackendString(ctx, "fs") s, err := New( ctx, "", diff --git a/internal/store/leaf/write.go b/internal/store/leaf/write.go index 4a53f079cf..9e34fc9f6d 100644 --- a/internal/store/leaf/write.go +++ b/internal/store/leaf/write.go @@ -56,7 +56,7 @@ func (s *Store) Set(ctx context.Context, name string, sec gopass.Byter) error { return nil } - if err := s.rcs.Add(ctx, p); err != nil { + if err := s.storage.Add(ctx, p); err != nil { if errors.Cause(err) == store.ErrGitNotInit { return nil } @@ -77,7 +77,7 @@ func (s *Store) Set(ctx context.Context, name string, sec gopass.Byter) error { func (s *Store) gitCommitAndPush(ctx context.Context, name string) error { debug.Log("syncing with remote ...") - if err := s.rcs.Commit(ctx, fmt.Sprintf("Save secret to %s: %s", name, ctxutil.GetCommitMessage(ctx))); err != nil { + if err := s.storage.Commit(ctx, fmt.Sprintf("Save secret to %s: %s", name, ctxutil.GetCommitMessage(ctx))); err != nil { switch errors.Cause(err) { case store.ErrGitNotInit: debug.Log("commitAndPush - skipping git commit - git not initialized") @@ -88,7 +88,7 @@ func (s *Store) gitCommitAndPush(ctx context.Context, name string) error { } } - if err := s.rcs.Push(ctx, "", ""); err != nil { + if err := s.storage.Push(ctx, "", ""); err != nil { if errors.Cause(err) == store.ErrGitNotInit { msg := "Warning: git is not initialized for this.storage. Ignoring auto-push option\n" + "Run: gopass git init" diff --git a/internal/backend/storage/kv/inmem/store.go b/internal/store/mockstore/inmem/store.go similarity index 60% rename from internal/backend/storage/kv/inmem/store.go rename to internal/store/mockstore/inmem/store.go index 68a09a2951..e21cde316e 100644 --- a/internal/backend/storage/kv/inmem/store.go +++ b/internal/store/mockstore/inmem/store.go @@ -8,8 +8,10 @@ import ( "sort" "strings" "sync" + "time" "github.com/blang/semver" + "github.com/gopasspw/gopass/internal/backend" ) // InMem is a in-memory store @@ -132,3 +134,72 @@ func (m *InMem) Path() string { func (m *InMem) Fsck(ctx context.Context) error { return nil } + +// Add does nothing +func (m *InMem) Add(ctx context.Context, args ...string) error { + return nil +} + +// Commit does nothing +func (m *InMem) Commit(ctx context.Context, msg string) error { + return nil +} + +// Push does nothing +func (m *InMem) Push(ctx context.Context, origin, branch string) error { + return nil +} + +// Pull does nothing +func (m *InMem) Pull(ctx context.Context, origin, branch string) error { + return nil +} + +// Cmd does nothing +func (m *InMem) Cmd(ctx context.Context, name string, args ...string) error { + return nil +} + +// Init does nothing +func (m *InMem) Init(context.Context, string, string) error { + return nil +} + +// InitConfig does nothing +func (m *InMem) InitConfig(context.Context, string, string) error { + return nil +} + +// AddRemote does nothing +func (m *InMem) AddRemote(ctx context.Context, remote, url string) error { + return nil +} + +// RemoveRemote does nothing +func (m *InMem) RemoveRemote(ctx context.Context, remote string) error { + return nil +} + +// Revisions is not implemented +func (m *InMem) Revisions(context.Context, string) ([]backend.Revision, error) { + return []backend.Revision{ + { + Hash: "latest", + Date: time.Now(), + }}, nil +} + +// GetRevision is not implemented +func (m *InMem) GetRevision(context.Context, string, string) ([]byte, error) { + return []byte("foo\nbar"), nil +} + +// Status is not implemented +func (m *InMem) Status(context.Context) ([]byte, error) { + return []byte(""), nil +} + +// Compact is not implemented +func (m *InMem) Compact(context.Context) error { + return nil +} diff --git a/internal/store/mockstore/store.go b/internal/store/mockstore/store.go index 017d8ad7c0..a6885e46a6 100644 --- a/internal/store/mockstore/store.go +++ b/internal/store/mockstore/store.go @@ -6,8 +6,7 @@ import ( "github.com/gopasspw/gopass/internal/backend" "github.com/gopasspw/gopass/internal/backend/crypto/plain" - "github.com/gopasspw/gopass/internal/backend/rcs/noop" - "github.com/gopasspw/gopass/internal/backend/storage/kv/inmem" + "github.com/gopasspw/gopass/internal/store/mockstore/inmem" "github.com/gopasspw/gopass/internal/tree" "github.com/gopasspw/gopass/pkg/gopass" "github.com/gopasspw/gopass/pkg/gopass/secret/secparse" @@ -117,11 +116,6 @@ func (m *MockStore) URL() string { return "mockstore://" } -// RCS does nothing -func (m *MockStore) RCS() backend.RCS { - return noop.New() -} - // Crypto does nothing func (m *MockStore) Crypto() backend.Crypto { return plain.New() diff --git a/internal/store/root/convert.go b/internal/store/root/convert.go index 5ae20cf1dd..18156b1c9a 100644 --- a/internal/store/root/convert.go +++ b/internal/store/root/convert.go @@ -9,13 +9,13 @@ import ( // Convert will try to convert a given mount to a different set of // backends. -func (r *Store) Convert(ctx context.Context, name string, cryptoBe backend.CryptoBackend, rcsBe backend.RCSBackend, storageBe backend.StorageBackend, move bool) error { +func (r *Store) Convert(ctx context.Context, name string, cryptoBe backend.CryptoBackend, storageBe backend.StorageBackend, move bool) error { _, sub, err := r.GetSubStore(ctx, name) if err != nil { return err } - debug.Log("converting %s to crypto: %s, rcs: %s, storage: %s", name, cryptoBe, rcsBe, storageBe) - if err := sub.Convert(ctx, cryptoBe, rcsBe, storageBe, move); err != nil { + debug.Log("converting %s to crypto: %s, rcs: %s, storage: %s", name, cryptoBe, storageBe) + if err := sub.Convert(ctx, cryptoBe, storageBe, move); err != nil { return err } if name == "" { diff --git a/internal/store/root/init.go b/internal/store/root/init.go index cf8ca190bf..2878e7e41b 100644 --- a/internal/store/root/init.go +++ b/internal/store/root/init.go @@ -28,9 +28,6 @@ func (r *Store) Init(ctx context.Context, alias, path string, ids ...string) err if !backend.HasCryptoBackend(ctx) { ctx = backend.WithCryptoBackend(ctx, backend.GPGCLI) } - if !backend.HasRCSBackend(ctx) { - ctx = backend.WithRCSBackend(ctx, backend.GitCLI) - } if !backend.HasStorageBackend(ctx) { ctx = backend.WithStorageBackend(ctx, backend.FS) } diff --git a/internal/store/root/move.go b/internal/store/root/move.go index 4ae9e510b9..4cff563d95 100644 --- a/internal/store/root/move.go +++ b/internal/store/root/move.go @@ -42,7 +42,7 @@ func (r *Store) move(ctx context.Context, from, to string, delete bool) error { if err := r.moveFromTo(ctxFrom, ctxTo, subFrom, from, to, fromPrefix, srcIsDir, delete); err != nil { return err } - if err := subFrom.RCS().Commit(ctxFrom, fmt.Sprintf("Move from %s to %s", from, to)); delete && err != nil { + if err := subFrom.Storage().Commit(ctxFrom, fmt.Sprintf("Move from %s to %s", from, to)); delete && err != nil { switch errors.Cause(err) { case store.ErrGitNotInit: debug.Log("skipping git commit - git not initialized") @@ -51,7 +51,7 @@ func (r *Store) move(ctx context.Context, from, to string, delete bool) error { } } if !subFrom.Equals(subTo) { - if err := subTo.RCS().Commit(ctxTo, fmt.Sprintf("Move from %s to %s", from, to)); err != nil { + if err := subTo.Storage().Commit(ctxTo, fmt.Sprintf("Move from %s to %s", from, to)); err != nil { switch errors.Cause(err) { case store.ErrGitNotInit: debug.Log("skipping git commit - git not initialized") @@ -61,7 +61,7 @@ func (r *Store) move(ctx context.Context, from, to string, delete bool) error { } } - if err := subFrom.RCS().Push(ctx, "", ""); err != nil { + if err := subFrom.Storage().Push(ctx, "", ""); err != nil { if errors.Cause(err) == store.ErrGitNotInit { msg := "Warning: git is not initialized for this.storage. Ignoring auto-push option\n" + "Run: gopass git init" @@ -77,7 +77,7 @@ func (r *Store) move(ctx context.Context, from, to string, delete bool) error { return errors.Wrapf(err, "failed to push change to git remote") } if !subFrom.Equals(subTo) { - if err := subTo.RCS().Push(ctx, "", ""); err != nil { + if err := subTo.Storage().Push(ctx, "", ""); err != nil { if errors.Cause(err) == store.ErrGitNotInit { msg := "Warning: git is not initialized for this.storage. Ignoring auto-push option\n" + "Run: gopass git init" diff --git a/internal/store/root/rcs.go b/internal/store/root/rcs.go index 7074e93b0e..b086a33708 100644 --- a/internal/store/root/rcs.go +++ b/internal/store/root/rcs.go @@ -9,15 +9,6 @@ import ( "github.com/gopasspw/gopass/pkg/gopass" ) -// RCS returns the sync backend -func (r *Store) RCS(ctx context.Context, name string) backend.RCS { - _, sub, _ := r.getStore(ctx, name) - if sub == nil || !sub.Valid() { - return nil - } - return sub.RCS() -} - // RCSInit initializes the version control repo func (r *Store) RCSInit(ctx context.Context, name, userName, userEmail string) error { ctx, store, _ := r.getStore(ctx, name) @@ -29,31 +20,31 @@ func (r *Store) RCSInit(ctx context.Context, name, userName, userEmail string) e // RCSInitConfig initializes the git repos local config func (r *Store) RCSInitConfig(ctx context.Context, name, userName, userEmail string) error { ctx, store, _ := r.getStore(ctx, name) - return store.RCS().InitConfig(ctx, userName, userEmail) + return store.Storage().InitConfig(ctx, userName, userEmail) } // RCSAddRemote adds a git remote func (r *Store) RCSAddRemote(ctx context.Context, name, remote, url string) error { ctx, store, _ := r.getStore(ctx, name) - return store.RCS().AddRemote(ctx, remote, url) + return store.Storage().AddRemote(ctx, remote, url) } // RCSRemoveRemote removes a git remote func (r *Store) RCSRemoveRemote(ctx context.Context, name, remote string) error { ctx, store, _ := r.getStore(ctx, name) - return store.RCS().RemoveRemote(ctx, remote) + return store.Storage().RemoveRemote(ctx, remote) } // RCSPull performs a git pull func (r *Store) RCSPull(ctx context.Context, name, origin, remote string) error { ctx, store, _ := r.getStore(ctx, name) - return store.RCS().Pull(ctx, origin, remote) + return store.Storage().Pull(ctx, origin, remote) } // RCSPush performs a git push func (r *Store) RCSPush(ctx context.Context, name, origin, remote string) error { ctx, store, _ := r.getStore(ctx, name) - return store.RCS().Push(ctx, origin, remote) + return store.Storage().Push(ctx, origin, remote) } // ListRevisions will list all revisions for the named entity diff --git a/internal/store/root/rcs_test.go b/internal/store/root/rcs_test.go index 9cc5bc3323..ac1789899d 100644 --- a/internal/store/root/rcs_test.go +++ b/internal/store/root/rcs_test.go @@ -24,7 +24,6 @@ func TestRCS(t *testing.T) { rs, err := createRootStore(ctx, u) require.NoError(t, err) - assert.NotNil(t, rs.RCS(ctx, "")) assert.NoError(t, rs.RCSStatus(ctx, "")) revs, err := rs.ListRevisions(ctx, "foo") diff --git a/internal/store/root/store_test.go b/internal/store/root/store_test.go index fd001b07ce..56fafc3675 100644 --- a/internal/store/root/store_test.go +++ b/internal/store/root/store_test.go @@ -11,7 +11,6 @@ import ( "github.com/gopasspw/gopass/internal/gptest" _ "github.com/gopasspw/gopass/internal/backend/crypto" - _ "github.com/gopasspw/gopass/internal/backend/rcs" _ "github.com/gopasspw/gopass/internal/backend/storage" "github.com/stretchr/testify/assert" @@ -35,7 +34,6 @@ func TestSimpleList(t *testing.T) { func TestListMulti(t *testing.T) { ctx := context.Background() ctx = backend.WithCryptoBackend(ctx, backend.Plain) - ctx = backend.WithRCSBackend(ctx, backend.Noop) u := gptest.NewUnitTester(t) defer u.Remove() @@ -76,7 +74,6 @@ func TestListMulti(t *testing.T) { func TestListNested(t *testing.T) { ctx := context.Background() ctx = backend.WithCryptoBackend(ctx, backend.Plain) - ctx = backend.WithRCSBackend(ctx, backend.Noop) u := gptest.NewUnitTester(t) defer u.Remove() @@ -125,7 +122,6 @@ func TestListNested(t *testing.T) { } func createRootStore(ctx context.Context, u *gptest.Unit) (*Store, error) { - ctx = backend.WithRCSBackendString(ctx, "noop") ctx = backend.WithCryptoBackendString(ctx, "plain") s := New( &config.Config{ diff --git a/main.go b/main.go index 575b2c8e8a..81aae9c942 100644 --- a/main.go +++ b/main.go @@ -17,7 +17,6 @@ import ( "github.com/gopasspw/gopass/internal/action/xc" _ "github.com/gopasspw/gopass/internal/backend/crypto" "github.com/gopasspw/gopass/internal/backend/crypto/gpg" - _ "github.com/gopasspw/gopass/internal/backend/rcs" _ "github.com/gopasspw/gopass/internal/backend/storage" "github.com/gopasspw/gopass/internal/queue" "github.com/gopasspw/gopass/pkg/ctxutil" diff --git a/main_test.go b/main_test.go index 27f561faa1..47189fbfb4 100644 --- a/main_test.go +++ b/main_test.go @@ -109,7 +109,6 @@ func TestGetCommands(t *testing.T) { ctx = ctxutil.WithInteractive(ctx, false) ctx = ctxutil.WithTerminal(ctx, false) ctx = out.WithHidden(ctx, true) - ctx = backend.WithRCSBackendString(ctx, "noop") ctx = backend.WithCryptoBackendString(ctx, "plain") act, err := action.New(cfg, semver.Version{}) diff --git a/pkg/gopass/api/api.go b/pkg/gopass/api/api.go index e16755fedc..1deec33fed 100644 --- a/pkg/gopass/api/api.go +++ b/pkg/gopass/api/api.go @@ -5,7 +5,6 @@ import ( "fmt" _ "github.com/gopasspw/gopass/internal/backend/crypto" // load crypto backends - _ "github.com/gopasspw/gopass/internal/backend/rcs" // load rcs backends _ "github.com/gopasspw/gopass/internal/backend/storage" // load storage backends "github.com/gopasspw/gopass/internal/config" "github.com/gopasspw/gopass/internal/queue" diff --git a/tests/tester.go b/tests/tester.go index fe029ccaa8..2a1077f452 100644 --- a/tests/tester.go +++ b/tests/tester.go @@ -12,6 +12,7 @@ import ( "strings" "testing" + "github.com/gopasspw/gopass/internal/gptest" shellquote "github.com/kballard/go-shellquote" "github.com/pkg/errors" "github.com/stretchr/testify/require" @@ -34,6 +35,7 @@ type tester struct { Binary string sourceDir string tempDir string + resetFn func() } func newTester(t *testing.T) *tester { @@ -69,11 +71,13 @@ func newTester(t *testing.T) *tester { ts.tempDir = td // prepare ENVIRONMENT + ts.resetFn = gptest.UnsetVars("GNUPGHOME", "GOPASS_DEBUG", "GOPASS_NOCOLOR", "GOPASS_CONFIG", "GOPASS_NO_NOTIFY", "GOPASS_HOMEDIR") require.NoError(t, os.Setenv("GNUPGHOME", ts.gpgDir())) require.NoError(t, os.Setenv("GOPASS_DEBUG", "")) require.NoError(t, os.Setenv("GOPASS_NOCOLOR", "true")) require.NoError(t, os.Setenv("GOPASS_CONFIG", ts.gopassConfig())) require.NoError(t, os.Setenv("GOPASS_NO_NOTIFY", "true")) + require.NoError(t, os.Setenv("GOPASS_HOMEDIR", td)) // write config require.NoError(t, os.MkdirAll(filepath.Dir(ts.gopassConfig()), 0700)) @@ -119,6 +123,7 @@ func (ts tester) workDir() string { } func (ts tester) teardown() { + ts.resetFn() // restore env vars if ts.tempDir == "" { return } @@ -188,7 +193,7 @@ func (ts tester) runWithInputReader(arg string, input io.Reader) ([]byte, error) } func (ts *tester) initStore() { - out, err := ts.run("init --crypto=gpgcli --rcs=noop " + keyID) + out, err := ts.run("init --crypto=gpgcli --storage=fs " + keyID) require.NoError(ts.t, err, "failed to init password store:\n%s", out) }