diff --git a/src/Common/CosmosClient.ts b/src/Common/CosmosClient.ts index 6d813ac68..2d3c82778 100644 --- a/src/Common/CosmosClient.ts +++ b/src/Common/CosmosClient.ts @@ -20,10 +20,11 @@ export const tokenProvider = async (requestInfo: Cosmos.RequestInfo) => { const { verb, resourceId, resourceType, headers } = requestInfo; if (userContext.features.enableAadDataPlane || (userContext.dataPlaneRbacEnabled && userContext.apiType === "SQL")) { - if(!userContext.aadToken) - { - logConsoleError(`AAD token does not exist. Please use "Login for Entra ID" prior to performing Entra ID RBAC operations`); - return; + if (!userContext.aadToken) { + logConsoleError( + `AAD token does not exist. Please use "Login for Entra ID" prior to performing Entra ID RBAC operations`, + ); + return null; } const AUTH_PREFIX = `type=aad&ver=1.0&sig=`; const authorizationToken = `${AUTH_PREFIX}${userContext.aadToken}`; @@ -79,19 +80,29 @@ export const tokenProvider = async (requestInfo: Cosmos.RequestInfo) => { if (userContext.masterKey) { // TODO This SDK method mutates the headers object. Find a better one or fix the SDK. - await Cosmos.setAuthorizationTokenHeaderUsingMasterKey(verb, resourceId, resourceType, headers, userContext.masterKey); + await Cosmos.setAuthorizationTokenHeaderUsingMasterKey( + verb, + resourceId, + resourceType, + headers, + userContext.masterKey, + ); return decodeURIComponent(headers.authorization); - } - else - { + } else { const { databaseAccount: account, subscriptionId, resourceGroup } = userContext; const keys: DatabaseAccountListKeysResult = await listKeys(subscriptionId, resourceGroup, account.name); - if(keys.primaryMasterKey) { - updateUserContext ({ masterKey: keys.primaryMasterKey}); - // TODO This SDK method mutates the headers object. Find a better one or fix the SDK. - await Cosmos.setAuthorizationTokenHeaderUsingMasterKey(verb, resourceId, resourceType, headers, keys.primaryMasterKey); - return decodeURIComponent(headers.authorization); + if (keys.primaryMasterKey) { + updateUserContext({ masterKey: keys.primaryMasterKey }); + // TODO This SDK method mutates the headers object. Find a better one or fix the SDK. + await Cosmos.setAuthorizationTokenHeaderUsingMasterKey( + verb, + resourceId, + resourceType, + headers, + keys.primaryMasterKey, + ); + return decodeURIComponent(headers.authorization); } } diff --git a/src/Explorer/Explorer.tsx b/src/Explorer/Explorer.tsx index 0ead6fa91..6ebdae241 100644 --- a/src/Explorer/Explorer.tsx +++ b/src/Explorer/Explorer.tsx @@ -38,11 +38,11 @@ import { stringToBlob } from "../Utils/BlobUtils"; import { isCapabilityEnabled } from "../Utils/CapabilityUtils"; import { fromContentUri, toRawContentUri } from "../Utils/GitHubUtils"; import * as NotificationConsoleUtils from "../Utils/NotificationConsoleUtils"; -import { logConsoleError, logConsoleInfo } from "../Utils/NotificationConsoleUtils"; +import { logConsoleError, logConsoleInfo, logConsoleProgress } from "../Utils/NotificationConsoleUtils"; import { useSidePanel } from "../hooks/useSidePanel"; import { useTabs } from "../hooks/useTabs"; import "./ComponentRegisterer"; -import { useDialog } from "./Controls/Dialog"; +import { DialogProps, useDialog } from "./Controls/Dialog"; import { GalleryTab as GalleryTabKind } from "./Controls/NotebookGallery/GalleryViewerComponent"; import { useCommandBar } from "./Menus/CommandBar/CommandBarComponentAdapter"; import * as FileSystemUtil from "./Notebook/FileSystemUtil"; @@ -67,6 +67,7 @@ import { ResourceTreeAdapter } from "./Tree/ResourceTreeAdapter"; import StoredProcedure from "./Tree/StoredProcedure"; import { useDatabases } from "./useDatabases"; import { useSelectedNode } from "./useSelectedNode"; +import { update } from "Utils/arm/generatedClients/cosmos/databaseAccounts"; BindingHandlersRegisterer.registerBindingHandlers(); @@ -256,7 +257,10 @@ export default class Explorer { public async openLoginForEntraIDPopUp(): Promise { if (userContext.databaseAccount.properties?.documentEndpoint) { - const hrefEndpoint = new URL(userContext.databaseAccount.properties.documentEndpoint).href.replace(/\/$/, "/.default"); + const hrefEndpoint = new URL(userContext.databaseAccount.properties.documentEndpoint).href.replace( + /\/$/, + "/.default", + ); const msalInstance = await getMsalInstance(); try { @@ -267,25 +271,28 @@ export default class Explorer { localStorage.setItem("cachedTenantId", response.tenantId); const cachedAccount = msalInstance.getAllAccounts()?.[0]; msalInstance.setActiveAccount(cachedAccount); - let aadToken = await acquireTokenWithMsal(msalInstance, { + const aadToken = await acquireTokenWithMsal(msalInstance, { forceRefresh: true, scopes: [hrefEndpoint], authority: `${configContext.AAD_ENDPOINT}${localStorage.getItem("cachedTenantId")}`, }); - updateUserContext({aadToken: aadToken}); - } catch (error) { + updateUserContext({ aadToken: aadToken }); + } catch (error) { if (error instanceof msal.AuthError && error.errorCode === msal.BrowserAuthErrorMessage.popUpWindowError.code) { - useDialog - .getState() - .showOkModalDialog( - "Pop up blocked", - "We were unable to establish authorization for this account, due to pop-ups being disabled in the browser.\nPlease enable pop-ups for this site and try again", - ); + useDialog + .getState() + .showOkModalDialog( + "Pop up blocked", + "We were unable to establish authorization for this account, due to pop-ups being disabled in the browser.\nPlease enable pop-ups for this site and try again", + ); } else { const errorJson = JSON.stringify(error); useDialog - .getState() - .showOkModalDialog("Failed to perform authorization", `We were unable to establish authorization for this account, due to the following error: \n${errorJson}`); + .getState() + .showOkModalDialog( + "Failed to perform authorization", + `We were unable to establish authorization for this account, due to the following error: \n${errorJson}`, + ); } } } diff --git a/src/Explorer/Panes/SettingsPane/SettingsPane.tsx b/src/Explorer/Panes/SettingsPane/SettingsPane.tsx index 6393f19f3..7e9d2e48f 100644 --- a/src/Explorer/Panes/SettingsPane/SettingsPane.tsx +++ b/src/Explorer/Panes/SettingsPane/SettingsPane.tsx @@ -15,7 +15,7 @@ import { import * as Constants from "Common/Constants"; import { SplitterDirection } from "Common/Splitter"; import { InfoTooltip } from "Common/Tooltip/InfoTooltip"; -import { Platform, configContext } from "ConfigContext" +import { Platform, configContext } from "ConfigContext"; import { useDatabases } from "Explorer/useDatabases"; import { DefaultRUThreshold, @@ -51,9 +51,10 @@ export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({ ); const [enableDataPlaneRBACOption, setEnableDataPlaneRBACOption] = useState( - LocalStorageUtility.hasItem(StorageKey.DataPlaneRbacEnabled) - ? LocalStorageUtility.getEntryString(StorageKey.DataPlaneRbacEnabled) - : Constants.RBACOptions.setAutomaticRBACOption); + LocalStorageUtility.hasItem(StorageKey.DataPlaneRbacEnabled) + ? LocalStorageUtility.getEntryString(StorageKey.DataPlaneRbacEnabled) + : Constants.RBACOptions.setAutomaticRBACOption, + ); const [showDataPlaneRBACWarning, setShowDataPlaneRBACWarning] = useState(false); const [ruThresholdEnabled, setRUThresholdEnabled] = useState(isRUThresholdEnabled()); @@ -136,15 +137,18 @@ export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({ LocalStorageUtility.setEntryNumber(StorageKey.CustomItemPerPage, customItemPerPage); LocalStorageUtility.setEntryString(StorageKey.DataPlaneRbacEnabled, enableDataPlaneRBACOption); - if(enableDataPlaneRBACOption === Constants.RBACOptions.setTrueRBACOption || (enableDataPlaneRBACOption === Constants.RBACOptions.setAutomaticRBACOption && userContext.databaseAccount.properties.disableLocalAuth)) { + if ( + enableDataPlaneRBACOption === Constants.RBACOptions.setTrueRBACOption || + (enableDataPlaneRBACOption === Constants.RBACOptions.setAutomaticRBACOption && + userContext.databaseAccount.properties.disableLocalAuth) + ) { updateUserContext({ - dataPlaneRbacEnabled: true + dataPlaneRbacEnabled: true, }); - } - else { + } else { updateUserContext({ dataPlaneRbacEnabled: false, - }) + }); } LocalStorageUtility.setEntryBoolean(StorageKey.RUThresholdEnabled, ruThresholdEnabled); @@ -261,9 +265,11 @@ export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({ option: IChoiceGroupOption, ): void => { setEnableDataPlaneRBACOption(option.key); - - const shouldShowWarning = option.key === Constants.RBACOptions.setTrueRBACOption || - (option.key === Constants.RBACOptions.setAutomaticRBACOption && userContext.databaseAccount.properties.disableLocalAuth === true); + + const shouldShowWarning = + option.key === Constants.RBACOptions.setTrueRBACOption || + (option.key === Constants.RBACOptions.setAutomaticRBACOption && + userContext.databaseAccount.properties.disableLocalAuth === true); setShowDataPlaneRBACWarning(shouldShowWarning); }; @@ -427,48 +433,56 @@ export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({ )} - {(userContext.apiType === "SQL" && userContext.authType == AuthType.AAD) && ( + {userContext.apiType === "SQL" && userContext.authType === AuthType.AAD && ( <> -
-
-
- - Enable Entra ID RBAC - - - Choose Automatic to enable Entra ID RBAC automatically. True/False to force enable/disable Entra ID RBAC. - Learn more - - } - > - - - {(showDataPlaneRBACWarning && configContext.platform == Platform.Portal) && ( - setShowDataPlaneRBACWarning(false)} - dismissButtonAriaLabel="Close" - > - Please click on "Login for Entra ID RBAC" prior to performing Entra ID RBAC operations - - )} - -
+
+
+
+ + Enable Entra ID RBAC + + + Choose Automatic to enable Entra ID RBAC automatically. True/False to force enable/disable Entra + ID RBAC. + + {" "} + Learn more{" "} + + + } + > + + + {showDataPlaneRBACWarning && configContext.platform === Platform.Portal && ( + setShowDataPlaneRBACWarning(false)} + dismissButtonAriaLabel="Close" + > + Please click on "Login for Entra ID RBAC" prior to performing Entra ID RBAC operations + + )} + +
+
-
)} - {(userContext.apiType === "SQL") && ( - <> + {userContext.apiType === "SQL" && ( + <>
diff --git a/src/hooks/useKnockoutExplorer.ts b/src/hooks/useKnockoutExplorer.ts index 7a4e92896..9aef090e8 100644 --- a/src/hooks/useKnockoutExplorer.ts +++ b/src/hooks/useKnockoutExplorer.ts @@ -255,7 +255,7 @@ async function configureHostedWithAAD(config: AAD): Promise { const subscriptionId = accountResourceId && accountResourceId.split("subscriptions/")[1].split("/")[0]; const resourceGroup = accountResourceId && accountResourceId.split("resourceGroups/")[1].split("/")[0]; let aadToken; - let keys: DatabaseAccountListKeysResult = {}; + const keys: DatabaseAccountListKeysResult = {}; if (account.properties?.documentEndpoint) { const hrefEndpoint = new URL(account.properties.documentEndpoint).href.replace(/\/$/, "/.default"); const msalInstance = await getMsalInstance(); @@ -273,27 +273,25 @@ async function configureHostedWithAAD(config: AAD): Promise { } } try { - if (userContext.apiType === "SQL") - { - if (LocalStorageUtility.hasItem(StorageKey.DataPlaneRbacEnabled)) { - const isDataPlaneRbacSetting = LocalStorageUtility.getEntryString(StorageKey.DataPlaneRbacEnabled); - - let dataPlaneRbacEnabled; - if (isDataPlaneRbacSetting === Constants.RBACOptions.setAutomaticRBACOption) { - dataPlaneRbacEnabled = account.properties.disableLocalAuth; - } else { - dataPlaneRbacEnabled = isDataPlaneRbacSetting === Constants.RBACOptions.setTrueRBACOption; - } - - updateUserContext({ dataPlaneRbacEnabled }); - } - } - else { - let keys: DatabaseAccountListKeysResult = await listKeys(subscriptionId, resourceGroup, account.name); - updateUserContext({ - masterKey: keys.primaryMasterKey - }); - } + if (userContext.apiType === "SQL") { + if (LocalStorageUtility.hasItem(StorageKey.DataPlaneRbacEnabled)) { + const isDataPlaneRbacSetting = LocalStorageUtility.getEntryString(StorageKey.DataPlaneRbacEnabled); + + let dataPlaneRbacEnabled; + if (isDataPlaneRbacSetting === Constants.RBACOptions.setAutomaticRBACOption) { + dataPlaneRbacEnabled = account.properties.disableLocalAuth; + } else { + dataPlaneRbacEnabled = isDataPlaneRbacSetting === Constants.RBACOptions.setTrueRBACOption; + } + + updateUserContext({ dataPlaneRbacEnabled }); + } + } else { + const keys: DatabaseAccountListKeysResult = await listKeys(subscriptionId, resourceGroup, account.name); + updateUserContext({ + masterKey: keys.primaryMasterKey, + }); + } } catch (e) { if (userContext.features.enableAadDataPlane) { console.warn(e); @@ -473,28 +471,26 @@ async function configurePortal(): Promise { } const { databaseAccount: account, subscriptionId, resourceGroup } = userContext; - - if (userContext.apiType === "SQL") - { - if (LocalStorageUtility.hasItem(StorageKey.DataPlaneRbacEnabled)) { - const isDataPlaneRbacSetting = LocalStorageUtility.getEntryString(StorageKey.DataPlaneRbacEnabled); - - let dataPlaneRbacEnabled; - if (isDataPlaneRbacSetting === Constants.RBACOptions.setAutomaticRBACOption) { - dataPlaneRbacEnabled = account.properties.disableLocalAuth; - } else { - dataPlaneRbacEnabled = isDataPlaneRbacSetting === Constants.RBACOptions.setTrueRBACOption; - } - - updateUserContext({ dataPlaneRbacEnabled }); - } + + if (userContext.apiType === "SQL") { + if (LocalStorageUtility.hasItem(StorageKey.DataPlaneRbacEnabled)) { + const isDataPlaneRbacSetting = LocalStorageUtility.getEntryString(StorageKey.DataPlaneRbacEnabled); + + let dataPlaneRbacEnabled; + if (isDataPlaneRbacSetting === Constants.RBACOptions.setAutomaticRBACOption) { + dataPlaneRbacEnabled = account.properties.disableLocalAuth; + } else { + dataPlaneRbacEnabled = isDataPlaneRbacSetting === Constants.RBACOptions.setTrueRBACOption; + } + + updateUserContext({ dataPlaneRbacEnabled }); } - else { - let keys: DatabaseAccountListKeysResult = await listKeys(subscriptionId, resourceGroup, account.name); + } else { + const keys: DatabaseAccountListKeysResult = await listKeys(subscriptionId, resourceGroup, account.name); updateUserContext({ - masterKey: keys.primaryMasterKey + masterKey: keys.primaryMasterKey, }); - } + } if (openAction) { handleOpenAction(openAction, useDatabases.getState().databases, explorer);