cosmos-explorer/src/UserContext.ts
sindhuba c30a9681fe
Add Data Plane RBAC functionality (#1878)
* Fix API endpoint for CassandraProxy query API

* activate Mongo Proxy and Cassandra Proxy in Prod

* Add CP Prod endpoint

* Run npm format and tests

* Revert code

* fix bug that blocked local mongo proxy and cassandra proxy development

* Add prod endpoint

* fix pr check tests

* Remove prod

* Remove prod endpoint

* Remove dev endpoint

* Support data plane RBAC

* Support data plane RBAC

* Add additional changes for Portal RBAC functionality

* Address errors and checks

* Cleanup DP RBAC code

* Run format

* Fix unit tests

* Remove unnecessary code

* Run npm format

* Fix enableAadDataPlane feature flag behavior

* Fix  enable AAD dataplane feature flag behavior

* Address feedback comments

* Minor fix

* Add new fixes

* Fix Tables test

* Run npm format

---------

Co-authored-by: Asier Isayas <aisayas@microsoft.com>
2024-07-01 09:33:07 -07:00

196 lines
6.1 KiB
TypeScript

import { FabricDatabaseConnectionInfo } from "Contracts/FabricMessagesContract";
import { ParsedResourceTokenConnectionString } from "Platform/Hosted/Helpers/ResourceTokenUtils";
import { Action } from "Shared/Telemetry/TelemetryConstants";
import { traceOpen } from "Shared/Telemetry/TelemetryProcessor";
import { useCarousel } from "hooks/useCarousel";
import { usePostgres } from "hooks/usePostgres";
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 interface VCoreMongoConnectionParams {
adminLogin: string;
connectionString: string;
}
interface FabricContext {
connectionId: string;
databaseConnectionInfo: FabricDatabaseConnectionInfo | undefined;
isReadOnly: boolean;
isVisible: boolean;
}
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;
readonly authType?: AuthType;
readonly masterKey?: string;
readonly subscriptionId?: 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;
sampleDataConnectionInfo?: ParsedResourceTokenConnectionString;
readonly vcoreMongoConnectionParams?: VCoreMongoConnectionParams;
readonly feedbackPolicies?: AdminFeedbackPolicySettings;
readonly dataPlaneRbacEnabled?: boolean;
}
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,
};
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);
}
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 };