Skip to content

Commit

Permalink
fix(compass,atlas-service): apply proxy to main thread HTTP calls COM…
Browse files Browse the repository at this point in the history
  • Loading branch information
addaleax authored Aug 16, 2024
1 parent 3440cbc commit 3bd6e57
Show file tree
Hide file tree
Showing 8 changed files with 62 additions and 124 deletions.
68 changes: 3 additions & 65 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 2 additions & 3 deletions packages/atlas-service/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -79,17 +79,16 @@
"@mongodb-js/compass-user-data": "^0.3.3",
"@mongodb-js/compass-utils": "^0.6.9",
"@mongodb-js/devtools-connect": "^3.2.5",
"@mongodb-js/devtools-proxy-support": "^0.3.5",
"@mongodb-js/oidc-plugin": "^1.0.0",
"hadron-app-registry": "^9.2.2",
"compass-preferences-model": "^2.26.0",
"electron": "^29.4.5",
"hadron-ipc": "^3.2.20",
"lodash": "^4.17.21",
"node-fetch": "^2.7.0",
"react": "^17.0.2",
"react-redux": "^8.1.3",
"redux": "^4.2.1",
"redux-thunk": "^2.4.2",
"system-ca": "^2.0.0"
"redux-thunk": "^2.4.2"
}
}
25 changes: 4 additions & 21 deletions packages/atlas-service/src/main.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,11 +85,12 @@ describe('CompassAuthServiceMain', function () {
createHandle: sandbox.stub(),
};
CompassAuthService['fetch'] = mockFetch as any;
CompassAuthService['httpClient'] = { fetch: mockFetch } as any;
CompassAuthService['createMongoDBOIDCPlugin'] = () => mockOidcPlugin;

CompassAuthService['config'] = defaultConfig;

await CompassAuthService['setupPlugin']();
CompassAuthService['setupPlugin']();
CompassAuthService['attachOidcPluginLoggerEvents']();

preferences = await createSandboxFromDefaultPreferences();
Expand Down Expand Up @@ -289,27 +290,9 @@ describe('CompassAuthServiceMain', function () {
CompassAuthService as any,
'setupPlugin'
);
await CompassAuthService.init(preferences);
await CompassAuthService.init(preferences, {} as any);
expect(setupPluginSpy).to.have.been.calledOnce;
});

it('should pass the system ca to the plugin as a custom http option', async function () {
const createOIDCPluginSpy = sandbox.spy(
CompassAuthService as any,
'createMongoDBOIDCPlugin'
);
await CompassAuthService.init(preferences);
expect(createOIDCPluginSpy).to.have.been.calledOnce;
try {
expect(
createOIDCPluginSpy.firstCall.args[0].customHttpOptions.ca
).to.include('-----BEGIN CERTIFICATE-----');
} catch (e) {
throw new Error(
'Expected ca to be included in the customHttpOptions, but it was not.'
);
}
});
});

