Fix CORS issue using Mongo Shell from new origin (#1422)

* Fix CORS issue using Mongo Shell from new origin

* Add unit tests for getMongoShellOrigin and getMongoShellUrl

* Fix lint errors
This commit is contained in:
Armando Trejo Oliver 2023-04-04 18:19:18 -07:00 committed by GitHub
parent 9d2d0e4754
commit 874cec26fc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 315 additions and 36 deletions

View File

@ -1,6 +1,6 @@
import React, { Component } from "react"; import React, { Component } from "react";
import * as Constants from "../../../Common/Constants"; import * as Constants from "../../../Common/Constants";
import { configContext, Platform } from "../../../ConfigContext"; import { configContext } from "../../../ConfigContext";
import * as ViewModels from "../../../Contracts/ViewModels"; import * as ViewModels from "../../../Contracts/ViewModels";
import { Action, ActionModifiers } from "../../../Shared/Telemetry/TelemetryConstants"; import { Action, ActionModifiers } from "../../../Shared/Telemetry/TelemetryConstants";
import * as TelemetryProcessor from "../../../Shared/Telemetry/TelemetryProcessor"; import * as TelemetryProcessor from "../../../Shared/Telemetry/TelemetryProcessor";
@ -9,6 +9,8 @@ import { isInvalidParentFrameOrigin, isReadyMessage } from "../../../Utils/Messa
import { logConsoleError, logConsoleInfo, logConsoleProgress } from "../../../Utils/NotificationConsoleUtils"; import { logConsoleError, logConsoleInfo, logConsoleProgress } from "../../../Utils/NotificationConsoleUtils";
import Explorer from "../../Explorer"; import Explorer from "../../Explorer";
import TabsBase from "../TabsBase"; import TabsBase from "../TabsBase";
import { getMongoShellOrigin } from "./getMongoShellOrigin";
import { getMongoShellUrl } from "./getMongoShellUrl";
//eslint-disable-next-line //eslint-disable-next-line
class MessageType { class MessageType {
@ -47,7 +49,6 @@ export default class MongoShellTabComponent extends Component<
IMongoShellTabComponentProps, IMongoShellTabComponentProps,
IMongoShellTabComponentStates IMongoShellTabComponentStates
> { > {
private _runtimeEndpoint: string;
private _logTraces: Map<string, number>; private _logTraces: Map<string, number>;
constructor(props: IMongoShellTabComponentProps) { constructor(props: IMongoShellTabComponentProps) {
@ -55,7 +56,7 @@ export default class MongoShellTabComponent extends Component<
this._logTraces = new Map(); this._logTraces = new Map();
this.state = { this.state = {
url: this.getURL(), url: getMongoShellUrl(),
}; };
props.onMongoShellTabAccessor({ props.onMongoShellTabAccessor({
@ -65,38 +66,6 @@ export default class MongoShellTabComponent extends Component<
window.addEventListener("message", this.handleMessage.bind(this), false); window.addEventListener("message", this.handleMessage.bind(this), false);
} }
public getURL(): string {
const { databaseAccount: account } = userContext;
const resourceId = account?.id;
const accountName = account?.name;
const mongoEndpoint = account?.properties?.mongoEndpoint || account?.properties?.documentEndpoint;
this._runtimeEndpoint = configContext.platform === Platform.Hosted ? configContext.BACKEND_ENDPOINT : "";
const extensionEndpoint: string = configContext.BACKEND_ENDPOINT || this._runtimeEndpoint || "";
let baseUrl = "/content/mongoshell/dist/";
if (userContext.portalEnv === "localhost") {
baseUrl = "/content/mongoshell/";
}
if (userContext.features.enableLegacyMongoShellV1 === true) {
return "/mongoshell/index.html";
}
if (userContext.features.enableLegacyMongoShellV1Dist === true) {
return "/mongoshell/dist/index.html";
}
if (userContext.features.enableLegacyMongoShellV2 === true) {
return "/mongoshell/indexv2.html";
}
if (userContext.features.enableLegacyMongoShellV2Dist === true) {
return "/mongoshell/dist/indexv2.html";
}
return `${extensionEndpoint}${baseUrl}index.html?resourceId=${resourceId}&accountName=${accountName}&mongoEndpoint=${mongoEndpoint}`;
}
//eslint-disable-next-line //eslint-disable-next-line
public setContentFocus(event: React.SyntheticEvent<HTMLIFrameElement, Event>): void {} public setContentFocus(event: React.SyntheticEvent<HTMLIFrameElement, Event>): void {}
@ -152,6 +121,7 @@ export default class MongoShellTabComponent extends Component<
const collectionId = this.props.collection.id(); const collectionId = this.props.collection.id();
const apiEndpoint = configContext.BACKEND_ENDPOINT; const apiEndpoint = configContext.BACKEND_ENDPOINT;
const encryptedAuthToken: string = userContext.accessToken; const encryptedAuthToken: string = userContext.accessToken;
const targetOrigin = getMongoShellOrigin();
shellIframe.contentWindow.postMessage( shellIframe.contentWindow.postMessage(
{ {
@ -167,7 +137,7 @@ export default class MongoShellTabComponent extends Component<
apiEndpoint: apiEndpoint, apiEndpoint: apiEndpoint,
}, },
}, },
configContext.BACKEND_ENDPOINT targetOrigin
); );
} }

View File

@ -0,0 +1,73 @@
import { extractFeatures } from "Platform/Hosted/extractFeatures";
import { configContext } from "../../../ConfigContext";
import { updateUserContext } from "../../../UserContext";
import { getMongoShellOrigin } from "./getMongoShellOrigin";
describe("getMongoShellOrigin", () => {
(window as { origin: string }).origin = "window_origin";
beforeEach(() => {
updateUserContext({
features: extractFeatures(
new URLSearchParams({
"feature.enableLegacyMongoShellV1": "false",
"feature.enableLegacyMongoShellV2": "false",
"feature.enableLegacyMongoShellV1Dist": "false",
"feature.enableLegacyMongoShellV2Dist": "false",
})
),
});
});
it("should return BACKEND_ENDPOINT by default", () => {
expect(getMongoShellOrigin()).toBe(configContext.BACKEND_ENDPOINT);
});
it("should return /mongoshell/index.html when enableLegacyMongoShellV1", () => {
updateUserContext({
features: extractFeatures(
new URLSearchParams({
"feature.enableLegacyMongoShellV1": "true",
})
),
});
expect(getMongoShellOrigin()).toBe(window.origin);
});
it("should return /mongoshell/index.html when enableLegacyMongoShellV2===true", () => {
updateUserContext({
features: extractFeatures(
new URLSearchParams({
"feature.enableLegacyMongoShellV2": "true",
})
),
});
expect(getMongoShellOrigin()).toBe(window.origin);
});
it("should return /mongoshell/index.html when enableLegacyMongoShellV1Dist===true", () => {
updateUserContext({
features: extractFeatures(
new URLSearchParams({
"feature.enableLegacyMongoShellV1Dist": "true",
})
),
});
expect(getMongoShellOrigin()).toBe(window.origin);
});
it("should return /mongoshell/index.html when enableLegacyMongoShellV2Dist===true", () => {
updateUserContext({
features: extractFeatures(
new URLSearchParams({
"feature.enableLegacyMongoShellV2Dist": "true",
})
),
});
expect(getMongoShellOrigin()).toBe(window.origin);
});
});

View File

@ -0,0 +1,15 @@
import { configContext } from "../../../ConfigContext";
import { userContext } from "../../../UserContext";
export function getMongoShellOrigin(): string {
if (
userContext.features.enableLegacyMongoShellV1 === true ||
userContext.features.enableLegacyMongoShellV2 === true ||
userContext.features.enableLegacyMongoShellV1Dist === true ||
userContext.features.enableLegacyMongoShellV2Dist === true
) {
return window.origin;
}
return configContext.BACKEND_ENDPOINT;
}

View File

@ -0,0 +1,179 @@
import { extractFeatures } from "Platform/Hosted/extractFeatures";
import { Platform, resetConfigContext, updateConfigContext } from "../../../ConfigContext";
import { updateUserContext, userContext } from "../../../UserContext";
import { getExtensionEndpoint, getMongoShellUrl } from "./getMongoShellUrl";
const mongoBackendEndpoint = "https://localhost:1234";
describe("getMongoShellUrl", () => {
let queryString = "";
beforeEach(() => {
resetConfigContext();
updateConfigContext({
BACKEND_ENDPOINT: mongoBackendEndpoint,
platform: Platform.Hosted,
});
updateUserContext({
subscriptionId: "fakeSubscriptionId",
resourceGroup: "fakeResourceGroup",
databaseAccount: {
id: "fakeId",
name: "fakeName",
location: "fakeLocation",
type: "fakeType",
kind: "fakeKind",
properties: {
documentEndpoint: "fakeDocumentEndpoint",
tableEndpoint: "fakeTableEndpoint",
gremlinEndpoint: "fakeGremlinEndpoint",
cassandraEndpoint: "fakeCassandraEndpoint",
},
},
features: extractFeatures(
new URLSearchParams({
"feature.enableLegacyMongoShellV1": "false",
"feature.enableLegacyMongoShellV2": "false",
"feature.enableLegacyMongoShellV1Dist": "false",
"feature.enableLegacyMongoShellV2Dist": "false",
})
),
portalEnv: "prod",
});
queryString = `resourceId=${userContext.databaseAccount.id}&accountName=${userContext.databaseAccount.name}&mongoEndpoint=${userContext.databaseAccount.properties.documentEndpoint}`;
});
it("should return {mongoBackendEndpoint}/content/mongoshell/dist/index.html by default ", () => {
expect(getMongoShellUrl()).toBe(`${mongoBackendEndpoint}/content/mongoshell/dist/index.html?${queryString}`);
});
it("should return {mongoBackendEndpoint}/content/mongoshell/index.html when portalEnv==localhost ", () => {
updateUserContext({
portalEnv: "localhost",
});
expect(getMongoShellUrl()).toBe(`${mongoBackendEndpoint}/content/mongoshell/index.html?${queryString}`);
});
it("should return {mongoBackendEndpoint}/content/mongoshell/dist/index.html when configContext.platform !== Platform.Hosted", () => {
updateConfigContext({
platform: Platform.Portal,
});
expect(getMongoShellUrl()).toBe(`${mongoBackendEndpoint}/content/mongoshell/dist/index.html?${queryString}`);
});
it("should return /content/mongoshell/index.html when configContext.BACKEND_ENDPOINT === '' and configContext.platform === Platform.Hosted", () => {
resetConfigContext();
updateConfigContext({
platform: Platform.Hosted,
});
expect(getMongoShellUrl()).toBe(`/content/mongoshell/dist/index.html?${queryString}`);
});
it("should return /content/mongoshell/index.html when configContext.BACKEND_ENDPOINT === '' and configContext.platform !== Platform.Hosted", () => {
resetConfigContext();
updateConfigContext({
platform: Platform.Portal,
});
expect(getMongoShellUrl()).toBe(`/content/mongoshell/dist/index.html?${queryString}`);
});
it("should return /content/mongoshell/index.html when configContext.BACKEND_ENDPOINT !== '' and configContext.platform !== Platform.Hosted", () => {
resetConfigContext();
updateConfigContext({
platform: Platform.Portal,
BACKEND_ENDPOINT: mongoBackendEndpoint,
});
expect(getMongoShellUrl()).toBe(`${mongoBackendEndpoint}/content/mongoshell/dist/index.html?${queryString}`);
});
it("should return /mongoshell/index.html when enableLegacyMongoShellV1===true", () => {
updateUserContext({
features: extractFeatures(
new URLSearchParams({
"feature.enableLegacyMongoShellV1": "true",
})
),
});
expect(getMongoShellUrl()).toBe(`/mongoshell/index.html?${queryString}`);
});
it("should return /mongoshell/index.html when enableLegacyMongoShellV2===true", () => {
updateUserContext({
features: extractFeatures(
new URLSearchParams({
"feature.enableLegacyMongoShellV2": "true",
})
),
});
expect(getMongoShellUrl()).toBe(`/mongoshell/indexv2.html?${queryString}`);
});
it("should return /mongoshell/index.html when enableLegacyMongoShellV1Dist===true", () => {
updateUserContext({
features: extractFeatures(
new URLSearchParams({
"feature.enableLegacyMongoShellV1Dist": "true",
})
),
});
expect(getMongoShellUrl()).toBe(`/mongoshell/dist/index.html?${queryString}`);
});
it("should return /mongoshell/index.html when enableLegacyMongoShellV2Dist===true", () => {
updateUserContext({
features: extractFeatures(
new URLSearchParams({
"feature.enableLegacyMongoShellV2Dist": "true",
})
),
});
expect(getMongoShellUrl()).toBe(`/mongoshell/dist/indexv2.html?${queryString}`);
});
});
describe("getExtensionEndpoint", () => {
it("when platform === Platform.Hosted, backendEndpoint is undefined ", () => {
expect(getExtensionEndpoint(Platform.Hosted, undefined)).toBe("");
});
it("when platform === Platform.Hosted, backendEndpoint === ''", () => {
expect(getExtensionEndpoint(Platform.Hosted, "")).toBe("");
});
it("when platform === Platform.Hosted, backendEndpoint === null", () => {
expect(getExtensionEndpoint(Platform.Hosted, null)).toBe("");
});
it("when platform === Platform.Hosted, backendEndpoint != '' ", () => {
expect(getExtensionEndpoint(Platform.Hosted, "foo")).toBe("foo");
});
it("when platform === Platform.Portal, backendEndpoint is udefined ", () => {
expect(getExtensionEndpoint(Platform.Portal, undefined)).toBe("");
});
it("when platform === Platform.Portal, backendEndpoint === '' ", () => {
expect(getExtensionEndpoint(Platform.Portal, "")).toBe("");
});
it("when platform === Platform.Portal, backendEndpoint === null", () => {
expect(getExtensionEndpoint(Platform.Portal, null)).toBe("");
});
it("when platform !== Platform.Portal, backendEndpoint != '' ", () => {
expect(getExtensionEndpoint(Platform.Portal, "foo")).toBe("foo");
});
});

View File

@ -0,0 +1,42 @@
import { configContext, Platform } from "../../../ConfigContext";
import { userContext } from "../../../UserContext";
export function getMongoShellUrl(): string {
const { databaseAccount: account } = userContext;
const resourceId = account?.id;
const accountName = account?.name;
const mongoEndpoint = account?.properties?.mongoEndpoint || account?.properties?.documentEndpoint;
const queryString = `resourceId=${resourceId}&accountName=${accountName}&mongoEndpoint=${mongoEndpoint}`;
if (userContext.features.enableLegacyMongoShellV1 === true) {
return `/mongoshell/index.html?${queryString}`;
}
if (userContext.features.enableLegacyMongoShellV1Dist === true) {
return `/mongoshell/dist/index.html?${queryString}`;
}
if (userContext.features.enableLegacyMongoShellV2 === true) {
return `/mongoshell/indexv2.html?${queryString}`;
}
if (userContext.features.enableLegacyMongoShellV2Dist === true) {
return `/mongoshell/dist/indexv2.html?${queryString}`;
}
const extensionEndpoint: string = getExtensionEndpoint(configContext.platform, configContext.BACKEND_ENDPOINT);
if (userContext.portalEnv === "localhost") {
return `${extensionEndpoint}/content/mongoshell/index.html?${queryString}`;
}
return `${extensionEndpoint}/content/mongoshell/dist/index.html?${queryString}`;
}
export function getExtensionEndpoint(platform: string, backendEndpoint: string): string {
const runtimeEndpoint = platform === Platform.Hosted ? backendEndpoint : "";
const extensionEndpoint: string = backendEndpoint || runtimeEndpoint || "";
return extensionEndpoint;
}