Introduce ServerAdapter

Summary:
Introduce ServerAdapter which should be used as a base class for different server implementations e.g. RSocket, WebSocket.

The type is not used elsewhere at this point but this is a good chance to look at the API and suggest changes and/or improvements.

Reviewed By: passy

Differential Revision: D29958434

fbshipit-source-id: 50ba46332d40e836b0a87bcf354d3237bf8fe7c5
This commit is contained in:
Lorenzo Blasa
2021-07-28 08:04:08 -07:00
committed by Facebook GitHub Bot
parent 06d45247b4
commit b0e47bf75e

View File

@@ -0,0 +1,173 @@
/**
* 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 {
CertificateExchangeMedium,
SecureServerConfig,
} from '../utils/CertificateProvider';
import Client, {ClientQuery} from '../Client';
import {ClientConnection} from './ClientConnection';
import {transformCertificateExchangeMediumToType} from './Utilities';
/**
* ClientCsrQuery defines a client query with CSR
* information.
*/
export type ClientCsrQuery = {
csr?: string | undefined;
csr_path?: string | undefined;
};
/**
* SecureClientQuery combines a ClientQuery with
* ClientCsrQuery. It also adds medium information.
*/
export type SecureClientQuery = ClientQuery &
ClientCsrQuery & {medium: number | undefined};
/**
* Defines an interface for events triggered by a running server interacting
* with a client.
*/
export interface ServerEventsListener {
/**
* Server started and listening at the specified port.
* @param port The port in which the server is listening to.
*/
onListening(port: number): void;
/**
* An insecure connection attempt has been made by a client. At this
* point, a connection should be already be available but needs to be
* validated by the server.
* @param clientQuery A ClientQuery instance containing metadata about
* the client e.g. OS, device, app, etc.
*/
onConnectionAttempt(clientQuery: ClientQuery): void;
/**
* A TLS connection attempt has been made by a client. At this
* point, a connection should be already be available but needs to be
* validated by the server.
* @param clientQuery A SecureClientQuery instance containing metadata about
* the client and CSR information as exchanged on the previously
* established insecure connection.
*/
onSecureConnectionAttempt(clientQuery: SecureClientQuery): void;
/**
* CSR received by the server and needs to be processed. If successfully
* processed, it should return a generated device identifier.
* @param unsanitizedCSR CSR as sent by the client, will need to be sanitized
* before usage.
* @param clientQuery A ClientQuery instance containing metadata about
* the client e.g. OS, device, app, etc.
* @param appDirectory App directory in which to deploy the CA and client
* certificates.
* @param medium Certificate exchange medium type e.g. FS_ACCESS, WWW.
*/
onProcessCSR(
unsanitizedCSR: string,
clientQuery: ClientQuery,
appDirectory: string,
medium: CertificateExchangeMedium,
): Promise<{deviceId: string}>;
/**
* A secure connection has been established with a validated client.
* A promise to a Client instance needs to be returned.
* @param clientQuery A SecureClientQuery instance containing metadata about
* the client and CSR information as exchanged on the previously
* established insecure connection.
* @param clientConnection A valid client connection.
*/
onConnectionCreated(
clientQuery: SecureClientQuery,
clientConnection: ClientConnection,
): Promise<Client>;
/**
* A connection with a client has been closed.
* @param id The client identifier.
*/
onConnectionClosed(id: string): void;
/**
* An error has occurred.
* @param error An Error instance.
*/
onError(error: Error): void;
}
/**
* Defines the base class to be used by any server implementation e.g.
* RSocket, WebSocket, etc.
*/
abstract class ServerAdapter {
listener: ServerEventsListener;
constructor(listener: ServerEventsListener) {
this.listener = listener;
}
/**
* Start and bind server to the specified port.
* @param port A port number.
* @param sslConfig An optional SSL configuration to be used for
* TLS servers.
*/
abstract start(
port: number,
sslConfig?: SecureServerConfig,
): Promise<boolean>;
/**
* Stop the server.
*/
abstract stop(): Promise<void>;
/**
* Handle a message received over an insecure connection. The only
* supported message is to sign certificates.
* @param clientQuery A ClientQuery instance containing metadata about
* the client e.g. OS, device, app, etc.
* @param rawData Raw data as sent by the client.
* @returns The response to be sent back to the client. If the received
* request is to sign a certificate and no errors were found, the response
* should contain the device identifier to use by the client.
*/
async _onHandleUntrustedMessage(
clientQuery: ClientQuery,
rawData: any,
): Promise<any> {
// OSS's older Client SDK might not send medium information.
// This is not an issue for internal FB users, as Flipper release
// is insync with client SDK through launcher.
const message: {
method: 'signCertificate';
csr: string;
destination: string;
medium: number | undefined;
} = rawData;
if (message.method === 'signCertificate') {
console.debug('CSR received from device', 'server');
const {csr, destination, medium} = message;
const result = await this.listener.onProcessCSR(
csr,
clientQuery,
destination,
transformCertificateExchangeMediumToType(medium),
);
const response = JSON.stringify({
deviceId: result.deviceId,
});
return response;
}
return Promise.resolve();
}
}
export default ServerAdapter;