describe('with networkTraffic turned off', function () {
Expand Down Expand Up @@ -346,7 +329,7 @@ describe('CompassAuthServiceMain', function () {
CompassAuthService['currentUser'] = {
sub: '1234',
} as any;
await CompassAuthService.init(preferences);
await CompassAuthService.init(preferences, {} as any);
CompassAuthService['config'] = defaultConfig;
expect(getListenerCount(logger)).to.eq(27);
// We did all preparations, reset sinon history for easier assertions
Expand Down
49 changes: 20 additions & 29 deletions packages/atlas-service/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,7 @@ import {
hookLoggerToMongoLogWriter as oidcPluginHookLoggerToMongoLogWriter,
} from '@mongodb-js/oidc-plugin';
import { oidcServerRequestHandler } from '@mongodb-js/devtools-connect';
import { systemCertsAsync } from 'system-ca';
import type { Options as SystemCAOptions } from 'system-ca';
import type { RequestInfo, RequestInit, Response } from 'node-fetch';
import https from 'https';
import nodeFetch from 'node-fetch';
import type { Agent } from 'https';
import type { IntrospectInfo, AtlasUserInfo, AtlasServiceConfig } from './util';
import { throwIfAborted } from '@mongodb-js/compass-utils';
import type { HadronIpcMain } from 'hadron-ipc';
Expand All @@ -27,6 +23,7 @@ import { OidcPluginLogger } from './oidc-plugin-logger';
import { spawn } from 'child_process';
import { getAtlasConfig } from './util';
import { createIpcTrack } from '@mongodb-js/compass-telemetry';
import type { RequestInit, Response } from '@mongodb-js/devtools-proxy-support';

const { log } = createLogger('COMPASS-ATLAS-SERVICE');
const track = createIpcTrack();
Expand All @@ -36,27 +33,25 @@ const redirectRequestHandler = oidcServerRequestHandler.bind(null, {
productDocsLink: 'https://www.mongodb.com/docs/compass',
});

async function getSystemCA() {
// It is possible for OIDC login flow to fail if system CA certs are different from
// the ones packaged with the application. To avoid this, we include the system CA
// certs in the OIDC plugin options. See COMPASS-7950 for more details.
const systemCAOpts: SystemCAOptions = { includeNodeCertificates: true };
const ca = await systemCertsAsync(systemCAOpts);
return ca.join('\n');
}

const TOKEN_TYPE_TO_HINT = {
accessToken: 'access_token',
refreshToken: 'refresh_token',
} as const;

interface CompassAuthHTTPClient {
agent: Agent | undefined;
fetch: (url: string, init: RequestInit) => Promise<Response>;
}

export class CompassAuthService {
private constructor() {
// singleton
}

private static initPromise: Promise<void> | null = null;

private static httpClient: CompassAuthHTTPClient;

private static oidcPluginLogger = new OidcPluginLogger();

private static plugin: MongoDBOIDCPlugin | null = null;
Expand All @@ -66,7 +61,7 @@ export class CompassAuthService {
private static signInPromise: Promise<AtlasUserInfo> | null = null;

private static fetch = async (
url: RequestInfo,
url: string,
init: RequestInit = {}
): Promise<Response> => {
await this.initPromise;
Expand All @@ -79,15 +74,7 @@ export class CompassAuthService {
{ url }
);
try {
const res = await nodeFetch(url, {
// Tests use 'http'.
...(url.toString().includes('https')
? {
agent: new https.Agent({
ca: await getSystemCA(),
}),
}
: {}),
const res = await this.httpClient.fetch(url, {
...init,
headers: {
...init.headers,
Expand Down Expand Up @@ -145,7 +132,7 @@ export class CompassAuthService {

private static createMongoDBOIDCPlugin = createMongoDBOIDCPlugin;

private static async setupPlugin(serializedState?: string) {
private static setupPlugin(serializedState?: string) {
this.plugin = this.createMongoDBOIDCPlugin({
redirectServerRequestHandler: (data) => {
if (data.result === 'redirecting') {
Expand All @@ -167,7 +154,7 @@ export class CompassAuthService {
logger: this.oidcPluginLogger,
serializedState,
customHttpOptions: {
ca: await getSystemCA(),
agent: this.httpClient.agent,
},
});
oidcPluginHookLoggerToMongoLogWriter(
Expand All @@ -177,7 +164,11 @@ export class CompassAuthService {
);
}

static init(preferences: PreferencesAccess): Promise<void> {
static init(
preferences: PreferencesAccess,
httpClient: CompassAuthHTTPClient
): Promise<void> {
this.httpClient = httpClient;
this.preferences = preferences;
this.config = getAtlasConfig(preferences);
return (this.initPromise ??= (async () => {
Expand All @@ -199,7 +190,7 @@ export class CompassAuthService {
{ config: this.config }
);
const serializedState = await this.secretStore.getState();
await this.setupPlugin(serializedState);
this.setupPlugin(serializedState);
})());
}

Expand Down Expand Up @@ -318,7 +309,7 @@ export class CompassAuthService {
this.attachOidcPluginLoggerEvents();
// Destroy old plugin and setup new one
await this.plugin?.destroy();
await this.setupPlugin();
this.setupPlugin();
// Revoke tokens. Revoking refresh token will also revoke associated access
// tokens
// https://developer.okta.com/docs/guides/revoke-tokens/main/#revoke-an-access-token-or-a-refresh-token
Expand Down
5 changes: 4 additions & 1 deletion packages/compass-utils/src/cancellable-promise.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@ class AbortError extends Error {
name = 'AbortError';
}

export const throwIfAborted = (signal?: AbortSignal) => {
export const throwIfAborted = (signal?: {
aborted: boolean;
reason?: Error;
}) => {
if (signal?.aborted) {
throw signal.reason ?? createCancelError();
}
Expand Down
1 change: 0 additions & 1 deletion packages/compass/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,6 @@
"mongodb-instance-model": "^12.23.3",
"mongodb-log-writer": "^1.4.2",
"mongodb-ns": "^2.4.2",
"node-fetch": "^2.7.0",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"resolve-mongodb-srv": "^1.1.5",
Expand Down
Loading

0 comments on commit 3bd6e57

Please sign in to comment.