diff --git a/src/Explorer/Menus/CommandBar/CommandBarComponentButtonFactory.tsx b/src/Explorer/Menus/CommandBar/CommandBarComponentButtonFactory.tsx index 471f20048..90f12fc8a 100644 --- a/src/Explorer/Menus/CommandBar/CommandBarComponentButtonFactory.tsx +++ b/src/Explorer/Menus/CommandBar/CommandBarComponentButtonFactory.tsx @@ -17,12 +17,18 @@ import SynapseIcon from "../../../../images/synapse-link.svg"; import VSCodeIcon from "../../../../images/vscode.svg"; import { AuthType } from "../../../AuthType"; import * as Constants from "../../../Common/Constants"; -import { Platform, configContext } from "../../../ConfigContext"; +import { configContext, Platform } from "../../../ConfigContext"; import * as ViewModels from "../../../Contracts/ViewModels"; -import { userContext } from "../../../UserContext"; +import { + isVCoreMongoNativeAuthDisabled, + userContext, + VCoreMongoNativeAuthDisabledMessage, + VCoreMongoNativeAuthLearnMoreUrl, +} from "../../../UserContext"; import { isRunningOnNationalCloud } from "../../../Utils/CloudUtils"; import { useSidePanel } from "../../../hooks/useSidePanel"; import { CommandButtonComponentProps } from "../../Controls/CommandButton/CommandButtonComponent"; +import { useDialog } from "../../Controls/Dialog"; import Explorer from "../../Explorer"; import { useNotebook } from "../../Notebook/useNotebook"; import { BrowseQueriesPane } from "../../Panes/BrowseQueriesPane/BrowseQueriesPane"; @@ -508,12 +514,23 @@ function createOpenTerminalButtonByKind( const label = `Open ${terminalFriendlyName()} shell`; const tooltip = "This feature is not yet available in your account's region. View supported regions here: https://aka.ms/cosmos-enable-notebooks."; + const isNativeAuthDisabled = + terminalKind === ViewModels.TerminalKind.VCoreMongo && isVCoreMongoNativeAuthDisabled(); const disableButton = - !useNotebook.getState().isNotebooksEnabledForAccount && !useNotebook.getState().isNotebookEnabled; + (!useNotebook.getState().isNotebooksEnabledForAccount && !useNotebook.getState().isNotebookEnabled) || + isNativeAuthDisabled; return { iconSrc: HostedTerminalIcon, iconAlt: label, onCommandClick: () => { + if (isNativeAuthDisabled) { + useDialog.getState().showOkModalDialog( + "Native Authentication Disabled", + VCoreMongoNativeAuthDisabledMessage, + { linkText: "Learn more", linkUrl: VCoreMongoNativeAuthLearnMoreUrl }, + ); + return; + } if (useNotebook.getState().isNotebookEnabled || userContext.features.enableCloudShell) { container.openNotebookTerminal(terminalKind); } @@ -522,7 +539,7 @@ function createOpenTerminalButtonByKind( hasPopup: false, disabled: disableButton, ariaLabel: label, - tooltipText: !disableButton ? "" : tooltip, + tooltipText: isNativeAuthDisabled ? VCoreMongoNativeAuthDisabledMessage : !disableButton ? "" : tooltip, }; } diff --git a/src/Explorer/SplashScreen/SplashScreen.tsx b/src/Explorer/SplashScreen/SplashScreen.tsx index 7376f5c8e..d104f8f41 100644 --- a/src/Explorer/SplashScreen/SplashScreen.tsx +++ b/src/Explorer/SplashScreen/SplashScreen.tsx @@ -34,8 +34,14 @@ import VisualStudioIcon from "../../../images/VisualStudio.svg"; import NotebookIcon from "../../../images/notebook/Notebook-resource.svg"; import CollectionIcon from "../../../images/tree-collection.svg"; import * as Constants from "../../Common/Constants"; -import { userContext } from "../../UserContext"; +import { + isVCoreMongoNativeAuthDisabled, + userContext, + VCoreMongoNativeAuthDisabledMessage, + VCoreMongoNativeAuthLearnMoreUrl, +} from "../../UserContext"; import { getCollectionName } from "../../Utils/APITypeUtils"; +import { useDialog } from "../Controls/Dialog"; import Explorer from "../Explorer"; import * as MostRecentActivity from "../MostRecentActivity/MostRecentActivity"; import { useNotebook } from "../Notebook/useNotebook"; @@ -423,11 +429,22 @@ export const SplashScreen: React.FC = ({ explorer }) => { } if (userContext.apiType === "VCoreMongo") { + const isNativeAuthDisabled = isVCoreMongoNativeAuthDisabled(); return { iconSrc: PowerShellIcon, title: "Mongo Shell", description: "Create a collection and interact with data using MongoDB's shell interface", - onClick: () => container.openNotebookTerminal(TerminalKind.VCoreMongo), + onClick: () => { + if (isNativeAuthDisabled) { + useDialog.getState().showOkModalDialog( + "Native Authentication Disabled", + VCoreMongoNativeAuthDisabledMessage, + { linkText: "Learn more", linkUrl: VCoreMongoNativeAuthLearnMoreUrl }, + ); + } else { + container.openNotebookTerminal(TerminalKind.VCoreMongo); + } + }, }; } diff --git a/src/Explorer/Tabs/VCoreMongoQuickstartTab.tsx b/src/Explorer/Tabs/VCoreMongoQuickstartTab.tsx index 3fb8dde4b..c12d21db8 100644 --- a/src/Explorer/Tabs/VCoreMongoQuickstartTab.tsx +++ b/src/Explorer/Tabs/VCoreMongoQuickstartTab.tsx @@ -1,4 +1,4 @@ -import { Spinner, SpinnerSize, Stack, Text } from "@fluentui/react"; +import { Link, MessageBar, MessageBarType, Spinner, SpinnerSize, Stack, Text } from "@fluentui/react"; import { PoolIdType } from "Common/Constants"; import { NotebookWorkspaceConnectionInfo } from "Contracts/DataModels"; import { MessageTypes } from "Contracts/ExplorerContracts"; @@ -8,7 +8,12 @@ import { useNotebook } from "Explorer/Notebook/useNotebook"; import { QuickstartFirewallNotification } from "Explorer/Quickstart/QuickstartFirewallNotification"; import { VcoreMongoQuickstartGuide } from "Explorer/Quickstart/VCoreMongoQuickstartGuide"; import { checkFirewallRules } from "Explorer/Tabs/Shared/CheckFirewallRules"; -import { userContext } from "UserContext"; +import { + isVCoreMongoNativeAuthDisabled, + userContext, + VCoreMongoNativeAuthDisabledMessage, + VCoreMongoNativeAuthLearnMoreUrl, +} from "UserContext"; import React, { useEffect, useState } from "react"; import FirewallRuleScreenshot from "../../../images/vcoreMongoFirewallRule.png"; @@ -21,6 +26,7 @@ export const VcoreMongoQuickstartTab: React.FC = ( }: VCoreMongoQuickstartTabProps): JSX.Element => { const notebookServerInfo = useNotebook((state) => state.notebookServerInfo); const [isAllPublicIPAddressEnabled, setIsAllPublicIPAddressEnabled] = useState(true); + const isNativeAuthDisabled = isVCoreMongoNativeAuthDisabled(); const getNotebookServerInfo = (): NotebookWorkspaceConnectionInfo => ({ authToken: notebookServerInfo.authToken, @@ -48,14 +54,26 @@ export const VcoreMongoQuickstartTab: React.FC = ( - {!isAllPublicIPAddressEnabled && ( + {isNativeAuthDisabled && ( + + + + {VCoreMongoNativeAuthDisabledMessage}{" "} + + Learn more + + + + + )} + {!isNativeAuthDisabled && !isAllPublicIPAddressEnabled && ( )} - {isAllPublicIPAddressEnabled && notebookServerInfo?.notebookServerEndpoint && ( + {!isNativeAuthDisabled && isAllPublicIPAddressEnabled && notebookServerInfo?.notebookServerEndpoint && ( = ( username={userContext.vcoreMongoConnectionParams.adminLogin} /> )} - {isAllPublicIPAddressEnabled && !notebookServerInfo?.notebookServerEndpoint && ( + {!isNativeAuthDisabled && isAllPublicIPAddressEnabled && !notebookServerInfo?.notebookServerEndpoint && ( Connecting to the Mongo shell. diff --git a/src/UserContext.ts b/src/UserContext.ts index a495b9971..6583b6441 100644 --- a/src/UserContext.ts +++ b/src/UserContext.ts @@ -42,10 +42,25 @@ export interface PostgresConnectionStrParams { isMarlinServerGroup: boolean; isFreeTier: boolean; } +export type VCoreMongoAuthMode = "NativeAuth" | "MicrosoftEntraID"; + +export interface VCoreMongoAuthConfig { + allowedModes?: VCoreMongoAuthMode[]; +} export interface VCoreMongoConnectionParams { adminLogin: string; connectionString: string; + authConfig?: VCoreMongoAuthConfig; +} + +export const VCoreMongoNativeAuthLearnMoreUrl = "https://go.microsoft.com/fwlink/?linkid=2340100"; + +export const VCoreMongoNativeAuthDisabledMessage = + "Native DocumentDB authentication is disabled on this cluster. You can use MongoDB Shell with Entra ID authentication outside of the Azure portal."; +export function isVCoreMongoNativeAuthDisabled(): boolean { + const allowedModes = userContext.vcoreMongoConnectionParams?.authConfig?.allowedModes || []; + return allowedModes.length > 0 && !allowedModes.includes("NativeAuth"); } export interface FabricArtifactInfo { @@ -212,3 +227,4 @@ export function apiType(account: DatabaseAccount | undefined): ApiType { } export { updateUserContext, userContext }; +