Medium refactoring

Summary:
Simplifies medium usage. Clients report this value as an integer. Internally, we transform this integer as type (a set of valid strings).

Instead of transform this value in different places, do it once when the client query is received.

Reviewed By: antonk52

Differential Revision: D46358024

fbshipit-source-id: ecd2b6c6ccbe7c38787a89d4e2f81930c7b91864
This commit is contained in:
Lorenzo Blasa
2023-06-02 03:59:15 -07:00
committed by Facebook GitHub Bot
parent 3607b7f996
commit 2f9e633fad
15 changed files with 84 additions and 41 deletions

View File

@@ -30,6 +30,7 @@ import {LoggerInfo} from './utils/Logger';
// Since flipper-plugin however is currently shared among server, client and defines a lot of base types, leaving it here for now. // Since flipper-plugin however is currently shared among server, client and defines a lot of base types, leaving it here for now.
export type FlipperServerType = 'embedded' | 'external'; export type FlipperServerType = 'embedded' | 'external';
export type CertificateExchangeMedium = 'FS_ACCESS' | 'WWW' | 'NONE';
export type FlipperServerState = export type FlipperServerState =
| 'pending' | 'pending'
@@ -87,6 +88,7 @@ export type ClientQuery = {
readonly device: string; readonly device: string;
readonly device_id: string; readonly device_id: string;
readonly sdk_version?: number; readonly sdk_version?: number;
medium: CertificateExchangeMedium;
rsocket?: boolean; rsocket?: boolean;
}; };

View File

@@ -11,7 +11,6 @@ import './utils/macCa';
import './utils/fetch-polyfill'; import './utils/fetch-polyfill';
import EventEmitter from 'events'; import EventEmitter from 'events';
import {ServerController} from './comms/ServerController'; import {ServerController} from './comms/ServerController';
import {CertificateExchangeMedium} from './utils/CertificateProvider';
import {AndroidDeviceManager} from './devices/android/androidDeviceManager'; import {AndroidDeviceManager} from './devices/android/androidDeviceManager';
import {IOSDeviceManager} from './devices/ios/iOSDeviceManager'; import {IOSDeviceManager} from './devices/ios/iOSDeviceManager';
import metroDevice from './devices/metro/metroDeviceManager'; import metroDevice from './devices/metro/metroDeviceManager';
@@ -26,6 +25,7 @@ import {
Logger, Logger,
FlipperServerExecOptions, FlipperServerExecOptions,
DeviceDebugData, DeviceDebugData,
CertificateExchangeMedium,
} from 'flipper-common'; } from 'flipper-common';
import {ServerDevice} from './devices/ServerDevice'; import {ServerDevice} from './devices/ServerDevice';
import {Base64} from 'js-base64'; import {Base64} from 'js-base64';

View File

