From e742322eb10405428c64bbd3aecb80df69d52486 Mon Sep 17 00:00:00 2001 From: Michel Weststrate Date: Wed, 8 Dec 2021 04:25:28 -0800 Subject: [PATCH] Move keychain storage to server, some constants cleanup Summary: This diff moves keychain storage to the server. Figured to leave request logic itself in the UI-core, as basically all use cases happen there, except for streaming download for mobile build plugin, so sending the requests from the backend doesn't really seem to add value, unless we run into some CORS issues later. Reviewed By: passy Differential Revision: D32596715 fbshipit-source-id: f5ab9d794f91a6eb8a8dc07ae723bf2890726771 --- .../app/src/electron/initializeElectron.tsx | 8 +++- desktop/app/src/init.tsx | 19 ++++++-- desktop/flipper-common/src/server-types.tsx | 8 +++- .../src/FlipperServerImpl.tsx | 13 +++++- .../flipper-server-core/src/utils/keytar.tsx | 45 +++++++++++++++++++ desktop/flipper-ui-core/src/RenderHost.tsx | 8 +++- .../src/fb-stubs/constants.tsx | 3 ++ 7 files changed, 96 insertions(+), 8 deletions(-) create mode 100644 desktop/flipper-server-core/src/utils/keytar.tsx diff --git a/desktop/app/src/electron/initializeElectron.tsx b/desktop/app/src/electron/initializeElectron.tsx index 3a54fbf65..4d3291961 100644 --- a/desktop/app/src/electron/initializeElectron.tsx +++ b/desktop/app/src/electron/initializeElectron.tsx @@ -26,7 +26,7 @@ import type {RenderHost} from 'flipper-ui-core'; import fs from 'fs'; import {setupMenuBar} from './setupMenuBar'; import os from 'os'; -import {FlipperServerConfig} from 'flipper-common'; +import {FlipperServer, FlipperServerConfig} from 'flipper-common'; declare global { interface Window { @@ -45,7 +45,10 @@ if (process.env.NODE_ENV === 'development' && os.platform() === 'darwin') { global.electronRequire('mac-ca'); } -export function initializeElectron(flipperServerConfig: FlipperServerConfig) { +export function initializeElectron( + flipperServer: FlipperServer, + flipperServerConfig: FlipperServerConfig, +) { const execPath = process.execPath || remote.process.execPath; const isProduction = !/node_modules[\\/]electron[\\/]/.test(execPath); @@ -191,6 +194,7 @@ export function initializeElectron(flipperServerConfig: FlipperServerConfig) { GK(gatekeeper) { return flipperServerConfig.gatekeepers[gatekeeper] ?? false; }, + flipperServer, }; setupMenuBar(); diff --git a/desktop/app/src/init.tsx b/desktop/app/src/init.tsx index 8e6e03e67..9dea3a39d 100644 --- a/desktop/app/src/init.tsx +++ b/desktop/app/src/init.tsx @@ -26,7 +26,7 @@ import { loadSettings, setupPrefetcher, } from 'flipper-server-core'; -import {getLogger, Logger, setLoggerInstance} from 'flipper-common'; +import {getLogger, isTest, Logger, setLoggerInstance} from 'flipper-common'; import constants from './fb-stubs/constants'; import {initializeElectron} from './electron/initializeElectron'; import path from 'path'; @@ -54,19 +54,31 @@ if (process.env.NODE_ENV === 'development' && os.platform() === 'darwin') { async function start() { const app = remote.app; const execPath = process.execPath || remote.process.execPath; + const appPath = app.getAppPath(); const isProduction = !/node_modules[\\/]electron[\\/]/.test(execPath); const env = process.env; const logger = createDelegatedLogger(); setLoggerInstance(logger); + let keytar: any = undefined; + try { + if (!isTest()) { + keytar = (global.electronRequire || require)( + path.join(appPath, 'native-modules', `keytar-${process.platform}.node`), + ); + } + } catch (e) { + console.error('Failed to load keytar:', e); + } + const flipperServer = new FlipperServerImpl( { env, gatekeepers: getGatekeepers(), isProduction, paths: { - appPath: app.getAppPath(), + appPath, homePath: app.getPath('home'), execPath, staticPath: getStaticDir(), @@ -79,12 +91,13 @@ async function start() { validWebSocketOrigins: constants.VALID_WEB_SOCKET_REQUEST_ORIGIN_PREFIXES, }, logger, + keytar, ); await flipperServer.connect(); const flipperServerConfig = await flipperServer.exec('get-config'); - initializeElectron(flipperServerConfig); + initializeElectron(flipperServer, flipperServerConfig); // By turning this in a require, we force the JS that the body of this module (init) has completed (initializeElectron), // before starting the rest of the Flipper process. diff --git a/desktop/flipper-common/src/server-types.tsx b/desktop/flipper-common/src/server-types.tsx index ef2f9dab9..55052508b 100644 --- a/desktop/flipper-common/src/server-types.tsx +++ b/desktop/flipper-common/src/server-types.tsx @@ -155,13 +155,19 @@ export type FlipperServerCommands = { 'ios-launch-simulator': (udid: string) => Promise; 'persist-settings': (settings: Settings) => Promise; 'persist-launcher-settings': (settings: LauncherSettings) => Promise; + 'keychain-write': (service: string, token: string) => Promise; + 'keychain-read': (service: string) => Promise; + 'keychain-unset': (service: string) => Promise; }; type ENVIRONMENT_VARIABLES = | 'NODE_ENV' | 'DEV_SERVER_URL' | 'CONFIG' - | 'FLIPPER_ENABLED_PLUGINS'; + | 'FLIPPER_ENABLED_PLUGINS' + | 'FB_ONDEMAND' + | 'FLIPPER_INTERNGRAPH_URL'; + type ENVIRONMENT_PATHS = | 'appPath' | 'homePath' diff --git a/desktop/flipper-server-core/src/FlipperServerImpl.tsx b/desktop/flipper-server-core/src/FlipperServerImpl.tsx index ae956d0ae..88b2ece71 100644 --- a/desktop/flipper-server-core/src/FlipperServerImpl.tsx +++ b/desktop/flipper-server-core/src/FlipperServerImpl.tsx @@ -33,6 +33,7 @@ import {launchEmulator} from './devices/android/AndroidDevice'; import {setFlipperServerConfig} from './FlipperServerConfig'; import {saveSettings} from './utils/settings'; import {saveLauncherSettings} from './utils/launcherSettings'; +import {KeytarManager} from './utils/keytar'; /** * FlipperServer takes care of all incoming device & client connections. @@ -51,12 +52,18 @@ export class FlipperServerImpl implements FlipperServer { state: FlipperServerState = 'pending'; android: AndroidDeviceManager; ios: IOSDeviceManager; + keytarManager: KeytarManager; - constructor(public config: FlipperServerConfig, public logger: Logger) { + constructor( + public config: FlipperServerConfig, + public logger: Logger, + keytarModule?: any, + ) { setFlipperServerConfig(config); const server = (this.server = new ServerController(this)); this.android = new AndroidDeviceManager(this); this.ios = new IOSDeviceManager(this); + this.keytarManager = new KeytarManager(keytarModule); server.addListener('error', (err) => { this.emit('server-error', err); @@ -227,6 +234,10 @@ export class FlipperServerImpl implements FlipperServer { 'persist-settings': async (settings) => saveSettings(settings), 'persist-launcher-settings': async (settings) => saveLauncherSettings(settings), + 'keychain-read': (service) => this.keytarManager.retrieveToken(service), + 'keychain-write': (service, password) => + this.keytarManager.writeKeychain(service, password), + 'keychain-unset': (service) => this.keytarManager.unsetKeychain(service), }; registerDevice(device: ServerDevice) { diff --git a/desktop/flipper-server-core/src/utils/keytar.tsx b/desktop/flipper-server-core/src/utils/keytar.tsx new file mode 100644 index 000000000..4d262aa40 --- /dev/null +++ b/desktop/flipper-server-core/src/utils/keytar.tsx @@ -0,0 +1,45 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + */ + +import os from 'os'; +import {UserNotSignedInError} from 'flipper-common'; + +export class KeytarManager { + keytar: any; + + constructor(keytarModule: any) { + this.keytar = keytarModule; + } + + public async writeKeychain(service: string, password: string): Promise { + if (this.keytar == null) { + throw new Error('Keytar is not available.'); + } + await this.keytar.deletePassword(service, os.userInfo().username); + await this.keytar.setPassword(service, os.userInfo().username, password); + } + + public async unsetKeychain(service: string): Promise { + await this.keytar.deletePassword(service, os.userInfo().username); + } + + public async retrieveToken(service: string): Promise { + if (this.keytar == null) { + throw new Error('Keytar is not available.'); + } + const token = await this.keytar.getPassword( + service, + os.userInfo().username, + ); + if (!token) { + throw new UserNotSignedInError(); + } + return token; + } +} diff --git a/desktop/flipper-ui-core/src/RenderHost.tsx b/desktop/flipper-ui-core/src/RenderHost.tsx index 5b864791c..a5b5427de 100644 --- a/desktop/flipper-ui-core/src/RenderHost.tsx +++ b/desktop/flipper-ui-core/src/RenderHost.tsx @@ -11,7 +11,12 @@ import type {NotificationEvents} from './dispatcher/notifications'; import type {PluginNotification} from './reducers/notifications'; import type {NotificationConstructorOptions} from 'electron'; import {FlipperLib} from 'flipper-plugin'; -import {FlipperServerConfig, ReleaseChannel, Tristate} from 'flipper-common'; +import { + FlipperServer, + FlipperServerConfig, + ReleaseChannel, + Tristate, +} from 'flipper-common'; // TODO: those imports are only used for testing, require conditionally? import {tmpdir} from 'os'; import {resolve} from 'path'; @@ -99,6 +104,7 @@ export interface RenderHost { openLink(url: string): void; loadDefaultPlugins(): Record; GK(gatekeeper: string): boolean; + flipperServer?: FlipperServer; serverConfig: FlipperServerConfig; } diff --git a/desktop/flipper-ui-core/src/fb-stubs/constants.tsx b/desktop/flipper-ui-core/src/fb-stubs/constants.tsx index 2c7e688e7..deb6c5348 100644 --- a/desktop/flipper-ui-core/src/fb-stubs/constants.tsx +++ b/desktop/flipper-ui-core/src/fb-stubs/constants.tsx @@ -42,4 +42,7 @@ export default Object.freeze({ }, SUPPORT_GROUPS: [], + + INTERN_URL: '', + INTERNGRAPH_API: '', });