Extract WWW certificate provider
Summary: Extract WWW certificate provider from the iOS certificate provider. Hide its implementation from OSS since it is not relevant for OSS folks. Reviewed By: mweststrate Differential Revision: D33895378 fbshipit-source-id: 376afda3b5fa3857c0eb280b92555314eb1a0d1f
This commit is contained in:
committed by
Facebook GitHub Bot
parent
29f6d0e711
commit
fd13399cb9
@@ -46,6 +46,7 @@ import {
|
|||||||
loadSecureServerConfig,
|
loadSecureServerConfig,
|
||||||
} from '../utils/certificateUtils';
|
} from '../utils/certificateUtils';
|
||||||
import DesktopCertificateProvider from '../devices/desktop/DesktopCertificateProvider';
|
import DesktopCertificateProvider from '../devices/desktop/DesktopCertificateProvider';
|
||||||
|
import WWWCertificateProvider from '../fb-stubs/WWWCertificateProvider';
|
||||||
|
|
||||||
type ClientTimestampTracker = {
|
type ClientTimestampTracker = {
|
||||||
insecureStart?: number;
|
insecureStart?: number;
|
||||||
@@ -285,15 +286,19 @@ class ServerController extends EventEmitter implements ServerEventsListener {
|
|||||||
}
|
}
|
||||||
case 'iOS': {
|
case 'iOS': {
|
||||||
certificateProvider = this.flipperServer.ios.certificateProvider;
|
certificateProvider = this.flipperServer.ios.certificateProvider;
|
||||||
|
|
||||||
|
if (medium === 'WWW') {
|
||||||
|
certificateProvider = new WWWCertificateProvider(
|
||||||
|
this.flipperServer.keytarManager,
|
||||||
|
);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
// Used by Spark AR studio (search for SkylightFlipperClient)
|
// Used by Spark AR studio (search for SkylightFlipperClient)
|
||||||
// See D30992087
|
// See D30992087
|
||||||
case 'MacOS':
|
case 'MacOS':
|
||||||
case 'Windows': {
|
case 'Windows': {
|
||||||
certificateProvider = new DesktopCertificateProvider(
|
certificateProvider = new DesktopCertificateProvider();
|
||||||
this.flipperServer.keytarManager,
|
|
||||||
);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default: {
|
default: {
|
||||||
@@ -309,7 +314,6 @@ class ServerController extends EventEmitter implements ServerEventsListener {
|
|||||||
unsanitizedCSR,
|
unsanitizedCSR,
|
||||||
clientQuery.os,
|
clientQuery.os,
|
||||||
appDirectory,
|
appDirectory,
|
||||||
medium,
|
|
||||||
),
|
),
|
||||||
'processCertificateSigningRequest',
|
'processCertificateSigningRequest',
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -9,15 +9,14 @@
|
|||||||
|
|
||||||
import CertificateProvider from '../../utils/CertificateProvider';
|
import CertificateProvider from '../../utils/CertificateProvider';
|
||||||
import {Client} from 'adbkit';
|
import {Client} from 'adbkit';
|
||||||
import {KeytarManager} from '../../utils/keytar';
|
|
||||||
import * as androidUtil from './androidContainerUtility';
|
import * as androidUtil from './androidContainerUtility';
|
||||||
import {csrFileName} from '../../utils/certificateUtils';
|
import {csrFileName, extractAppNameFromCSR} from '../../utils/certificateUtils';
|
||||||
|
|
||||||
const logTag = 'AndroidCertificateProvider';
|
const logTag = 'AndroidCertificateProvider';
|
||||||
|
|
||||||
export default class AndroidCertificateProvider extends CertificateProvider {
|
export default class AndroidCertificateProvider extends CertificateProvider {
|
||||||
constructor(keytarManager: KeytarManager, private adb: Client) {
|
constructor(private adb: Client) {
|
||||||
super(keytarManager);
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
async getTargetDeviceId(
|
async getTargetDeviceId(
|
||||||
@@ -75,13 +74,13 @@ export default class AndroidCertificateProvider extends CertificateProvider {
|
|||||||
return matchingIds[0];
|
return matchingIds[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async handleFSBasedDeploy(
|
protected async deployOrStageFileForDevice(
|
||||||
destination: string,
|
destination: string,
|
||||||
filename: string,
|
filename: string,
|
||||||
contents: string,
|
contents: string,
|
||||||
csr: string,
|
csr: string,
|
||||||
appName: string,
|
|
||||||
) {
|
) {
|
||||||
|
const appName = await extractAppNameFromCSR(csr);
|
||||||
const deviceId = await this.getTargetDeviceId(appName, destination, csr);
|
const deviceId = await this.getTargetDeviceId(appName, destination, csr);
|
||||||
await androidUtil.push(
|
await androidUtil.push(
|
||||||
this.adb,
|
this.adb,
|
||||||
|
|||||||
@@ -31,10 +31,7 @@ export class AndroidDeviceManager {
|
|||||||
this.adbClient,
|
this.adbClient,
|
||||||
'AndroidDeviceManager.certificateProvider -> missing adbClient',
|
'AndroidDeviceManager.certificateProvider -> missing adbClient',
|
||||||
);
|
);
|
||||||
return new AndroidCertificateProvider(
|
return new AndroidCertificateProvider(this.adbClient);
|
||||||
this.flipperServer.keytarManager,
|
|
||||||
this.adbClient,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private createDevice(
|
private createDevice(
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ export default class DesktopCertificateProvider extends CertificateProvider {
|
|||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async handleFSBasedDeploy(
|
protected async deployOrStageFileForDevice(
|
||||||
destination: string,
|
destination: string,
|
||||||
filename: string,
|
filename: string,
|
||||||
contents: string,
|
contents: string,
|
||||||
|
|||||||
@@ -8,21 +8,20 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import {IdbConfig} from './idbConfig';
|
import {IdbConfig} from './idbConfig';
|
||||||
import {KeytarManager} from '../../utils/keytar';
|
|
||||||
import CertificateProvider from '../../utils/CertificateProvider';
|
import CertificateProvider from '../../utils/CertificateProvider';
|
||||||
import iosUtil from './iOSContainerUtility';
|
import iosUtil from './iOSContainerUtility';
|
||||||
import fs from 'fs-extra';
|
import fs from 'fs-extra';
|
||||||
import {promisify} from 'util';
|
import {promisify} from 'util';
|
||||||
import tmp, {DirOptions} from 'tmp';
|
import tmp, {DirOptions} from 'tmp';
|
||||||
import {csrFileName} from '../../utils/certificateUtils';
|
import {csrFileName, extractAppNameFromCSR} from '../../utils/certificateUtils';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
|
|
||||||
const tmpDir = promisify(tmp.dir) as (options?: DirOptions) => Promise<string>;
|
const tmpDir = promisify(tmp.dir) as (options?: DirOptions) => Promise<string>;
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||||
export default class iOSCertificateProvider extends CertificateProvider {
|
export default class iOSCertificateProvider extends CertificateProvider {
|
||||||
constructor(keytarManager: KeytarManager, private idbConfig: IdbConfig) {
|
constructor(private idbConfig: IdbConfig) {
|
||||||
super(keytarManager);
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
async getTargetDeviceId(
|
async getTargetDeviceId(
|
||||||
@@ -62,13 +61,13 @@ export default class iOSCertificateProvider extends CertificateProvider {
|
|||||||
return matchingIds[0];
|
return matchingIds[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async handleFSBasedDeploy(
|
protected async deployOrStageFileForDevice(
|
||||||
destination: string,
|
destination: string,
|
||||||
filename: string,
|
filename: string,
|
||||||
contents: string,
|
contents: string,
|
||||||
csr: string,
|
csr: string,
|
||||||
appName: string,
|
|
||||||
) {
|
) {
|
||||||
|
const appName = await extractAppNameFromCSR(csr);
|
||||||
try {
|
try {
|
||||||
await fs.writeFile(destination + filename, contents);
|
await fs.writeFile(destination + filename, contents);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
|||||||
@@ -46,10 +46,7 @@ export class IOSDeviceManager {
|
|||||||
this.idbConfig,
|
this.idbConfig,
|
||||||
'IOSDeviceManager.certificateProvider -> missing idbConfig',
|
'IOSDeviceManager.certificateProvider -> missing idbConfig',
|
||||||
);
|
);
|
||||||
return new iOSCertificateProvider(
|
return new iOSCertificateProvider(this.idbConfig);
|
||||||
this.flipperServer.keytarManager,
|
|
||||||
this.idbConfig,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private forwardPort(port: number, multiplexChannelPort: number) {
|
private forwardPort(port: number, multiplexChannelPort: number) {
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) Meta Platforms, Inc. and 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 {KeytarManager} from '../utils/keytar';
|
||||||
|
import CertificateProvider from '../utils/CertificateProvider';
|
||||||
|
|
||||||
|
export default class WWWCertificateProvider extends CertificateProvider {
|
||||||
|
constructor(private keytarManager: KeytarManager) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
async processCertificateSigningRequest(): Promise<{deviceId: string}> {
|
||||||
|
throw new Error('WWWCertificateProvider is not implemented');
|
||||||
|
}
|
||||||
|
|
||||||
|
async getTargetDeviceId(): Promise<string> {
|
||||||
|
throw new Error('WWWCertificateProvider is not implemented');
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async deployOrStageFileForDevice(): Promise<void> {
|
||||||
|
throw new Error('WWWCertificateProvider is not implemented');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,14 +7,6 @@
|
|||||||
* @format
|
* @format
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {promisify} from 'util';
|
|
||||||
import fs from 'fs-extra';
|
|
||||||
import tmp from 'tmp';
|
|
||||||
import {reportPlatformFailures} from 'flipper-common';
|
|
||||||
import archiver from 'archiver';
|
|
||||||
import {timeout} from 'flipper-common';
|
|
||||||
import {v4 as uuid} from 'uuid';
|
|
||||||
import {internGraphPOSTAPIRequest} from '../fb-stubs/internRequests';
|
|
||||||
import {
|
import {
|
||||||
deviceCAcertFile,
|
deviceCAcertFile,
|
||||||
deviceClientCertFile,
|
deviceClientCertFile,
|
||||||
@@ -23,62 +15,26 @@ import {
|
|||||||
generateClientCertificate,
|
generateClientCertificate,
|
||||||
getCACertificate,
|
getCACertificate,
|
||||||
} from './certificateUtils';
|
} from './certificateUtils';
|
||||||
import {KeytarManager, SERVICE_FLIPPER} from './keytar';
|
|
||||||
|
|
||||||
export type CertificateExchangeMedium = 'FS_ACCESS' | 'WWW' | 'NONE';
|
export type CertificateExchangeMedium = 'FS_ACCESS' | 'WWW' | 'NONE';
|
||||||
|
|
||||||
export default abstract class CertificateProvider {
|
export default abstract class CertificateProvider {
|
||||||
constructor(private readonly keytarManager: KeytarManager) {}
|
|
||||||
|
|
||||||
private uploadFiles = async (
|
|
||||||
zipPath: string,
|
|
||||||
deviceID: string,
|
|
||||||
): Promise<void> => {
|
|
||||||
return reportPlatformFailures(
|
|
||||||
timeout(
|
|
||||||
5 * 60 * 1000,
|
|
||||||
internGraphPOSTAPIRequest(
|
|
||||||
'flipper/certificates',
|
|
||||||
{
|
|
||||||
device_id: deviceID,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
certificate_zip: {
|
|
||||||
path: zipPath,
|
|
||||||
filename: 'certs.zip',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{timeout: 5 * 60 * 1000},
|
|
||||||
await this.keytarManager.retrieveToken(SERVICE_FLIPPER),
|
|
||||||
).then(() => {}),
|
|
||||||
'Timed out uploading Flipper certificates to WWW.',
|
|
||||||
),
|
|
||||||
'uploadCertificates',
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
async processCertificateSigningRequest(
|
async processCertificateSigningRequest(
|
||||||
unsanitizedCsr: string,
|
unsanitizedCsr: string,
|
||||||
os: string,
|
os: string,
|
||||||
appDirectory: string,
|
appDirectory: string,
|
||||||
medium: CertificateExchangeMedium,
|
|
||||||
): Promise<{deviceId: string}> {
|
): Promise<{deviceId: string}> {
|
||||||
const csr = this.santitizeString(unsanitizedCsr);
|
const csr = this.santitizeString(unsanitizedCsr);
|
||||||
if (csr === '') {
|
if (csr === '') {
|
||||||
return Promise.reject(new Error(`Received empty CSR from ${os} device`));
|
return Promise.reject(new Error(`Received empty CSR from ${os} device`));
|
||||||
}
|
}
|
||||||
await ensureOpenSSLIsAvailable();
|
await ensureOpenSSLIsAvailable();
|
||||||
const rootFolder = await promisify(tmp.dir)();
|
|
||||||
const certFolder = rootFolder + '/FlipperCerts/';
|
|
||||||
const certsZipPath = rootFolder + '/certs.zip';
|
|
||||||
const caCert = await getCACertificate();
|
const caCert = await getCACertificate();
|
||||||
await this.deployOrStageFileForDevice(
|
await this.deployOrStageFileForDevice(
|
||||||
appDirectory,
|
appDirectory,
|
||||||
deviceCAcertFile,
|
deviceCAcertFile,
|
||||||
caCert,
|
caCert,
|
||||||
csr,
|
csr,
|
||||||
medium,
|
|
||||||
certFolder,
|
|
||||||
);
|
);
|
||||||
const clientCert = await generateClientCertificate(csr);
|
const clientCert = await generateClientCertificate(csr);
|
||||||
await this.deployOrStageFileForDevice(
|
await this.deployOrStageFileForDevice(
|
||||||
@@ -86,39 +42,9 @@ export default abstract class CertificateProvider {
|
|||||||
deviceClientCertFile,
|
deviceClientCertFile,
|
||||||
clientCert,
|
clientCert,
|
||||||
csr,
|
csr,
|
||||||
medium,
|
|
||||||
certFolder,
|
|
||||||
);
|
);
|
||||||
const appName = await extractAppNameFromCSR(csr);
|
const appName = await extractAppNameFromCSR(csr);
|
||||||
const deviceId =
|
const deviceId = await this.getTargetDeviceId(appName, appDirectory, csr);
|
||||||
medium === 'FS_ACCESS'
|
|
||||||
? await this.getTargetDeviceId(appName, appDirectory, csr)
|
|
||||||
: uuid();
|
|
||||||
if (medium === 'WWW') {
|
|
||||||
const zipPromise = new Promise((resolve, reject) => {
|
|
||||||
const output = fs.createWriteStream(certsZipPath);
|
|
||||||
const archive = archiver('zip', {
|
|
||||||
zlib: {level: 9}, // Sets the compression level.
|
|
||||||
});
|
|
||||||
archive.directory(certFolder, false);
|
|
||||||
output.on('close', function () {
|
|
||||||
resolve(certsZipPath);
|
|
||||||
});
|
|
||||||
archive.on('warning', reject);
|
|
||||||
archive.on('error', reject);
|
|
||||||
archive.pipe(output);
|
|
||||||
archive.finalize();
|
|
||||||
});
|
|
||||||
|
|
||||||
await reportPlatformFailures(
|
|
||||||
zipPromise,
|
|
||||||
'www-certs-exchange-zipping-certs',
|
|
||||||
);
|
|
||||||
await reportPlatformFailures(
|
|
||||||
this.uploadFiles(certsZipPath, deviceId),
|
|
||||||
'www-certs-exchange-uploading-certs',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return {
|
return {
|
||||||
deviceId,
|
deviceId,
|
||||||
};
|
};
|
||||||
@@ -130,39 +56,11 @@ export default abstract class CertificateProvider {
|
|||||||
_csr: string,
|
_csr: string,
|
||||||
): Promise<string>;
|
): Promise<string>;
|
||||||
|
|
||||||
private async deployOrStageFileForDevice(
|
protected abstract deployOrStageFileForDevice(
|
||||||
destination: string,
|
destination: string,
|
||||||
filename: string,
|
filename: string,
|
||||||
contents: string,
|
contents: string,
|
||||||
csr: string,
|
csr: string,
|
||||||
medium: CertificateExchangeMedium,
|
|
||||||
certFolder: string,
|
|
||||||
): Promise<void> {
|
|
||||||
if (medium === 'WWW') {
|
|
||||||
const certPathExists = await fs.pathExists(certFolder);
|
|
||||||
if (!certPathExists) {
|
|
||||||
await fs.mkdir(certFolder);
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
await fs.writeFile(certFolder + filename, contents);
|
|
||||||
return;
|
|
||||||
} catch (e) {
|
|
||||||
throw new Error(
|
|
||||||
`Failed to write ${filename} to temporary folder. Error: ${e}`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const appName = await extractAppNameFromCSR(csr);
|
|
||||||
this.handleFSBasedDeploy(destination, filename, contents, csr, appName);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected abstract handleFSBasedDeploy(
|
|
||||||
_destination: string,
|
|
||||||
_filename: string,
|
|
||||||
_contents: string,
|
|
||||||
_csr: string,
|
|
||||||
_appName: string,
|
|
||||||
): Promise<void>;
|
): Promise<void>;
|
||||||
|
|
||||||
protected santitizeString(csrString: string): string {
|
protected santitizeString(csrString: string): string {
|
||||||
|
|||||||
Reference in New Issue
Block a user