105 lines
3.0 KiB
TypeScript
105 lines
3.0 KiB
TypeScript
import { app, BrowserWindow, ipcMain } from 'electron'
|
|
import keytar from 'keytar'
|
|
import { MessageToMainProcess } from '../../Shared/IpcMessages'
|
|
import { Store, StoreKeys } from '../Store'
|
|
import { AppName } from '../Strings'
|
|
import { keychainAccessIsUserConfigurable } from '../Types/Constants'
|
|
import { Paths, Urls } from '../Types/Paths'
|
|
import { isLinux } from '../Types/Platforms'
|
|
import { isDev, isTesting } from '../Utils/Utils'
|
|
import { KeychainInterface } from './KeychainInterface'
|
|
|
|
const ServiceName = isTesting() ? AppName + ' (Testing)' : isDev() ? AppName + ' (Development)' : AppName
|
|
const AccountName = 'Standard Notes Account'
|
|
|
|
async function ensureKeychainAccess(store: Store): Promise<BrowserWindow | undefined> {
|
|
if (!isLinux()) {
|
|
/** Assume keychain is accessible */
|
|
return
|
|
}
|
|
const useNativeKeychain = store.get(StoreKeys.UseNativeKeychain)
|
|
if (useNativeKeychain === null) {
|
|
/**
|
|
* App has never attempted to access keychain before. Do it and set the
|
|
* store value according to what happens
|
|
*/
|
|
try {
|
|
await getKeychainValue()
|
|
store.set(StoreKeys.UseNativeKeychain, true)
|
|
} catch (_) {
|
|
/** Can't access keychain. */
|
|
if (keychainAccessIsUserConfigurable) {
|
|
return askForKeychainAccess(store)
|
|
} else {
|
|
/** User can't configure keychain access, fall back to local storage */
|
|
store.set(StoreKeys.UseNativeKeychain, false)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function askForKeychainAccess(store: Store): Promise<BrowserWindow> {
|
|
const window = new BrowserWindow({
|
|
width: 540,
|
|
height: 400,
|
|
center: true,
|
|
show: false,
|
|
webPreferences: {
|
|
preload: Paths.grantLinuxPasswordsAccessJs,
|
|
nodeIntegration: false,
|
|
contextIsolation: true,
|
|
},
|
|
autoHideMenuBar: true,
|
|
})
|
|
|
|
window.on('ready-to-show', window.show)
|
|
|
|
void window.loadURL(Urls.grantLinuxPasswordsAccessHtml)
|
|
|
|
const quit = () => {
|
|
app.quit()
|
|
}
|
|
ipcMain.once(MessageToMainProcess.Quit, quit)
|
|
window.once('close', quit)
|
|
|
|
ipcMain.on(MessageToMainProcess.LearnMoreAboutKeychainAccess, () => {
|
|
window.setSize(window.getSize()[0], 600, true)
|
|
})
|
|
|
|
return new Promise((resolve) => {
|
|
ipcMain.once(MessageToMainProcess.UseLocalstorageForKeychain, () => {
|
|
store.set(StoreKeys.UseNativeKeychain, false)
|
|
ipcMain.removeListener(MessageToMainProcess.Quit, quit)
|
|
window.removeListener('close', quit)
|
|
resolve(window)
|
|
})
|
|
})
|
|
}
|
|
|
|
async function getKeychainValue(): Promise<unknown> {
|
|
try {
|
|
const value = await keytar.getPassword(ServiceName, AccountName)
|
|
if (value) {
|
|
return JSON.parse(value)
|
|
}
|
|
} catch (error) {
|
|
console.error(error)
|
|
throw error
|
|
}
|
|
}
|
|
|
|
function setKeychainValue(value: unknown): Promise<void> {
|
|
return keytar.setPassword(ServiceName, AccountName, JSON.stringify(value))
|
|
}
|
|
|
|
function clearKeychainValue(): Promise<boolean> {
|
|
return keytar.deletePassword(ServiceName, AccountName)
|
|
}
|
|
|
|
export const Keychain: KeychainInterface = {
|
|
ensureKeychainAccess,
|
|
getKeychainValue,
|
|
setKeychainValue,
|
|
clearKeychainValue,
|
|
}
|