diff --git a/src/HostedExplorer.tsx b/src/HostedExplorer.tsx index 2561f543a..642141002 100644 --- a/src/HostedExplorer.tsx +++ b/src/HostedExplorer.tsx @@ -1,6 +1,5 @@ import { initializeIcons } from "@fluentui/react"; import { useBoolean } from "@fluentui/react-hooks"; -import { getErrorMessage } from "Common/ErrorHandlingUtils"; import { AadAuthorizationFailure } from "Platform/Hosted/Components/AadAuthorizationFailure"; import * as React from "react"; import { render } from "react-dom"; diff --git a/test/cassandra/connectionStringLogin.spec.ts b/test/cassandra/connectionStringLogin.spec.ts index f89254f15..b01aa5c60 100644 --- a/test/cassandra/connectionStringLogin.spec.ts +++ b/test/cassandra/connectionStringLogin.spec.ts @@ -2,15 +2,15 @@ import { Page, expect, test } from "@playwright/test"; import { CosmosDBManagementClient } from "@azure/arm-cosmosdb"; import { - CommandBarButton, - DataExplorer, - ONE_MINUTE_MS, - TestAccount, - generateUniqueName, - getAccountName, - getAzureCLICredentials, - resourceGroupName, - subscriptionId, + CommandBarButton, + DataExplorer, + ONE_MINUTE_MS, + TestAccount, + generateUniqueName, + getAccountName, + getAzureCLICredentials, + resourceGroupName, + subscriptionId, } from "../fx"; const keyspaceId = generateUniqueName("keyspace"); diff --git a/test/fx.ts b/test/fx.ts index f42be798a..a128e2cee 100644 --- a/test/fx.ts +++ b/test/fx.ts @@ -43,6 +43,7 @@ export enum TestAccount { Cassandra = "Cassandra", Gremlin = "Gremlin", Mongo = "Mongo", + MongoConnectionStringPublicNetworkAccessDisabled = "MongoConnectionStringPublicNetworkAccessDisabled", MongoReadonly = "MongoReadOnly", Mongo32 = "Mongo32", SQL = "SQL", @@ -74,6 +75,8 @@ export function getDefaultAccountName(accountType: TestAccount): string { return `${accountNamePrefix}-de-test-gremlin-1`; case TestAccount.Mongo: return `${accountNamePrefix}-de-test-mongo-1`; + case TestAccount.MongoConnectionStringPublicNetworkAccessDisabled: + return `${accountNamePrefix}-de-test-mongo-connstring-nopublic-1`; case TestAccount.MongoReadonly: return `${accountNamePrefix}-de-test-mongo-readonly`; case TestAccount.Mongo32: @@ -262,6 +265,7 @@ export async function getTestExplorerUrl(accountType: TestAccount, options?: Tes case TestAccount.SQLConnectionString: case TestAccount.SQLConnectionStringPublicNetworkAccessDisabled: + case TestAccount.MongoConnectionStringPublicNetworkAccessDisabled: case TestAccount.TableConnectionString: case TestAccount.GremlinConnectionString: // Connection string (account key) login navigates directly to hostedExplorer.html and doesn't diff --git a/test/mongo/connectionStringLogin.spec.ts b/test/mongo/connectionStringLogin.spec.ts index a49b29734..4de114278 100644 --- a/test/mongo/connectionStringLogin.spec.ts +++ b/test/mongo/connectionStringLogin.spec.ts @@ -2,15 +2,15 @@ import { Page, expect, test } from "@playwright/test"; import { CosmosDBManagementClient } from "@azure/arm-cosmosdb"; import { - CommandBarButton, - DataExplorer, - ONE_MINUTE_MS, - TestAccount, - generateUniqueName, - getAccountName, - getAzureCLICredentials, - resourceGroupName, - subscriptionId, + CommandBarButton, + DataExplorer, + ONE_MINUTE_MS, + TestAccount, + generateUniqueName, + getAccountName, + getAzureCLICredentials, + resourceGroupName, + subscriptionId, } from "../fx"; const databaseId = generateUniqueName("db"); @@ -143,6 +143,31 @@ test.describe("Mongo account using connection string login", () => { await expect(page.locator(".errorDetails")).not.toBeEmpty(); }); + test("blocks Data Explorer when the account does not have Portal middleware services' IPs allowlisted", async ({ + page, + }) => { + const blockedAccountName = getAccountName(TestAccount.MongoConnectionStringPublicNetworkAccessDisabled); + const { connectionStrings = [] } = await armClient.databaseAccounts.listConnectionStrings( + resourceGroupName, + blockedAccountName, + ); + + const blockedConnectionString = connectionStrings.find((cs) => cs.type === "MongoDB")?.connectionString; + if (!blockedConnectionString) { + throw new Error(`Account ${blockedAccountName} did not return a MongoDB connection string`); + } + + await loginWithConnectionString(page, blockedConnectionString); + + // Unlike SQL, Mongo exchanges the connection string through the Portal Backend before opening + // Data Explorer. The account firewall rejects that middleware request, so login remains blocked. + await expect(page.locator("#connectExplorer")).toBeVisible(); + await expect(page.locator(".errorDetails")).toContainText("Couldn't authenticate with Cosmos DB", { + timeout: ONE_MINUTE_MS, + }); + await expect(page.getByRole("link", { name: "Allow access from Azure Portal" })).toBeVisible(); + }); + test("shows an error when the connection string is malformed", async ({ page }) => { await loginWithConnectionString(page, "this-is-not-a-connection-string");