diff --git a/app/package-lock.json b/app/package-lock.json index 30b34bf013..d6d056f83a 100644 --- a/app/package-lock.json +++ b/app/package-lock.json @@ -2214,11 +2214,18 @@ } }, "keytar": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/keytar/-/keytar-3.0.0.tgz", - "integrity": "sha1-p6wvNb2IlPtNgUHcLGd1PobVBQE=", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/keytar/-/keytar-4.0.4.tgz", + "integrity": "sha512-eFRONkbOD3cqmtrgVeE3A01LDoO7i5Vkek7+I5v0vfhj8FMX9LN7DSrInmpuAQer0AIszXq7QxCjn6HAeYK01A==", "requires": { - "nan": "2.6.2" + "nan": "2.5.1" + }, + "dependencies": { + "nan": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.5.1.tgz", + "integrity": "sha1-1bAWkSUzJql6K77p5hxV2NYDUeI=" + } } }, "kind-of": { diff --git a/app/package.json b/app/package.json index d1a109ed45..2d024fe3a9 100644 --- a/app/package.json +++ b/app/package.json @@ -39,7 +39,7 @@ "jasmine-reporters": "1.x.x", "jasmine-tagged": "^1.1.2", "juice": "^1.4", - "keytar": "3.0.0", + "keytar": "4.0.4", "less-cache": "0.21", "lru-cache": "^4.0.1", "marked": "^0.3", diff --git a/app/src/flux/mailsync-bridge.es6 b/app/src/flux/mailsync-bridge.es6 index 39fdcdd20d..aa544235b7 100644 --- a/app/src/flux/mailsync-bridge.es6 +++ b/app/src/flux/mailsync-bridge.es6 @@ -211,8 +211,8 @@ export default class MailsyncBridge { // Private - _launchClient(account, { force } = {}) { - const fullAccountJSON = KeyManager.insertAccountSecrets(account).toJSON(); + async _launchClient(account, { force } = {}) { + const fullAccountJSON = (await KeyManager.insertAccountSecrets(account)).toJSON(); const identity = IdentityStore.identity(); const id = account.id; diff --git a/app/src/flux/stores/account-store.es6 b/app/src/flux/stores/account-store.es6 index 32b7314d38..7ad6eed90f 100644 --- a/app/src/flux/stores/account-store.es6 +++ b/app/src/flux/stores/account-store.es6 @@ -210,14 +210,14 @@ class AccountStore extends MailspringStore { this._save(); }; - addAccount = account => { + addAccount = async account => { if (!account.emailAddress || !account.provider || !(account instanceof Account)) { throw new Error(`Returned account data is invalid: ${JSON.stringify(account)}`); } // send the account JSON and cloud token to the KeyManager, // which gives us back a version with no secrets. - const cleanAccount = KeyManager.extractAccountSecrets(account); + const cleanAccount = await KeyManager.extractAccountSecrets(account); this._loadAccounts(); diff --git a/app/src/flux/stores/identity-store.es6 b/app/src/flux/stores/identity-store.es6 index 4f6ad56d47..2cd1414194 100644 --- a/app/src/flux/stores/identity-store.es6 +++ b/app/src/flux/stores/identity-store.es6 @@ -93,9 +93,9 @@ class IdentityStore extends MailspringStore { * When the identity changes in the database, update our local store * cache and set the token from the keychain. */ - _onIdentityChanged = () => { + _onIdentityChanged = async () => { this._identity = AppEnv.config.get('identity') || {}; - this._identity.token = KeyManager.getPassword(KEYCHAIN_NAME); + this._identity.token = await KeyManager.getPassword(KEYCHAIN_NAME); this.trigger(); }; diff --git a/app/src/key-manager.es6 b/app/src/key-manager.es6 index 5201bb1e1c..201994e389 100644 --- a/app/src/key-manager.es6 +++ b/app/src/key-manager.es6 @@ -11,70 +11,79 @@ import keytar from 'keytar'; */ class KeyManager { constructor() { - this.SERVICE_NAME = 'Mailspring'; - if (AppEnv.inDevMode()) { - this.SERVICE_NAME = 'Mailspring Dev'; - } + this.SERVICE_NAME = AppEnv.inDevMode() ? 'Mailspring Dev' : 'Mailspring'; this.KEY_NAME = 'Mailspring Keys'; } - deleteAccountSecrets(account) { - this._try(() => { - const keys = this._getKeyHash(); + async deleteAccountSecrets(account) { + try { + const keys = await this._getKeyHash(); delete keys[`${account.emailAddress}-imap`]; delete keys[`${account.emailAddress}-smtp`]; delete keys[`${account.emailAddress}-refresh-token`]; - return this._writeKeyHash(keys); - }); + await this._writeKeyHash(keys); + } catch (err) { + this._handleError(err); + } } - extractAccountSecrets(account) { + async extractAccountSecrets(account) { + try { + const keys = await this._getKeyHash(); + keys[`${account.emailAddress}-imap`] = account.settings.imap_password; + keys[`${account.emailAddress}-smtp`] = account.settings.smtp_password; + keys[`${account.emailAddress}-refresh-token`] = account.settings.refresh_token; + await this._writeKeyHash(keys); + } catch (err) { + this._handleError(err); + } const next = account.clone(); - this._try(() => { - const keys = this._getKeyHash(); - keys[`${account.emailAddress}-imap`] = next.settings.imap_password; - delete next.settings.imap_password; - keys[`${account.emailAddress}-smtp`] = next.settings.smtp_password; - delete next.settings.smtp_password; - keys[`${account.emailAddress}-refresh-token`] = next.settings.refresh_token; - delete next.settings.refresh_token; - return this._writeKeyHash(keys); - }); + delete next.settings.imap_password; + delete next.settings.smtp_password; + delete next.settings.refresh_token; return next; } - insertAccountSecrets(account) { + async insertAccountSecrets(account) { const next = account.clone(); - const keys = this._getKeyHash(); + const keys = await this._getKeyHash(); next.settings.imap_password = keys[`${account.emailAddress}-imap`]; next.settings.smtp_password = keys[`${account.emailAddress}-smtp`]; next.settings.refresh_token = keys[`${account.emailAddress}-refresh-token`]; return next; } - replacePassword(keyName, newVal) { - this._try(() => { - const keys = this._getKeyHash(); + async replacePassword(keyName, newVal) { + try { + const keys = await this._getKeyHash(); keys[keyName] = newVal; - return this._writeKeyHash(keys); - }); + await this._writeKeyHash(keys); + } catch (err) { + this._handleError(err); + } } - deletePassword(keyName) { - this._try(() => { - const keys = this._getKeyHash(); + async deletePassword(keyName) { + try { + const keys = await this._getKeyHash(); delete keys[keyName]; - return this._writeKeyHash(keys); - }); + await this._writeKeyHash(keys); + } catch (err) { + this._handleError(err); + } } - getPassword(keyName) { - const keys = this._getKeyHash(); - return keys[keyName]; + async getPassword(keyName) { + try { + const keys = await this._getKeyHash(); + return keys[keyName]; + } catch (err) { + this._handleError(err); + } } - _getKeyHash() { - const raw = keytar.getPassword(this.SERVICE_NAME, this.KEY_NAME) || '{}'; + async _getKeyHash() { + const raw = (await keytar.getPassword(this.SERVICE_NAME, this.KEY_NAME)) || '{}'; try { return JSON.parse(raw); } catch (err) { @@ -82,23 +91,17 @@ class KeyManager { } } - _writeKeyHash(keys) { - // returns true if successful - return keytar.replacePassword(this.SERVICE_NAME, this.KEY_NAME, JSON.stringify(keys)); + async _writeKeyHash(keys) { + await keytar.setPassword(this.SERVICE_NAME, this.KEY_NAME, JSON.stringify(keys)); } - _try(fn) { - const ERR_MSG = - "We couldn't store your password securely! For more information, visit http://support.getmailspring.com/hc/en-us/articles/115001875571"; - try { - if (!fn()) { - remote.dialog.showErrorBox('Password Management Error', ERR_MSG); - AppEnv.reportError(new Error(`Password Management Error: ${ERR_MSG}`)); - } - } catch (err) { - remote.dialog.showErrorBox('Password Management Error', ERR_MSG); - AppEnv.reportError(err); - } + _handleError(err) { + remote.dialog.showErrorBox( + 'Password Management Error', + "We couldn't store your password securely! For more information, visit http://support.getmailspring.com/hc/en-us/articles/115001875571" + ); + AppEnv.reportError(err); } } + export default new KeyManager();