Move sending intern requests from client to server

Summary: This diff moves send intern request from the browser to the server. The reason to make this change is that making such requests from a browser environment causes CORS restrictions to kick in.

Reviewed By: nikoant

Differential Revision: D32835449

fbshipit-source-id: e8e92e51ca963aa50b3c859bb61c2381171e85ae
This commit is contained in:
Michel Weststrate
2021-12-08 04:25:28 -08:00
committed by Facebook GitHub Bot
parent ad4a55f263
commit 943d535e86
18 changed files with 180 additions and 96 deletions

View File

@@ -42,7 +42,6 @@ export {
getStringFromErrorLike, getStringFromErrorLike,
getErrorFromErrorLike, getErrorFromErrorLike,
} from './utils/errors'; } from './utils/errors';
export * from './user-session';
export * from './GK'; export * from './GK';
export * from './clientUtils'; export * from './clientUtils';
export * from './settings'; export * from './settings';

View File

@@ -191,6 +191,39 @@ export type FlipperServerCommands = {
name: string, name: string,
) => Promise<FlipperDoctor.HealthcheckResult>; ) => Promise<FlipperDoctor.HealthcheckResult>;
'open-file': (path: string) => Promise<void>; 'open-file': (path: string) => Promise<void>;
'intern-graph-post': (
endpoint: string,
formFields: Record<string, any>,
fileFields: Record<string, GraphFileUpload>,
options: {
timeout?: number;
internGraphUrl?: string;
},
) => Promise<GraphResponse>;
'intern-graph-get': (
endpoint: string,
params: Record<string, any>,
options: {
timeout?: number;
internGraphUrl?: string;
},
) => Promise<GraphResponse>;
'intern-upload-scribe-logs': (
messages: {category: string; message: string}[],
) => Promise<void>;
};
export type GraphResponse = {
status: number;
data: any;
headers: Record<string, any>;
};
export type GraphFileUpload = {
path?: string;
contents?: string;
filename?: string;
contentType?: string;
}; };
/** /**

View File

@@ -1,41 +0,0 @@
/**
* 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
*/
/**
* APIs need to integration with Facebook services.
*/
export type UserSessionManager = {
internGraphPOSTAPIRequest: typeof internGraphPOSTAPIRequest;
};
let instance: UserSessionManager | undefined = undefined;
function getInstance(): UserSessionManager {
if (!instance) {
throw new Error('UserSessionManager not available or implemented');
}
return instance;
}
export function setUserSessionManagerInstance(i: UserSessionManager) {
instance = i;
}
export async function internGraphPOSTAPIRequest(
_endpoint: string,
_formFields: {
[key: string]: any;
} = {},
_internGraphUrl?: string,
_timeout?: number,
): Promise<any> {
// eslint-disable-next-line
return getInstance().internGraphPOSTAPIRequest.apply(null, arguments as any);
}

View File

