mirror of
https://github.com/ProtonMail/proton-bridge.git
synced 2025-12-10 04:36:43 +00:00
feat(GODT-2277): Move Keychain helpers creation in main.
This commit is contained in:
@ -31,14 +31,18 @@ const (
|
||||
MacOSKeychain = "macos-keychain"
|
||||
)
|
||||
|
||||
func init() { //nolint:gochecknoinits
|
||||
Helpers = make(map[string]helperConstructor)
|
||||
func listHelpers() (Helpers, string) {
|
||||
helpers := make(Helpers)
|
||||
|
||||
// MacOS always provides a keychain.
|
||||
Helpers[MacOSKeychain] = newMacOSHelper
|
||||
if isUsable(newMacOSHelper("")) {
|
||||
helpers[MacOSKeychain] = newMacOSHelper
|
||||
} else {
|
||||
logrus.WithField("keychain", "MacOSKeychain").Warn("Keychain is not available.")
|
||||
}
|
||||
|
||||
// Use MacOSKeychain by default.
|
||||
DefaultHelper = MacOSKeychain
|
||||
return helpers, MacOSKeychain
|
||||
}
|
||||
|
||||
func parseError(original error) error {
|
||||
|
||||
@ -18,8 +18,6 @@
|
||||
package keychain
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
"github.com/docker/docker-credential-helpers/credentials"
|
||||
"github.com/docker/docker-credential-helpers/pass"
|
||||
"github.com/docker/docker-credential-helpers/secretservice"
|
||||
@ -33,30 +31,37 @@ const (
|
||||
SecretServiceDBus = "secret-service-dbus"
|
||||
)
|
||||
|
||||
func init() { //nolint:gochecknoinits
|
||||
Helpers = make(map[string]helperConstructor)
|
||||
func listHelpers() (Helpers, string) {
|
||||
helpers := make(Helpers)
|
||||
|
||||
if isUsable(newDBusHelper("")) {
|
||||
Helpers[SecretServiceDBus] = newDBusHelper
|
||||
helpers[SecretServiceDBus] = newDBusHelper
|
||||
} else {
|
||||
logrus.WithField("keychain", "SecretServiceDBus").Warn("Keychain is not available.")
|
||||
}
|
||||
|
||||
if _, err := execabs.LookPath("gnome-keyring"); err == nil && isUsable(newSecretServiceHelper("")) {
|
||||
Helpers[SecretService] = newSecretServiceHelper
|
||||
helpers[SecretService] = newSecretServiceHelper
|
||||
} else {
|
||||
logrus.WithField("keychain", "SecretService").Warn("Keychain is not available.")
|
||||
}
|
||||
|
||||
if _, err := execabs.LookPath("pass"); err == nil && isUsable(newPassHelper("")) {
|
||||
Helpers[Pass] = newPassHelper
|
||||
helpers[Pass] = newPassHelper
|
||||
} else {
|
||||
logrus.WithField("keychain", "Pass").Warn("Keychain is not available.")
|
||||
}
|
||||
|
||||
DefaultHelper = SecretServiceDBus
|
||||
defaultHelper := SecretServiceDBus
|
||||
|
||||
// If Pass is available, use it by default.
|
||||
// Otherwise, if SecretService is available, use it by default.
|
||||
if _, ok := Helpers[Pass]; ok {
|
||||
DefaultHelper = Pass
|
||||
} else if _, ok := Helpers[SecretService]; ok {
|
||||
DefaultHelper = SecretService
|
||||
if _, ok := helpers[Pass]; ok {
|
||||
defaultHelper = Pass
|
||||
} else if _, ok := helpers[SecretService]; ok {
|
||||
defaultHelper = SecretService
|
||||
}
|
||||
return helpers, defaultHelper
|
||||
}
|
||||
|
||||
func newDBusHelper(string) (credentials.Helper, error) {
|
||||
@ -70,36 +75,3 @@ func newPassHelper(string) (credentials.Helper, error) {
|
||||
func newSecretServiceHelper(string) (credentials.Helper, error) {
|
||||
return &secretservice.Secretservice{}, nil
|
||||
}
|
||||
|
||||
// isUsable returns whether the credentials helper is usable.
|
||||
func isUsable(helper credentials.Helper, err error) bool {
|
||||
l := logrus.WithField("helper", reflect.TypeOf(helper))
|
||||
|
||||
if err != nil {
|
||||
l.WithError(err).Warn("Keychain helper couldn't be created")
|
||||
return false
|
||||
}
|
||||
|
||||
creds := &credentials.Credentials{
|
||||
ServerURL: "bridge/check",
|
||||
Username: "check",
|
||||
Secret: "check",
|
||||
}
|
||||
|
||||
if err := helper.Add(creds); err != nil {
|
||||
l.WithError(err).Warn("Failed to add test credentials to keychain")
|
||||
return false
|
||||
}
|
||||
|
||||
if _, _, err := helper.Get(creds.ServerURL); err != nil {
|
||||
l.WithError(err).Warn("Failed to get test credentials from keychain")
|
||||
return false
|
||||
}
|
||||
|
||||
if err := helper.Delete(creds.ServerURL); err != nil {
|
||||
l.WithError(err).Warn("Failed to delete test credentials from keychain")
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
@ -20,18 +20,21 @@ package keychain
|
||||
import (
|
||||
"github.com/docker/docker-credential-helpers/credentials"
|
||||
"github.com/docker/docker-credential-helpers/wincred"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
const WindowsCredentials = "windows-credentials"
|
||||
|
||||
func init() { //nolint:gochecknoinits
|
||||
Helpers = make(map[string]helperConstructor)
|
||||
|
||||
func listHelpers() (Helpers, string) {
|
||||
helpers := make(Helpers)
|
||||
// Windows always provides a keychain.
|
||||
Helpers[WindowsCredentials] = newWinCredHelper
|
||||
|
||||
if isUsable(newWinCredHelper("")) {
|
||||
helpers[WindowsCredentials] = newWinCredHelper
|
||||
} else {
|
||||
logrus.WithField("keychain", "WindowsCredentials").Warn("Keychain is not available.")
|
||||
}
|
||||
// Use WindowsCredentials by default.
|
||||
DefaultHelper = WindowsCredentials
|
||||
return helpers, WindowsCredentials
|
||||
}
|
||||
|
||||
func newWinCredHelper(string) (credentials.Helper, error) {
|
||||
|
||||
@ -21,9 +21,12 @@ package keychain
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/docker/docker-credential-helpers/credentials"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// helperConstructor constructs a keychain helperConstructor.
|
||||
@ -38,28 +41,53 @@ var (
|
||||
|
||||
// ErrMacKeychainRebuild is returned on macOS with blocked or corrupted keychain.
|
||||
ErrMacKeychainRebuild = errors.New("keychain error -25293")
|
||||
|
||||
// Helpers holds all discovered keychain helpers. It is populated in init().
|
||||
Helpers map[string]helperConstructor //nolint:gochecknoglobals
|
||||
|
||||
// DefaultHelper is the default helper to use if the user hasn't yet set a preference.
|
||||
DefaultHelper string //nolint:gochecknoglobals
|
||||
)
|
||||
|
||||
type Helpers map[string]helperConstructor
|
||||
|
||||
type List struct {
|
||||
helpers Helpers
|
||||
defaultHelper string
|
||||
locker sync.Locker
|
||||
}
|
||||
|
||||
// NewList checks availability of every keychains detected on the User Operating System
|
||||
// This will ask the user to unlock keychain(s) to check their usability.
|
||||
// This should only be called once.
|
||||
func NewList() *List {
|
||||
var list = List{locker: &sync.Mutex{}}
|
||||
list.helpers, list.defaultHelper = listHelpers()
|
||||
return &list
|
||||
}
|
||||
|
||||
func (kcl *List) GetHelpers() Helpers {
|
||||
kcl.locker.Lock()
|
||||
defer kcl.locker.Unlock()
|
||||
|
||||
return kcl.helpers
|
||||
}
|
||||
|
||||
func (kcl *List) GetDefaultHelper() string {
|
||||
kcl.locker.Lock()
|
||||
defer kcl.locker.Unlock()
|
||||
|
||||
return kcl.defaultHelper
|
||||
}
|
||||
|
||||
// NewKeychain creates a new native keychain.
|
||||
func NewKeychain(preferred, keychainName string) (*Keychain, error) {
|
||||
func NewKeychain(preferred, keychainName string, helpers Helpers, defaultHelper string) (*Keychain, error) {
|
||||
// There must be at least one keychain helper available.
|
||||
if len(Helpers) < 1 {
|
||||
if len(helpers) < 1 {
|
||||
return nil, ErrNoKeychain
|
||||
}
|
||||
|
||||
// If the preferred keychain is unsupported, fallback to the default one.
|
||||
if _, ok := Helpers[preferred]; !ok {
|
||||
preferred = DefaultHelper
|
||||
if _, ok := helpers[preferred]; !ok {
|
||||
preferred = defaultHelper
|
||||
}
|
||||
|
||||
// Load the user's preferred keychain helper.
|
||||
helperConstructor, ok := Helpers[preferred]
|
||||
helperConstructor, ok := helpers[preferred]
|
||||
if !ok {
|
||||
return nil, ErrNoKeychain
|
||||
}
|
||||
@ -163,3 +191,49 @@ func (kc *Keychain) Put(userID, secret string) error {
|
||||
func (kc *Keychain) secretURL(userID string) string {
|
||||
return fmt.Sprintf("%v/%v", kc.url, userID)
|
||||
}
|
||||
|
||||
// isUsable returns whether the credentials helper is usable.
|
||||
func isUsable(helper credentials.Helper, err error) bool {
|
||||
l := logrus.WithField("helper", reflect.TypeOf(helper))
|
||||
|
||||
if err != nil {
|
||||
l.WithError(err).Warn("Keychain helper couldn't be created")
|
||||
return false
|
||||
}
|
||||
|
||||
creds := &credentials.Credentials{
|
||||
ServerURL: "bridge/check",
|
||||
Username: "check",
|
||||
Secret: "check",
|
||||
}
|
||||
|
||||
if err := retry(func() error {
|
||||
return helper.Add(creds)
|
||||
}); err != nil {
|
||||
l.WithError(err).Warn("Failed to add test credentials to keychain")
|
||||
return false
|
||||
}
|
||||
|
||||
if _, _, err := helper.Get(creds.ServerURL); err != nil {
|
||||
l.WithError(err).Warn("Failed to get test credentials from keychain")
|
||||
return false
|
||||
}
|
||||
|
||||
if err := helper.Delete(creds.ServerURL); err != nil {
|
||||
l.WithError(err).Warn("Failed to delete test credentials from keychain")
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func retry(condition func() error) error {
|
||||
var maxRetry = 5
|
||||
for r := 0; ; r++ {
|
||||
err := condition()
|
||||
if err == nil || r >= maxRetry {
|
||||
return err
|
||||
}
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
|
||||
@ -17,10 +17,22 @@
|
||||
|
||||
package keychain
|
||||
|
||||
import "github.com/docker/docker-credential-helpers/credentials"
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/docker/docker-credential-helpers/credentials"
|
||||
)
|
||||
|
||||
type TestHelper map[string]*credentials.Credentials
|
||||
|
||||
func NewTestKeychainsList() *List {
|
||||
keychainHelper := NewTestHelper()
|
||||
helpers := make(Helpers)
|
||||
helpers["mock"] = func(string) (credentials.Helper, error) { return keychainHelper, nil }
|
||||
var list = List{helpers: helpers, defaultHelper: "mock", locker: &sync.Mutex{}}
|
||||
return &list
|
||||
}
|
||||
|
||||
func NewTestHelper() TestHelper {
|
||||
return make(TestHelper)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user