diff --git a/Makefile b/Makefile index 563c8175a1..304ab65c04 100644 --- a/Makefile +++ b/Makefile @@ -147,7 +147,7 @@ codequality: $(GO) get -u github.com/fzipp/gocyclo; \ fi @$(foreach gofile, $(GOFILES_NOVENDOR),\ - gocyclo -over 20 $(gofile) || exit 1;) + gocyclo -over 22 $(gofile) || exit 1;) @$(call ok) @echo -n " LINT " diff --git a/action/action_test.go b/action/action_test.go index 8c07db8a3e..b2446525c2 100644 --- a/action/action_test.go +++ b/action/action_test.go @@ -16,7 +16,7 @@ import ( func newMock(ctx context.Context, u *gptest.Unit) (*Action, error) { cfg := config.New() - cfg.Root.Path = u.StoreDir("") + cfg.Root.Path = backend.FromPath(u.StoreDir("")) ctx = backend.WithSyncBackendString(ctx, "gitmock") ctx = backend.WithCryptoBackendString(ctx, "gpgmock") @@ -50,7 +50,7 @@ func TestNew(t *testing.T) { _, err = New(ctx, cfg, sv) assert.Error(t, err) - cfg.Root.Path = filepath.Join(td, "store") + cfg.Root.Path = backend.FromPath(filepath.Join(td, "store")) _, err = New(ctx, cfg, sv) assert.NoError(t, err) } diff --git a/action/clone.go b/action/clone.go index 82ad426832..0f729a4d5d 100644 --- a/action/clone.go +++ b/action/clone.go @@ -70,13 +70,13 @@ func (s *Action) clone(ctx context.Context, repo, mount, path string) error { return exitError(ctx, ExitMount, err, "Failed to add mount: %s", err) } out.Green(ctx, "Mounted password store %s at mount point `%s` ...", path, mount) - s.cfg.Mounts[mount].CryptoBackend = backend.CryptoBackendName(backend.GetCryptoBackend(ctx)) - s.cfg.Mounts[mount].SyncBackend = backend.SyncBackendName(backend.GetSyncBackend(ctx)) - s.cfg.Mounts[mount].StoreBackend = backend.StoreBackendName(backend.GetStoreBackend(ctx)) + s.cfg.Mounts[mount].Path.Crypto = backend.GetCryptoBackend(ctx) + s.cfg.Mounts[mount].Path.Sync = backend.GetSyncBackend(ctx) + s.cfg.Mounts[mount].Path.Store = backend.GetStoreBackend(ctx) } else { - s.cfg.Root.CryptoBackend = backend.CryptoBackendName(backend.GetCryptoBackend(ctx)) - s.cfg.Root.SyncBackend = backend.SyncBackendName(backend.GetSyncBackend(ctx)) - s.cfg.Root.StoreBackend = backend.StoreBackendName(backend.GetStoreBackend(ctx)) + s.cfg.Root.Path.Crypto = backend.GetCryptoBackend(ctx) + s.cfg.Root.Path.Sync = backend.GetSyncBackend(ctx) + s.cfg.Root.Path.Store = backend.GetStoreBackend(ctx) } // save new mount in config file diff --git a/action/config_test.go b/action/config_test.go index 2075bb3dd4..42cb790a9c 100644 --- a/action/config_test.go +++ b/action/config_test.go @@ -8,6 +8,7 @@ import ( "strings" "testing" + "github.com/justwatchcom/gopass/backend" "github.com/justwatchcom/gopass/config" "github.com/justwatchcom/gopass/tests/gptest" "github.com/justwatchcom/gopass/utils/out" @@ -41,16 +42,13 @@ func TestConfig(t *testing.T) { autoimport: true autosync: true cliptimeout: 45 - cryptobackend: gpg nocolor: false noconfirm: false nopager: false notifications: true ` - want += " path: " + u.StoreDir("") + "\n" + want += " path: " + backend.FromPath(u.StoreDir("")).String() + "\n" want += ` safecontent: false - storebackend: fs - syncbackend: git usesymbols: false ` assert.Equal(t, want, buf.String()) @@ -58,7 +56,7 @@ func TestConfig(t *testing.T) { // action.setConfigValue assert.NoError(t, act.setConfigValue(ctx, "", "nopager", "true")) - assert.Equal(t, "nopager: true", strings.TrimSpace(buf.String())) + assert.Equal(t, "nopager: true", strings.TrimSpace(buf.String()), "action.setConfigValue") buf.Reset() // action.printConfigValues @@ -66,12 +64,12 @@ func TestConfig(t *testing.T) { act.printConfigValues(ctx, "", "nopager") want = `nopager: true foo/nopager: false` - assert.Equal(t, want, strings.TrimSpace(buf.String())) + assert.Equal(t, want, strings.TrimSpace(buf.String()), "action.printConfigValues") buf.Reset() // action.setConfigValue on substore assert.NoError(t, act.setConfigValue(ctx, "foo", "cliptimeout", "23")) - assert.Equal(t, "foo/cliptimeout: 23", strings.TrimSpace(buf.String())) + assert.Equal(t, "foo/cliptimeout: 23", strings.TrimSpace(buf.String()), "action.setConfigValue on substore") buf.Reset() // action.printConfigValues @@ -81,16 +79,13 @@ foo/nopager: false` autoimport: true autosync: true cliptimeout: 45 - cryptobackend: gpg nocolor: false noconfirm: false nopager: true notifications: true ` - want += " path: " + u.StoreDir("") + "\n" + want += " path: " + backend.FromPath(u.StoreDir("")).String() + "\n" want += ` safecontent: false - storebackend: fs - syncbackend: git usesymbols: false mount 'foo' config: autoimport: false @@ -98,8 +93,9 @@ mount 'foo' config: cliptimeout: 23 nopager: false notifications: false - path:` - assert.Equal(t, want, strings.TrimSpace(buf.String())) +` + want += " path: " + backend.FromPath("").String() + assert.Equal(t, want, strings.TrimSpace(buf.String()), "action.setConfigValues") buf.Reset() delete(act.cfg.Mounts, "foo") @@ -127,15 +123,12 @@ mount 'foo' config: autoimport autosync cliptimeout -cryptobackend nocolor noconfirm nopager notifications path safecontent -storebackend -syncbackend usesymbols ` assert.Equal(t, want, buf.String()) diff --git a/action/history_test.go b/action/history_test.go index 324f03b332..1d7028797a 100644 --- a/action/history_test.go +++ b/action/history_test.go @@ -27,7 +27,7 @@ func TestHistory(t *testing.T) { ctx = backend.WithCryptoBackend(ctx, backend.GPGMock) cfg := config.New() - cfg.Root.Path = u.StoreDir("") + cfg.Root.Path = backend.FromPath(u.StoreDir("")) act, err := newAction(ctx, cfg, semver.Version{}) assert.NoError(t, err) diff --git a/backend/context.go b/backend/context.go index 61a3fd25ab..753c1bf19f 100644 --- a/backend/context.go +++ b/backend/context.go @@ -12,36 +12,15 @@ const ( // CryptoBackendName returns the name of the given backend func CryptoBackendName(cb CryptoBackend) string { - switch cb { - case GPGMock: - return "gpgmock" - case GPGCLI: - return "gpgcli" - case XC: - return "xc" - case OpenPGP: - return "openpgp" - default: - return "" - } + return cryptoNameFromBackend(cb) } // WithCryptoBackendString returns a context with the given crypto backend set func WithCryptoBackendString(ctx context.Context, be string) context.Context { - switch be { - case "gpg": - fallthrough - case "gpgcli": - return WithCryptoBackend(ctx, GPGCLI) - case "gpgmock": - return WithCryptoBackend(ctx, GPGMock) - case "xc": - return WithCryptoBackend(ctx, XC) - case "openpgp": - return WithCryptoBackend(ctx, OpenPGP) - default: - return ctx + if cb := cryptoBackendFromName(be); cb >= 0 { + ctx = WithCryptoBackend(ctx, cb) } + return ctx } // WithCryptoBackend returns a context with the given crypto backend set @@ -66,32 +45,15 @@ func GetCryptoBackend(ctx context.Context) CryptoBackend { // SyncBackendName returns the name of the given backend func SyncBackendName(sb SyncBackend) string { - switch sb { - case GitMock: - return "gitmock" - case GitCLI: - return "gitcli" - case GoGit: - return "gogit" - default: - return "" - } + return syncNameFromBackend(sb) } // WithSyncBackendString returns a context with the given sync backend set func WithSyncBackendString(ctx context.Context, sb string) context.Context { - switch sb { - case "git": - fallthrough - case "gitcli": - return WithSyncBackend(ctx, GitCLI) - case "gogit": - return WithSyncBackend(ctx, GoGit) - case "gitmock": - return WithSyncBackend(ctx, GitMock) - default: - return WithSyncBackend(ctx, GitMock) + if be := syncBackendFromName(sb); be >= 0 { + return WithSyncBackend(ctx, be) } + return WithSyncBackend(ctx, GitMock) } // WithSyncBackend returns a context with the given sync backend set @@ -116,14 +78,7 @@ func GetSyncBackend(ctx context.Context) SyncBackend { // WithStoreBackendString returns a context with the given store backend set func WithStoreBackendString(ctx context.Context, sb string) context.Context { - switch sb { - case "kvmock": - return WithStoreBackend(ctx, KVMock) - case "fs": - return WithStoreBackend(ctx, FS) - default: - return WithStoreBackend(ctx, FS) - } + return WithStoreBackend(ctx, storeBackendFromName(sb)) } // WithStoreBackend returns a context with the given store backend set @@ -140,14 +95,13 @@ func GetStoreBackend(ctx context.Context) StoreBackend { return be } +// HasStoreBackend returns true if a value for store backend was set +func HasStoreBackend(ctx context.Context) bool { + _, ok := ctx.Value(ctxKeyStoreBackend).(StoreBackend) + return ok +} + // StoreBackendName returns the name of the given backend func StoreBackendName(sb StoreBackend) string { - switch sb { - case FS: - return "fs" - case KVMock: - return "kvmock" - default: - return "" - } + return storeNameFromBackend(sb) } diff --git a/backend/crypto.go b/backend/crypto.go index d47bf55d6d..7351f64bff 100644 --- a/backend/crypto.go +++ b/backend/crypto.go @@ -20,6 +20,10 @@ const ( OpenPGP ) +func (c CryptoBackend) String() string { + return cryptoNameFromBackend(c) +} + // Keyring is a public/private key manager type Keyring interface { ImportPublicKey(ctx context.Context, key []byte) error diff --git a/backend/crypto/gpg/cli/binary.go b/backend/crypto/gpg/cli/binary.go index 83c7da9365..b3515f4344 100644 --- a/backend/crypto/gpg/cli/binary.go +++ b/backend/crypto/gpg/cli/binary.go @@ -25,13 +25,13 @@ func Binary(ctx context.Context, bin string) (string, error) { } bv := make(byVersion, 0, len(bins)) for _, b := range bins { - out.Debug(ctx, "gpg.detectBinary - Looking for '%s' ...", b) + // TODO out.Debug(ctx, "gpg.detectBinary - Looking for '%s' ...", b) if p, err := exec.LookPath(b); err == nil { gb := gpgBin{ path: p, ver: version(ctx, p), } - out.Debug(ctx, "gpg.detectBinary - Found '%s' at '%s' (%s)", b, p, gb.ver.String()) + // TODO out.Debug(ctx, "gpg.detectBinary - Found '%s' at '%s' (%s)", b, p, gb.ver.String()) bv = append(bv, gb) } } diff --git a/backend/store.go b/backend/store.go index 56d213ee3a..9d65adf5ad 100644 --- a/backend/store.go +++ b/backend/store.go @@ -10,12 +10,16 @@ import ( type StoreBackend int const ( - // KVMock is an in-memory mock store for tests - KVMock StoreBackend = iota // FS is a filesystem-backend storage - FS + FS StoreBackend = iota + // KVMock is an in-memory mock store for tests + KVMock ) +func (s StoreBackend) String() string { + return storeNameFromBackend(s) +} + // Store is an storage backend type Store interface { Get(ctx context.Context, name string) ([]byte, error) diff --git a/backend/strings.go b/backend/strings.go new file mode 100644 index 0000000000..c6fccb0986 --- /dev/null +++ b/backend/strings.go @@ -0,0 +1,76 @@ +package backend + +var ( + cryptoNameToBackendMap = map[string]CryptoBackend{ + "gpgmock": GPGMock, + "gpgcli": GPGCLI, + "xc": XC, + "openpgp": OpenPGP, + } + cryptoBackendToNameMap = map[CryptoBackend]string{} + syncNameToBackendMap = map[string]SyncBackend{ + "gitcli": GitCLI, + "gitmock": GitMock, + "gogit": GoGit, + } + syncBackendToNameMap = map[SyncBackend]string{} + storeNameToBackendMap = map[string]StoreBackend{ + "kvmock": KVMock, + "fs": FS, + } + storeBackendToNameMap = map[StoreBackend]string{} +) + +func init() { + for k, v := range cryptoNameToBackendMap { + cryptoBackendToNameMap[v] = k + } + for k, v := range syncNameToBackendMap { + syncBackendToNameMap[v] = k + } + for k, v := range storeNameToBackendMap { + storeBackendToNameMap[v] = k + } +} + +func cryptoBackendFromName(name string) CryptoBackend { + if b, found := cryptoNameToBackendMap[name]; found { + return b + } + return -1 +} + +func cryptoNameFromBackend(be CryptoBackend) string { + if b, found := cryptoBackendToNameMap[be]; found { + return b + } + return "" +} + +func syncBackendFromName(name string) SyncBackend { + if b, found := syncNameToBackendMap[name]; found { + return b + } + return -1 +} + +func syncNameFromBackend(be SyncBackend) string { + if b, found := syncBackendToNameMap[be]; found { + return b + } + return "" +} + +func storeBackendFromName(name string) StoreBackend { + if b, found := storeNameToBackendMap[name]; found { + return b + } + return FS +} + +func storeNameFromBackend(be StoreBackend) string { + if b, found := storeBackendToNameMap[be]; found { + return b + } + return "" +} diff --git a/backend/sync.go b/backend/sync.go index a68f72ad35..f4d1bad11b 100644 --- a/backend/sync.go +++ b/backend/sync.go @@ -19,6 +19,10 @@ const ( GoGit ) +func (s SyncBackend) String() string { + return syncNameFromBackend(s) +} + // Sync is a sync backend type Sync interface { Add(ctx context.Context, args ...string) error diff --git a/backend/url.go b/backend/url.go new file mode 100644 index 0000000000..087315270e --- /dev/null +++ b/backend/url.go @@ -0,0 +1,130 @@ +package backend + +import ( + "fmt" + "net/url" + "strings" +) + +// URL is a parsed backend URL +type URL struct { + url *url.URL + + Crypto CryptoBackend + Sync SyncBackend + Store StoreBackend + Scheme string + Path string + Username string + Password string + Query url.Values +} + +// FromPath returns a new backend URL with the given path +// and default backends (GitCLI, GPGCLI, FS) +func FromPath(path string) *URL { + return &URL{ + Crypto: GPGCLI, + Sync: GitCLI, + Store: FS, + Scheme: "file", + Path: path, + } +} + +// ParseURL attempts to parse an backend URL +func ParseURL(us string) (*URL, error) { + // if it's no URL build file URL and parse that + nu, err := url.Parse(us) + if err != nil { + nu, err = url.Parse("gpgcli-gitcli-fs+file://" + us) + if err != nil { + return nil, err + } + } + u := &URL{ + url: nu, + } + if err := u.parseScheme(); err != nil { + return u, err + } + u.Path = nu.Path + if nu.User != nil { + u.Username = nu.User.Username() + u.Password, _ = nu.User.Password() + } + u.Query = nu.Query() + return u, nil +} + +// String implements fmt.Stringer +func (u *URL) String() string { + if u.url == nil { + u.url = &url.URL{} + } + + scheme := u.Scheme + if scheme == "" { + scheme = "file" + } + u.url.Scheme = fmt.Sprintf( + "%s-%s-%s+%s", + u.Crypto, + u.Sync, + u.Store, + scheme, + ) + u.url.Path = u.Path + if u.Username != "" { + u.url.User = url.UserPassword(u.Username, u.Password) + } + u.url.RawQuery = u.Query.Encode() + return u.url.String() +} + +func (u *URL) parseScheme() error { + crypto, sync, store, scheme, err := splitBackends(u.url.Scheme) + if err != nil { + return err + } + + u.Crypto = cryptoBackendFromName(crypto) + u.Sync = syncBackendFromName(sync) + u.Store = storeBackendFromName(store) + u.Scheme = scheme + + return nil +} + +// MarshalYAML implements yaml.Marshaler +func (u *URL) MarshalYAML() (interface{}, error) { + return u.String(), nil +} + +// UnmarshalYAML implements yaml.Unmarshaler +func (u *URL) UnmarshalYAML(umf func(interface{}) error) error { + path := "" + if err := umf(&path); err != nil { + return err + } + um, err := ParseURL(path) + if err != nil { + return err + } + *u = *um + return nil +} + +func splitBackends(in string) (string, string, string, string, error) { + p := strings.Split(in, "+") + if len(p) < 2 { + return "gpgcli", "gitcli", "fs", "file", nil + } + backends := p[0] + scheme := p[1] + p = strings.Split(backends, "-") + if len(p) < 3 { + return "", "", "", "", fmt.Errorf("invalid") + } + return p[0], p[1], p[2], scheme, nil +} diff --git a/backend/url_test.go b/backend/url_test.go new file mode 100644 index 0000000000..de6adac9f4 --- /dev/null +++ b/backend/url_test.go @@ -0,0 +1,129 @@ +package backend + +import ( + "testing" + + yaml "gopkg.in/yaml.v2" + + "github.com/stretchr/testify/assert" +) + +/* + +url = [scheme:][//[userinfo@]host][/]path[?query][#fragment] +letter = "a" ... "z" ; +digit = "0" ... "9" ; +backend = letter , { letter | digit } ; +backends = backend , "-" , backend , "-" , backend +path = backends , "+" , url + +- format (all mandatory) +crypto-sync-store+url + +- examples +gpgcli-gitcli-fs+file:///tmp/foo +xc-gitmock-consul+http://localhost:8500/v1/foo/bar +xc-gitmock-consul+https://localhost:8500/v1/foo/bar +file:///tmp/foo -> gpgcli, gitcli, fs (using defaults) +/tmp/foo -> gpgcli, gitcli, fs (using defaults) + +*/ + +func TestURLString(t *testing.T) { + for _, tc := range []struct { + name string + in *URL + out string + }{ + { + name: "defaults", + in: &URL{}, + out: "gpgmock-gitmock-fs+file:", + }, + { + name: "xc+gogit", + in: &URL{ + Crypto: XC, + Sync: GoGit, + Store: FS, + Path: "/tmp/foo", + }, + out: "xc-gogit-fs+file:///tmp/foo", + }, + } { + assert.Equal(t, tc.out, tc.in.String(), tc.name) + } +} + +func TestParseScheme(t *testing.T) { + for _, tc := range []struct { + Name string + URL string + Crypto CryptoBackend + Sync SyncBackend + Store StoreBackend + }{ + { + Name: "legacy file path", + URL: "/tmp/foo", + Crypto: GPGCLI, + Sync: GitCLI, + Store: FS, + }, + { + Name: "XC+gogit+file", + URL: "xc-gogit-fs+file:///tmp/foo", + Crypto: XC, + Sync: GoGit, + Store: FS, + }, + //{ + // URL: "xc+consul://localhost:8500/api/v1/foo/bar?token=bla", + // Crypto: XC, + // Sync: GitMock, + // Store: Consul, + //}, + //{ + // URL: "vaults://localhost:9600/foo/bar", + // Crypto: GPGMock, + // Sync: GitMock, + // Store: Vault, + //} + } { + u, err := ParseURL(tc.URL) + assert.NoError(t, err) + assert.NotNil(t, u) + assert.Equal(t, tc.Crypto, u.Crypto) + assert.Equal(t, tc.Sync, u.Sync) + assert.Equal(t, tc.Store, u.Store) + } +} + +type testConfig struct { + Path *URL `yaml:"path"` +} + +func TestUnmarshalYAML(t *testing.T) { + in := `--- +path: xc-gogit-fs+file:///tmp/foo +` + cfg := testConfig{} + assert.NoError(t, yaml.Unmarshal([]byte(in), &cfg)) + assert.Equal(t, "/tmp/foo", cfg.Path.Path) +} + +func TestMarshalYAML(t *testing.T) { + out := `path: xc-gogit-fs+file:///tmp/foo +` + cfg := testConfig{ + Path: &URL{ + Crypto: XC, + Sync: GoGit, + Store: FS, + Path: "/tmp/foo", + }, + } + buf, err := yaml.Marshal(&cfg) + assert.NoError(t, err) + assert.Equal(t, out, string(buf)) +} diff --git a/commands_test.go b/commands_test.go index 49953dda17..79972262c2 100644 --- a/commands_test.go +++ b/commands_test.go @@ -70,7 +70,7 @@ func TestGetCommands(t *testing.T) { }() cfg := config.New() - cfg.Root.Path = u.StoreDir("") + cfg.Root.Path = backend.FromPath(u.StoreDir("")) clipboard.Unsupported = true diff --git a/config/config.go b/config/config.go index 6e58688a6a..127c694d96 100644 --- a/config/config.go +++ b/config/config.go @@ -40,13 +40,10 @@ func New() *Config { AutoImport: true, AutoSync: true, ClipTimeout: 45, - CryptoBackend: "gpg", NoColor: false, NoConfirm: false, NoPager: false, SafeContent: false, - StoreBackend: "fs", - SyncBackend: "git", UseSymbols: false, Notifications: true, }, @@ -83,33 +80,22 @@ func (c *Config) SetConfigValue(mount, key, value string) error { return c.Save() } -func (c *Config) checkDefaults() { +func (c *Config) checkDefaults() error { if c == nil { - return + return fmt.Errorf("config is nil") } if c.Root == nil { c.Root = &StoreConfig{} } - if c.Root.CryptoBackend == "" { - c.Root.CryptoBackend = "gpg" - } - if c.Root.SyncBackend == "" { - c.Root.SyncBackend = "git" - } - if c.Root.StoreBackend == "" { - c.Root.StoreBackend = "fs" + if err := c.Root.checkDefaults(); err != nil { + return err } for _, sc := range c.Mounts { - if sc.CryptoBackend == "" { - sc.CryptoBackend = "gpg" - } - if sc.SyncBackend == "" { - sc.SyncBackend = "git" - } - if sc.StoreBackend == "" { - sc.StoreBackend = "fs" + if err := sc.checkDefaults(); err != nil { + return err } } + return nil } func (c *Config) String() string { diff --git a/config/config_test.go b/config/config_test.go index a0dc9b222a..2a0a0a10af 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -5,6 +5,7 @@ import ( "path/filepath" "testing" + "github.com/justwatchcom/gopass/backend" "github.com/stretchr/testify/assert" ) @@ -17,22 +18,22 @@ func TestNewConfig(t *testing.T) { cfg := New() assert.Equal(t, false, cfg.Root.AskForMore) - cfg.checkDefaults() - assert.Equal(t, "gpg", cfg.Root.CryptoBackend) - assert.Equal(t, "git", cfg.Root.SyncBackend) - assert.Equal(t, "fs", cfg.Root.StoreBackend) - assert.Equal(t, "Config[Root:StoreConfig[AskForMore:false,AutoImport:true,AutoSync:true,ClipTimeout:45,CryptoBackend:gpg,NoColor:false,NoConfirm:false,NoPager:false,Path:,SafeContent:false,SyncBackend:git,UseSymbols:false],Mounts(),Version:]", cfg.String()) + assert.NoError(t, cfg.checkDefaults()) + assert.Equal(t, backend.GPGCLI, cfg.Root.Path.Crypto) + assert.Equal(t, backend.GitCLI, cfg.Root.Path.Sync) + assert.Equal(t, backend.FS, cfg.Root.Path.Store) + assert.Equal(t, "Config[Root:StoreConfig[AskForMore:false,AutoImport:true,AutoSync:true,ClipTimeout:45,NoColor:false,NoConfirm:false,NoPager:false,Path:gpgcli-gitcli-fs+file:,SafeContent:false,UseSymbols:false],Mounts(),Version:]", cfg.String()) cfg = nil - cfg.checkDefaults() + assert.Error(t, cfg.checkDefaults()) cfg = &Config{ Mounts: make(map[string]*StoreConfig, 2), } cfg.Mounts["foo"] = &StoreConfig{} cfg.Mounts["bar"] = &StoreConfig{} - cfg.checkDefaults() - assert.Equal(t, "Config[Root:StoreConfig[AskForMore:false,AutoImport:false,AutoSync:false,ClipTimeout:0,CryptoBackend:gpg,NoColor:false,NoConfirm:false,NoPager:false,Path:,SafeContent:false,SyncBackend:git,UseSymbols:false],Mounts(bar=>StoreConfig[AskForMore:false,AutoImport:false,AutoSync:false,ClipTimeout:0,CryptoBackend:gpg,NoColor:false,NoConfirm:false,NoPager:false,Path:,SafeContent:false,SyncBackend:git,UseSymbols:false]foo=>StoreConfig[AskForMore:false,AutoImport:false,AutoSync:false,ClipTimeout:0,CryptoBackend:gpg,NoColor:false,NoConfirm:false,NoPager:false,Path:,SafeContent:false,SyncBackend:git,UseSymbols:false]),Version:]", cfg.String()) + assert.NoError(t, cfg.checkDefaults()) + assert.Equal(t, "Config[Root:StoreConfig[AskForMore:false,AutoImport:false,AutoSync:false,ClipTimeout:0,NoColor:false,NoConfirm:false,NoPager:false,Path:gpgcli-gitcli-fs+file:,SafeContent:false,UseSymbols:false],Mounts(bar=>StoreConfig[AskForMore:false,AutoImport:false,AutoSync:false,ClipTimeout:0,NoColor:false,NoConfirm:false,NoPager:false,Path:gpgcli-gitcli-fs+file:,SafeContent:false,UseSymbols:false]foo=>StoreConfig[AskForMore:false,AutoImport:false,AutoSync:false,ClipTimeout:0,NoColor:false,NoConfirm:false,NoPager:false,Path:gpgcli-gitcli-fs+file:,SafeContent:false,UseSymbols:false]),Version:]", cfg.String()) } func TestSetConfigValue(t *testing.T) { diff --git a/config/io.go b/config/io.go index d400414b6f..85a9bdb249 100644 --- a/config/io.go +++ b/config/io.go @@ -6,6 +6,7 @@ import ( "os" "path/filepath" + "github.com/justwatchcom/gopass/backend" "github.com/justwatchcom/gopass/utils/fsutil" "github.com/pkg/errors" "gopkg.in/yaml.v2" @@ -21,15 +22,15 @@ func Load() *Config { if err != nil { panic(err) } - cfg.checkDefaults() + _ = cfg.checkDefaults() if debug { fmt.Printf("[DEBUG] Loaded config from %s: %+v\n", l, cfg) } return cfg } cfg := New() - cfg.Root.Path = PwStoreDir("") - cfg.checkDefaults() + cfg.Root.Path = backend.FromPath(PwStoreDir("")) + _ = cfg.checkDefaults() if debug { fmt.Printf("[DEBUG] config.Load(): %+v\n", cfg) } @@ -108,11 +109,15 @@ func decode(buf []byte) (*Config, error) { // Save saves the config func (c *Config) Save() error { - c.checkDefaults() + if err := c.checkDefaults(); err != nil { + return err + } + buf, err := yaml.Marshal(c) if err != nil { return errors.Wrapf(err, "failed to marshal YAML") } + cfgLoc := configLocation() cfgDir := filepath.Dir(cfgLoc) if !fsutil.IsDir(cfgDir) { diff --git a/config/io_test.go b/config/io_test.go index b83ef2af10..e5bfd0ce64 100644 --- a/config/io_test.go +++ b/config/io_test.go @@ -11,6 +11,7 @@ import ( "testing" "github.com/fatih/color" + "github.com/justwatchcom/gopass/backend" "github.com/stretchr/testify/assert" ) @@ -154,8 +155,8 @@ func TestLoad(t *testing.T) { assert.NoError(t, os.Setenv("GOPASS_HOMEDIR", td)) cfg := Load() - assert.Equal(t, filepath.Join(td, ".password-store"), cfg.Root.Path) - assert.Equal(t, "gpg", cfg.Root.CryptoBackend) + assert.Equal(t, backend.FromPath(filepath.Join(td, ".password-store")).String(), cfg.Root.Path.String()) + assert.Equal(t, backend.GPGCLI, cfg.Root.Path.Crypto) assert.NoError(t, ioutil.WriteFile(gcfg, []byte(testConfig), 0600)) cfg = Load() @@ -183,7 +184,7 @@ func TestLoadError(t *testing.T) { gcfg = filepath.Join(os.TempDir(), "foo", ".gopass.yml") assert.NoError(t, os.Setenv("GOPASS_CONFIG", gcfg)) - assert.NoError(t, cfg.Save()) + assert.Error(t, cfg.Save()) } func TestDecodeError(t *testing.T) { diff --git a/config/legacy.go b/config/legacy.go index 2edc2e7a84..8cc9222aab 100644 --- a/config/legacy.go +++ b/config/legacy.go @@ -1,5 +1,7 @@ package config +import "github.com/justwatchcom/gopass/backend" + // Pre140 is the gopass config structure before version 1.4.0 type Pre140 struct { AskForMore bool `yaml:"askformore"` // ask for more data on generate @@ -29,7 +31,7 @@ func (c *Pre140) Config() *Config { AutoSync: c.AutoSync, ClipTimeout: c.ClipTimeout, NoConfirm: c.NoConfirm, - Path: c.Path, + Path: backend.FromPath(c.Path), SafeContent: c.SafeContent, } cfg := &Config{ @@ -39,7 +41,7 @@ func (c *Pre140) Config() *Config { } for k, v := range c.Mounts { subSc := sc - subSc.Path = v + subSc.Path = backend.FromPath(v) cfg.Mounts[k] = &subSc } return cfg @@ -81,7 +83,7 @@ func (c *Pre130) Config() *Config { AutoSync: c.AutoPull && c.AutoPush, ClipTimeout: c.ClipTimeout, NoConfirm: c.NoConfirm, - Path: c.Path, + Path: backend.FromPath(c.Path), SafeContent: c.SafeContent, } cfg := &Config{ @@ -91,7 +93,7 @@ func (c *Pre130) Config() *Config { } for k, v := range c.Mounts { subSc := sc - subSc.Path = v + subSc.Path = backend.FromPath(v) cfg.Mounts[k] = &subSc } return cfg diff --git a/config/store_config.go b/config/store_config.go index 478dae7787..7b76af4c31 100644 --- a/config/store_config.go +++ b/config/store_config.go @@ -6,25 +6,33 @@ import ( "strconv" "strings" + "github.com/justwatchcom/gopass/backend" "github.com/pkg/errors" ) // StoreConfig is a per-store (root or mount) config type StoreConfig struct { - AskForMore bool `yaml:"askformore"` // ask for more data on generate - AutoImport bool `yaml:"autoimport"` // import missing public keys w/o asking - AutoSync bool `yaml:"autosync"` // push to git remote after commit, pull before push if necessary - ClipTimeout int `yaml:"cliptimeout"` // clear clipboard after seconds - CryptoBackend string `yaml:"cryptobackend"` // encryption backend (e.g. GPG, XC) - NoColor bool `yaml:"nocolor"` // do not use color when outputing text - NoConfirm bool `yaml:"noconfirm"` // do not confirm recipients when encrypting - NoPager bool `yaml:"nopager"` // do not invoke a pager to display long lists - Path string `yaml:"path"` // path to the root store - SafeContent bool `yaml:"safecontent"` // avoid showing passwords in terminal - StoreBackend string `yaml:"storebackend"` // storage backend (e.g. FS, K/V, ...) - SyncBackend string `yaml:"syncbackend"` // sync backend (e.g. GitCLI, GoGit, ...) - UseSymbols bool `yaml:"usesymbols"` // always use symbols when generating passwords - Notifications bool `yaml:"notifications"` // enable desktop notifications + AskForMore bool `yaml:"askformore"` // ask for more data on generate + AutoImport bool `yaml:"autoimport"` // import missing public keys w/o asking + AutoSync bool `yaml:"autosync"` // push to git remote after commit, pull before push if necessary + ClipTimeout int `yaml:"cliptimeout"` // clear clipboard after seconds + NoColor bool `yaml:"nocolor"` // do not use color when outputing text + NoConfirm bool `yaml:"noconfirm"` // do not confirm recipients when encrypting + NoPager bool `yaml:"nopager"` // do not invoke a pager to display long lists + Path *backend.URL `yaml:"path"` // path to the root store + SafeContent bool `yaml:"safecontent"` // avoid showing passwords in terminal + UseSymbols bool `yaml:"usesymbols"` // always use symbols when generating passwords + Notifications bool `yaml:"notifications"` // enable desktop notifications +} + +func (c *StoreConfig) checkDefaults() error { + if c == nil { + return nil + } + if c.Path == nil { + c.Path = backend.FromPath("") + } + return nil } // ConfigMap returns a map of stringified config values for easy printing @@ -45,6 +53,14 @@ func (c *StoreConfig) ConfigMap() map[string]string { strVal = fmt.Sprintf("%t", f.Bool()) case reflect.Int: strVal = fmt.Sprintf("%d", f.Int()) + case reflect.Ptr: + switch bup := f.Interface().(type) { + case *backend.URL: + if bup == nil { + continue + } + strVal = bup.String() + } default: continue } @@ -93,5 +109,5 @@ func (c *StoreConfig) SetConfigValue(key, value string) error { } func (c *StoreConfig) String() string { - return fmt.Sprintf("StoreConfig[AskForMore:%t,AutoImport:%t,AutoSync:%t,ClipTimeout:%d,CryptoBackend:%s,NoColor:%t,NoConfirm:%t,NoPager:%t,Path:%s,SafeContent:%t,SyncBackend:%s,UseSymbols:%t]", c.AskForMore, c.AutoImport, c.AutoSync, c.ClipTimeout, c.CryptoBackend, c.NoColor, c.NoConfirm, c.NoPager, c.Path, c.SafeContent, c.SyncBackend, c.UseSymbols) + return fmt.Sprintf("StoreConfig[AskForMore:%t,AutoImport:%t,AutoSync:%t,ClipTimeout:%d,NoColor:%t,NoConfirm:%t,NoPager:%t,Path:%s,SafeContent:%t,UseSymbols:%t]", c.AskForMore, c.AutoImport, c.AutoSync, c.ClipTimeout, c.NoColor, c.NoConfirm, c.NoPager, c.Path, c.SafeContent, c.UseSymbols) } diff --git a/store/root/init.go b/store/root/init.go index 7f6b691a97..b1ebf47a23 100644 --- a/store/root/init.go +++ b/store/root/init.go @@ -27,16 +27,22 @@ func (r *Store) Init(ctx context.Context, alias, path string, ids ...string) err return err } if alias == "" { - r.cfg.Root.CryptoBackend = backend.CryptoBackendName(backend.GetCryptoBackend(ctx)) - r.cfg.Root.SyncBackend = backend.SyncBackendName(backend.GetSyncBackend(ctx)) - r.cfg.Root.StoreBackend = backend.StoreBackendName(backend.GetStoreBackend(ctx)) + if r.cfg.Root.Path == nil { + r.cfg.Root.Path = backend.FromPath(path) + } + r.cfg.Root.Path.Crypto = backend.GetCryptoBackend(ctx) + r.cfg.Root.Path.Sync = backend.GetSyncBackend(ctx) + r.cfg.Root.Path.Store = backend.GetStoreBackend(ctx) } else { if sc := r.cfg.Mounts[alias]; sc == nil { r.cfg.Mounts[alias] = &config.StoreConfig{} } - r.cfg.Mounts[alias].CryptoBackend = backend.CryptoBackendName(backend.GetCryptoBackend(ctx)) - r.cfg.Mounts[alias].SyncBackend = backend.SyncBackendName(backend.GetSyncBackend(ctx)) - r.cfg.Mounts[alias].StoreBackend = backend.StoreBackendName(backend.GetStoreBackend(ctx)) + if r.cfg.Mounts[alias].Path == nil { + r.cfg.Mounts[alias].Path = backend.FromPath(path) + } + r.cfg.Mounts[alias].Path.Crypto = backend.GetCryptoBackend(ctx) + r.cfg.Mounts[alias].Path.Sync = backend.GetSyncBackend(ctx) + r.cfg.Mounts[alias].Path.Store = backend.GetStoreBackend(ctx) } return nil } diff --git a/store/root/init_test.go b/store/root/init_test.go index a23fa3a188..0c2a7062bd 100644 --- a/store/root/init_test.go +++ b/store/root/init_test.go @@ -22,7 +22,7 @@ func TestInit(t *testing.T) { ctx = backend.WithCryptoBackend(ctx, backend.GPGMock) cfg := config.New() - cfg.Root.Path = u.StoreDir("rs") + cfg.Root.Path = backend.FromPath(u.StoreDir("rs")) rs, err := New(ctx, cfg) assert.NoError(t, err) diff --git a/store/root/mount.go b/store/root/mount.go index 6a3a539ef5..4ad3044a35 100644 --- a/store/root/mount.go +++ b/store/root/mount.go @@ -40,10 +40,10 @@ func (r *Store) addMount(ctx context.Context, alias, path string, sc *config.Sto // propagate our config settings to the sub store if sc != nil { if !backend.HasCryptoBackend(ctx) { - ctx = backend.WithCryptoBackendString(ctx, sc.CryptoBackend) + ctx = backend.WithCryptoBackend(ctx, sc.Path.Crypto) } if !backend.HasSyncBackend(ctx) { - ctx = backend.WithSyncBackendString(ctx, sc.SyncBackend) + ctx = backend.WithSyncBackend(ctx, sc.Path.Sync) } } s, err := sub.New(ctx, alias, path, config.Directory()) @@ -69,17 +69,25 @@ func (r *Store) addMount(ctx context.Context, alias, path string, sc *config.Sto if r.cfg.Mounts == nil { r.cfg.Mounts = make(map[string]*config.StoreConfig, 1) } + pathURL, err := backend.ParseURL(path) + if err != nil { + return errors.Wrapf(err, "failed to parse backend URL '%s': %s", path, err) + } if sc == nil { // imporant: copy root config to avoid overwriting it with sub store // values cp := *r.cfg.Root sc = &cp - sc.CryptoBackend = backend.CryptoBackendName(backend.GetCryptoBackend(ctx)) - sc.SyncBackend = backend.SyncBackendName(backend.GetSyncBackend(ctx)) - sc.StoreBackend = backend.StoreBackendName(backend.GetStoreBackend(ctx)) } - if path != "" { - sc.Path = path + sc.Path = pathURL + if backend.HasCryptoBackend(ctx) { + sc.Path.Crypto = backend.GetCryptoBackend(ctx) + } + if backend.HasSyncBackend(ctx) { + sc.Path.Sync = backend.GetSyncBackend(ctx) + } + if backend.HasStoreBackend(ctx) { + sc.Path.Store = backend.GetStoreBackend(ctx) } r.cfg.Mounts[alias] = sc return nil diff --git a/store/root/store.go b/store/root/store.go index 5139e0d18f..94cb1ed0b7 100644 --- a/store/root/store.go +++ b/store/root/store.go @@ -8,7 +8,6 @@ import ( "github.com/justwatchcom/gopass/backend" "github.com/justwatchcom/gopass/config" "github.com/justwatchcom/gopass/store/sub" - "github.com/justwatchcom/gopass/utils/fsutil" "github.com/justwatchcom/gopass/utils/out" "github.com/pkg/errors" ) @@ -17,7 +16,7 @@ import ( type Store struct { cfg *config.Config mounts map[string]*sub.Store - path string // path to the root store + url *backend.URL // url of the root store store *sub.Store version string } @@ -27,22 +26,22 @@ func New(ctx context.Context, cfg *config.Config) (*Store, error) { if cfg == nil { cfg = &config.Config{} } - if cfg.Root != nil && cfg.Root.Path == "" { + if cfg.Root != nil && (cfg.Root.Path == nil || cfg.Root.Path.Path == "") { return nil, errors.Errorf("need path") } r := &Store{ cfg: cfg, mounts: make(map[string]*sub.Store, len(cfg.Mounts)), - path: cfg.Root.Path, + url: cfg.Root.Path, version: cfg.Version, } // create the base store if !backend.HasCryptoBackend(ctx) { - ctx = backend.WithCryptoBackendString(ctx, cfg.Root.CryptoBackend) + ctx = backend.WithCryptoBackend(ctx, cfg.Root.Path.Crypto) } if !backend.HasSyncBackend(ctx) { - ctx = backend.WithSyncBackendString(ctx, cfg.Root.SyncBackend) + ctx = backend.WithSyncBackend(ctx, cfg.Root.Path.Sync) } s, err := sub.New(ctx, "", r.Path(), config.Directory()) if err != nil { @@ -52,9 +51,8 @@ func New(ctx context.Context, cfg *config.Config) (*Store, error) { // initialize all mounts for alias, sc := range cfg.Mounts { - path := fsutil.CleanPath(sc.Path) - if err := r.addMount(ctx, alias, path, sc); err != nil { - out.Red(ctx, "Failed to initialize mount %s (%s). Ignoring: %s", alias, path, err) + if err := r.addMount(ctx, alias, sc.Path.String(), sc); err != nil { + out.Red(ctx, "Failed to initialize mount %s (%s). Ignoring: %s", alias, sc.Path.String(), err) continue } } @@ -89,7 +87,7 @@ func (r *Store) String() string { // Path returns the store path func (r *Store) Path() string { - return r.path + return r.url.Path } // Alias always returns an empty string diff --git a/store/root/store_test.go b/store/root/store_test.go index ec45ec511d..71cf647c0b 100644 --- a/store/root/store_test.go +++ b/store/root/store_test.go @@ -122,7 +122,7 @@ func createRootStore(ctx context.Context, u *gptest.Unit) (*Store, error) { ctx, &config.Config{ Root: &config.StoreConfig{ - Path: u.StoreDir(""), + Path: backend.FromPath(u.StoreDir("")), }, }, ) diff --git a/store/sub/git.go b/store/sub/git.go index 395797ba70..3796ada628 100644 --- a/store/sub/git.go +++ b/store/sub/git.go @@ -24,14 +24,14 @@ func (s *Store) GitInit(ctx context.Context, un, ue string) error { switch backend.GetSyncBackend(ctx) { case backend.GoGit: out.Cyan(ctx, "WARNING: Using experimental sync backend 'go-git'") - git, err := gogit.Init(ctx, s.path) + git, err := gogit.Init(ctx, s.url.Path) if err != nil { return errors.Wrapf(err, "failed to init git: %s", err) } s.sync = git return nil case backend.GitCLI: - git, err := gitcli.Init(ctx, s.path, un, ue) + git, err := gitcli.Init(ctx, s.url.Path, un, ue) if err != nil { return errors.Wrapf(err, "failed to init git: %s", err) } diff --git a/store/sub/list_test.go b/store/sub/list_test.go index 3a63c17f39..566dcc53fb 100644 --- a/store/sub/list_test.go +++ b/store/sub/list_test.go @@ -7,6 +7,7 @@ import ( "os" "testing" + "github.com/justwatchcom/gopass/backend" gpgmock "github.com/justwatchcom/gopass/backend/crypto/gpg/mock" "github.com/justwatchcom/gopass/backend/store/fs" gitmock "github.com/justwatchcom/gopass/backend/sync/git/mock" @@ -72,7 +73,7 @@ func TestList(t *testing.T) { s := &Store{ alias: "", - path: tempdir, + url: backend.FromPath(tempdir), crypto: gpgmock.New(), sync: gitmock.New(), store: fs.New(tempdir), diff --git a/store/sub/move_test.go b/store/sub/move_test.go index aea486ba88..cca0393499 100644 --- a/store/sub/move_test.go +++ b/store/sub/move_test.go @@ -7,6 +7,7 @@ import ( "os" "testing" + "github.com/justwatchcom/gopass/backend" gpgmock "github.com/justwatchcom/gopass/backend/crypto/gpg/mock" "github.com/justwatchcom/gopass/backend/store/fs" gitmock "github.com/justwatchcom/gopass/backend/sync/git/mock" @@ -84,7 +85,7 @@ func TestCopy(t *testing.T) { s := &Store{ alias: "", - path: tempdir, + url: backend.FromPath(tempdir), crypto: gpgmock.New(), sync: gitmock.New(), store: fs.New(tempdir), @@ -162,7 +163,7 @@ func TestMove(t *testing.T) { s := &Store{ alias: "", - path: tempdir, + url: backend.FromPath(tempdir), crypto: gpgmock.New(), sync: gitmock.New(), store: fs.New(tempdir), @@ -219,7 +220,7 @@ func TestDelete(t *testing.T) { s := &Store{ alias: "", - path: tempdir, + url: backend.FromPath(tempdir), crypto: gpgmock.New(), sync: gitmock.New(), store: fs.New(tempdir), @@ -297,7 +298,7 @@ func TestPrune(t *testing.T) { s := &Store{ alias: "", - path: tempdir, + url: backend.FromPath(tempdir), crypto: gpgmock.New(), sync: gitmock.New(), store: fs.New(tempdir), diff --git a/store/sub/recipients.go b/store/sub/recipients.go index 684f6dd115..ed39457695 100644 --- a/store/sub/recipients.go +++ b/store/sub/recipients.go @@ -188,7 +188,7 @@ func (s *Store) saveRecipients(ctx context.Context, rs []string, msg string, exp } // save recipients' public keys - if err := os.MkdirAll(filepath.Join(s.path, keyDir), dirMode); err != nil { + if err := os.MkdirAll(filepath.Join(s.url.Path, keyDir), dirMode); err != nil { return errors.Wrapf(err, "failed to create key dir '%s'", keyDir) } diff --git a/store/sub/recipients_test.go b/store/sub/recipients_test.go index 9f11c2ffa1..eef0c69b9c 100644 --- a/store/sub/recipients_test.go +++ b/store/sub/recipients_test.go @@ -39,7 +39,7 @@ func TestGetRecipientsDefault(t *testing.T) { s := &Store{ alias: "", - path: tempdir, + url: backend.FromPath(tempdir), crypto: gpgmock.New(), sync: gitmock.New(), store: fs.New(tempdir), @@ -71,7 +71,7 @@ func TestGetRecipientsSubID(t *testing.T) { s := &Store{ alias: "", - path: tempdir, + url: backend.FromPath(tempdir), crypto: gpgmock.New(), sync: gitmock.New(), store: fs.New(tempdir), @@ -109,7 +109,7 @@ func TestSaveRecipients(t *testing.T) { recp := []string{"john.doe"} s := &Store{ alias: "", - path: tempdir, + url: backend.FromPath(tempdir), crypto: gpgmock.New(), sync: gitmock.New(), store: fs.New(tempdir), @@ -163,7 +163,7 @@ func TestAddRecipient(t *testing.T) { s := &Store{ alias: "", - path: tempdir, + url: backend.FromPath(tempdir), crypto: gpgmock.New(), sync: gitmock.New(), store: fs.New(tempdir), @@ -202,7 +202,7 @@ func TestRemoveRecipient(t *testing.T) { s := &Store{ alias: "", - path: tempdir, + url: backend.FromPath(tempdir), crypto: gpgmock.New(), sync: gitmock.New(), store: fs.New(tempdir), diff --git a/store/sub/store.go b/store/sub/store.go index cb98936c6f..0ec3c2a276 100644 --- a/store/sub/store.go +++ b/store/sub/store.go @@ -28,7 +28,7 @@ import ( // Store is password store type Store struct { alias string - path string + url *backend.URL crypto backend.Crypto sync backend.Sync store backend.Store @@ -36,17 +36,24 @@ type Store struct { // New creates a new store, copying settings from the given root store func New(ctx context.Context, alias, path string, cfgdir string) (*Store, error) { - path = fsutil.CleanPath(path) + u, err := backend.ParseURL(path) + if err != nil { + return nil, errors.Wrapf(err, "failed to parse path URL '%s': %s", path, err) + } + s := &Store{ alias: alias, - path: path, + url: u, sync: gitmock.New(), } // init store backend - switch backend.GetStoreBackend(ctx) { + if backend.HasStoreBackend(ctx) { + s.url.Store = backend.GetStoreBackend(ctx) + } + switch s.url.Store { case backend.FS: - s.store = fs.New(path) + s.store = fs.New(u.Path) out.Debug(ctx, "Using Store Backend: fs") case backend.KVMock: s.store = kvmock.New() @@ -56,10 +63,13 @@ func New(ctx context.Context, alias, path string, cfgdir string) (*Store, error) } // init sync backend - switch backend.GetSyncBackend(ctx) { + if backend.HasSyncBackend(ctx) { + s.url.Sync = backend.GetSyncBackend(ctx) + } + switch s.url.Sync { case backend.GoGit: out.Cyan(ctx, "WARNING: Using experimental sync backend 'go-git'") - git, err := gogit.Open(path) + git, err := gogit.Open(u.Path) if err != nil { out.Debug(ctx, "Failed to initialize sync backend 'gogit': %s", err) } else { @@ -68,12 +78,12 @@ func New(ctx context.Context, alias, path string, cfgdir string) (*Store, error) } case backend.GitCLI: gpgBin, _ := gpgcli.Binary(ctx, "") - git, err := gitcli.Open(path, gpgBin) + git, err := gitcli.Open(u.Path, gpgBin) if err != nil { - out.Debug(ctx, "Failed to initialize sync backend 'git': %s", err) + out.Debug(ctx, "Failed to initialize sync backend 'gitcli': %s", err) } else { s.sync = git - out.Debug(ctx, "Using Sync Backend: git-cli") + out.Debug(ctx, "Using Sync Backend: gitcli") } case backend.GitMock: // no-op @@ -83,7 +93,10 @@ func New(ctx context.Context, alias, path string, cfgdir string) (*Store, error) } // init crypto backend - switch backend.GetCryptoBackend(ctx) { + if backend.HasCryptoBackend(ctx) { + s.url.Crypto = backend.GetCryptoBackend(ctx) + } + switch s.url.Crypto { case backend.GPGCLI: gpg, err := gpgcli.New(ctx, gpgcli.Config{ Umask: fsutil.Umask(), @@ -148,7 +161,7 @@ func (s *Store) Equals(other *Store) bool { if other == nil { return false } - return s.path == other.path + return s.url.String() == other.url.String() } // IsDir returns true if the entry is folder inside the store @@ -186,7 +199,7 @@ func (s *Store) passfile(name string) string { // String implement fmt.Stringer func (s *Store) String() string { - return fmt.Sprintf("Store(Alias: %s, Path: %s)", s.alias, s.path) + return fmt.Sprintf("Store(Alias: %s, Path: %s)", s.alias, s.url.String()) } // reencrypt will re-encrypt all entries for the current recipients @@ -269,7 +282,7 @@ func (s *Store) reencryptGitPush(ctx context.Context) error { // Path returns the value of path func (s *Store) Path() string { - return s.path + return s.url.Path } // Alias returns the value of alias diff --git a/utils/jsonapi/api_test.go b/utils/jsonapi/api_test.go index f5e795eac6..be1d12d392 100644 --- a/utils/jsonapi/api_test.go +++ b/utils/jsonapi/api_test.go @@ -193,7 +193,7 @@ func runRespondRawMessages(t *testing.T, requests []verifiedRequest, secrets []s ctx, &config.Config{ Root: &config.StoreConfig{ - Path: tempdir, + Path: backend.FromPath(tempdir), }, }, )