mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2024-11-25 06:56:38 +00:00
Cleanup DP RBAC code
This commit is contained in:
parent
4792e2d1c7
commit
d3fb5eabdb
9
images/EntraID.svg
Normal file
9
images/EntraID.svg
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg id="uuid-f8d4d392-7c12-4bd9-baff-66fbf7814b91" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 18 18">
|
||||||
|
<path d="m3.802,14.032c.388.242,1.033.511,1.715.511.621,0,1.198-.18,1.676-.487,0,0,.001,0,.002-.001l1.805-1.128v4.073c-.286,0-.574-.078-.824-.234l-4.374-2.734Z" fill="#225086"/>
|
||||||
|
<path d="m7.853,1.507L.353,9.967c-.579.654-.428,1.642.323,2.111,0,0,2.776,1.735,3.126,1.954.388.242,1.033.511,1.715.511.621,0,1.198-.18,1.676-.487,0,0,.001,0,.002-.001l1.805-1.128-4.364-2.728,4.365-4.924V1s0,0,0,0c-.424,0-.847.169-1.147.507Z" fill="#6df"/>
|
||||||
|
<polygon points="4.636 10.199 4.688 10.231 9 12.927 9.001 12.927 9.001 12.927 9.001 5.276 9 5.275 4.636 10.199" fill="#cbf8ff"/>
|
||||||
|
<path d="m17.324,12.078c.751-.469.902-1.457.323-2.111l-4.921-5.551c-.397-.185-.842-.291-1.313-.291-.925,0-1.752.399-2.302,1.026l-.109.123h0s4.364,4.924,4.364,4.924h0s0,0,0,0l-4.365,2.728v4.073c.287,0,.573-.078.823-.234l7.5-4.688Z" fill="#074793"/>
|
||||||
|
<path d="m9.001,1v4.275s.109-.123.109-.123c.55-.627,1.377-1.026,2.302-1.026.472,0,.916.107,1.313.291l-2.579-2.909c-.299-.338-.723-.507-1.146-.507Z" fill="#0294e4"/>
|
||||||
|
<polygon points="13.365 10.199 13.365 10.199 13.365 10.199 9.001 5.276 9.001 12.926 13.365 10.199" fill="#96bcc2"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.3 KiB |
@ -185,9 +185,6 @@ export class CassandraProxyAPIs {
|
|||||||
export class Queries {
|
export class Queries {
|
||||||
public static CustomPageOption: string = "custom";
|
public static CustomPageOption: string = "custom";
|
||||||
public static UnlimitedPageOption: string = "unlimited";
|
public static UnlimitedPageOption: string = "unlimited";
|
||||||
public static setAutomaticRBACOption: string = "Automatic";
|
|
||||||
public static setTrueRBACOption: string = "True";
|
|
||||||
public static setFalseRBACOption: string = "False";
|
|
||||||
public static itemsPerPage: number = 100;
|
public static itemsPerPage: number = 100;
|
||||||
public static unlimitedItemsPerPage: number = 100; // TODO: Figure out appropriate value so it works for accounts with a large number of partitions
|
public static unlimitedItemsPerPage: number = 100; // TODO: Figure out appropriate value so it works for accounts with a large number of partitions
|
||||||
public static containersPerPage: number = 50;
|
public static containersPerPage: number = 50;
|
||||||
|
@ -3,10 +3,12 @@ import { getAuthorizationTokenUsingResourceTokens } from "Common/getAuthorizatio
|
|||||||
import { AuthorizationToken } from "Contracts/FabricMessageTypes";
|
import { AuthorizationToken } from "Contracts/FabricMessageTypes";
|
||||||
import { checkDatabaseResourceTokensValidity } from "Platform/Fabric/FabricUtil";
|
import { checkDatabaseResourceTokensValidity } from "Platform/Fabric/FabricUtil";
|
||||||
import { LocalStorageUtility, StorageKey } from "Shared/StorageUtility";
|
import { LocalStorageUtility, StorageKey } from "Shared/StorageUtility";
|
||||||
|
import { listKeys } from "Utils/arm/generatedClients/cosmos/databaseAccounts";
|
||||||
|
import { DatabaseAccountListKeysResult } from "Utils/arm/generatedClients/cosmos/types";
|
||||||
import { AuthType } from "../AuthType";
|
import { AuthType } from "../AuthType";
|
||||||
import { PriorityLevel } from "../Common/Constants";
|
import { PriorityLevel } from "../Common/Constants";
|
||||||
import { Platform, configContext } from "../ConfigContext";
|
import { Platform, configContext } from "../ConfigContext";
|
||||||
import { userContext } from "../UserContext";
|
import { updateUserContext, userContext } from "../UserContext";
|
||||||
import { logConsoleError } from "../Utils/NotificationConsoleUtils";
|
import { logConsoleError } from "../Utils/NotificationConsoleUtils";
|
||||||
import * as PriorityBasedExecutionUtils from "../Utils/PriorityBasedExecutionUtils";
|
import * as PriorityBasedExecutionUtils from "../Utils/PriorityBasedExecutionUtils";
|
||||||
import { EmulatorMasterKey, HttpHeaders } from "./Constants";
|
import { EmulatorMasterKey, HttpHeaders } from "./Constants";
|
||||||
@ -17,19 +19,14 @@ const _global = typeof self === "undefined" ? window : self;
|
|||||||
export const tokenProvider = async (requestInfo: Cosmos.RequestInfo) => {
|
export const tokenProvider = async (requestInfo: Cosmos.RequestInfo) => {
|
||||||
const { verb, resourceId, resourceType, headers } = requestInfo;
|
const { verb, resourceId, resourceType, headers } = requestInfo;
|
||||||
|
|
||||||
console.log(`AAD Data Plane RBAC enabled "${userContext.dataPlaneRbacEnabled}" `);
|
if (userContext.features.enableAadDataPlane || (userContext.dataPlaneRbacEnabled && userContext.apiType === "SQL")) {
|
||||||
if ((userContext.features.enableAadDataPlane || userContext.dataPlaneRbacEnabled) && userContext.aadToken) {
|
if(!userContext.aadToken)
|
||||||
console.log(` Getting Auth token `);
|
{
|
||||||
|
logConsoleError(`AAD token does not exist. Please use "Login for Entra ID" prior to performing Entra ID RBAC operations`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
const AUTH_PREFIX = `type=aad&ver=1.0&sig=`;
|
const AUTH_PREFIX = `type=aad&ver=1.0&sig=`;
|
||||||
const authorizationToken = `${AUTH_PREFIX}${userContext.aadToken}`;
|
const authorizationToken = `${AUTH_PREFIX}${userContext.aadToken}`;
|
||||||
console.log(`Returning Auth token`);
|
|
||||||
return authorizationToken;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (userContext.dataPlaneRbacEnabled && userContext.authorizationToken) {
|
|
||||||
console.log(` Getting Portal Auth token `);
|
|
||||||
const authorizationToken = `${userContext.authorizationToken}`;
|
|
||||||
console.log(`Returning Portal Auth token`);
|
|
||||||
return authorizationToken;
|
return authorizationToken;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -82,9 +79,21 @@ export const tokenProvider = async (requestInfo: Cosmos.RequestInfo) => {
|
|||||||
|
|
||||||
if (userContext.masterKey) {
|
if (userContext.masterKey) {
|
||||||
// TODO This SDK method mutates the headers object. Find a better one or fix the SDK.
|
// TODO This SDK method mutates the headers object. Find a better one or fix the SDK.
|
||||||
await Cosmos.setAuthorizationTokenHeaderUsingMasterKey(verb, resourceId, resourceType, headers, EmulatorMasterKey);
|
await Cosmos.setAuthorizationTokenHeaderUsingMasterKey(verb, resourceId, resourceType, headers, userContext.masterKey);
|
||||||
return decodeURIComponent(headers.authorization);
|
return decodeURIComponent(headers.authorization);
|
||||||
}
|
}
|
||||||
|
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 (userContext.resourceToken) {
|
if (userContext.resourceToken) {
|
||||||
return userContext.resourceToken;
|
return userContext.resourceToken;
|
||||||
@ -167,7 +176,6 @@ export function client(): Cosmos.CosmosClient {
|
|||||||
|
|
||||||
const options: Cosmos.CosmosClientOptions = {
|
const options: Cosmos.CosmosClientOptions = {
|
||||||
endpoint: endpoint() || "https://cosmos.azure.com", // CosmosClient gets upset if we pass a bad URL. This should never actually get called
|
endpoint: endpoint() || "https://cosmos.azure.com", // CosmosClient gets upset if we pass a bad URL. This should never actually get called
|
||||||
key: userContext.masterKey,
|
|
||||||
tokenProvider,
|
tokenProvider,
|
||||||
userAgentSuffix: "Azure Portal",
|
userAgentSuffix: "Azure Portal",
|
||||||
defaultHeaders: _defaultHeaders,
|
defaultHeaders: _defaultHeaders,
|
||||||
|
@ -7,11 +7,13 @@ import { getCopilotEnabled, isCopilotFeatureRegistered } from "Explorer/QueryCop
|
|||||||
import { IGalleryItem } from "Juno/JunoClient";
|
import { IGalleryItem } from "Juno/JunoClient";
|
||||||
import { scheduleRefreshDatabaseResourceToken } from "Platform/Fabric/FabricUtil";
|
import { scheduleRefreshDatabaseResourceToken } from "Platform/Fabric/FabricUtil";
|
||||||
import { LocalStorageUtility, StorageKey } from "Shared/StorageUtility";
|
import { LocalStorageUtility, StorageKey } from "Shared/StorageUtility";
|
||||||
|
import { acquireTokenWithMsal, getMsalInstance } from "Utils/AuthorizationUtils";
|
||||||
import { allowedNotebookServerUrls, validateEndpoint } from "Utils/EndpointUtils";
|
import { allowedNotebookServerUrls, validateEndpoint } from "Utils/EndpointUtils";
|
||||||
import { useQueryCopilot } from "hooks/useQueryCopilot";
|
import { useQueryCopilot } from "hooks/useQueryCopilot";
|
||||||
import * as ko from "knockout";
|
import * as ko from "knockout";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import _ from "underscore";
|
import _ from "underscore";
|
||||||
|
import * as msal from "@azure/msal-browser";
|
||||||
import shallow from "zustand/shallow";
|
import shallow from "zustand/shallow";
|
||||||
import { AuthType } from "../AuthType";
|
import { AuthType } from "../AuthType";
|
||||||
import { BindingHandlersRegisterer } from "../Bindings/BindingHandlersRegisterer";
|
import { BindingHandlersRegisterer } from "../Bindings/BindingHandlersRegisterer";
|
||||||
@ -30,18 +32,17 @@ import { PhoenixClient } from "../Phoenix/PhoenixClient";
|
|||||||
import * as ExplorerSettings from "../Shared/ExplorerSettings";
|
import * as ExplorerSettings from "../Shared/ExplorerSettings";
|
||||||
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";
|
||||||
import { isAccountNewerThanThresholdInMs, userContext } from "../UserContext";
|
import { isAccountNewerThanThresholdInMs, updateUserContext, userContext } from "../UserContext";
|
||||||
import { getCollectionName, getUploadName } from "../Utils/APITypeUtils";
|
import { getCollectionName, getUploadName } from "../Utils/APITypeUtils";
|
||||||
import { stringToBlob } from "../Utils/BlobUtils";
|
import { stringToBlob } from "../Utils/BlobUtils";
|
||||||
import { isCapabilityEnabled } from "../Utils/CapabilityUtils";
|
import { isCapabilityEnabled } from "../Utils/CapabilityUtils";
|
||||||
import { fromContentUri, toRawContentUri } from "../Utils/GitHubUtils";
|
import { fromContentUri, toRawContentUri } from "../Utils/GitHubUtils";
|
||||||
import * as NotificationConsoleUtils from "../Utils/NotificationConsoleUtils";
|
import * as NotificationConsoleUtils from "../Utils/NotificationConsoleUtils";
|
||||||
import { logConsoleError, logConsoleInfo, logConsoleProgress } from "../Utils/NotificationConsoleUtils";
|
import { logConsoleError, logConsoleInfo } from "../Utils/NotificationConsoleUtils";
|
||||||
import { update } from "../Utils/arm/generatedClients/cosmos/databaseAccounts";
|
|
||||||
import { useSidePanel } from "../hooks/useSidePanel";
|
import { useSidePanel } from "../hooks/useSidePanel";
|
||||||
import { useTabs } from "../hooks/useTabs";
|
import { useTabs } from "../hooks/useTabs";
|
||||||
import "./ComponentRegisterer";
|
import "./ComponentRegisterer";
|
||||||
import { DialogProps, useDialog } from "./Controls/Dialog";
|
import { useDialog } from "./Controls/Dialog";
|
||||||
import { GalleryTab as GalleryTabKind } from "./Controls/NotebookGallery/GalleryViewerComponent";
|
import { GalleryTab as GalleryTabKind } from "./Controls/NotebookGallery/GalleryViewerComponent";
|
||||||
import { useCommandBar } from "./Menus/CommandBar/CommandBarComponentAdapter";
|
import { useCommandBar } from "./Menus/CommandBar/CommandBarComponentAdapter";
|
||||||
import * as FileSystemUtil from "./Notebook/FileSystemUtil";
|
import * as FileSystemUtil from "./Notebook/FileSystemUtil";
|
||||||
@ -203,7 +204,7 @@ export default class Explorer {
|
|||||||
this.refreshNotebookList();
|
this.refreshNotebookList();
|
||||||
}
|
}
|
||||||
|
|
||||||
public openEnableSynapseLinkDialog(): void {
|
public async openEnableSynapseLinkDialog(): Promise<void> {
|
||||||
const addSynapseLinkDialogProps: DialogProps = {
|
const addSynapseLinkDialogProps: DialogProps = {
|
||||||
linkProps: {
|
linkProps: {
|
||||||
linkText: "Learn more",
|
linkText: "Learn more",
|
||||||
@ -251,8 +252,43 @@ export default class Explorer {
|
|||||||
};
|
};
|
||||||
useDialog.getState().openDialog(addSynapseLinkDialogProps);
|
useDialog.getState().openDialog(addSynapseLinkDialogProps);
|
||||||
TelemetryProcessor.traceStart(Action.EnableAzureSynapseLink);
|
TelemetryProcessor.traceStart(Action.EnableAzureSynapseLink);
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: return result
|
public async openLoginForEntraIDPopUp(): Promise<void> {
|
||||||
|
if (userContext.databaseAccount.properties?.documentEndpoint) {
|
||||||
|
const hrefEndpoint = new URL(userContext.databaseAccount.properties.documentEndpoint).href.replace(/\/$/, "/.default");
|
||||||
|
const msalInstance = await getMsalInstance();
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await msalInstance.loginPopup({
|
||||||
|
redirectUri: configContext.msalRedirectURI,
|
||||||
|
scopes: [],
|
||||||
|
});
|
||||||
|
localStorage.setItem("cachedTenantId", response.tenantId);
|
||||||
|
const cachedAccount = msalInstance.getAllAccounts()?.[0];
|
||||||
|
msalInstance.setActiveAccount(cachedAccount);
|
||||||
|
let aadToken = await acquireTokenWithMsal(msalInstance, {
|
||||||
|
forceRefresh: true,
|
||||||
|
scopes: [hrefEndpoint],
|
||||||
|
authority: `${configContext.AAD_ENDPOINT}${localStorage.getItem("cachedTenantId")}`,
|
||||||
|
});
|
||||||
|
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",
|
||||||
|
);
|
||||||
|
} 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}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public openNPSSurveyDialog(): void {
|
public openNPSSurveyDialog(): void {
|
||||||
|
@ -15,6 +15,7 @@ import OpenQueryFromDiskIcon from "../../../../images/OpenQueryFromDisk.svg";
|
|||||||
import OpenInTabIcon from "../../../../images/open-in-tab.svg";
|
import OpenInTabIcon from "../../../../images/open-in-tab.svg";
|
||||||
import SettingsIcon from "../../../../images/settings_15x15.svg";
|
import SettingsIcon from "../../../../images/settings_15x15.svg";
|
||||||
import SynapseIcon from "../../../../images/synapse-link.svg";
|
import SynapseIcon from "../../../../images/synapse-link.svg";
|
||||||
|
import EntraIDIcon from "../../../../images/EntraID.svg";
|
||||||
import { AuthType } from "../../../AuthType";
|
import { AuthType } from "../../../AuthType";
|
||||||
import * as Constants from "../../../Common/Constants";
|
import * as Constants from "../../../Common/Constants";
|
||||||
import { Platform, configContext } from "../../../ConfigContext";
|
import { Platform, configContext } from "../../../ConfigContext";
|
||||||
@ -69,6 +70,15 @@ export function createStaticCommandBarButtons(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (userContext.apiType === "SQL") {
|
||||||
|
const addLoginForEntraIDBtn = createLoginForEntraIDButton(container);
|
||||||
|
|
||||||
|
if (addLoginForEntraIDBtn) {
|
||||||
|
addDivider();
|
||||||
|
buttons.push(addLoginForEntraIDBtn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (userContext.apiType !== "Tables") {
|
if (userContext.apiType !== "Tables") {
|
||||||
newCollectionBtn.children = [createNewCollectionGroup(container)];
|
newCollectionBtn.children = [createNewCollectionGroup(container)];
|
||||||
const newDatabaseBtn = createNewDatabase(container);
|
const newDatabaseBtn = createNewDatabase(container);
|
||||||
@ -275,6 +285,22 @@ function createOpenSynapseLinkDialogButton(container: Explorer): CommandButtonCo
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function createLoginForEntraIDButton(container: Explorer): CommandButtonComponentProps {
|
||||||
|
if (configContext.platform !== Platform.Portal) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const label = "Login for Entra ID RBAC";
|
||||||
|
return {
|
||||||
|
iconSrc: EntraIDIcon,
|
||||||
|
iconAlt: label,
|
||||||
|
onCommandClick: () => container.openLoginForEntraIDPopUp(),
|
||||||
|
commandButtonLabel: label,
|
||||||
|
hasPopup: true,
|
||||||
|
ariaLabel: label,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
function createNewDatabase(container: Explorer): CommandButtonComponentProps {
|
function createNewDatabase(container: Explorer): CommandButtonComponentProps {
|
||||||
const label = "New " + getDatabaseName();
|
const label = "New " + getDatabaseName();
|
||||||
return {
|
return {
|
||||||
|
@ -4,14 +4,18 @@ import {
|
|||||||
IChoiceGroupOption,
|
IChoiceGroupOption,
|
||||||
ISpinButtonStyles,
|
ISpinButtonStyles,
|
||||||
IToggleStyles,
|
IToggleStyles,
|
||||||
|
Icon,
|
||||||
|
MessageBar,
|
||||||
|
MessageBarType,
|
||||||
Position,
|
Position,
|
||||||
SpinButton,
|
SpinButton,
|
||||||
Toggle,
|
Toggle,
|
||||||
|
TooltipHost,
|
||||||
} from "@fluentui/react";
|
} from "@fluentui/react";
|
||||||
import * as Constants from "Common/Constants";
|
import * as Constants from "Common/Constants";
|
||||||
import { SplitterDirection } from "Common/Splitter";
|
import { SplitterDirection } from "Common/Splitter";
|
||||||
import { InfoTooltip } from "Common/Tooltip/InfoTooltip";
|
import { InfoTooltip } from "Common/Tooltip/InfoTooltip";
|
||||||
import { configContext } from "ConfigContext";
|
import { Platform, configContext } from "ConfigContext"
|
||||||
import { useDatabases } from "Explorer/useDatabases";
|
import { useDatabases } from "Explorer/useDatabases";
|
||||||
import {
|
import {
|
||||||
DefaultRUThreshold,
|
DefaultRUThreshold,
|
||||||
@ -22,7 +26,7 @@ import {
|
|||||||
ruThresholdEnabled as isRUThresholdEnabled,
|
ruThresholdEnabled as isRUThresholdEnabled,
|
||||||
} from "Shared/StorageUtility";
|
} from "Shared/StorageUtility";
|
||||||
import * as StringUtility from "Shared/StringUtility";
|
import * as StringUtility from "Shared/StringUtility";
|
||||||
import { userContext } from "UserContext";
|
import { updateUserContext, userContext } from "UserContext";
|
||||||
import { logConsoleInfo } from "Utils/NotificationConsoleUtils";
|
import { logConsoleInfo } from "Utils/NotificationConsoleUtils";
|
||||||
import * as PriorityBasedExecutionUtils from "Utils/PriorityBasedExecutionUtils";
|
import * as PriorityBasedExecutionUtils from "Utils/PriorityBasedExecutionUtils";
|
||||||
import { useQueryCopilot } from "hooks/useQueryCopilot";
|
import { useQueryCopilot } from "hooks/useQueryCopilot";
|
||||||
@ -30,6 +34,7 @@ import { useSidePanel } from "hooks/useSidePanel";
|
|||||||
import React, { FunctionComponent, useState } from "react";
|
import React, { FunctionComponent, useState } from "react";
|
||||||
import Explorer from "../../Explorer";
|
import Explorer from "../../Explorer";
|
||||||
import { RightPaneForm, RightPaneFormProps } from "../RightPaneForm/RightPaneForm";
|
import { RightPaneForm, RightPaneFormProps } from "../RightPaneForm/RightPaneForm";
|
||||||
|
import { AuthType } from "AuthType";
|
||||||
|
|
||||||
export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({
|
export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({
|
||||||
explorer,
|
explorer,
|
||||||
@ -44,13 +49,13 @@ export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({
|
|||||||
? Constants.Queries.UnlimitedPageOption
|
? Constants.Queries.UnlimitedPageOption
|
||||||
: Constants.Queries.CustomPageOption,
|
: Constants.Queries.CustomPageOption,
|
||||||
);
|
);
|
||||||
|
|
||||||
const [enableDataPlaneRBACOption, setEnableDataPlaneRBACOption] = useState<string>(
|
const [enableDataPlaneRBACOption, setEnableDataPlaneRBACOption] = useState<string>(
|
||||||
LocalStorageUtility.getEntryString(StorageKey.DataPlaneRbacEnabled) === Constants.RBACOptions.setAutomaticRBACOption
|
LocalStorageUtility.hasItem(StorageKey.DataPlaneRbacEnabled)
|
||||||
? Constants.RBACOptions.setAutomaticRBACOption
|
? LocalStorageUtility.getEntryString(StorageKey.DataPlaneRbacEnabled)
|
||||||
: LocalStorageUtility.getEntryString(StorageKey.DataPlaneRbacEnabled) === Constants.RBACOptions.setTrueRBACOption
|
: Constants.RBACOptions.setAutomaticRBACOption);
|
||||||
? Constants.RBACOptions.setTrueRBACOption
|
const [showDataPlaneRBACWarning, setShowDataPlaneRBACWarning] = useState<boolean>(false);
|
||||||
: Constants.RBACOptions.setFalseRBACOption,
|
|
||||||
);
|
|
||||||
const [ruThresholdEnabled, setRUThresholdEnabled] = useState<boolean>(isRUThresholdEnabled());
|
const [ruThresholdEnabled, setRUThresholdEnabled] = useState<boolean>(isRUThresholdEnabled());
|
||||||
const [ruThreshold, setRUThreshold] = useState<number>(getRUThreshold());
|
const [ruThreshold, setRUThreshold] = useState<number>(getRUThreshold());
|
||||||
const [queryTimeoutEnabled, setQueryTimeoutEnabled] = useState<boolean>(
|
const [queryTimeoutEnabled, setQueryTimeoutEnabled] = useState<boolean>(
|
||||||
@ -109,6 +114,7 @@ export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({
|
|||||||
const [copilotSampleDBEnabled, setCopilotSampleDBEnabled] = useState<boolean>(
|
const [copilotSampleDBEnabled, setCopilotSampleDBEnabled] = useState<boolean>(
|
||||||
LocalStorageUtility.getEntryString(StorageKey.CopilotSampleDBEnabled) === "true",
|
LocalStorageUtility.getEntryString(StorageKey.CopilotSampleDBEnabled) === "true",
|
||||||
);
|
);
|
||||||
|
|
||||||
const explorerVersion = configContext.gitSha;
|
const explorerVersion = configContext.gitSha;
|
||||||
const shouldShowQueryPageOptions = userContext.apiType === "SQL";
|
const shouldShowQueryPageOptions = userContext.apiType === "SQL";
|
||||||
const shouldShowGraphAutoVizOption = userContext.apiType === "Gremlin";
|
const shouldShowGraphAutoVizOption = userContext.apiType === "Gremlin";
|
||||||
@ -130,6 +136,16 @@ export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({
|
|||||||
LocalStorageUtility.setEntryNumber(StorageKey.CustomItemPerPage, customItemPerPage);
|
LocalStorageUtility.setEntryNumber(StorageKey.CustomItemPerPage, customItemPerPage);
|
||||||
|
|
||||||
LocalStorageUtility.setEntryString(StorageKey.DataPlaneRbacEnabled, enableDataPlaneRBACOption);
|
LocalStorageUtility.setEntryString(StorageKey.DataPlaneRbacEnabled, enableDataPlaneRBACOption);
|
||||||
|
if(enableDataPlaneRBACOption === Constants.RBACOptions.setTrueRBACOption || (enableDataPlaneRBACOption === Constants.RBACOptions.setAutomaticRBACOption && userContext.databaseAccount.properties.disableLocalAuth)) {
|
||||||
|
updateUserContext({
|
||||||
|
dataPlaneRbacEnabled: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
updateUserContext({
|
||||||
|
dataPlaneRbacEnabled: false,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
LocalStorageUtility.setEntryBoolean(StorageKey.RUThresholdEnabled, ruThresholdEnabled);
|
LocalStorageUtility.setEntryBoolean(StorageKey.RUThresholdEnabled, ruThresholdEnabled);
|
||||||
LocalStorageUtility.setEntryBoolean(StorageKey.QueryTimeoutEnabled, queryTimeoutEnabled);
|
LocalStorageUtility.setEntryBoolean(StorageKey.QueryTimeoutEnabled, queryTimeoutEnabled);
|
||||||
@ -245,6 +261,10 @@ export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({
|
|||||||
option: IChoiceGroupOption,
|
option: IChoiceGroupOption,
|
||||||
): void => {
|
): void => {
|
||||||
setEnableDataPlaneRBACOption(option.key);
|
setEnableDataPlaneRBACOption(option.key);
|
||||||
|
|
||||||
|
const shouldShowWarning = option.key === Constants.RBACOptions.setTrueRBACOption ||
|
||||||
|
(option.key === Constants.RBACOptions.setAutomaticRBACOption && userContext.databaseAccount.properties.disableLocalAuth === true);
|
||||||
|
setShowDataPlaneRBACWarning(shouldShowWarning);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleOnRUThresholdToggleChange = (ev: React.MouseEvent<HTMLElement>, checked?: boolean): void => {
|
const handleOnRUThresholdToggleChange = (ev: React.MouseEvent<HTMLElement>, checked?: boolean): void => {
|
||||||
@ -407,30 +427,48 @@ export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{
|
{(userContext.apiType === "SQL" && userContext.authType == AuthType.AAD) && (
|
||||||
|
<>
|
||||||
<div className="settingsSection">
|
<div className="settingsSection">
|
||||||
<div className="settingsSectionPart">
|
<div className="settingsSectionPart">
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<legend id="enableDataPlaneRBACOptions" className="settingsSectionLabel legendLabel">
|
<legend id="enableDataPlaneRBACOptions" className="settingsSectionLabel legendLabel">
|
||||||
Enable DataPlane RBAC
|
Enable Entra ID RBAC
|
||||||
</legend>
|
</legend>
|
||||||
<InfoTooltip>
|
<TooltipHost
|
||||||
Choose Automatic to enable DataPlane RBAC automatically. True/False to voluntarily enable/disable
|
content={
|
||||||
DataPlane RBAC
|
<>
|
||||||
</InfoTooltip>
|
Choose Automatic to enable Entra ID RBAC automatically. True/False to force enable/disable Entra ID RBAC.
|
||||||
|
<a href="https://learn.microsoft.com/en-us/azure/cosmos-db/how-to-setup-rbac#use-data-explorer" target="_blank" rel="noopener noreferrer"> Learn more </a>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Icon iconName="Info" ariaLabel="Info tooltip" className="panelInfoIcon" tabIndex={0} />
|
||||||
|
</TooltipHost>
|
||||||
|
{(showDataPlaneRBACWarning && configContext.platform == Platform.Portal) && (
|
||||||
|
<MessageBar
|
||||||
|
messageBarType={MessageBarType.warning}
|
||||||
|
isMultiline={true}
|
||||||
|
onDismiss={() => setShowDataPlaneRBACWarning(false)}
|
||||||
|
dismissButtonAriaLabel="Close"
|
||||||
|
>
|
||||||
|
Please click on "Login for Entra ID RBAC" prior to performing Entra ID RBAC operations
|
||||||
|
</MessageBar>
|
||||||
|
)}
|
||||||
<ChoiceGroup
|
<ChoiceGroup
|
||||||
ariaLabelledBy="enableDataPlaneRBACOptions"
|
ariaLabelledBy="enableDataPlaneRBACOptions"
|
||||||
selectedKey={enableDataPlaneRBACOption}
|
|
||||||
options={dataPlaneRBACOptionsList}
|
options={dataPlaneRBACOptionsList}
|
||||||
styles={choiceButtonStyles}
|
styles={choiceButtonStyles}
|
||||||
|
selectedKey={enableDataPlaneRBACOption}
|
||||||
onChange={handleOnDataPlaneRBACOptionChange}
|
onChange={handleOnDataPlaneRBACOptionChange}
|
||||||
/>
|
/>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
</>
|
||||||
{userContext.apiType === "SQL" && (
|
)}
|
||||||
<>
|
{(userContext.apiType === "SQL") && (
|
||||||
|
<>
|
||||||
<div className="settingsSection">
|
<div className="settingsSection">
|
||||||
<div className="settingsSectionPart">
|
<div className="settingsSectionPart">
|
||||||
<div>
|
<div>
|
||||||
|
@ -273,27 +273,27 @@ async function configureHostedWithAAD(config: AAD): Promise<Explorer> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
if (LocalStorageUtility.hasItem(StorageKey.DataPlaneRbacEnabled)) {
|
if (userContext.apiType === "SQL")
|
||||||
const isDataPlaneRbacSetting = LocalStorageUtility.getEntryString(StorageKey.DataPlaneRbacEnabled);
|
{
|
||||||
if (isDataPlaneRbacSetting === Constants.RBACOptions.setAutomaticRBACOption) {
|
if (LocalStorageUtility.hasItem(StorageKey.DataPlaneRbacEnabled)) {
|
||||||
if (!account.properties.disableLocalAuth) {
|
const isDataPlaneRbacSetting = LocalStorageUtility.getEntryString(StorageKey.DataPlaneRbacEnabled);
|
||||||
keys = await listKeys(subscriptionId, resourceGroup, account.name);
|
|
||||||
} else {
|
let dataPlaneRbacEnabled;
|
||||||
updateUserContext({
|
if (isDataPlaneRbacSetting === Constants.RBACOptions.setAutomaticRBACOption) {
|
||||||
dataPlaneRbacEnabled: true,
|
dataPlaneRbacEnabled = account.properties.disableLocalAuth;
|
||||||
});
|
} else {
|
||||||
}
|
dataPlaneRbacEnabled = isDataPlaneRbacSetting === Constants.RBACOptions.setTrueRBACOption;
|
||||||
} else if (isDataPlaneRbacSetting === Constants.RBACOptions.setTrueRBACOption) {
|
}
|
||||||
updateUserContext({
|
|
||||||
dataPlaneRbacEnabled: true,
|
updateUserContext({ dataPlaneRbacEnabled });
|
||||||
});
|
}
|
||||||
} else {
|
}
|
||||||
keys = await listKeys(subscriptionId, resourceGroup, account.name);
|
else {
|
||||||
updateUserContext({
|
let keys: DatabaseAccountListKeysResult = await listKeys(subscriptionId, resourceGroup, account.name);
|
||||||
dataPlaneRbacEnabled: false,
|
updateUserContext({
|
||||||
});
|
masterKey: keys.primaryMasterKey
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (userContext.features.enableAadDataPlane) {
|
if (userContext.features.enableAadDataPlane) {
|
||||||
console.warn(e);
|
console.warn(e);
|
||||||
@ -472,33 +472,29 @@ async function configurePortal(): Promise<Explorer> {
|
|||||||
setTimeout(() => explorer.openNPSSurveyDialog(), 3000);
|
setTimeout(() => explorer.openNPSSurveyDialog(), 3000);
|
||||||
}
|
}
|
||||||
|
|
||||||
const account = userContext.databaseAccount;
|
const { databaseAccount: account, subscriptionId, resourceGroup } = userContext;
|
||||||
const subscriptionId = userContext.subscriptionId;
|
|
||||||
const resourceGroup = userContext.resourceGroup;
|
if (userContext.apiType === "SQL")
|
||||||
|
{
|
||||||
if (LocalStorageUtility.hasItem(StorageKey.DataPlaneRbacEnabled)) {
|
if (LocalStorageUtility.hasItem(StorageKey.DataPlaneRbacEnabled)) {
|
||||||
const isDataPlaneRbacSetting = LocalStorageUtility.getEntryString(StorageKey.DataPlaneRbacEnabled);
|
const isDataPlaneRbacSetting = LocalStorageUtility.getEntryString(StorageKey.DataPlaneRbacEnabled);
|
||||||
if (isDataPlaneRbacSetting === Constants.RBACOptions.setAutomaticRBACOption) {
|
|
||||||
if (!account.properties.disableLocalAuth) {
|
let dataPlaneRbacEnabled;
|
||||||
await listKeys(subscriptionId, resourceGroup, account.name);
|
if (isDataPlaneRbacSetting === Constants.RBACOptions.setAutomaticRBACOption) {
|
||||||
} else {
|
dataPlaneRbacEnabled = account.properties.disableLocalAuth;
|
||||||
updateUserContext({
|
} else {
|
||||||
dataPlaneRbacEnabled: true,
|
dataPlaneRbacEnabled = isDataPlaneRbacSetting === Constants.RBACOptions.setTrueRBACOption;
|
||||||
authorizationToken: message.inputs.authorizationToken,
|
}
|
||||||
});
|
|
||||||
}
|
updateUserContext({ dataPlaneRbacEnabled });
|
||||||
} else if (isDataPlaneRbacSetting === Constants.RBACOptions.setTrueRBACOption) {
|
}
|
||||||
updateUserContext({
|
|
||||||
dataPlaneRbacEnabled: true,
|
|
||||||
authorizationToken: message.inputs.authorizationToken,
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
await listKeys(subscriptionId, resourceGroup, account.name);
|
|
||||||
updateUserContext({
|
|
||||||
dataPlaneRbacEnabled: false,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
else {
|
||||||
|
let keys: DatabaseAccountListKeysResult = await listKeys(subscriptionId, resourceGroup, account.name);
|
||||||
|
updateUserContext({
|
||||||
|
masterKey: keys.primaryMasterKey
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (openAction) {
|
if (openAction) {
|
||||||
handleOpenAction(openAction, useDatabases.getState().databases, explorer);
|
handleOpenAction(openAction, useDatabases.getState().databases, explorer);
|
||||||
@ -540,7 +536,6 @@ function updateContextsFromPortalMessage(inputs: DataExplorerInputsFrame) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const authorizationToken = inputs.authorizationToken || "";
|
const authorizationToken = inputs.authorizationToken || "";
|
||||||
const masterKey = inputs.masterKey || "";
|
|
||||||
const databaseAccount = inputs.databaseAccount;
|
const databaseAccount = inputs.databaseAccount;
|
||||||
|
|
||||||
updateConfigContext({
|
updateConfigContext({
|
||||||
@ -553,7 +548,6 @@ function updateContextsFromPortalMessage(inputs: DataExplorerInputsFrame) {
|
|||||||
|
|
||||||
updateUserContext({
|
updateUserContext({
|
||||||
authorizationToken,
|
authorizationToken,
|
||||||
masterKey,
|
|
||||||
databaseAccount,
|
databaseAccount,
|
||||||
resourceGroup: inputs.resourceGroup,
|
resourceGroup: inputs.resourceGroup,
|
||||||
subscriptionId: inputs.subscriptionId,
|
subscriptionId: inputs.subscriptionId,
|
||||||
|
Loading…
Reference in New Issue
Block a user