diff --git a/Changelog.md b/Changelog.md
index becde52a..34bd981e 100644
--- a/Changelog.md
+++ b/Changelog.md
@@ -2,17 +2,28 @@
Changelog [format](http://keepachangelog.com/en/1.0.0/)
+## [Bridge 3.0.7] Perth Narrows
+
+### Fixed
+* Other: Increase default UIDVALIDITY.
+* GODT-2173: fix: Migrate Bridge passwords from v2.X.
+* GODT-2207: Fix encoding of non utf7 mailbox names.
+* Other: Increase worker count (2 -> 4).
+
+
## [Bridge 3.0.6] Perth Narrows
### Fixed
* GODT-2187: Skip messages during sync that fail to build/parse.
+
## [Bridge 3.0.5] Perth Narrows
### Fixed
* GODT-2178: Bump go-proton-api to fix drafts.
* GODT-2180: Allow login with FIDO2.
+
## [Bridge 3.0.4] Perth Narrows
### Changed
@@ -26,6 +37,7 @@ Changelog [format](http://keepachangelog.com/en/1.0.0/)
* GODT-2170: Update draft event means delete old and create new message.
* GODT-2170: User create draft route: first steps.
+
## [Bridge 3.0.3] Perth Narrows
### Fixed
@@ -56,6 +68,7 @@ Changelog [format](http://keepachangelog.com/en/1.0.0/)
* Other: Ensure context is string in sentry reports.
* GODT-2160: Ensure we can safely move cache file.
+
## [Bridge 3.0.1] Perth Narrows
### Changed
diff --git a/Makefile b/Makefile
index 0b0e5771..b43fd137 100644
--- a/Makefile
+++ b/Makefile
@@ -11,7 +11,7 @@ ROOT_DIR:=$(shell dirname $(realpath $(firstword $(MAKEFILE_LIST))))
.PHONY: build build-gui build-nogui build-launcher versioner hasher
# Keep version hardcoded so app build works also without Git repository.
-BRIDGE_APP_VERSION?=3.0.6+git
+BRIDGE_APP_VERSION?=3.0.7+git
APP_VERSION:=${BRIDGE_APP_VERSION}
APP_FULL_NAME:=Proton Mail Bridge
APP_VENDOR:=Proton AG
diff --git a/go.mod b/go.mod
index 0a9efeef..229ab664 100644
--- a/go.mod
+++ b/go.mod
@@ -5,7 +5,7 @@ go 1.18
require (
github.com/0xAX/notificator v0.0.0-20220220101646-ee9b8921e557
github.com/Masterminds/semver/v3 v3.1.1
- github.com/ProtonMail/gluon v0.14.2-0.20221207071431-0faa318d3c9f
+ github.com/ProtonMail/gluon v0.14.2-0.20221213102402-d76bb11db6c5
github.com/ProtonMail/go-autostart v0.0.0-20210130080809-00ed301c8e9a
github.com/ProtonMail/go-proton-api v0.2.2-0.20221213121236-3439b3eda101
github.com/ProtonMail/go-rfc5322 v0.11.0
diff --git a/go.sum b/go.sum
index 1eb2a82a..8948e76a 100644
--- a/go.sum
+++ b/go.sum
@@ -28,8 +28,8 @@ github.com/ProtonMail/bcrypt v0.0.0-20211005172633-e235017c1baf h1:yc9daCCYUefEs
github.com/ProtonMail/bcrypt v0.0.0-20211005172633-e235017c1baf/go.mod h1:o0ESU9p83twszAU8LBeJKFAAMX14tISa0yk4Oo5TOqo=
github.com/ProtonMail/docker-credential-helpers v1.1.0 h1:+kvUIpwWcbtP3WFv5sSvkFn/XLzSqPOB5AAthuk9xPk=
github.com/ProtonMail/docker-credential-helpers v1.1.0/go.mod h1:mK0aBveCxhnQ756AmaTfXMZDeULvheYVhF/MWMErN5g=
-github.com/ProtonMail/gluon v0.14.2-0.20221207071431-0faa318d3c9f h1:73b28jayIkYr1cJPHSHFMGyFgk1h6iJ127kuYX8UaRo=
-github.com/ProtonMail/gluon v0.14.2-0.20221207071431-0faa318d3c9f/go.mod h1:z2AxLIiBCT1K+0OBHyaDI7AEaO5qI6/BEC2TE42vs4Q=
+github.com/ProtonMail/gluon v0.14.2-0.20221213102402-d76bb11db6c5 h1:OWskNw0tLTR3puf0uizqYVNQPKOmhWXQqrYO0Wm7Hs4=
+github.com/ProtonMail/gluon v0.14.2-0.20221213102402-d76bb11db6c5/go.mod h1:z2AxLIiBCT1K+0OBHyaDI7AEaO5qI6/BEC2TE42vs4Q=
github.com/ProtonMail/go-autostart v0.0.0-20210130080809-00ed301c8e9a h1:D+aZah+k14Gn6kmL7eKxoo/4Dr/lK3ChBcwce2+SQP4=
github.com/ProtonMail/go-autostart v0.0.0-20210130080809-00ed301c8e9a/go.mod h1:oTGdE7/DlWIr23G0IKW3OXK9wZ5Hw1GGiaJFccTvZi4=
github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo=
diff --git a/internal/app/migration.go b/internal/app/migration.go
index 127ca35e..f4313688 100644
--- a/internal/app/migration.go
+++ b/internal/app/migration.go
@@ -32,6 +32,7 @@ import (
"github.com/ProtonMail/proton-bridge/v3/internal/locations"
"github.com/ProtonMail/proton-bridge/v3/internal/updater"
"github.com/ProtonMail/proton-bridge/v3/internal/vault"
+ "github.com/ProtonMail/proton-bridge/v3/pkg/algo"
"github.com/ProtonMail/proton-bridge/v3/pkg/keychain"
"github.com/allan-simon/go-singleinstance"
"github.com/hashicorp/go-multierror"
@@ -43,6 +44,16 @@ import (
func migrateKeychainHelper(locations *locations.Locations) error {
logrus.Info("Migrating keychain helper")
+ settings, err := locations.ProvideSettingsPath()
+ if err != nil {
+ return fmt.Errorf("failed to get settings path: %w", err)
+ }
+
+ if keychainName, _ := vault.GetHelper(settings); keychainName != "" {
+ // If uncorupted keychain file is already there do not migrate again.
+ return nil
+ }
+
configDir, err := os.UserConfigDir()
if err != nil {
return fmt.Errorf("failed to get user config dir: %w", err)
@@ -63,11 +74,6 @@ func migrateKeychainHelper(locations *locations.Locations) error {
return fmt.Errorf("failed to unmarshal old prefs file: %w", err)
}
- settings, err := locations.ProvideSettingsPath()
- if err != nil {
- return fmt.Errorf("failed to get settings path: %w", err)
- }
-
return vault.SetHelper(settings, prefs.Helper)
}
@@ -115,26 +121,52 @@ func migrateOldAccounts(locations *locations.Locations, v *vault.Vault) error {
return fmt.Errorf("failed to create credentials store: %w", err)
}
+ var migrationErrors error
+
for _, userID := range users {
logrus.WithField("userID", userID).Info("Migrating account")
-
- creds, err := store.Get(userID)
- if err != nil {
- return fmt.Errorf("failed to get user: %w", err)
+ if err := migrateOldAccount(userID, store, v); err != nil {
+ migrationErrors = multierror.Append(migrationErrors, err)
}
+ }
- authUID, authRef, err := creds.SplitAPIToken()
- if err != nil {
- return fmt.Errorf("failed to split api token: %w", err)
- }
+ return migrationErrors
+}
- user, err := v.AddUser(creds.UserID, creds.EmailList()[0], authUID, authRef, creds.MailboxPassword)
- if err != nil {
- return fmt.Errorf("failed to add user: %w", err)
- }
+func migrateOldAccount(userID string, store *credentials.Store, v *vault.Vault) error {
+ creds, err := store.Get(userID)
+ if err != nil {
+ return fmt.Errorf("failed to get user %q: %w", userID, err)
+ }
+ authUID, authRef, err := creds.SplitAPIToken()
+ if err != nil {
+ return fmt.Errorf("failed to split api token for user %q: %w", userID, err)
+ }
+
+ user, err := v.AddUser(creds.UserID, creds.EmailList()[0], authUID, authRef, creds.MailboxPassword)
+ if err != nil {
+ return fmt.Errorf("failed to add user %q: %w", userID, err)
+ }
+
+ defer func() {
if err := user.Close(); err != nil {
- return fmt.Errorf("failed to close user: %w", err)
+ logrus.WithField("userID", userID).WithError(err).Error("Failed to close vault user after migration")
+ }
+ }()
+
+ dec, err := algo.B64RawDecode([]byte(creds.BridgePassword))
+ if err != nil {
+ return fmt.Errorf("failed to decode bridge password for user %q: %w", userID, err)
+ }
+
+ if err := user.SetBridgePass(dec); err != nil {
+ return fmt.Errorf("failed to set bridge password to user %q: %w", userID, err)
+ }
+
+ if !creds.IsCombinedAddressMode {
+ if err := user.SetAddressMode(vault.SplitMode); err != nil {
+ return fmt.Errorf("failed to set split address mode to user %q: %w", userID, err)
}
}
diff --git a/internal/app/migration_test.go b/internal/app/migration_test.go
index ce811d34..bd7ac4b9 100644
--- a/internal/app/migration_test.go
+++ b/internal/app/migration_test.go
@@ -22,11 +22,19 @@ import (
"net/url"
"os"
"path/filepath"
+ "runtime"
"testing"
+ "github.com/ProtonMail/gopenpgp/v2/crypto"
+ "github.com/ProtonMail/proton-bridge/v3/internal/bridge"
"github.com/ProtonMail/proton-bridge/v3/internal/cookies"
+ "github.com/ProtonMail/proton-bridge/v3/internal/legacy/credentials"
+ "github.com/ProtonMail/proton-bridge/v3/internal/locations"
"github.com/ProtonMail/proton-bridge/v3/internal/updater"
"github.com/ProtonMail/proton-bridge/v3/internal/vault"
+ "github.com/ProtonMail/proton-bridge/v3/pkg/algo"
+ "github.com/ProtonMail/proton-bridge/v3/pkg/keychain"
+ dockerCredentials "github.com/docker/docker-credential-helpers/credentials"
"github.com/stretchr/testify/require"
)
@@ -79,3 +87,113 @@ func TestMigratePrefsToVault(t *testing.T) {
// There should be a cookie for the API.
require.NotEmpty(t, cookies.Cookies(url))
}
+
+func TestKeychainMigration(t *testing.T) {
+ // Migration tested only for linux.
+ if runtime.GOOS != "linux" {
+ return
+ }
+
+ tmpDir := t.TempDir()
+
+ // Prepare for keychain migration test
+ {
+ require.NoError(t, os.Setenv("XDG_CONFIG_HOME", tmpDir))
+ oldCacheDir := filepath.Join(tmpDir, "protonmail", "bridge")
+ require.NoError(t, os.MkdirAll(oldCacheDir, 0o700))
+
+ oldPrefs, err := os.ReadFile(filepath.Join("testdata", "prefs.json"))
+ require.NoError(t, err)
+
+ require.NoError(t, os.WriteFile(
+ filepath.Join(oldCacheDir, "prefs.json"),
+ oldPrefs, 0o600,
+ ))
+ }
+
+ locations := locations.New(bridge.NewTestLocationsProvider(tmpDir), "config-name")
+ settingsFolder, err := locations.ProvideSettingsPath()
+ require.NoError(t, err)
+
+ // Check that there is nothing yet
+ keychainName, err := vault.GetHelper(settingsFolder)
+ require.NoError(t, err)
+ require.Equal(t, "", keychainName)
+
+ // Check migration
+ require.NoError(t, migrateKeychainHelper(locations))
+ keychainName, err = vault.GetHelper(settingsFolder)
+ require.NoError(t, err)
+ require.Equal(t, "secret-service", keychainName)
+
+ // Change the migrated value
+ require.NoError(t, vault.SetHelper(settingsFolder, "different"))
+
+ // Calling migration again will not overwrite existing prefs
+ require.NoError(t, migrateKeychainHelper(locations))
+ keychainName, err = vault.GetHelper(settingsFolder)
+ require.NoError(t, err)
+ require.Equal(t, "different", keychainName)
+}
+
+func TestUserMigration(t *testing.T) {
+ keychainHelper := keychain.NewTestHelper()
+
+ keychain.Helpers["mock"] = func(string) (dockerCredentials.Helper, error) { return keychainHelper, nil }
+
+ kc, err := keychain.NewKeychain("mock", "bridge")
+ require.NoError(t, err)
+
+ require.NoError(t, kc.Put("brokenID", "broken"))
+ require.NoError(t, kc.Put(
+ "emptyID",
+ (&credentials.Credentials{}).Marshal(),
+ ))
+
+ wantUID := "uidtoken"
+ wantRefresh := "refreshtoken"
+
+ wantCredentials := credentials.Credentials{
+ UserID: "validID",
+ Name: "user@pm.me",
+ Emails: "user@pm.me;alias@pm.me",
+ APIToken: wantUID + ":" + wantRefresh,
+ MailboxPassword: []byte("secret"),
+ BridgePassword: "bElu2Q1Vusy28J3Wf56cIg",
+ Version: "v2.3.X",
+ Timestamp: 100,
+ IsCombinedAddressMode: true,
+ }
+ require.NoError(t, kc.Put(
+ wantCredentials.UserID,
+ wantCredentials.Marshal(),
+ ))
+
+ tmpDir := t.TempDir()
+ locations := locations.New(bridge.NewTestLocationsProvider(tmpDir), "config-name")
+ settingsFolder, err := locations.ProvideSettingsPath()
+ require.NoError(t, err)
+ require.NoError(t, vault.SetHelper(settingsFolder, "mock"))
+
+ token, err := crypto.RandomToken(32)
+ require.NoError(t, err)
+
+ v, corrupt, err := vault.New(settingsFolder, settingsFolder, token)
+ require.NoError(t, err)
+ require.False(t, corrupt)
+
+ require.NoError(t, migrateOldAccounts(locations, v))
+ require.Equal(t, []string{wantCredentials.UserID}, v.GetUserIDs())
+
+ require.NoError(t, v.GetUser(wantCredentials.UserID, func(u *vault.User) {
+ require.Equal(t, wantCredentials.UserID, u.UserID())
+ require.Equal(t, wantUID, u.AuthUID())
+ require.Equal(t, wantRefresh, u.AuthRef())
+ require.Equal(t, wantCredentials.MailboxPassword, u.KeyPass())
+ require.Equal(t,
+ []byte(wantCredentials.BridgePassword),
+ algo.B64RawEncode(u.BridgePass()),
+ )
+ require.Equal(t, vault.CombinedMode, u.AddressMode())
+ }))
+}
diff --git a/internal/bridge/refresh_test.go b/internal/bridge/refresh_test.go
index 4933065c..682fa6c7 100644
--- a/internal/bridge/refresh_test.go
+++ b/internal/bridge/refresh_test.go
@@ -73,7 +73,7 @@ func TestBridge_Refresh(t *testing.T) {
for _, name := range names {
status, err := client.Select("Folders/"+name, false)
require.NoError(t, err)
- require.Equal(t, uint32(1), status.UidValidity)
+ require.Equal(t, uint32(1000), status.UidValidity)
}
})
@@ -106,7 +106,7 @@ func TestBridge_Refresh(t *testing.T) {
for _, name := range names {
status, err := client.Select("Folders/"+name, false)
require.NoError(t, err)
- require.Equal(t, uint32(2), status.UidValidity)
+ require.Equal(t, uint32(1001), status.UidValidity)
}
})
})
diff --git a/internal/user/sync_build.go b/internal/user/sync_build.go
index ef5032a7..f5052f67 100644
--- a/internal/user/sync_build.go
+++ b/internal/user/sync_build.go
@@ -25,6 +25,7 @@ import (
"github.com/ProtonMail/gluon/imap"
"github.com/ProtonMail/go-proton-api"
"github.com/ProtonMail/gopenpgp/v2/crypto"
+ "github.com/ProtonMail/proton-bridge/v3/pkg/algo"
"github.com/ProtonMail/proton-bridge/v3/pkg/message"
"github.com/bradenaw/juniper/xslices"
)
@@ -135,7 +136,7 @@ func newFailedMessageLiteral(
"Error": syncErr.Error(),
}); err != nil {
panic(err)
- } else if _, err := buf.Write(lineWrap(b64Encode(b))); err != nil {
+ } else if _, err := buf.Write(lineWrap(algo.B64Encode(b))); err != nil {
panic(err)
}
diff --git a/internal/user/types.go b/internal/user/types.go
index acd3fb3c..5cefc62a 100644
--- a/internal/user/types.go
+++ b/internal/user/types.go
@@ -18,7 +18,6 @@
package user
import (
- "encoding/base64"
"fmt"
"reflect"
"strings"
@@ -58,36 +57,6 @@ func groupBy[Key comparable, Value any](items []Value, key func(Value) Key) map[
return groups
}
-// b64Encode returns the base64 encoding of the given byte slice.
-func b64Encode(b []byte) []byte {
- enc := make([]byte, base64.StdEncoding.EncodedLen(len(b)))
-
- base64.StdEncoding.Encode(enc, b)
-
- return enc
-}
-
-// b64RawEncode returns the base64 encoding of the given byte slice.
-func b64RawEncode(b []byte) []byte {
- enc := make([]byte, base64.RawURLEncoding.EncodedLen(len(b)))
-
- base64.RawURLEncoding.Encode(enc, b)
-
- return enc
-}
-
-// b64RawDecode returns the bytes represented by the base64 encoding of the given byte slice.
-func b64RawDecode(b []byte) ([]byte, error) {
- dec := make([]byte, base64.RawURLEncoding.DecodedLen(len(b)))
-
- n, err := base64.RawURLEncoding.Decode(dec, b)
- if err != nil {
- return nil, err
- }
-
- return dec[:n], nil
-}
-
// getAddrID returns the address ID for the given email address.
func getAddrID(apiAddrs map[string]proton.Address, email string) (string, error) {
for _, addr := range apiAddrs {
diff --git a/internal/user/user.go b/internal/user/user.go
index 1b55a70d..a0fe970c 100644
--- a/internal/user/user.go
+++ b/internal/user/user.go
@@ -36,6 +36,7 @@ import (
"github.com/ProtonMail/proton-bridge/v3/internal/logging"
"github.com/ProtonMail/proton-bridge/v3/internal/safe"
"github.com/ProtonMail/proton-bridge/v3/internal/vault"
+ "github.com/ProtonMail/proton-bridge/v3/pkg/algo"
"github.com/bradenaw/juniper/xslices"
"github.com/go-resty/resty/v2"
"github.com/sirupsen/logrus"
@@ -355,7 +356,7 @@ func (user *User) GluonKey() []byte {
// BridgePass returns the user's bridge password, used for authentication over SMTP and IMAP.
func (user *User) BridgePass() []byte {
- return b64RawEncode(user.vault.BridgePass())
+ return algo.B64RawEncode(user.vault.BridgePass())
}
// UsedSpace returns the total space used by the user on the API.
@@ -431,7 +432,7 @@ func (user *User) CheckAuth(email string, password []byte) (string, error) {
panic("your wish is my command.. I crash")
}
- dec, err := b64RawDecode(password)
+ dec, err := algo.B64RawDecode(password)
if err != nil {
return "", fmt.Errorf("failed to decode password: %w", err)
}
diff --git a/internal/vault/helper.go b/internal/vault/helper.go
index a39ab01c..fffc2d48 100644
--- a/internal/vault/helper.go
+++ b/internal/vault/helper.go
@@ -29,14 +29,16 @@ type Keychain struct {
Helper string
}
-func GetHelper(vaultDir string) (string, error) {
- filePath := filepath.Clean(filepath.Join(vaultDir, "keychain.json"))
+func getKeychainPrefPath(vaultDir string) string {
+ return filepath.Clean(filepath.Join(vaultDir, "keychain.json"))
+}
- if _, err := os.Stat(filePath); errors.Is(err, fs.ErrNotExist) {
+func GetHelper(vaultDir string) (string, error) {
+ if _, err := os.Stat(getKeychainPrefPath(vaultDir)); errors.Is(err, fs.ErrNotExist) {
return "", nil
}
- b, err := os.ReadFile(filePath)
+ b, err := os.ReadFile(getKeychainPrefPath(vaultDir))
if err != nil {
return "", err
}
@@ -56,5 +58,5 @@ func SetHelper(vaultDir, helper string) error {
return err
}
- return os.WriteFile(filepath.Clean(filepath.Join(vaultDir, "keychain.json")), b, 0o600)
+ return os.WriteFile(getKeychainPrefPath(vaultDir), b, 0o600)
}
diff --git a/internal/vault/types_user.go b/internal/vault/types_user.go
index b58c76c2..d2c20338 100644
--- a/internal/vault/types_user.go
+++ b/internal/vault/types_user.go
@@ -28,7 +28,7 @@ type UserData struct {
GluonKey []byte
GluonIDs map[string]string
UIDValidity map[string]imap.UID
- BridgePass []byte
+ BridgePass []byte // raw token represented as byte slice (needs to be encoded)
AddressMode AddressMode
AuthUID string
diff --git a/internal/vault/user.go b/internal/vault/user.go
index 50867f42..07a3dcae 100644
--- a/internal/vault/user.go
+++ b/internal/vault/user.go
@@ -74,7 +74,7 @@ func (user *User) GetUIDValidity(addrID string) imap.UID {
return validity
}
- if err := user.SetUIDValidity(addrID, 1); err != nil {
+ if err := user.SetUIDValidity(addrID, 1000); err != nil {
panic(err)
}
@@ -99,11 +99,18 @@ func (user *User) SetAddressMode(mode AddressMode) error {
})
}
-// BridgePass returns the user's bridge password (unencoded).
+// BridgePass returns the user's bridge password as raw token bytes (unencoded).
func (user *User) BridgePass() []byte {
return user.vault.getUser(user.userID).BridgePass
}
+// SetBridgePass saves bridge password as raw token bytes (unecoded).
+func (user *User) SetBridgePass(newPass []byte) error {
+ return user.vault.modUser(user.userID, func(data *UserData) {
+ data.BridgePass = newPass
+ })
+}
+
// AuthUID returns the user's auth UID.
func (user *User) AuthUID() string {
return user.vault.getUser(user.userID).AuthUID
diff --git a/pkg/algo/encode.go b/pkg/algo/encode.go
new file mode 100644
index 00000000..34a565d1
--- /dev/null
+++ b/pkg/algo/encode.go
@@ -0,0 +1,50 @@
+// Copyright (c) 2022 Proton AG
+//
+// This file is part of Proton Mail Bridge.
+//
+// Proton Mail Bridge is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Proton Mail Bridge is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Proton Mail Bridge. If not, see .
+
+package algo
+
+import "encoding/base64"
+
+// B64Encode returns the base64 encoding of the given byte slice.
+func B64Encode(b []byte) []byte {
+ enc := make([]byte, base64.StdEncoding.EncodedLen(len(b)))
+
+ base64.StdEncoding.Encode(enc, b)
+
+ return enc
+}
+
+// B64RawEncode returns the base64 encoding of the given byte slice.
+func B64RawEncode(b []byte) []byte {
+ enc := make([]byte, base64.RawURLEncoding.EncodedLen(len(b)))
+
+ base64.RawURLEncoding.Encode(enc, b)
+
+ return enc
+}
+
+// B64RawDecode returns the bytes represented by the base64 encoding of the given byte slice.
+func B64RawDecode(b []byte) ([]byte, error) {
+ dec := make([]byte, base64.RawURLEncoding.DecodedLen(len(b)))
+
+ n, err := base64.RawURLEncoding.Decode(dec, b)
+ if err != nil {
+ return nil, err
+ }
+
+ return dec[:n], nil
+}
diff --git a/pkg/keychain/keychain_test.go b/pkg/keychain/keychain_test.go
index d7eebe09..8e024b71 100644
--- a/pkg/keychain/keychain_test.go
+++ b/pkg/keychain/keychain_test.go
@@ -21,7 +21,6 @@ import (
"encoding/base64"
"testing"
- "github.com/docker/docker-credential-helpers/credentials"
"github.com/stretchr/testify/require"
)
@@ -33,7 +32,7 @@ var testData = map[string]string{ //nolint:gochecknoglobals
}
func TestInsertReadRemove(t *testing.T) {
- keychain := newKeychain(newTestHelper(), hostURL("bridge"))
+ keychain := newKeychain(NewTestHelper(), hostURL("bridge"))
for id, secret := range testData {
expectedList, _ := keychain.List()
@@ -115,35 +114,3 @@ func TestInsertReadRemove(t *testing.T) {
require.NotContains(t, actualList, id)
}
}
-
-type testHelper map[string]*credentials.Credentials
-
-func newTestHelper() testHelper {
- return make(testHelper)
-}
-
-func (h testHelper) Add(creds *credentials.Credentials) error {
- h[creds.ServerURL] = creds
- return nil
-}
-
-func (h testHelper) Delete(url string) error {
- delete(h, url)
- return nil
-}
-
-func (h testHelper) Get(url string) (string, string, error) {
- creds := h[url]
-
- return creds.Username, creds.Secret, nil
-}
-
-func (h testHelper) List() (map[string]string, error) {
- list := make(map[string]string)
-
- for url, creds := range h {
- list[url] = creds.Username
- }
-
- return list, nil
-}
diff --git a/pkg/keychain/test_helper.go b/pkg/keychain/test_helper.go
new file mode 100644
index 00000000..28eaf760
--- /dev/null
+++ b/pkg/keychain/test_helper.go
@@ -0,0 +1,52 @@
+// Copyright (c) 2022 Proton AG
+//
+// This file is part of Proton Mail Bridge.
+//
+// Proton Mail Bridge is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Proton Mail Bridge is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Proton Mail Bridge. If not, see .
+
+package keychain
+
+import "github.com/docker/docker-credential-helpers/credentials"
+
+type TestHelper map[string]*credentials.Credentials
+
+func NewTestHelper() TestHelper {
+ return make(TestHelper)
+}
+
+func (h TestHelper) Add(creds *credentials.Credentials) error {
+ h[creds.ServerURL] = creds
+ return nil
+}
+
+func (h TestHelper) Delete(url string) error {
+ delete(h, url)
+ return nil
+}
+
+func (h TestHelper) Get(url string) (string, string, error) {
+ creds := h[url]
+
+ return creds.Username, creds.Secret, nil
+}
+
+func (h TestHelper) List() (map[string]string, error) {
+ list := make(map[string]string)
+
+ for url, creds := range h {
+ list[url] = creds.Username
+ }
+
+ return list, nil
+}