mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2024-11-25 15:06:55 +00:00
Support data plane RBAC
This commit is contained in:
parent
4d8bb5c3ea
commit
be871737ad
@ -185,6 +185,9 @@ 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;
|
||||||
|
@ -44,6 +44,13 @@ export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({
|
|||||||
? Constants.Queries.UnlimitedPageOption
|
? Constants.Queries.UnlimitedPageOption
|
||||||
: Constants.Queries.CustomPageOption,
|
: Constants.Queries.CustomPageOption,
|
||||||
);
|
);
|
||||||
|
const [enableDataPlaneRBACOption, setEnableDataPlaneRBACOption] = useState<string>(
|
||||||
|
LocalStorageUtility.getEntryString(StorageKey.DataPlaneRbacEnabled) === Constants.Queries.setAutomaticRBACOption
|
||||||
|
? Constants.Queries.setAutomaticRBACOption
|
||||||
|
: LocalStorageUtility.getEntryString(StorageKey.DataPlaneRbacEnabled) === Constants.Queries.setTrueRBACOption
|
||||||
|
? Constants.Queries.setTrueRBACOption
|
||||||
|
: Constants.Queries.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>(
|
||||||
@ -119,7 +126,14 @@ export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({
|
|||||||
StorageKey.ActualItemPerPage,
|
StorageKey.ActualItemPerPage,
|
||||||
isCustomPageOptionSelected() ? customItemPerPage : Constants.Queries.unlimitedItemsPerPage,
|
isCustomPageOptionSelected() ? customItemPerPage : Constants.Queries.unlimitedItemsPerPage,
|
||||||
);
|
);
|
||||||
|
|
||||||
LocalStorageUtility.setEntryNumber(StorageKey.CustomItemPerPage, customItemPerPage);
|
LocalStorageUtility.setEntryNumber(StorageKey.CustomItemPerPage, customItemPerPage);
|
||||||
|
|
||||||
|
LocalStorageUtility.setEntryString(
|
||||||
|
StorageKey.DataPlaneRbacEnabled,
|
||||||
|
enableDataPlaneRBACOption
|
||||||
|
);
|
||||||
|
|
||||||
LocalStorageUtility.setEntryBoolean(StorageKey.RUThresholdEnabled, ruThresholdEnabled);
|
LocalStorageUtility.setEntryBoolean(StorageKey.RUThresholdEnabled, ruThresholdEnabled);
|
||||||
LocalStorageUtility.setEntryBoolean(StorageKey.QueryTimeoutEnabled, queryTimeoutEnabled);
|
LocalStorageUtility.setEntryBoolean(StorageKey.QueryTimeoutEnabled, queryTimeoutEnabled);
|
||||||
LocalStorageUtility.setEntryNumber(StorageKey.RetryAttempts, retryAttempts);
|
LocalStorageUtility.setEntryNumber(StorageKey.RetryAttempts, retryAttempts);
|
||||||
@ -207,9 +221,10 @@ export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({
|
|||||||
{ key: Constants.PriorityLevel.High, text: "High" },
|
{ key: Constants.PriorityLevel.High, text: "High" },
|
||||||
];
|
];
|
||||||
|
|
||||||
const defaultQueryResultsViewOptionList: IChoiceGroupOption[] = [
|
const dataPlaneRBACOptionsList: IChoiceGroupOption[] = [
|
||||||
{ key: SplitterDirection.Vertical, text: "Vertical" },
|
{ key: Constants.Queries.setAutomaticRBACOption, text: "Automatic" },
|
||||||
{ key: SplitterDirection.Horizontal, text: "Horizontal" },
|
{ key: Constants.Queries.setTrueRBACOption, text: "True" },
|
||||||
|
{ key: Constants.Queries.setFalseRBACOption, text: "False"}
|
||||||
];
|
];
|
||||||
|
|
||||||
const handleOnPriorityLevelOptionChange = (
|
const handleOnPriorityLevelOptionChange = (
|
||||||
@ -223,6 +238,10 @@ export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({
|
|||||||
setPageOption(option.key);
|
setPageOption(option.key);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleOnDataPlaneRBACOptionChange = (ev: React.FormEvent<HTMLInputElement>, option: IChoiceGroupOption): void => {
|
||||||
|
setEnableDataPlaneRBACOption(option.key);
|
||||||
|
};
|
||||||
|
|
||||||
const handleOnRUThresholdToggleChange = (ev: React.MouseEvent<HTMLElement>, checked?: boolean): void => {
|
const handleOnRUThresholdToggleChange = (ev: React.MouseEvent<HTMLElement>, checked?: boolean): void => {
|
||||||
setRUThresholdEnabled(checked);
|
setRUThresholdEnabled(checked);
|
||||||
};
|
};
|
||||||
@ -383,6 +402,27 @@ export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
{(
|
||||||
|
<div className="settingsSection">
|
||||||
|
<div className="settingsSectionPart">
|
||||||
|
<fieldset>
|
||||||
|
<legend id="enableDataPlaneRBACOptions" className="settingsSectionLabel legendLabel">
|
||||||
|
Enable DataPlane RBAC
|
||||||
|
</legend>
|
||||||
|
<InfoTooltip>
|
||||||
|
Choose Automatic to enable DataPlane RBAC automatically. True/False to voluntarily enable/disable DataPlane RBAC
|
||||||
|
</InfoTooltip>
|
||||||
|
<ChoiceGroup
|
||||||
|
ariaLabelledBy="enableDataPlaneRBACOptions"
|
||||||
|
selectedKey={enableDataPlaneRBACOption}
|
||||||
|
options={dataPlaneRBACOptionsList}
|
||||||
|
styles={choiceButtonStyles}
|
||||||
|
onChange={handleOnDataPlaneRBACOptionChange}
|
||||||
|
/>
|
||||||
|
</fieldset>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
{userContext.apiType === "SQL" && (
|
{userContext.apiType === "SQL" && (
|
||||||
<>
|
<>
|
||||||
<div className="settingsSection">
|
<div className="settingsSection">
|
||||||
|
@ -14,6 +14,7 @@ export type Features = {
|
|||||||
readonly enableTtl: boolean;
|
readonly enableTtl: boolean;
|
||||||
readonly executeSproc: boolean;
|
readonly executeSproc: boolean;
|
||||||
readonly enableAadDataPlane: boolean;
|
readonly enableAadDataPlane: boolean;
|
||||||
|
readonly enableDataPlaneRbac: boolean;
|
||||||
readonly enableResourceGraph: boolean;
|
readonly enableResourceGraph: boolean;
|
||||||
readonly enableKoResourceTree: boolean;
|
readonly enableKoResourceTree: boolean;
|
||||||
readonly hostedDataExplorer: boolean;
|
readonly hostedDataExplorer: boolean;
|
||||||
@ -69,6 +70,7 @@ export function extractFeatures(given = new URLSearchParams(window.location.sear
|
|||||||
canExceedMaximumValue: "true" === get("canexceedmaximumvalue"),
|
canExceedMaximumValue: "true" === get("canexceedmaximumvalue"),
|
||||||
cosmosdb: "true" === get("cosmosdb"),
|
cosmosdb: "true" === get("cosmosdb"),
|
||||||
enableAadDataPlane: "true" === get("enableaaddataplane"),
|
enableAadDataPlane: "true" === get("enableaaddataplane"),
|
||||||
|
enableDataPlaneRbac: "true" === get("enabledataplanerbac"),
|
||||||
enableResourceGraph: "true" === get("enableresourcegraph"),
|
enableResourceGraph: "true" === get("enableresourcegraph"),
|
||||||
enableChangeFeedPolicy: "true" === get("enablechangefeedpolicy"),
|
enableChangeFeedPolicy: "true" === get("enablechangefeedpolicy"),
|
||||||
enableFixedCollectionWithSharedThroughput: "true" === get("enablefixedcollectionwithsharedthroughput"),
|
enableFixedCollectionWithSharedThroughput: "true" === get("enablefixedcollectionwithsharedthroughput"),
|
||||||
|
@ -6,6 +6,9 @@ import * as StringUtility from "./StringUtility";
|
|||||||
export { LocalStorageUtility, SessionStorageUtility };
|
export { LocalStorageUtility, SessionStorageUtility };
|
||||||
export enum StorageKey {
|
export enum StorageKey {
|
||||||
ActualItemPerPage,
|
ActualItemPerPage,
|
||||||
|
DataPlaneRbacEnabled,
|
||||||
|
DataPlaneRbacDisabled,
|
||||||
|
isDataPlaneRbacAutomatic,
|
||||||
RUThresholdEnabled,
|
RUThresholdEnabled,
|
||||||
RUThreshold,
|
RUThreshold,
|
||||||
QueryTimeoutEnabled,
|
QueryTimeoutEnabled,
|
||||||
|
@ -101,6 +101,7 @@ export interface UserContext {
|
|||||||
sampleDataConnectionInfo?: ParsedResourceTokenConnectionString;
|
sampleDataConnectionInfo?: ParsedResourceTokenConnectionString;
|
||||||
readonly vcoreMongoConnectionParams?: VCoreMongoConnectionParams;
|
readonly vcoreMongoConnectionParams?: VCoreMongoConnectionParams;
|
||||||
readonly feedbackPolicies?: AdminFeedbackPolicySettings;
|
readonly feedbackPolicies?: AdminFeedbackPolicySettings;
|
||||||
|
readonly dataPlaneRbacEnabled?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ApiType = "SQL" | "Mongo" | "Gremlin" | "Tables" | "Cassandra" | "Postgres" | "VCoreMongo";
|
export type ApiType = "SQL" | "Mongo" | "Gremlin" | "Tables" | "Cassandra" | "Postgres" | "VCoreMongo";
|
||||||
|
@ -5,6 +5,7 @@ import { FABRIC_RPC_VERSION, FabricMessageV2 } from "Contracts/FabricMessagesCon
|
|||||||
import Explorer from "Explorer/Explorer";
|
import Explorer from "Explorer/Explorer";
|
||||||
import { useSelectedNode } from "Explorer/useSelectedNode";
|
import { useSelectedNode } from "Explorer/useSelectedNode";
|
||||||
import { scheduleRefreshDatabaseResourceToken } from "Platform/Fabric/FabricUtil";
|
import { scheduleRefreshDatabaseResourceToken } from "Platform/Fabric/FabricUtil";
|
||||||
|
import { LocalStorageUtility, StorageKey } from "Shared/StorageUtility";
|
||||||
import { getNetworkSettingsWarningMessage } from "Utils/NetworkUtility";
|
import { getNetworkSettingsWarningMessage } from "Utils/NetworkUtility";
|
||||||
import { logConsoleError } from "Utils/NotificationConsoleUtils";
|
import { logConsoleError } from "Utils/NotificationConsoleUtils";
|
||||||
import { useQueryCopilot } from "hooks/useQueryCopilot";
|
import { useQueryCopilot } from "hooks/useQueryCopilot";
|
||||||
@ -271,8 +272,30 @@ async function configureHostedWithAAD(config: AAD): Promise<Explorer> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
if (!account.properties.disableLocalAuth) {
|
if(LocalStorageUtility.hasItem(StorageKey.DataPlaneRbacEnabled)) {
|
||||||
keys = await listKeys(subscriptionId, resourceGroup, account.name);
|
var isDataPlaneRbacSetting = LocalStorageUtility.getEntryString(StorageKey.DataPlaneRbacEnabled);
|
||||||
|
if (isDataPlaneRbacSetting == "Automatic")
|
||||||
|
{
|
||||||
|
if (!account.properties.disableLocalAuth) {
|
||||||
|
keys = await listKeys(subscriptionId, resourceGroup, account.name);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
updateUserContext({
|
||||||
|
dataPlaneRbacEnabled: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(isDataPlaneRbacSetting == "True") {
|
||||||
|
updateUserContext({
|
||||||
|
dataPlaneRbacEnabled: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
keys = await listKeys(subscriptionId, resourceGroup, account.name);
|
||||||
|
updateUserContext({
|
||||||
|
dataPlaneRbacEnabled: false
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (userContext.features.enableAadDataPlane) {
|
if (userContext.features.enableAadDataPlane) {
|
||||||
@ -394,8 +417,9 @@ async function configurePortal(): Promise<Explorer> {
|
|||||||
updateUserContext({
|
updateUserContext({
|
||||||
authType: AuthType.AAD,
|
authType: AuthType.AAD,
|
||||||
});
|
});
|
||||||
|
|
||||||
let explorer: Explorer;
|
let explorer: Explorer;
|
||||||
return new Promise((resolve) => {
|
return new Promise(async (resolve) => {
|
||||||
// In development mode, try to load the iframe message from session storage.
|
// In development mode, try to load the iframe message from session storage.
|
||||||
// This allows webpack hot reload to function properly in the portal
|
// This allows webpack hot reload to function properly in the portal
|
||||||
if (process.env.NODE_ENV === "development" && !window.location.search.includes("disablePortalInitCache")) {
|
if (process.env.NODE_ENV === "development" && !window.location.search.includes("disablePortalInitCache")) {
|
||||||
@ -408,6 +432,7 @@ async function configurePortal(): Promise<Explorer> {
|
|||||||
console.dir(message);
|
console.dir(message);
|
||||||
updateContextsFromPortalMessage(message);
|
updateContextsFromPortalMessage(message);
|
||||||
explorer = new Explorer();
|
explorer = new Explorer();
|
||||||
|
|
||||||
// In development mode, save the iframe message from the portal in session storage.
|
// In development mode, save the iframe message from the portal in session storage.
|
||||||
// This allows webpack hot reload to funciton properly
|
// This allows webpack hot reload to funciton properly
|
||||||
if (process.env.NODE_ENV === "development") {
|
if (process.env.NODE_ENV === "development") {
|
||||||
@ -416,11 +441,11 @@ async function configurePortal(): Promise<Explorer> {
|
|||||||
resolve(explorer);
|
resolve(explorer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// In the Portal, configuration of Explorer happens via iframe message
|
// In the Portal, configuration of Explorer happens via iframe message
|
||||||
window.addEventListener(
|
window.addEventListener(
|
||||||
"message",
|
"message",
|
||||||
(event) => {
|
async (event) => {
|
||||||
if (isInvalidParentFrameOrigin(event)) {
|
if (isInvalidParentFrameOrigin(event)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -450,6 +475,37 @@ async function configurePortal(): Promise<Explorer> {
|
|||||||
setTimeout(() => explorer.openNPSSurveyDialog(), 3000);
|
setTimeout(() => explorer.openNPSSurveyDialog(), 3000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let dbAccount = userContext.databaseAccount;
|
||||||
|
let keys: DatabaseAccountListKeysResult = {};
|
||||||
|
const account = userContext.databaseAccount;
|
||||||
|
const subscriptionId = userContext.subscriptionId;
|
||||||
|
const resourceGroup = userContext.resourceGroup;
|
||||||
|
if(LocalStorageUtility.hasItem(StorageKey.DataPlaneRbacEnabled)) {
|
||||||
|
var isDataPlaneRbacSetting = LocalStorageUtility.getEntryString(StorageKey.DataPlaneRbacEnabled);
|
||||||
|
if (isDataPlaneRbacSetting == "Automatic")
|
||||||
|
{
|
||||||
|
if (!account.properties.disableLocalAuth) {
|
||||||
|
keys = await listKeys(subscriptionId, resourceGroup, account.name);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
updateUserContext({
|
||||||
|
dataPlaneRbacEnabled: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(isDataPlaneRbacSetting == "True") {
|
||||||
|
updateUserContext({
|
||||||
|
dataPlaneRbacEnabled: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
keys = await listKeys(subscriptionId, resourceGroup, account.name);
|
||||||
|
updateUserContext({
|
||||||
|
dataPlaneRbacEnabled: false
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (openAction) {
|
if (openAction) {
|
||||||
handleOpenAction(openAction, useDatabases.getState().databases, explorer);
|
handleOpenAction(openAction, useDatabases.getState().databases, explorer);
|
||||||
}
|
}
|
||||||
@ -470,9 +526,11 @@ async function configurePortal(): Promise<Explorer> {
|
|||||||
},
|
},
|
||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
|
|
||||||
sendReadyMessage();
|
sendReadyMessage();
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function shouldForwardMessage(message: PortalMessage, messageOrigin: string) {
|
function shouldForwardMessage(message: PortalMessage, messageOrigin: string) {
|
||||||
|
Loading…
Reference in New Issue
Block a user