@@ -144,7 +144,7 @@ class BrowserServerWebSocket extends SecureServerWebSocket {
if (!parsedBaseQuery) { if (!parsedBaseQuery) {
return; return;
} }
return {...parsedBaseQuery, medium: 3}; return {...parsedBaseQuery, medium: 'NONE'};
} }
protected verifyClient(): ws.VerifyClientCallbackSync { protected verifyClient(): ws.VerifyClientCallbackSync {

View File

@@ -7,9 +7,7 @@
* @format * @format
*/ */
import {CertificateExchangeMedium} from '../utils/CertificateProvider';
import {ClientConnection} from './ClientConnection'; import {ClientConnection} from './ClientConnection';
import {transformCertificateExchangeMediumToType} from './Utilities';
import { import {
ClientDescription, ClientDescription,
ClientQuery, ClientQuery,
@@ -30,8 +28,7 @@ export type ClientCsrQuery = {
* SecureClientQuery combines a ClientQuery with * SecureClientQuery combines a ClientQuery with
* ClientCsrQuery. It also adds medium information. * ClientCsrQuery. It also adds medium information.
*/ */
export type SecureClientQuery = ClientQuery & export type SecureClientQuery = ClientQuery & ClientCsrQuery;
ClientCsrQuery & {medium: 1 /*FS*/ | 2 /*WWW*/ | 3 /*NONE*/ | undefined};
/** /**
* Defines an interface for events triggered by a running server interacting * Defines an interface for events triggered by a running server interacting
@@ -75,7 +72,6 @@ export interface ServerEventsListener {
unsanitizedCSR: string, unsanitizedCSR: string,
clientQuery: ClientQuery, clientQuery: ClientQuery,
appDirectory: string, appDirectory: string,
medium: CertificateExchangeMedium,
): Promise<{deviceId: string}>; ): Promise<{deviceId: string}>;
/** /**
* A secure connection has been established with a validated client. * A secure connection has been established with a validated client.
@@ -161,7 +157,7 @@ abstract class ServerAdapter {
if (message.method === 'signCertificate') { if (message.method === 'signCertificate') {
console.debug('CSR received from device', 'server'); console.debug('CSR received from device', 'server');
const {csr, destination, medium} = message; const {csr, destination} = message;
console.info( console.info(
`[conn] Starting certificate exchange: ${clientQuery.app} on ${clientQuery.device}`, `[conn] Starting certificate exchange: ${clientQuery.app} on ${clientQuery.device}`,
@@ -171,7 +167,6 @@ abstract class ServerAdapter {
csr, csr,
clientQuery, clientQuery,
destination, destination,
transformCertificateExchangeMediumToType(medium),
); );
console.info( console.info(

View File

@@ -7,7 +7,6 @@
* @format * @format
*/ */
import {CertificateExchangeMedium} from '../utils/CertificateProvider';
import { import {
ClientDescription, ClientDescription,
ClientQuery, ClientQuery,
@@ -27,7 +26,6 @@ import {
appNameWithUpdateHint, appNameWithUpdateHint,
assertNotNull, assertNotNull,
cloneClientQuerySafeForLogging, cloneClientQuerySafeForLogging,
transformCertificateExchangeMediumToType,
} from './Utilities'; } from './Utilities';
import ServerAdapter, { import ServerAdapter, {
SecureClientQuery, SecureClientQuery,
@@ -182,7 +180,7 @@ export class ServerController
medium, medium,
rsocket, rsocket,
} = clientQuery; } = clientQuery;
const transformedMedium = transformCertificateExchangeMediumToType(medium);
console.info( console.info(
`[conn] Connection established: ${app} on ${device_id}. Medium ${medium}. CSR: ${csr_path}`, `[conn] Connection established: ${app} on ${device_id}. Medium ${medium}. CSR: ${csr_path}`,
cloneClientQuerySafeForLogging(clientQuery), cloneClientQuerySafeForLogging(clientQuery),
@@ -204,7 +202,7 @@ export class ServerController
device, device,
device_id, device_id,
sdk_version, sdk_version,
medium: transformedMedium, medium,
rsocket, rsocket,
}, },
{csr, csr_path}, {csr, csr_path},
@@ -233,10 +231,13 @@ export class ServerController
...timestamp, ...timestamp,
}); });
tracker.track( tracker.track('app-connection-secure-attempt', {
'app-connection-secure-attempt', app: clientQuery.app,
(({csr, ...o}) => o)(clientQuery), os: clientQuery.os,
); device: clientQuery.device,
device_id: clientQuery.device_id,
medium: clientQuery.medium,
});
const {os, app, device_id} = clientQuery; const {os, app, device_id} = clientQuery;
// without these checks, the user might see a connection timeout error instead, which would be much harder to track down // without these checks, the user might see a connection timeout error instead, which would be much harder to track down
@@ -260,16 +261,13 @@ export class ServerController
clearTimeout(timeout); clearTimeout(timeout);
} }
const transformedMedium = transformCertificateExchangeMediumToType( if (clientQuery.medium === 'WWW' || clientQuery.medium === 'NONE') {
clientQuery.medium,
);
if (transformedMedium === 'WWW' || transformedMedium === 'NONE') {
this.flipperServer.registerDevice( this.flipperServer.registerDevice(
new DummyDevice( new DummyDevice(
this.flipperServer, this.flipperServer,
clientQuery.device_id, clientQuery.device_id,
clientQuery.app + clientQuery.app +
(transformedMedium === 'WWW' ? ' Server Exchanged' : ''), (clientQuery.medium === 'WWW' ? ' Server Exchanged' : ''),
clientQuery.os, clientQuery.os,
), ),
); );
@@ -299,7 +297,6 @@ export class ServerController
unsanitizedCSR: string, unsanitizedCSR: string,
clientQuery: ClientQuery, clientQuery: ClientQuery,
appDirectory: string, appDirectory: string,
medium: CertificateExchangeMedium,
): Promise<{deviceId: string}> { ): Promise<{deviceId: string}> {
let certificateProvider: CertificateProvider; let certificateProvider: CertificateProvider;
switch (clientQuery.os) { switch (clientQuery.os) {
@@ -318,7 +315,7 @@ export class ServerController
); );
certificateProvider = this.flipperServer.ios.certificateProvider; certificateProvider = this.flipperServer.ios.certificateProvider;
if (medium === 'WWW') { if (clientQuery.medium === 'WWW') {
certificateProvider = new WWWCertificateProvider( certificateProvider = new WWWCertificateProvider(
this.flipperServer.keytarManager, this.flipperServer.keytarManager,
); );
@@ -339,7 +336,7 @@ export class ServerController
} }
} }
certificateProvider.verifyMedium(medium); certificateProvider.verifyMedium(clientQuery.medium);
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
reportPlatformFailures( reportPlatformFailures(
@@ -367,7 +364,7 @@ export class ServerController
setTimeout(() => { setTimeout(() => {
this.emit('client-unresponsive-error', { this.emit('client-unresponsive-error', {
client, client,
medium, medium: clientQuery.medium,
deviceID: response.deviceId, deviceID: response.deviceId,
}); });
}, 30 * 1000), }, 30 * 1000),
@@ -428,7 +425,7 @@ export class ServerController
*/ */
async addConnection( async addConnection(
connection: ClientConnection, connection: ClientConnection,
query: ClientQuery & {medium: CertificateExchangeMedium}, query: ClientQuery,
csrQuery: ClientCsrQuery, csrQuery: ClientCsrQuery,
silentReplace?: boolean, silentReplace?: boolean,
): Promise<ClientDescription> { ): Promise<ClientDescription> {

View File

@@ -99,7 +99,7 @@ class ServerWebSocket extends ServerAdapter {
}); });
try { try {
this.onConnection(ws, request); this.onConnection(ws, request); // insecure connection, with medium.
} catch (error) { } catch (error) {
// TODO: Investigate if we need to close the socket in the `error` listener // TODO: Investigate if we need to close the socket in the `error` listener
// DRI: @aigoncharov // DRI: @aigoncharov

View File

@@ -7,9 +7,13 @@
* @format * @format
*/ */
import {ClientQuery, DeviceOS, ResponseMessage} from 'flipper-common'; import {
CertificateExchangeMedium,
ClientQuery,
DeviceOS,
ResponseMessage,
} from 'flipper-common';
import {ParsedUrlQuery} from 'querystring'; import {ParsedUrlQuery} from 'querystring';
import {CertificateExchangeMedium} from '../utils/CertificateProvider';
import {SecureClientQuery} from './ServerAdapter'; import {SecureClientQuery} from './ServerAdapter';
/** /**
@@ -126,11 +130,23 @@ export function parseClientQuery(
return; return;
} }
let medium: number | undefined;
if (typeof query.medium === 'string') {
medium = parseInt(query.medium, 10);
} else if (typeof query.medium === 'number') {
medium = query.medium;
}
if (medium !== undefined && (medium < 1 || medium > 3)) {
throw new Error('Unsupported exchange medium: ' + medium);
}
const clientQuery: ClientQuery = { const clientQuery: ClientQuery = {
device_id, device_id,
device, device,
app, app,
os, os,
medium: transformCertificateExchangeMediumToType(medium),
}; };
if (typeof query.sdk_version === 'string') { if (typeof query.sdk_version === 'string') {
@@ -178,11 +194,19 @@ export function parseSecureClientQuery(
let medium: number | undefined; let medium: number | undefined;
if (typeof query.medium === 'string') { if (typeof query.medium === 'string') {
medium = parseInt(query.medium, 10); medium = parseInt(query.medium, 10);
} else if (typeof query.medium === 'number') {
medium = query.medium;
} }
if (medium !== undefined && (medium < 1 || medium > 3)) { if (medium !== undefined && (medium < 1 || medium > 3)) {
throw new Error('Unsupported exchange medium: ' + medium); throw new Error('Unsupported exchange medium: ' + medium);
} }
return {...clientQuery, csr, csr_path, medium: medium as any}; return {
...clientQuery,
csr,
csr_path,
medium: transformCertificateExchangeMediumToType(medium),
};
} }
export function cloneClientQuerySafeForLogging(clientQuery: SecureClientQuery) { export function cloneClientQuerySafeForLogging(clientQuery: SecureClientQuery) {

View File

@@ -81,7 +81,7 @@ describe('BrowserServerWebSocket', () => {
os, os,
app, app,
sdk_version: sdkVersion, sdk_version: sdkVersion,
medium: 3, medium: 'NONE',
}; };
expect(mockSEListener.onConnectionAttempt).toBeCalledWith( expect(mockSEListener.onConnectionAttempt).toBeCalledWith(
expectedClientQuery, expectedClientQuery,
@@ -183,7 +183,7 @@ describe('BrowserServerWebSocket', () => {
os: 'MacOS', os: 'MacOS',
app: device, app: device,
sdk_version: 4, sdk_version: 4,
medium: 3, medium: 'NONE',
}; };
expect(mockSEListener.onConnectionAttempt).toBeCalledWith( expect(mockSEListener.onConnectionAttempt).toBeCalledWith(
expectedClientQuery, expectedClientQuery,

View File

@@ -18,6 +18,7 @@ import WebSocket from 'ws';
import SecureServerWebSocket from '../SecureServerWebSocket'; import SecureServerWebSocket from '../SecureServerWebSocket';
import {SecureClientQuery} from '../ServerAdapter'; import {SecureClientQuery} from '../ServerAdapter';
import {transformCertificateExchangeMediumToType} from '../Utilities';
import WebSocketClientConnection from '../WebSocketClientConnection'; import WebSocketClientConnection from '../WebSocketClientConnection';
import {createMockSEListener, WSMessageAccumulator} from './utils'; import {createMockSEListener, WSMessageAccumulator} from './utils';
@@ -80,7 +81,7 @@ describe('SecureServerWebSocket', () => {
sdk_version: sdkVersion, sdk_version: sdkVersion,
csr, csr,
csr_path: csrPath, csr_path: csrPath,
medium, medium: transformCertificateExchangeMediumToType(medium),
}; };
expect(mockSEListener.onSecureConnectionAttempt).toBeCalledWith( expect(mockSEListener.onSecureConnectionAttempt).toBeCalledWith(
expectedClientQuery, expectedClientQuery,

View File

@@ -45,7 +45,7 @@ describe('ServerWebSocket', () => {
expect(mockSEListener.onConnectionAttempt).toBeCalledTimes(0); expect(mockSEListener.onConnectionAttempt).toBeCalledTimes(0);
wsClient = new WebSocket( wsClient = new WebSocket(
`ws://localhost:${port}?device_id=${deviceId}&device=${device}&app=${app}&os=${os}&sdk_version=${sdkVersion}`, `ws://localhost:${port}?device_id=${deviceId}&device=${device}&app=${app}&os=${os}&sdk_version=${sdkVersion}&medium=2`,
); );
const receivedMessages = new WSMessageAccumulator(); const receivedMessages = new WSMessageAccumulator();
await new Promise<void>((resolve, reject) => { await new Promise<void>((resolve, reject) => {
@@ -60,6 +60,7 @@ describe('ServerWebSocket', () => {
os, os,
app, app,
sdk_version: sdkVersion, sdk_version: sdkVersion,
medium: 'WWW',
}; };
expect(mockSEListener.onConnectionAttempt).toBeCalledWith( expect(mockSEListener.onConnectionAttempt).toBeCalledWith(
expectedClientQuery, expectedClientQuery,

View File

@@ -7,6 +7,7 @@
* @format * @format
*/ */
import {CertificateExchangeMedium} from 'flipper-common';
import { import {
deviceCAcertFile, deviceCAcertFile,
deviceClientCertFile, deviceClientCertFile,
@@ -16,8 +17,6 @@ import {
getCACertificate, getCACertificate,
} from './certificateUtils'; } from './certificateUtils';
export type CertificateExchangeMedium = 'FS_ACCESS' | 'WWW' | 'NONE';
export default abstract class CertificateProvider { export default abstract class CertificateProvider {
abstract medium: CertificateExchangeMedium; abstract medium: CertificateExchangeMedium;
abstract name: string; abstract name: string;

View File

@@ -7,14 +7,14 @@
* @format * @format
*/ */
import {getLogger} from 'flipper-common'; import {getLogger, CertificateExchangeMedium} from 'flipper-common';
type AppConnectionPayload = { type AppConnectionPayload = {
app: string; app: string;
os: string; os: string;
device: string; device: string;
device_id: string; device_id: string;
medium?: number | undefined; medium?: CertificateExchangeMedium;
}; };
type AppConnectionCertificateExchangePayload = AppConnectionPayload & { type AppConnectionCertificateExchangePayload = AppConnectionPayload & {

View File

@@ -9,6 +9,7 @@ exports[`can create a Fake flipper with legacy wrapper 1`] = `
"app": "TestApp", "app": "TestApp",
"device": "MockAndroidDevice", "device": "MockAndroidDevice",
"device_id": "serial", "device_id": "serial",
"medium": "NONE",
"os": "Android", "os": "Android",
"sdk_version": 4, "sdk_version": 4,
}, },

View File

@@ -180,6 +180,7 @@ export default class MockFlipper {
device: device.title, device: device.title,
device_id: device.serial, device_id: device.serial,
sdk_version: 4, sdk_version: 4,
medium: 'NONE',
}; };
const id = buildClientId({ const id = buildClientId({
app: query.app, app: query.app,

View File

@@ -105,7 +105,13 @@ function generateClientFromClientWithSalt(
const identifier = generateClientIdentifierWithSalt(client.id, salt); const identifier = generateClientIdentifierWithSalt(client.id, salt);
return { return {
id: identifier, id: identifier,
query: {app, os, device, device_id: salt + '-' + device_id}, query: {
app,
os,
device,
device_id: salt + '-' + device_id,
medium: client.query.medium,
},
}; };
} }
@@ -114,7 +120,7 @@ function generateClientFromDevice(device: Device, app: string): ClientExport {
const identifier = generateClientIdentifier(device, app); const identifier = generateClientIdentifier(device, app);
return { return {
id: identifier, id: identifier,
query: {app, os, device: deviceType, device_id: serial}, query: {app, os, device: deviceType, device_id: serial, medium: 'NONE'},
}; };
} }
@@ -149,6 +155,7 @@ test('test generateClientFromClientWithSalt helper function', () => {
os: 'iOS', os: 'iOS',
device: 'emulator', device: 'emulator',
device_id: 'salt-serial', device_id: 'salt-serial',
medium: 'NONE',
}, },
}); });
expect(client).toEqual({ expect(client).toEqual({
@@ -158,6 +165,7 @@ test('test generateClientFromClientWithSalt helper function', () => {
os: 'iOS', os: 'iOS',
device: 'emulator', device: 'emulator',
device_id: 'serial', device_id: 'serial',
medium: 'NONE',
}, },
}); });
}); });
@@ -178,6 +186,7 @@ test('test generateClientFromDevice helper function', () => {
os: 'iOS', os: 'iOS',
device: 'emulator', device: 'emulator',
device_id: 'serial', device_id: 'serial',
medium: 'NONE',
}, },
}); });
}); });
@@ -727,6 +736,7 @@ test('test determinePluginsToProcess for mutilple clients having plugins present
os: 'iOS', os: 'iOS',
device: 'TestiPhone', device: 'TestiPhone',
device_id: 'serial1', device_id: 'serial1',
medium: 'NONE',
}, },
null, null,
logger, logger,
@@ -742,6 +752,7 @@ test('test determinePluginsToProcess for mutilple clients having plugins present
os: 'iOS', os: 'iOS',
device: 'TestiPhone', device: 'TestiPhone',
device_id: 'serial1', device_id: 'serial1',
medium: 'NONE',
}, },
null, null,
logger, logger,
@@ -757,6 +768,7 @@ test('test determinePluginsToProcess for mutilple clients having plugins present
os: 'iOS', os: 'iOS',
device: 'TestiPhone', device: 'TestiPhone',
device_id: 'serial1', device_id: 'serial1',
medium: 'NONE',
}, },
null, null,
logger, logger,
@@ -816,6 +828,7 @@ test('test determinePluginsToProcess for no selected plugin present in any clien
os: 'iOS', os: 'iOS',
device: 'TestiPhone', device: 'TestiPhone',
device_id: 'serial1', device_id: 'serial1',
medium: 'NONE',
}, },
null, null,
logger, logger,
@@ -831,6 +844,7 @@ test('test determinePluginsToProcess for no selected plugin present in any clien
os: 'iOS', os: 'iOS',
device: 'TestiPhone', device: 'TestiPhone',
device_id: 'serial1', device_id: 'serial1',
medium: 'NONE',
}, },
null, null,
logger, logger,
@@ -873,6 +887,7 @@ test('test determinePluginsToProcess for multiple clients on same device', async
os: 'iOS', os: 'iOS',
device: 'TestiPhone', device: 'TestiPhone',
device_id: 'serial1', device_id: 'serial1',
medium: 'NONE',
}, },
null, null,
logger, logger,
@@ -888,6 +903,7 @@ test('test determinePluginsToProcess for multiple clients on same device', async
os: 'iOS', os: 'iOS',
device: 'TestiPhone', device: 'TestiPhone',
device_id: 'serial1', device_id: 'serial1',
medium: 'NONE',
}, },
null, null,
logger, logger,
@@ -935,6 +951,7 @@ test('test determinePluginsToProcess for multiple clients on different device',
os: 'iOS', os: 'iOS',
device: 'TestiPhone', device: 'TestiPhone',
device_id: 'serial1', device_id: 'serial1',
medium: 'NONE',
}, },
null, null,
logger, logger,
@@ -950,6 +967,7 @@ test('test determinePluginsToProcess for multiple clients on different device',
os: 'iOS', os: 'iOS',
device: 'TestiPhone', device: 'TestiPhone',
device_id: 'serial1', device_id: 'serial1',
medium: 'NONE',
}, },
null, null,
logger, logger,
@@ -965,6 +983,7 @@ test('test determinePluginsToProcess for multiple clients on different device',
os: 'iOS', os: 'iOS',
device: 'TestiPhone', device: 'TestiPhone',
device_id: 'serial2', device_id: 'serial2',
medium: 'NONE',
}, },
null, null,
logger, logger,
@@ -980,6 +999,7 @@ test('test determinePluginsToProcess for multiple clients on different device',
os: 'iOS', os: 'iOS',
device: 'TestiPhone', device: 'TestiPhone',
device_id: 'serial2', device_id: 'serial2',
medium: 'NONE',
}, },
null, null,
logger, logger,
@@ -1051,6 +1071,7 @@ test('test determinePluginsToProcess to ignore archived clients', async () => {
os: 'iOS', os: 'iOS',
device: 'TestiPhone', device: 'TestiPhone',
device_id: 'serial', device_id: 'serial',
medium: 'NONE',
}, },
null, null,
logger, logger,
@@ -1066,6 +1087,7 @@ test('test determinePluginsToProcess to ignore archived clients', async () => {
os: 'iOS', os: 'iOS',
device: 'TestiPhone', device: 'TestiPhone',
device_id: 'serial-archived', device_id: 'serial-archived',
medium: 'NONE',
}, },
null, null,
logger, logger,