@@ -11,15 +11,16 @@
"bugs": "https://github.com/facebook/flipper/issues", "bugs": "https://github.com/facebook/flipper/issues",
"dependencies": { "dependencies": {
"@iarna/toml": "^2.2.5", "@iarna/toml": "^2.2.5",
"axios": "^0.22.0",
"JSONStream": "^1.3.1", "JSONStream": "^1.3.1",
"adbkit": "^2.11.1", "adbkit": "^2.11.1",
"adbkit-logcat": "^2.0.1", "adbkit-logcat": "^2.0.1",
"archiver": "^5.3.0", "archiver": "^5.3.0",
"async-mutex": "^0.3.2", "async-mutex": "^0.3.2",
"flipper-plugin-lib": "0.0.0", "axios": "^0.22.0",
"flipper-common": "0.0.0", "flipper-common": "0.0.0",
"flipper-doctor": "0.0.0", "flipper-doctor": "0.0.0",
"flipper-plugin-lib": "0.0.0",
"form-data": "^4.0.0",
"fs-extra": "^10.0.0", "fs-extra": "^10.0.0",
"invariant": "^2.2.4", "invariant": "^2.2.4",
"js-base64": "^3.7.2", "js-base64": "^3.7.2",

View File

@@ -38,6 +38,13 @@ import {PluginManager} from './plugins/PluginManager';
import {runHealthcheck, getHealthChecks} from './utils/runHealthchecks'; import {runHealthcheck, getHealthChecks} from './utils/runHealthchecks';
import {openFile} from './utils/openFile'; import {openFile} from './utils/openFile';
import {getChangelog} from './utils/pathUtils'; import {getChangelog} from './utils/pathUtils';
import {sendScribeLogs} from './fb-stubs/sendScribeLogs';
import {
internGraphGETAPIRequest,
internGraphPOSTAPIRequest,
} from './fb-stubs/internRequests';
export const SERVICE_FLIPPER = 'flipper.oAuthToken';
/** /**
* FlipperServer takes care of all incoming device & client connections. * FlipperServer takes care of all incoming device & client connections.
@@ -184,14 +191,24 @@ export class FlipperServerImpl implements FlipperServer {
exec<Event extends keyof FlipperServerCommands>( exec<Event extends keyof FlipperServerCommands>(
event: Event, event: Event,
...args: Parameters<FlipperServerCommands[Event]> ...args: Parameters<FlipperServerCommands[Event]>
): ReturnType<FlipperServerCommands[Event]> { ): ReturnType<FlipperServerCommands[Event]>;
console.debug(`[FlipperServer] command ${event}: `, args); async exec<Event extends keyof FlipperServerCommands>(
event: Event,
...args: any[]
): Promise<any> {
try {
const handler: (...args: any[]) => Promise<any> = const handler: (...args: any[]) => Promise<any> =
this.commandHandler[event]; this.commandHandler[event];
if (!handler) { if (!handler) {
throw new Error(`Unimplemented server command: ${event}`); throw new Error(`Unimplemented server command: ${event}`);
} }
return handler(...args) as any; const result = await handler(...args);
console.debug(`[FlipperServer] command '${event}' - OK`);
return result;
} catch (e) {
console.debug(`[FlipperServer] command '${event}' - ERROR: ${e} `);
throw e;
}
} }
private commandHandler: FlipperServerCommands = { private commandHandler: FlipperServerCommands = {
@@ -276,6 +293,21 @@ export class FlipperServerImpl implements FlipperServer {
'doctor-get-healthchecks': getHealthChecks, 'doctor-get-healthchecks': getHealthChecks,
'doctor-run-healthcheck': runHealthcheck, 'doctor-run-healthcheck': runHealthcheck,
'open-file': openFile, 'open-file': openFile,
'intern-graph-post': async (endpoint, formfields, filefields, options) => {
const token = await this.keytarManager.retrieveToken(SERVICE_FLIPPER);
return internGraphPOSTAPIRequest(
endpoint,
formfields,
filefields,
options,
token,
);
},
'intern-graph-get': async (endpoint, params, options) => {
const token = await this.keytarManager.retrieveToken(SERVICE_FLIPPER);
return internGraphGETAPIRequest(endpoint, params, options, token);
},
'intern-upload-scribe-logs': sendScribeLogs,
}; };
registerDevice(device: ServerDevice) { registerDevice(device: ServerDevice) {

View File

@@ -95,7 +95,6 @@ class ServerController extends EventEmitter implements ServerEventsListener {
this.timestamps = new Map(); this.timestamps = new Map();
this.certificateProvider = new CertificateProvider( this.certificateProvider = new CertificateProvider(
this, this,
this.logger,
getFlipperServerConfig().settings, getFlipperServerConfig().settings,
); );
this.connectionTracker = new ConnectionTracker(this.logger); this.connectionTracker = new ConnectionTracker(this.logger);

View File

@@ -0,0 +1,41 @@
/**
* 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 {GraphFileUpload, GraphResponse} from 'flipper-common';
/* eslint-disable @typescript-eslint/no-unused-vars */
export async function internGraphPOSTAPIRequest(
endpoint: string,
formFields: {
[key: string]: any;
},
fileFields: Record<string, GraphFileUpload>,
options: {
timeout?: number;
internGraphUrl?: string;
},
token: string,
): Promise<GraphResponse> {
throw new Error('Feature not implemented');
}
export async function internGraphGETAPIRequest(
endpoint: string,
params: {
[key: string]: any;
},
_options: {
timeout?: number;
internGraphUrl?: string;
},
token: string,
): Promise<GraphResponse> {
throw new Error('Feature not implemented');
}

View File

@@ -0,0 +1,12 @@
/**
* 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
*/
export async function sendScribeLogs(
_messages: {category: string; message: string}[],
): Promise<void> {}

View File

@@ -7,8 +7,6 @@
* @format * @format
*/ */
import {Logger} from 'flipper-common';
import {internGraphPOSTAPIRequest} from 'flipper-common';
import ServerController from '../comms/ServerController'; import ServerController from '../comms/ServerController';
import {promisify} from 'util'; import {promisify} from 'util';
import fs from 'fs-extra'; import fs from 'fs-extra';
@@ -28,6 +26,8 @@ import {Client as ADBClient} from 'adbkit';
import archiver from 'archiver'; import archiver from 'archiver';
import {timeout, isTest} from 'flipper-common'; import {timeout, isTest} from 'flipper-common';
import {v4 as uuid} from 'uuid'; import {v4 as uuid} from 'uuid';
import {internGraphPOSTAPIRequest} from '../fb-stubs/internRequests';
import {SERVICE_FLIPPER} from '../FlipperServerImpl';
export type CertificateExchangeMedium = 'FS_ACCESS' | 'WWW' | 'NONE'; export type CertificateExchangeMedium = 'FS_ACCESS' | 'WWW' | 'NONE';
@@ -89,7 +89,6 @@ type CertificateProviderConfig = {
* Flipper CA. * Flipper CA.
*/ */
export default class CertificateProvider { export default class CertificateProvider {
private logger: Logger;
private _adb: Promise<ADBClient> | undefined; private _adb: Promise<ADBClient> | undefined;
private didCertificateSetup = false; private didCertificateSetup = false;
private config: CertificateProviderConfig; private config: CertificateProviderConfig;
@@ -105,12 +104,7 @@ export default class CertificateProvider {
throw new Error('Android is not enabled in settings'); throw new Error('Android is not enabled in settings');
} }
constructor( constructor(server: ServerController, config: CertificateProviderConfig) {
server: ServerController,
logger: Logger,
config: CertificateProviderConfig,
) {
this.logger = logger;
// TODO: refactor this code to create promise lazily // TODO: refactor this code to create promise lazily
this._adb = config.enableAndroid this._adb = config.enableAndroid
? (getAdbClient(config).catch((_e) => { ? (getAdbClient(config).catch((_e) => {
@@ -133,15 +127,25 @@ export default class CertificateProvider {
zipPath: string, zipPath: string,
deviceID: string, deviceID: string,
): Promise<void> => { ): Promise<void> => {
const buff = await fs.readFile(zipPath);
const file = new File([buff], 'certs.zip');
return reportPlatformFailures( return reportPlatformFailures(
timeout( timeout(
5 * 60 * 1000, 5 * 60 * 1000,
internGraphPOSTAPIRequest('flipper/certificates', { internGraphPOSTAPIRequest(
certificate_zip: file, 'flipper/certificates',
{
device_id: deviceID, device_id: deviceID,
}), },
{
certificate_zip: {
path: zipPath,
filename: 'certs.zip',
},
},
{timeout: 5 * 60 * 1000},
await this.server.flipperServer.keytarManager.retrieveToken(
SERVICE_FLIPPER,
),
).then(() => {}),
'Timed out uploading Flipper certificates to WWW.', 'Timed out uploading Flipper certificates to WWW.',
), ),
'uploadCertificates', 'uploadCertificates',

View File

@@ -21,6 +21,7 @@ export class KeytarManager {
if (this.keytar == null) { if (this.keytar == null) {
throw new Error('Keytar is not available.'); throw new Error('Keytar is not available.');
} }
await this.keytar.deletePassword(service, os.userInfo().username); await this.keytar.deletePassword(service, os.userInfo().username);
await this.keytar.setPassword(service, os.userInfo().username, password); await this.keytar.setPassword(service, os.userInfo().username, password);
} }
@@ -40,6 +41,7 @@ export class KeytarManager {
if (!token) { if (!token) {
throw new UserNotSignedInError(); throw new UserNotSignedInError();
} }
return token; return token;
} }
} }

View File

@@ -22,6 +22,11 @@ const rootDir = path.resolve(__dirname, '..', '..');
const staticDir = path.join(rootDir, 'static'); const staticDir = path.join(rootDir, 'static');
async function start() { async function start() {
// supress debug messages by default. TODO: make CLI flag
console.debug = function () {
// Noop
};
const {app, server, socket} = await startBaseServer({ const {app, server, socket} = await startBaseServer({
port: PORT, port: PORT,
staticDir, staticDir,

View File

@@ -27,7 +27,6 @@ export function startSocketServer(
flipperServer.onAny(onServerEvent); flipperServer.onAny(onServerEvent);
client.on('exec', (id, command, args) => { client.on('exec', (id, command, args) => {
console.log(id, command, args);
flipperServer flipperServer
.exec(command, ...args) .exec(command, ...args)
.then((result: any) => { .then((result: any) => {

View File

@@ -13,9 +13,9 @@ import {ReactReduxContext, ReactReduxContextValue} from 'react-redux';
import {Logger} from 'flipper-common'; import {Logger} from 'flipper-common';
import {IdlerImpl} from '../utils/Idler'; import {IdlerImpl} from '../utils/Idler';
import { import {
shareFlipperData,
DataExportResult, DataExportResult,
DataExportError, DataExportError,
shareFlipperData,
} from '../fb-stubs/user'; } from '../fb-stubs/user';
import { import {
exportStore, exportStore,

View File

@@ -10,19 +10,6 @@
import {DeviceOS} from 'flipper-plugin'; import {DeviceOS} from 'flipper-plugin';
export default Object.freeze({ export default Object.freeze({
GRAPH_APP_ID: '',
GRAPH_CLIENT_TOKEN: '',
GRAPH_ACCESS_TOKEN: '',
// this provides elevated access to scribe. we really shouldn't be exposing this.
// need to investigate how to abstract the scribe logging so it's safe.
GRAPH_SECRET: '',
GRAPH_SECRET_ACCESS_TOKEN: '',
// Provides access to Insights Validation endpoint on interngraph
INSIGHT_INTERN_APP_ID: '',
INSIGHT_INTERN_APP_TOKEN: '',
// Enables the flipper data to be exported through shareabale link // Enables the flipper data to be exported through shareabale link
ENABLE_SHAREABLE_LINK: false, ENABLE_SHAREABLE_LINK: false,
@@ -44,5 +31,4 @@ export default Object.freeze({
SUPPORT_GROUPS: [], SUPPORT_GROUPS: [],
INTERN_URL: '', INTERN_URL: '',
INTERNGRAPH_API: '',
}); });

View File

@@ -7,6 +7,7 @@
* @format * @format
*/ */
import {GraphFileUpload} from 'flipper-common';
import {Atom, createState} from 'flipper-plugin'; import {Atom, createState} from 'flipper-plugin';
import {User} from '../reducers/user'; import {User} from '../reducers/user';
@@ -19,7 +20,11 @@ export async function internGraphPOSTAPIRequest(
_formFields: { _formFields: {
[key: string]: any; [key: string]: any;
} = {}, } = {},
_internGraphUrl?: string, _fileFields: Record<string, GraphFileUpload> = {},
_options: {
timeout?: number;
internGraphUrl?: string;
} = {},
): Promise<any> { ): Promise<any> {
throw new Error('Feature not implemented'); throw new Error('Feature not implemented');
} }
@@ -29,7 +34,10 @@ export async function internGraphGETAPIRequest(
_params: { _params: {
[key: string]: any; [key: string]: any;
} = {}, } = {},
_internGraphUrl?: string, _options: {
timeout?: number;
internGraphUrl?: string;
} = {},
): Promise<any> { ): Promise<any> {
throw new Error('Feature not implemented'); throw new Error('Feature not implemented');
} }

View File

@@ -39,13 +39,7 @@ import styled from '@emotion/styled';
import {CopyOutlined} from '@ant-design/icons'; import {CopyOutlined} from '@ant-design/icons';
import {getVersionString} from './utils/versionString'; import {getVersionString} from './utils/versionString';
import {PersistGate} from 'redux-persist/integration/react'; import {PersistGate} from 'redux-persist/integration/react';
import { import {setLoggerInstance, FlipperServer} from 'flipper-common';
setLoggerInstance,
setUserSessionManagerInstance,
Settings,
FlipperServer,
} from 'flipper-common';
import {internGraphPOSTAPIRequest} from './fb-stubs/user';
import {getRenderHostInstance} from './RenderHost'; import {getRenderHostInstance} from './RenderHost';
import {startGlobalErrorHandling} from './utils/globalErrorHandling'; import {startGlobalErrorHandling} from './utils/globalErrorHandling';
import {loadTheme} from './utils/loadTheme'; import {loadTheme} from './utils/loadTheme';
@@ -168,9 +162,6 @@ function init(flipperServer: FlipperServer) {
else console.warn(msg, r.error); else console.warn(msg, r.error);
} }
}); });
setUserSessionManagerInstance({
internGraphPOSTAPIRequest,
});
ReactDOM.render( ReactDOM.render(
<AppFrame logger={logger} persistor={persistor} />, <AppFrame logger={logger} persistor={persistor} />,

View File

@@ -33,13 +33,13 @@ import {deconstructClientId} from 'flipper-common';
import {processMessageQueue} from './messageQueue'; import {processMessageQueue} from './messageQueue';
import {getPluginTitle} from './pluginUtils'; import {getPluginTitle} from './pluginUtils';
import {capture} from './screenshot'; import {capture} from './screenshot';
import {uploadFlipperMedia} from '../fb-stubs/user';
import {Dialog, Idler} from 'flipper-plugin'; import {Dialog, Idler} from 'flipper-plugin';
import {ClientQuery} from 'flipper-common'; import {ClientQuery} from 'flipper-common';
import ShareSheetExportUrl from '../chrome/ShareSheetExportUrl'; import ShareSheetExportUrl from '../chrome/ShareSheetExportUrl';
import ShareSheetExportFile from '../chrome/ShareSheetExportFile'; import ShareSheetExportFile from '../chrome/ShareSheetExportFile';
import ExportDataPluginSheet from '../chrome/ExportDataPluginSheet'; import ExportDataPluginSheet from '../chrome/ExportDataPluginSheet';
import {getRenderHostInstance} from '../RenderHost'; import {getRenderHostInstance} from '../RenderHost';
import {uploadFlipperMedia} from '../fb-stubs/user';
export const IMPORT_FLIPPER_TRACE_EVENT = 'import-flipper-trace'; export const IMPORT_FLIPPER_TRACE_EVENT = 'import-flipper-trace';
export const EXPORT_FLIPPER_TRACE_EVENT = 'export-flipper-trace'; export const EXPORT_FLIPPER_TRACE_EVENT = 'export-flipper-trace';

View File

@@ -14,3 +14,16 @@ declare const electronRequire: {
resolve: (module: string) => string; resolve: (module: string) => string;
cache: {[module: string]: any}; cache: {[module: string]: any};
}; };
// For Electron
declare module NodeJS {
interface Global {
__REVISION__: string | undefined;
__VERSION__: string;
electronRequire: {
(name: string): any;
resolve: (module: string) => string;
cache: {[module: string]: any};
};
}
}