Files
cosmos-explorer/src/UserContext.ts
T
sakshigupta12feb 4a881651d5 added a pop up in click of mongo shell if its entra only flow (#2531)
* added a pop up in click of mongo shell if its entra only flow

* formatting updated

---------

Co-authored-by: Sakshi Gupta <sakshig@microsoft.com>
2026-07-13 13:53:16 +05:30

241 lines
7.7 KiB
TypeScript

import { CosmosDbArtifactType, ResourceTokenInfo } from "Contracts/FabricMessagesContract";
import { Action } from "Shared/Telemetry/TelemetryConstants";
import { traceOpen } from "Shared/Telemetry/TelemetryProcessor";
import { useCarousel } from "hooks/useCarousel";
import { usePostgres } from "hooks/usePostgres";
import { v4 as uuidv4 } from "uuid";
import { AuthType } from "./AuthType";
import { DatabaseAccount } from "./Contracts/DataModels";
import { SubscriptionType } from "./Contracts/SubscriptionType";
import { Features, extractFeatures } from "./Platform/Hosted/extractFeatures";
import { CollectionCreation, CollectionCreationDefaults } from "./Shared/Constants";
interface ThroughputDefaults {
fixed: number;
unlimited:
| number
| {
collectionThreshold: number;
lessThanOrEqualToThreshold: number;
greatThanThreshold: number;
};
unlimitedmax: number;
unlimitedmin: number;
shared: number;
}
export interface CollectionCreationDefaults {
storage: string;
throughput: ThroughputDefaults;
}
export interface Node {
text: string;
value: string;
ariaLabel: string;
}
export interface PostgresConnectionStrParams {
adminLogin: string;
enablePublicIpAccess: boolean;
nodes: Node[];
isMarlinServerGroup: boolean;
isFreeTier: boolean;
}
export type VCoreMongoAuthMode = "NativeAuth" | "MicrosoftEntraID";
export interface VCoreMongoAuthConfig {
allowedModes?: VCoreMongoAuthMode[];
}
export interface VCoreMongoConnectionParams {
adminLogin: string;
connectionString: string;
authConfig?: VCoreMongoAuthConfig;
isNativeAuthDisabled?: boolean;
}
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 {
if (userContext.features?.mongoDisableNativeAuth) {
return true;
}
const params = userContext.vcoreMongoConnectionParams;
if (!params) {
return false;
}
if (params.isNativeAuthDisabled) {
return true;
}
const allowedModes = params.authConfig?.allowedModes || [];
return allowedModes.length > 0 && !allowedModes.includes("NativeAuth");
}
export interface FabricArtifactInfo {
[CosmosDbArtifactType.MIRRORED_KEY]: {
connectionId: string;
resourceTokenInfo: ResourceTokenInfo | undefined;
};
[CosmosDbArtifactType.MIRRORED_AAD]: undefined;
[CosmosDbArtifactType.NATIVE]: undefined;
}
export interface FabricContext<T extends CosmosDbArtifactType> {
fabricClientRpcVersion: string;
isReadOnly: boolean;
isVisible: boolean;
databaseName: string;
artifactType: CosmosDbArtifactType;
artifactInfo: FabricArtifactInfo[T];
}
export type AdminFeedbackControlPolicy =
| "connectedExperiences"
| "policyAllowFeedback"
| "policyAllowSurvey"
| "policyAllowScreenshot"
| "policyAllowContact"
| "policyAllowContent"
| "policyEmailCollectionDefault"
| "policyScreenshotDefault"
| "policyContentSamplesDefault";
export type AdminFeedbackPolicySettings = {
[key in AdminFeedbackControlPolicy]: boolean;
};
export interface UserContext {
readonly fabricContext?: FabricContext<CosmosDbArtifactType>;
readonly authType?: AuthType;
readonly masterKey?: string;
readonly subscriptionId?: string;
readonly tenantId?: string;
readonly userName?: string;
readonly resourceGroup?: string;
readonly databaseAccount?: DatabaseAccount;
readonly endpoint?: string;
readonly aadToken?: string;
readonly accessToken?: string;
readonly authorizationToken?: string;
readonly resourceToken?: string;
readonly subscriptionType?: SubscriptionType;
readonly quotaId?: string;
// API Type is not yet provided by ARM. You need to manually inspect all the capabilities+kind so we abstract that logic in userContext
// This is coming in a future Cosmos ARM API version as a prperty on databaseAccount
apiType: ApiType;
readonly isTryCosmosDBSubscription?: boolean;
readonly portalEnv?: PortalEnv;
readonly features: Features;
readonly hasWriteAccess: boolean;
readonly parsedResourceToken?: {
databaseId: string;
collectionId: string;
partitionKey?: string;
};
readonly postgresConnectionStrParams?: PostgresConnectionStrParams;
readonly isReplica?: boolean;
collectionCreationDefaults: CollectionCreationDefaults;
readonly selectedRegionalEndpoint?: string;
readonly writeEnabledInSelectedRegion?: boolean;
readonly vcoreMongoConnectionParams?: VCoreMongoConnectionParams;
readonly feedbackPolicies?: AdminFeedbackPolicySettings;
readonly dataPlaneRbacEnabled?: boolean;
readonly refreshCosmosClient?: boolean;
throughputBucketsEnabled?: boolean;
readonly sessionId: string;
}
export type ApiType = "SQL" | "Mongo" | "Gremlin" | "Tables" | "Cassandra" | "Postgres" | "VCoreMongo";
export type PortalEnv = "localhost" | "blackforest" | "fairfax" | "mooncake" | "prod1" | "rx" | "ex" | "prod" | "dev";
const ONE_WEEK_IN_MS = 604800000;
const features = extractFeatures();
const userContext: UserContext = {
apiType: "SQL",
hasWriteAccess: true,
isTryCosmosDBSubscription: false,
portalEnv: "prod",
features,
subscriptionType: CollectionCreation.DefaultSubscriptionType,
collectionCreationDefaults: CollectionCreationDefaults,
sessionId: uuidv4(), // Default sessionId - will be overwritten if provided by host
};
export function isAccountNewerThanThresholdInMs(createdAt: string, threshold: number) {
let createdAtMs: number = Date.parse(createdAt);
if (isNaN(createdAtMs)) {
createdAtMs = 0;
}
const nowMs: number = Date.now();
const millisecsSinceAccountCreation = nowMs - createdAtMs;
return threshold > millisecsSinceAccountCreation;
}
function updateUserContext(newContext: Partial<UserContext>): void {
if (newContext.databaseAccount) {
newContext.apiType = apiType(newContext.databaseAccount);
const isNewAccount = isAccountNewerThanThresholdInMs(
newContext.databaseAccount?.systemData?.createdAt || "",
ONE_WEEK_IN_MS,
);
if (!localStorage.getItem(newContext.databaseAccount.id)) {
if (newContext.isTryCosmosDBSubscription || isNewAccount) {
if (newContext.apiType === "Postgres" && !newContext.isReplica) {
usePostgres.getState().setShowResetPasswordBubble(true);
usePostgres.getState().setShowPostgreTeachingBubble(true);
} else {
useCarousel.getState().setShouldOpen(true);
localStorage.setItem(newContext.databaseAccount.id, "true");
traceOpen(Action.OpenCarousel);
}
} else if (newContext.apiType === "Postgres") {
usePostgres.getState().setShowPostgreTeachingBubble(true);
localStorage.setItem(newContext.databaseAccount.id, "true");
}
}
}
Object.assign(userContext, newContext);
}
export function apiType(account: DatabaseAccount | undefined): ApiType {
if (!account) {
return "SQL";
}
const capabilities = account.properties?.capabilities;
if (capabilities) {
if (capabilities.find((c) => c.name === "EnableCassandra")) {
return "Cassandra";
}
if (capabilities.find((c) => c.name === "EnableGremlin")) {
return "Gremlin";
}
if (capabilities.find((c) => c.name === "EnableMongo")) {
return "Mongo";
}
if (capabilities.find((c) => c.name === "EnableTable")) {
return "Tables";
}
}
if (account.kind === "MongoDB" || account.kind === "Parse") {
return "Mongo";
}
if (account.kind === "Postgres") {
return "Postgres";
}
if (account.kind === "VCoreMongo") {
return "VCoreMongo";
}
return "SQL";
}
export { updateUserContext, userContext };