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: '', });