From f370507a276385b9c3bc0eeaf12fd97c728f9f3f Mon Sep 17 00:00:00 2001 From: jawelton74 <103591340+jawelton74@users.noreply.github.com> Date: Tue, 8 Jul 2025 11:41:43 -0700 Subject: [PATCH] Refactor logic for determining if we should use data plane RBAC (#2180) * Refactor logic for determining if we should use data plane RBAC to a common function. * Move test function into test scope. --- src/Common/CosmosClient.ts | 5 +- src/Utils/AuthorizationUtils.test.ts | 80 +++++++++++++++++++++++++++- src/Utils/AuthorizationUtils.ts | 10 +++- 3 files changed, 90 insertions(+), 5 deletions(-) diff --git a/src/Common/CosmosClient.ts b/src/Common/CosmosClient.ts index 1ecd94944..54444d09e 100644 --- a/src/Common/CosmosClient.ts +++ b/src/Common/CosmosClient.ts @@ -4,12 +4,12 @@ import { CosmosDbArtifactType } from "Contracts/FabricMessagesContract"; import { AuthorizationToken } from "Contracts/FabricMessageTypes"; import { checkDatabaseResourceTokensValidity, isFabricMirroredKey } from "Platform/Fabric/FabricUtil"; import { LocalStorageUtility, StorageKey } from "Shared/StorageUtility"; +import { useDataplaneRbacAuthorization } from "Utils/AuthorizationUtils"; import { AuthType } from "../AuthType"; import { PriorityLevel } from "../Common/Constants"; import * as Logger from "../Common/Logger"; import { Platform, configContext } from "../ConfigContext"; import { FabricArtifactInfo, updateUserContext, userContext } from "../UserContext"; -import { isDataplaneRbacSupported } from "../Utils/APITypeUtils"; import { logConsoleError } from "../Utils/NotificationConsoleUtils"; import * as PriorityBasedExecutionUtils from "../Utils/PriorityBasedExecutionUtils"; import { EmulatorMasterKey, HttpHeaders } from "./Constants"; @@ -20,8 +20,7 @@ const _global = typeof self === "undefined" ? window : self; export const tokenProvider = async (requestInfo: Cosmos.RequestInfo) => { const { verb, resourceId, resourceType, headers } = requestInfo; - const dataPlaneRBACOptionEnabled = userContext.dataPlaneRbacEnabled && isDataplaneRbacSupported(userContext.apiType); - if (userContext.features.enableAadDataPlane || dataPlaneRBACOptionEnabled) { + if (useDataplaneRbacAuthorization(userContext)) { Logger.logInfo( `AAD Data Plane Feature flag set to ${userContext.features.enableAadDataPlane} for account with disable local auth ${userContext.databaseAccount.properties.disableLocalAuth} `, "Explorer/tokenProvider", diff --git a/src/Utils/AuthorizationUtils.test.ts b/src/Utils/AuthorizationUtils.test.ts index 3f9cfb2ea..bd2129954 100644 --- a/src/Utils/AuthorizationUtils.test.ts +++ b/src/Utils/AuthorizationUtils.test.ts @@ -1,10 +1,51 @@ import { AuthType } from "../AuthType"; import * as Constants from "../Common/Constants"; -import { updateUserContext } from "../UserContext"; +import { ApiType, updateUserContext, userContext } from "../UserContext"; import * as AuthorizationUtils from "./AuthorizationUtils"; jest.mock("../Explorer/Explorer"); describe("AuthorizationUtils", () => { + const setAadDataPlane = (enabled: boolean) => { + updateUserContext({ + features: { + enableAadDataPlane: enabled, + canExceedMaximumValue: false, + cosmosdb: false, + enableChangeFeedPolicy: false, + enableFixedCollectionWithSharedThroughput: false, + enableKOPanel: false, + enableNotebooks: false, + enableReactPane: false, + enableRightPanelV2: false, + enableSchema: false, + enableSDKoperations: false, + enableSpark: false, + enableTtl: false, + executeSproc: false, + enableResourceGraph: false, + enableKoResourceTree: false, + enableThroughputBuckets: false, + hostedDataExplorer: false, + sandboxNotebookOutputs: false, + showMinRUSurvey: false, + ttl90Days: false, + enableThroughputCap: false, + enableHierarchicalKeys: false, + enableCopilot: false, + disableCopilotPhoenixGateaway: false, + enableCopilotFullSchema: false, + copilotChatFixedMonacoEditorHeight: false, + enablePriorityBasedExecution: false, + disableConnectionStringLogin: false, + enableCloudShell: false, + autoscaleDefault: false, + partitionKeyDefault: false, + partitionKeyDefault2: false, + notebooksDownBanner: false, + }, + }); + }; + describe("getAuthorizationHeader()", () => { it("should return authorization header if authentication type is AAD", () => { updateUserContext({ @@ -54,4 +95,41 @@ describe("AuthorizationUtils", () => { ).toBeDefined(); }); }); + + describe("useDataplaneRbacAuthorization()", () => { + it("should return true if enableAadDataPlane feature flag is set", () => { + setAadDataPlane(true); + expect(AuthorizationUtils.useDataplaneRbacAuthorization(userContext)).toBe(true); + }); + + it("should return true if dataPlaneRbacEnabled is set to true and API supports RBAC", () => { + setAadDataPlane(false); + ["SQL", "Tables"].forEach((type) => { + updateUserContext({ + dataPlaneRbacEnabled: true, + apiType: type as ApiType, + }); + expect(AuthorizationUtils.useDataplaneRbacAuthorization(userContext)).toBe(true); + }); + }); + + it("should return false if dataPlaneRbacEnabled is set to true and API does not support RBAC", () => { + setAadDataPlane(false); + ["Mongo", "Gremlin", "Cassandra", "Postgres", "VCoreMongo"].forEach((type) => { + updateUserContext({ + dataPlaneRbacEnabled: true, + apiType: type as ApiType, + }); + expect(AuthorizationUtils.useDataplaneRbacAuthorization(userContext)).toBe(false); + }); + }); + + it("should return false if dataPlaneRbacEnabled is set to false", () => { + setAadDataPlane(false); + updateUserContext({ + dataPlaneRbacEnabled: false, + }); + expect(AuthorizationUtils.useDataplaneRbacAuthorization(userContext)).toBe(false); + }); + }); }); diff --git a/src/Utils/AuthorizationUtils.ts b/src/Utils/AuthorizationUtils.ts index d2ef4e8ff..359d2ba20 100644 --- a/src/Utils/AuthorizationUtils.ts +++ b/src/Utils/AuthorizationUtils.ts @@ -1,5 +1,6 @@ import * as msal from "@azure/msal-browser"; import { Action, ActionModifiers } from "Shared/Telemetry/TelemetryConstants"; +import { isDataplaneRbacSupported } from "Utils/APITypeUtils"; import { AuthType } from "../AuthType"; import * as Constants from "../Common/Constants"; import * as Logger from "../Common/Logger"; @@ -7,7 +8,7 @@ import { configContext } from "../ConfigContext"; import { DatabaseAccount } from "../Contracts/DataModels"; import * as ViewModels from "../Contracts/ViewModels"; import { trace, traceFailure } from "../Shared/Telemetry/TelemetryProcessor"; -import { userContext } from "../UserContext"; +import { UserContext, userContext } from "../UserContext"; export function getAuthorizationHeader(): ViewModels.AuthorizationTokenHeaderMetadata { if (userContext.authType === AuthType.EncryptedToken) { @@ -179,3 +180,10 @@ export async function acquireTokenWithMsal( } } } + +export function useDataplaneRbacAuthorization(userContext: UserContext): boolean { + return ( + userContext.features.enableAadDataPlane || + (userContext.dataPlaneRbacEnabled && isDataplaneRbacSupported(userContext.apiType)) + ); +}