mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2026-05-21 04:37:28 +01:00
Enable connection string login deeplink from Try Cosmos (#2490)
Co-authored-by: Asier Isayas <aisayas@microsoft.com>
This commit is contained in:
+72
-13
@@ -6,18 +6,21 @@ import { render } from "react-dom";
|
|||||||
import ChevronRight from "../images/chevron-right.svg";
|
import ChevronRight from "../images/chevron-right.svg";
|
||||||
import "../less/hostedexplorer.less";
|
import "../less/hostedexplorer.less";
|
||||||
import { AuthType } from "./AuthType";
|
import { AuthType } from "./AuthType";
|
||||||
|
import { logError } from "./Common/Logger";
|
||||||
import { DatabaseAccount } from "./Contracts/DataModels";
|
import { DatabaseAccount } from "./Contracts/DataModels";
|
||||||
import "./Explorer/Menus/NavBar/MeControlComponent.less";
|
import "./Explorer/Menus/NavBar/MeControlComponent.less";
|
||||||
import { HostedExplorerChildFrame } from "./HostedExplorerChildFrame";
|
import { HostedExplorerChildFrame } from "./HostedExplorerChildFrame";
|
||||||
import { AccountSwitcher } from "./Platform/Hosted/Components/AccountSwitcher";
|
import { AccountSwitcher } from "./Platform/Hosted/Components/AccountSwitcher";
|
||||||
import { ConnectExplorer } from "./Platform/Hosted/Components/ConnectExplorer";
|
import { ConnectExplorer, fetchEncryptedToken } from "./Platform/Hosted/Components/ConnectExplorer";
|
||||||
import { DirectoryPickerPanel } from "./Platform/Hosted/Components/DirectoryPickerPanel";
|
import { DirectoryPickerPanel } from "./Platform/Hosted/Components/DirectoryPickerPanel";
|
||||||
import { FeedbackCommandButton } from "./Platform/Hosted/Components/FeedbackCommandButton";
|
import { FeedbackCommandButton } from "./Platform/Hosted/Components/FeedbackCommandButton";
|
||||||
import { MeControl } from "./Platform/Hosted/Components/MeControl";
|
import { MeControl } from "./Platform/Hosted/Components/MeControl";
|
||||||
import { SignInButton } from "./Platform/Hosted/Components/SignInButton";
|
import { SignInButton } from "./Platform/Hosted/Components/SignInButton";
|
||||||
import "./Platform/Hosted/ConnectScreen.less";
|
import "./Platform/Hosted/ConnectScreen.less";
|
||||||
|
import { isResourceTokenConnectionString } from "./Platform/Hosted/Helpers/ResourceTokenUtils";
|
||||||
import { extractMasterKeyfromConnectionString } from "./Platform/Hosted/HostedUtils";
|
import { extractMasterKeyfromConnectionString } from "./Platform/Hosted/HostedUtils";
|
||||||
import "./Shared/appInsights";
|
import "./Shared/appInsights";
|
||||||
|
import { allowedHostedExplorerEndpoints } from "./Utils/EndpointUtils";
|
||||||
import { useAADAuth } from "./hooks/useAADAuth";
|
import { useAADAuth } from "./hooks/useAADAuth";
|
||||||
import { useConfig } from "./hooks/useConfig";
|
import { useConfig } from "./hooks/useConfig";
|
||||||
import { useTokenMetadata } from "./hooks/usePortalAccessToken";
|
import { useTokenMetadata } from "./hooks/usePortalAccessToken";
|
||||||
@@ -42,20 +45,68 @@ const App: React.FunctionComponent = () => {
|
|||||||
|
|
||||||
const ref = React.useRef<HTMLIFrameElement>();
|
const ref = React.useRef<HTMLIFrameElement>();
|
||||||
|
|
||||||
|
const connectWithConnectionString = React.useCallback(
|
||||||
|
(connStr: string) => {
|
||||||
|
if (!connStr || authType) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setConnectionString(connStr);
|
||||||
|
if (isResourceTokenConnectionString(connStr)) {
|
||||||
|
setAuthType(AuthType.ResourceToken);
|
||||||
|
} else {
|
||||||
|
fetchEncryptedToken(connStr)
|
||||||
|
.then((token) => {
|
||||||
|
setEncryptedToken(token);
|
||||||
|
setAuthType(AuthType.ConnectionString);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
logError(
|
||||||
|
`Failed to connect with connection string: ${error}`,
|
||||||
|
"HostedExplorer/connectWithConnectionString",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[authType],
|
||||||
|
);
|
||||||
|
|
||||||
|
// Listen for connection string sent via postMessage (from TryCosmosDB)
|
||||||
|
React.useEffect(() => {
|
||||||
|
const MSG_READY = "tryCosmosDBReady";
|
||||||
|
const MSG_CONNECTION_STRING = "tryCosmosDBConnectionString";
|
||||||
|
|
||||||
|
// Signal to the opener that we are ready to receive a connection string
|
||||||
|
if (window.opener) {
|
||||||
|
try {
|
||||||
|
for (const origin of allowedHostedExplorerEndpoints) {
|
||||||
|
window.opener.postMessage({ type: MSG_READY }, origin);
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
// opener may be cross-origin, ignore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handler = (event: MessageEvent) => {
|
||||||
|
if (!allowedHostedExplorerEndpoints.includes(event.origin)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (event.data?.type === MSG_CONNECTION_STRING && event.data?.connectionString) {
|
||||||
|
connectWithConnectionString(event.data.connectionString);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
window.addEventListener("message", handler);
|
||||||
|
return () => window.removeEventListener("message", handler);
|
||||||
|
}, [connectWithConnectionString]);
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
// If ref.current is undefined no iframe has been rendered
|
// If ref.current is undefined no iframe has been rendered
|
||||||
if (ref.current) {
|
if (ref.current) {
|
||||||
// In hosted mode, we can set global properties directly on the child iframe.
|
// In hosted mode, we can set global properties directly on the child iframe.
|
||||||
// This is not possible in the portal where the iframes have different origins
|
// This is not possible in the portal where the iframes have different origins
|
||||||
const frameWindow = ref.current.contentWindow as HostedExplorerChildFrame;
|
const frameWindow = ref.current.contentWindow as HostedExplorerChildFrame;
|
||||||
// AAD authenticated uses ALWAYS using AAD authType
|
|
||||||
if (isLoggedIn) {
|
if (authType === AuthType.EncryptedToken) {
|
||||||
frameWindow.hostedConfig = {
|
|
||||||
authType: AuthType.AAD,
|
|
||||||
databaseAccount,
|
|
||||||
authorizationToken: armToken,
|
|
||||||
};
|
|
||||||
} else if (authType === AuthType.EncryptedToken) {
|
|
||||||
frameWindow.hostedConfig = {
|
frameWindow.hostedConfig = {
|
||||||
authType: AuthType.EncryptedToken,
|
authType: AuthType.EncryptedToken,
|
||||||
encryptedToken,
|
encryptedToken,
|
||||||
@@ -73,12 +124,18 @@ const App: React.FunctionComponent = () => {
|
|||||||
authType: AuthType.ResourceToken,
|
authType: AuthType.ResourceToken,
|
||||||
resourceToken: connectionString,
|
resourceToken: connectionString,
|
||||||
};
|
};
|
||||||
|
} else if (isLoggedIn && !connectionString) {
|
||||||
|
frameWindow.hostedConfig = {
|
||||||
|
authType: AuthType.AAD,
|
||||||
|
databaseAccount,
|
||||||
|
authorizationToken: armToken,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const showExplorer =
|
const showExplorer =
|
||||||
(config && isLoggedIn && databaseAccount) ||
|
(config && isLoggedIn && databaseAccount && !connectionString) ||
|
||||||
(encryptedTokenMetadata && encryptedTokenMetadata) ||
|
(encryptedTokenMetadata && encryptedTokenMetadata) ||
|
||||||
(authType === AuthType.ResourceToken && connectionString);
|
(authType === AuthType.ResourceToken && connectionString);
|
||||||
|
|
||||||
@@ -99,12 +156,12 @@ const App: React.FunctionComponent = () => {
|
|||||||
{(isLoggedIn || encryptedTokenMetadata?.accountName) && (
|
{(isLoggedIn || encryptedTokenMetadata?.accountName) && (
|
||||||
<img className="chevronRight" src={ChevronRight} alt="account separator" />
|
<img className="chevronRight" src={ChevronRight} alt="account separator" />
|
||||||
)}
|
)}
|
||||||
{isLoggedIn && (
|
{isLoggedIn && !connectionString && (
|
||||||
<span className="accountSwitchComponentContainer">
|
<span className="accountSwitchComponentContainer">
|
||||||
<AccountSwitcher armToken={armToken} setDatabaseAccount={setDatabaseAccount} />
|
<AccountSwitcher armToken={armToken} setDatabaseAccount={setDatabaseAccount} />
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
{!isLoggedIn && encryptedTokenMetadata?.accountName && (
|
{(!isLoggedIn || connectionString) && encryptedTokenMetadata?.accountName && (
|
||||||
<span className="accountSwitchComponentContainer">
|
<span className="accountSwitchComponentContainer">
|
||||||
<span className="accountNameHeader">{encryptedTokenMetadata?.accountName}</span>
|
<span className="accountNameHeader">{encryptedTokenMetadata?.accountName}</span>
|
||||||
</span>
|
</span>
|
||||||
@@ -127,7 +184,9 @@ const App: React.FunctionComponent = () => {
|
|||||||
// It's possible this can be changed once all knockout code has been removed.
|
// It's possible this can be changed once all knockout code has been removed.
|
||||||
<iframe
|
<iframe
|
||||||
// Setting key is needed so React will re-render this element on any account change
|
// Setting key is needed so React will re-render this element on any account change
|
||||||
key={databaseAccount?.id || encryptedTokenMetadata?.accountName || authType}
|
key={
|
||||||
|
authType ? `${authType}-${encryptedTokenMetadata?.accountName || connectionString}` : databaseAccount?.id
|
||||||
|
}
|
||||||
ref={ref}
|
ref={ref}
|
||||||
data-test="DataExplorerFrame"
|
data-test="DataExplorerFrame"
|
||||||
id="explorerMenu"
|
id="explorerMenu"
|
||||||
|
|||||||
@@ -83,7 +83,10 @@ export const allowedEmulatorEndpoints: ReadonlyArray<string> = ["https://localho
|
|||||||
|
|
||||||
export const allowedArcadiaEndpoints: ReadonlyArray<string> = ["https://workspaceartifacts.projectarcadia.net"];
|
export const allowedArcadiaEndpoints: ReadonlyArray<string> = ["https://workspaceartifacts.projectarcadia.net"];
|
||||||
|
|
||||||
export const allowedHostedExplorerEndpoints: ReadonlyArray<string> = ["https://cosmos.azure.com/"];
|
export const allowedHostedExplorerEndpoints: ReadonlyArray<string> = [
|
||||||
|
"https://cosmos.azure.com",
|
||||||
|
"https://localhost:12900",
|
||||||
|
];
|
||||||
|
|
||||||
export const allowedMsalRedirectEndpoints: ReadonlyArray<string> = ["https://dataexplorer-preview.azurewebsites.net/"];
|
export const allowedMsalRedirectEndpoints: ReadonlyArray<string> = ["https://dataexplorer-preview.azurewebsites.net/"];
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user