From 874cec26fcc5df2bfac490516b280f01f65b2fbb Mon Sep 17 00:00:00 2001 From: Armando Trejo Oliver Date: Tue, 4 Apr 2023 18:19:18 -0700 Subject: [PATCH] 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 --- .../MongoShellTab/MongoShellTabComponent.tsx | 42 +--- .../MongoShellTab/getMongoShellOrigin.test.ts | 73 +++++++ .../Tabs/MongoShellTab/getMongoShellOrigin.ts | 15 ++ .../MongoShellTab/getMongoShellUrl.test.ts | 179 ++++++++++++++++++ .../Tabs/MongoShellTab/getMongoShellUrl.ts | 42 ++++ 5 files changed, 315 insertions(+), 36 deletions(-) create mode 100644 src/Explorer/Tabs/MongoShellTab/getMongoShellOrigin.test.ts create mode 100644 src/Explorer/Tabs/MongoShellTab/getMongoShellOrigin.ts create mode 100644 src/Explorer/Tabs/MongoShellTab/getMongoShellUrl.test.ts create mode 100644 src/Explorer/Tabs/MongoShellTab/getMongoShellUrl.ts diff --git a/src/Explorer/Tabs/MongoShellTab/MongoShellTabComponent.tsx b/src/Explorer/Tabs/MongoShellTab/MongoShellTabComponent.tsx index d3f4105a8..f0274638f 100644 --- a/src/Explorer/Tabs/MongoShellTab/MongoShellTabComponent.tsx +++ b/src/Explorer/Tabs/MongoShellTab/MongoShellTabComponent.tsx @@ -1,6 +1,6 @@ import React, { Component } from "react"; import * as Constants from "../../../Common/Constants"; -import { configContext, Platform } from "../../../ConfigContext"; +import { configContext } from "../../../ConfigContext"; import * as ViewModels from "../../../Contracts/ViewModels"; import { Action, ActionModifiers } from "../../../Shared/Telemetry/TelemetryConstants"; 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 Explorer from "../../Explorer"; import TabsBase from "../TabsBase"; +import { getMongoShellOrigin } from "./getMongoShellOrigin"; +import { getMongoShellUrl } from "./getMongoShellUrl"; //eslint-disable-next-line class MessageType { @@ -47,7 +49,6 @@ export default class MongoShellTabComponent extends Component< IMongoShellTabComponentProps, IMongoShellTabComponentStates > { - private _runtimeEndpoint: string; private _logTraces: Map; constructor(props: IMongoShellTabComponentProps) { @@ -55,7 +56,7 @@ export default class MongoShellTabComponent extends Component< this._logTraces = new Map(); this.state = { - url: this.getURL(), + url: getMongoShellUrl(), }; props.onMongoShellTabAccessor({ @@ -65,38 +66,6 @@ export default class MongoShellTabComponent extends Component< 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 public setContentFocus(event: React.SyntheticEvent): void {} @@ -152,6 +121,7 @@ export default class MongoShellTabComponent extends Component< const collectionId = this.props.collection.id(); const apiEndpoint = configContext.BACKEND_ENDPOINT; const encryptedAuthToken: string = userContext.accessToken; + const targetOrigin = getMongoShellOrigin(); shellIframe.contentWindow.postMessage( { @@ -167,7 +137,7 @@ export default class MongoShellTabComponent extends Component< apiEndpoint: apiEndpoint, }, }, - configContext.BACKEND_ENDPOINT + targetOrigin ); } diff --git a/src/Explorer/Tabs/MongoShellTab/getMongoShellOrigin.test.ts b/src/Explorer/Tabs/MongoShellTab/getMongoShellOrigin.test.ts new file mode 100644 index 000000000..4d3c5b668 --- /dev/null +++ b/src/Explorer/Tabs/MongoShellTab/getMongoShellOrigin.test.ts @@ -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); + }); +}); diff --git a/src/Explorer/Tabs/MongoShellTab/getMongoShellOrigin.ts b/src/Explorer/Tabs/MongoShellTab/getMongoShellOrigin.ts new file mode 100644 index 000000000..3f8a81c71 --- /dev/null +++ b/src/Explorer/Tabs/MongoShellTab/getMongoShellOrigin.ts @@ -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; +} diff --git a/src/Explorer/Tabs/MongoShellTab/getMongoShellUrl.test.ts b/src/Explorer/Tabs/MongoShellTab/getMongoShellUrl.test.ts new file mode 100644 index 000000000..58749aa7a --- /dev/null +++ b/src/Explorer/Tabs/MongoShellTab/getMongoShellUrl.test.ts @@ -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"); + }); +}); diff --git a/src/Explorer/Tabs/MongoShellTab/getMongoShellUrl.ts b/src/Explorer/Tabs/MongoShellTab/getMongoShellUrl.ts new file mode 100644 index 000000000..81a63a022 --- /dev/null +++ b/src/Explorer/Tabs/MongoShellTab/getMongoShellUrl.ts @@ -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; +}