feat: switchable keychain

This commit is contained in:
James Houlahan
2020-12-21 10:56:13 +01:00
parent 07d9bc0831
commit 082a803e47
13 changed files with 345 additions and 500 deletions

View File

@ -51,6 +51,7 @@ import (
"github.com/ProtonMail/proton-bridge/internal/updater"
"github.com/ProtonMail/proton-bridge/internal/users/credentials"
"github.com/ProtonMail/proton-bridge/internal/versioner"
"github.com/ProtonMail/proton-bridge/pkg/keychain"
"github.com/ProtonMail/proton-bridge/pkg/listener"
"github.com/ProtonMail/proton-bridge/pkg/pmapi"
"github.com/ProtonMail/proton-bridge/pkg/sentry"
@ -142,12 +143,12 @@ func New( // nolint[funlen]
listener := listener.New()
events.SetupEvents(listener)
// NOTE: If we can't load the credentials for whatever reason,
// do we really want to error out? Need to signal to frontend.
creds, err := credentials.NewStore(keychainName)
// If we can't load the keychain for whatever reason,
// we signal to frontend and supply a dummy keychain that always returns errors.
kc, err := keychain.NewKeychain(settingsObj, keychainName)
if err != nil {
logrus.WithError(err).Error("Could not get credentials store")
listener.Emit(events.CredentialsErrorEvent, err.Error())
kc = keychain.NewMissingKeychain()
}
jar, err := cookies.NewCookieJar(settingsObj)
@ -158,7 +159,6 @@ func New( // nolint[funlen]
cm := pmapi.NewClientManager(pmapi.GetAPIConfig(configName, constants.Version))
cm.SetRoundTripper(pmapi.GetRoundTripper(cm, listener))
cm.SetCookieJar(jar)
sentryReporter.SetUserAgentProvider(cm)
key, err := crypto.NewKeyFromArmored(updater.DefaultPublicKey)
@ -188,8 +188,6 @@ func New( // nolint[funlen]
runtime.GOOS,
)
tls := tls.New(settingsPath)
exe, err := os.Executable()
if err != nil {
return nil, err
@ -208,12 +206,12 @@ func New( // nolint[funlen]
Lock: lock,
Cache: cache,
Listener: listener,
Creds: creds,
Creds: credentials.NewStore(kc),
CM: cm,
CookieJar: jar,
Updater: updater,
Versioner: versioner,
TLS: tls,
TLS: tls.New(settingsPath),
Autostart: autostart,
Name: appName,

View File

@ -42,6 +42,7 @@ const (
LastVersionKey = "last_used_version"
UpdateChannelKey = "update_channel"
RolloutKey = "rollout"
PreferredKeychainKey = "preferred_keychain"
)
type Settings struct {
@ -78,6 +79,7 @@ func (s *Settings) setDefaultValues() {
s.setDefault(LastVersionKey, "")
s.setDefault(UpdateChannelKey, "")
s.setDefault(RolloutKey, fmt.Sprintf("%v", rand.Float64()))
s.setDefault(PreferredKeychainKey, "")
s.setDefault(APIPortKey, DefaultAPIPort)
s.setDefault(IMAPPortKey, DefaultIMAPPort)

View File

@ -137,7 +137,7 @@ func (a *Accounts) ClearKeychain() {
for _, user := range a.um.GetUsers() {
if err := a.um.DeleteUser(user.ID(), false); err != nil {
log.Error("While deleting user: ", err)
if err == keychain.ErrNoKeychainInstalled { // Probably not needed anymore.
if err == keychain.ErrNoKeychain { // Probably not needed anymore.
a.qml.NotifyHasNoKeychain()
}
}
@ -249,7 +249,7 @@ func (a *Accounts) DeleteAccount(iAccount int, removePreferences bool) {
userID := a.Model.get(iAccount).UserID()
if err := a.um.DeleteUser(userID, removePreferences); err != nil {
log.Warn("deleteUser: cannot remove user: ", err)
if err == keychain.ErrNoKeychainInstalled {
if err == keychain.ErrNoKeychain {
a.qml.NotifyHasNoKeychain()
return
}

View File

@ -94,7 +94,7 @@ func (s *FrontendQt) clearKeychain() {
for _, user := range s.bridge.GetUsers() {
if err := s.bridge.DeleteUser(user.ID(), false); err != nil {
log.Error("While deleting user: ", err)
if err == keychain.ErrNoKeychainInstalled { // Probably not needed anymore.
if err == keychain.ErrNoKeychain { // Probably not needed anymore.
s.Qml.NotifyHasNoKeychain()
}
}
@ -203,7 +203,7 @@ func (s *FrontendQt) deleteAccount(iAccount int, removePreferences bool) {
userID := s.Accounts.get(iAccount).UserID()
if err := s.bridge.DeleteUser(userID, removePreferences); err != nil {
log.Warn("deleteUser: cannot remove user: ", err)
if err == keychain.ErrNoKeychainInstalled {
if err == keychain.ErrNoKeychain {
s.Qml.NotifyHasNoKeychain()
return
}

View File

@ -31,15 +31,12 @@ var storeLocker = sync.RWMutex{} //nolint[gochecknoglobals]
// Store is an encrypted credentials store.
type Store struct {
secrets *keychain.Access
secrets *keychain.Keychain
}
// NewStore creates a new encrypted credentials store.
func NewStore(appName string) (*Store, error) {
secrets, err := keychain.NewAccess(appName)
return &Store{
secrets: secrets,
}, err
func NewStore(keychain *keychain.Keychain) *Store {
return &Store{secrets: keychain}
}
func (s *Store) Add(userID, userName, apiToken, mailboxPassword string, emails []string) (creds *Credentials, err error) {
@ -52,10 +49,6 @@ func (s *Store) Add(userID, userName, apiToken, mailboxPassword string, emails [
"emails": emails,
}).Trace("Adding new credentials")
if err = s.checkKeychain(); err != nil {
return
}
creds = &Credentials{
UserID: userID,
Name: userName,
@ -164,10 +157,6 @@ func (s *Store) List() (userIDs []string, err error) {
log.Trace("Listing credentials in credentials store")
if err = s.checkKeychain(); err != nil {
return
}
var allUserIDs []string
if allUserIDs, err = s.secrets.List(); err != nil {
log.WithError(err).Error("Could not list credentials")
@ -242,11 +231,7 @@ func (s *Store) Get(userID string) (creds *Credentials, err error) {
func (s *Store) get(userID string) (creds *Credentials, err error) {
log := log.WithField("user", userID)
if err = s.checkKeychain(); err != nil {
return
}
secret, err := s.secrets.Get(userID)
_, secret, err := s.secrets.Get(userID)
if err != nil {
log.WithError(err).Error("Could not get credentials from native keychain")
return
@ -265,32 +250,15 @@ func (s *Store) get(userID string) (creds *Credentials, err error) {
// saveCredentials encrypts and saves password to the keychain store.
func (s *Store) saveCredentials(credentials *Credentials) (err error) {
if err = s.checkKeychain(); err != nil {
return
}
credentials.Version = keychain.KeychainVersion
credentials.Version = keychain.Version
return s.secrets.Put(credentials.UserID, credentials.Marshal())
}
func (s *Store) checkKeychain() (err error) {
if s.secrets == nil {
err = keychain.ErrNoKeychainInstalled
log.WithError(err).Error("Store is unusable")
}
return
}
// Delete removes credentials from the store.
func (s *Store) Delete(userID string) (err error) {
storeLocker.Lock()
defer storeLocker.Unlock()
if err = s.checkKeychain(); err != nil {
return
}
return s.secrets.Delete(userID)
}