Skip to content

Commit

Permalink
Fix start without libsecret provider
Browse files Browse the repository at this point in the history
gives user an error that links to tutanota.com/faq/#secretstorage

fix #2929
  • Loading branch information
jowlo committed Apr 14, 2021
1 parent bc971a9 commit c8e2d24
Show file tree
Hide file tree
Showing 9 changed files with 46 additions and 25 deletions.
3 changes: 2 additions & 1 deletion flow/electron.js
Original file line number Diff line number Diff line change
Expand Up @@ -277,7 +277,8 @@ declare module 'electron' {
options: MessageBoxOptions
): Promise<{response: number, checkboxChecked: boolean}>,
showOpenDialog(browserWindow: ?BrowserWindow, options: OpenDialogOptions): Promise<{canceled: boolean, filePaths: string[]}>,
showSaveDialog(browserWindow: ?BrowserWindow, options: SaveDialogOptions): Promise<{canceled: boolean, filePath?: string, bookmark?: string}>
showSaveDialog(browserWindow: ?BrowserWindow, options: SaveDialogOptions): Promise<{canceled: boolean, filePath?: string, bookmark?: string}>,
showErrorBox(title: string, content: string): void,
}

declare export type OpenDialogOptions = {
Expand Down
10 changes: 10 additions & 0 deletions src/api/common/error/DeviceStorageUnavailableError.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// @flow
//@bundleInto:common-min

import {TutanotaError} from "./TutanotaError"

export class DeviceStorageUnavailableError extends TutanotaError {
constructor(msg: string, error: Error) {
super("DeviceStorageUnavailableError", error ? (msg + "> " + (error.stack ? error.stack : error.message)) : msg)
}
}
2 changes: 2 additions & 0 deletions src/api/common/utils/Utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ import {CancelledError} from "../error/CancelledError"
import {FileOpenError} from "../error/FileOpenError"
import {PermissionError} from "../error/PermissionError"
import {FileNotFoundError} from "../error/FileNotFoundError"
import {DeviceStorageUnavailableError} from "../error/DeviceStorageUnavailableError"

export type DeferredObject<T> = {
resolve: (T) => void,
Expand Down Expand Up @@ -470,6 +471,7 @@ const ErrorNameToType = {
CancelledError,
FileOpenError,
PayloadTooLargeError,
DeviceStorageUnavailableError,
Error,
"java.net.SocketTimeoutException": ConnectionError,
"java.net.ConnectException": ConnectionError,
Expand Down
14 changes: 6 additions & 8 deletions src/desktop/DesktopMain.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,13 +53,6 @@ const tray = new DesktopTray(conf)
const notifier = new DesktopNotifier(tray, new ElectronNotificationFactory())
const dl = new DesktopDownloadManager(conf, desktopNet, desktopUtils, fs, electron)
const alarmStorage = new DesktopAlarmStorage(conf, desktopCrypto, deviceKeyProvider)
deviceKeyProvider.getDeviceKey()
.then(() => {
log.debug("alarm storage initialized")
})
.catch(e => {
console.warn("alarm storage failed to initialize:", e)
})
const updater = new ElectronUpdater(conf, notifier, desktopCrypto, app, tray, new UpdaterWrapperImpl())
const shortcutManager = new LocalShortcutManager()
const wm = new WindowManager(conf, tray, notifier, electron, shortcutManager, dl)
Expand Down Expand Up @@ -102,7 +95,8 @@ function startupInstance() {

DesktopUtils.makeSingleInstance().then(willStay => {
if (!willStay) return
sse.start()
sse.start().catch(e => log.warn("unable to start sse client", e))

app.on('second-instance', (ev, args) => {
DesktopUtils.singleInstanceLockOverridden().then(overridden => {
if (overridden) {
Expand Down Expand Up @@ -134,6 +128,10 @@ function startupInstance() {
}

async function onAppReady() {
deviceKeyProvider.getDeviceKey().catch(() => {
electron.dialog.showErrorBox("Could not access secret storage", "Please see the FAQ at tutanota.com/faq/#secretstorage")
})

app.on('window-all-closed', async () => {
if (!(await conf.getVar('runAsTrayApp'))) {
app.quit()
Expand Down
5 changes: 4 additions & 1 deletion src/desktop/DesktopWindowManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,10 @@ export class WindowManager {
})
}
}
this._conf.on(DesktopConfigEncKey.sseInfo, sseValueListener, true)
this._conf.on(DesktopConfigEncKey.sseInfo, sseValueListener)
// call with value initially
this._conf.getVar(DesktopConfigEncKey.sseInfo)
.then(sseValueListener, (e) => log.error("Failed to get sseInfo", e))
}

hide() {
Expand Down
12 changes: 8 additions & 4 deletions src/desktop/DeviceKeyProviderImpl.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {base64ToKey, keyToBase64} from "../api/worker/crypto/CryptoUtils"
import type {SecretStorage} from "./sse/SecretStorage"
import {DesktopCryptoFacade} from "./DesktopCryptoFacade"
import {log} from "./DesktopLog"
import {DeviceStorageUnavailableError} from "../api/common/error/DeviceStorageUnavailableError"

// exported for testing
export const SERVICE_NAME = 'tutanota-vault'
Expand All @@ -25,10 +26,13 @@ export class DeviceKeyProviderImpl implements DeviceKeyProvider {

async getDeviceKey(): Promise<Aes256Key> {
if (this._deviceKey) return this._deviceKey

const storedKey = await this._secretStorage.getPassword(SERVICE_NAME, ACCOUNT_NAME)
this._deviceKey = storedKey ? base64ToKey(storedKey) : await this._generateAndStoreDeviceKey()
return this._deviceKey
try {
const storedKey = await this._secretStorage.getPassword(SERVICE_NAME, ACCOUNT_NAME)
this._deviceKey = storedKey ? base64ToKey(storedKey) : await this._generateAndStoreDeviceKey()
return this._deviceKey
} catch (e) {
throw new DeviceStorageUnavailableError("could not access device secret storage", e)
}
}

async _generateAndStoreDeviceKey(): Promise<Aes256Key> {
Expand Down
7 changes: 1 addition & 6 deletions src/desktop/config/DesktopConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -136,19 +136,14 @@ export class DesktopConfig {
* listen to changes in the config
* @param key the value you want to listen for. a key of "any" will be called with the complete config for any changes to the config.
* @param cb a function that's called when the config changes. argument is the new value or the entire config object in case of the "any" event.
* @param sendInitial {boolean} whether cb should be notified of current value right away. Default false.
* @returns {DesktopConfig}
*/
on(key: DesktopConfigKeyEnum | DesktopConfigEncKeyEnum, cb: (val: any) => void, sendInitial: boolean = false): DesktopConfig {
on(key: DesktopConfigKeyEnum | DesktopConfigEncKeyEnum, cb: (val: any) => void): DesktopConfig {
if (!this._onValueSetListeners[key]) {
this._onValueSetListeners[key] = [cb]
} else {
this._onValueSetListeners[key].push(cb)
}

if (sendInitial) {
this.getVar(key).then(cb)
}
return this
}

Expand Down
11 changes: 8 additions & 3 deletions src/desktop/config/migrations/migration-0003.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,24 @@ import {DesktopCryptoFacade} from "../../DesktopCryptoFacade"
import type {Config} from "../ConfigCommon"
import {downcast} from "../../../api/common/utils/Utils"
import type {DeviceKeyProvider} from "../../DeviceKeyProviderImpl"
import {log} from "../../DesktopLog"

async function migrate(oldConfig: Config, crypto: DesktopCryptoFacade, deviceKeyProvider: DeviceKeyProvider): Promise<void> {
Object.assign(oldConfig, {
"desktopConfigVersion": 3,
})

if (oldConfig.pushIdentifier) {
const deviceKey = await deviceKeyProvider.getDeviceKey()

Object.assign(oldConfig, {"sseInfo": crypto.aesEncryptObject(deviceKey, downcast(oldConfig.pushIdentifier))})
try {
const deviceKey = await deviceKeyProvider.getDeviceKey()
Object.assign(oldConfig, {"sseInfo": crypto.aesEncryptObject(deviceKey, downcast(oldConfig.pushIdentifier))})
} catch (e) {
// cannot read device key, just remove sseInfo from old config
log.warn("migration003: could not read device key, will not save sseInfo", e)
}
delete oldConfig.pushIdentifier
}

}

export const migrateClient = migrate
Expand Down
7 changes: 5 additions & 2 deletions src/native/main/PushServiceApp.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
//@flow
import {load, loadAll, setup, update} from "../../api/main/Entity"
import type {PushIdentifier} from "../../api/entities/sys/PushIdentifier"
import {_TypeModel as PushIdentifierModel, createPushIdentifier, PushIdentifierTypeRef} from "../../api/entities/sys/PushIdentifier"
import {neverNull} from "../../api/common/utils/Utils"
import type {PushServiceTypeEnum} from "../../api/common/TutanotaConstants"
Expand All @@ -12,9 +13,8 @@ import {logins} from "../../api/main/LoginController"
import {worker} from "../../api/main/WorkerClient"
import {client} from "../../misc/ClientDetector.js"
import {deviceConfig} from "../../misc/DeviceConfig"
import type {PushIdentifier} from "../../api/entities/sys/PushIdentifier"
import {getElementId} from "../../api/common/utils/EntityUtils";
import type {AlarmNotification} from "../../api/entities/sys/AlarmNotification"
import {DeviceStorageUnavailableError} from "../../api/common/error/DeviceStorageUnavailableError"

class PushServiceApp {
_pushNotification: ?Object;
Expand Down Expand Up @@ -54,6 +54,9 @@ class PushServiceApp {
}
})
.then(this._initPushNotifications)
.catch(DeviceStorageUnavailableError, (e) => {
console.warn("Device storage is unavailable, cannot register for push notifications", e)
})
} else if (isIOSApp()) {
return this._loadPushIdentifierFromNative()
.then(identifier => {
Expand Down

0 comments on commit c8e2d24

Please sign in to comment.