Show portal networking banner for new backend (#1952)

* show portal networking banner for new backend

* fixed valid endpoints

* format

* fixed tests

* Fixed tests

* fix tests

* fixed tests

---------

Co-authored-by: Asier Isayas <aisayas@microsoft.com>
This commit is contained in:
Asier Isayas 2024-08-29 18:42:13 -04:00 committed by GitHub
parent c5b7f599b3
commit 4b207f3fa6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 123 additions and 62 deletions

View File

@ -135,6 +135,7 @@ export class BackendApi {
public static readonly PortalSettings: string = "PortalSettings"; public static readonly PortalSettings: string = "PortalSettings";
public static readonly AccountRestrictions: string = "AccountRestrictions"; public static readonly AccountRestrictions: string = "AccountRestrictions";
public static readonly RuntimeProxy: string = "RuntimeProxy"; public static readonly RuntimeProxy: string = "RuntimeProxy";
public static readonly DisallowedLocations: string = "DisallowedLocations";
} }
export class PortalBackendEndpoints { export class PortalBackendEndpoints {

View File

@ -1,5 +1,6 @@
import { isPublicInternetAccessAllowed } from "Common/DatabaseAccountUtility"; import { isPublicInternetAccessAllowed } from "Common/DatabaseAccountUtility";
import { PhoenixClient } from "Phoenix/PhoenixClient"; import { PhoenixClient } from "Phoenix/PhoenixClient";
import { useNewPortalBackendEndpoint } from "Utils/EndpointUtils";
import { cloneDeep } from "lodash"; import { cloneDeep } from "lodash";
import create, { UseStore } from "zustand"; import create, { UseStore } from "zustand";
import { AuthType } from "../../AuthType"; import { AuthType } from "../../AuthType";
@ -127,7 +128,9 @@ export const useNotebook: UseStore<NotebookState> = create((set, get) => ({
userContext.apiType === "Postgres" || userContext.apiType === "VCoreMongo" userContext.apiType === "Postgres" || userContext.apiType === "VCoreMongo"
? databaseAccount?.location ? databaseAccount?.location
: databaseAccount?.properties?.writeLocations?.[0]?.locationName.toLowerCase(); : databaseAccount?.properties?.writeLocations?.[0]?.locationName.toLowerCase();
const disallowedLocationsUri = `${configContext.BACKEND_ENDPOINT}/api/disallowedLocations`; const disallowedLocationsUri: string = useNewPortalBackendEndpoint(Constants.BackendApi.DisallowedLocations)
? `${configContext.PORTAL_BACKEND_ENDPOINT}/api/disallowedlocations`
: `${configContext.BACKEND_ENDPOINT}/api/disallowedLocations`;
const authorizationHeader = getAuthorizationHeader(); const authorizationHeader = getAuthorizationHeader();
try { try {
const response = await fetch(disallowedLocationsUri, { const response = await fetch(disallowedLocationsUri, {

View File

@ -78,6 +78,13 @@ export const PortalBackendIPs: { [key: string]: string[] } = {
//usnat: ["7.28.202.68"], //usnat: ["7.28.202.68"],
}; };
export const PortalBackendOutboundIPs: { [key: string]: string[] } = {
[PortalBackendEndpoints.Mpac]: ["13.91.105.215", "4.210.172.107"],
[PortalBackendEndpoints.Prod]: ["13.88.56.148", "40.91.218.243"],
[PortalBackendEndpoints.Fairfax]: ["52.247.163.6", "52.244.134.181"],
[PortalBackendEndpoints.Mooncake]: ["163.228.137.6", "143.64.170.142"],
};
export const MongoProxyOutboundIPs: { [key: string]: string[] } = { export const MongoProxyOutboundIPs: { [key: string]: string[] } = {
[MongoProxyEndpoints.Mpac]: ["20.245.81.54", "40.118.23.126"], [MongoProxyEndpoints.Mpac]: ["20.245.81.54", "40.118.23.126"],
[MongoProxyEndpoints.Prod]: ["40.80.152.199", "13.95.130.121"], [MongoProxyEndpoints.Prod]: ["40.80.152.199", "13.95.130.121"],
@ -178,6 +185,13 @@ export function useNewPortalBackendEndpoint(backendApi: string): boolean {
PortalBackendEndpoints.Mpac, PortalBackendEndpoints.Mpac,
PortalBackendEndpoints.Prod, PortalBackendEndpoints.Prod,
], ],
[BackendApi.DisallowedLocations]: [
PortalBackendEndpoints.Development,
PortalBackendEndpoints.Mpac,
PortalBackendEndpoints.Prod,
PortalBackendEndpoints.Fairfax,
PortalBackendEndpoints.Mooncake,
],
}; };
if (!newBackendApiEnvironmentMap[backendApi] || !configContext.PORTAL_BACKEND_ENDPOINT) { if (!newBackendApiEnvironmentMap[backendApi] || !configContext.PORTAL_BACKEND_ENDPOINT) {

View File

@ -1,20 +1,15 @@
import { MongoProxyEndpoints, PortalBackendEndpoints } from "Common/Constants";
import { resetConfigContext, updateConfigContext } from "ConfigContext"; import { resetConfigContext, updateConfigContext } from "ConfigContext";
import { DatabaseAccount, IpRule } from "Contracts/DataModels"; import { DatabaseAccount, IpRule } from "Contracts/DataModels";
import { updateUserContext } from "UserContext"; import { updateUserContext } from "UserContext";
import { PortalBackendIPs } from "Utils/EndpointUtils"; import { MongoProxyOutboundIPs, PortalBackendIPs, PortalBackendOutboundIPs } from "Utils/EndpointUtils";
import { getNetworkSettingsWarningMessage } from "./NetworkUtility"; import { getNetworkSettingsWarningMessage } from "./NetworkUtility";
describe("NetworkUtility tests", () => { describe("NetworkUtility tests", () => {
describe("getNetworkSettingsWarningMessage", () => { describe("getNetworkSettingsWarningMessage", () => {
const legacyBackendEndpoint: string = "https://main.documentdb.ext.azure.com";
const publicAccessMessagePart = "Please enable public access to proceed"; const publicAccessMessagePart = "Please enable public access to proceed";
const accessMessagePart = "Please allow access from Azure Portal to proceed"; const accessMessagePart = "Please allow access from Azure Portal to proceed";
// validEnpoints are a subset of those from Utils/EndpointValidation/PortalBackendIPs
const validEndpoints = [
"https://main.documentdb.ext.azure.com",
"https://main.documentdb.ext.azure.cn",
"https://main.documentdb.ext.azure.us",
];
let warningMessageResult: string; let warningMessageResult: string;
const warningMessageFunc = (msg: string) => (warningMessageResult = msg); const warningMessageFunc = (msg: string) => (warningMessageResult = msg);
@ -52,52 +47,59 @@ describe("NetworkUtility tests", () => {
expect(warningMessageResult).toContain(publicAccessMessagePart); expect(warningMessageResult).toContain(publicAccessMessagePart);
}); });
it(`should return no message when the appropriate ip rules are added to mongo/cassandra account per endpoint`, () => { it(`should return no message when the appropriate ip rules are added to mongo/cassandra account per endpoint`, async () => {
validEndpoints.forEach(async (endpoint) => { const portalBackendOutboundIPsWithLegacyIPs: string[] = [
updateUserContext({ ...PortalBackendOutboundIPs[PortalBackendEndpoints.Mpac],
databaseAccount: { ...PortalBackendOutboundIPs[PortalBackendEndpoints.Prod],
kind: "MongoDB", ...MongoProxyOutboundIPs[MongoProxyEndpoints.Mpac],
properties: { ...MongoProxyOutboundIPs[MongoProxyEndpoints.Prod],
ipRules: PortalBackendIPs[endpoint].map((ip: string) => ({ ipAddressOrRange: ip }) as IpRule), ...PortalBackendIPs["https://main.documentdb.ext.azure.com"],
publicNetworkAccess: "Enabled", ];
}, updateUserContext({
} as DatabaseAccount, databaseAccount: {
}); kind: "MongoDB",
properties: {
updateConfigContext({ ipRules: portalBackendOutboundIPsWithLegacyIPs.map((ip: string) => ({ ipAddressOrRange: ip }) as IpRule),
BACKEND_ENDPOINT: endpoint, publicNetworkAccess: "Enabled",
}); },
} as DatabaseAccount,
let asyncWarningMessageResult: string;
const asyncWarningMessageFunc = (msg: string) => (asyncWarningMessageResult = msg);
await getNetworkSettingsWarningMessage(asyncWarningMessageFunc);
expect(asyncWarningMessageResult).toBeUndefined();
}); });
updateConfigContext({
BACKEND_ENDPOINT: legacyBackendEndpoint,
PORTAL_BACKEND_ENDPOINT: PortalBackendEndpoints.Mpac,
MONGO_PROXY_ENDPOINT: MongoProxyEndpoints.Mpac,
});
let asyncWarningMessageResult: string;
const asyncWarningMessageFunc = (msg: string) => (asyncWarningMessageResult = msg);
await getNetworkSettingsWarningMessage(asyncWarningMessageFunc);
expect(asyncWarningMessageResult).toBeUndefined();
}); });
it("should return accessMessage when incorrent ip rule is added to mongo/cassandra account per endpoint", () => { it("should return accessMessage when incorrent ip rule is added to mongo/cassandra account per endpoint", async () => {
validEndpoints.forEach(async (endpoint) => { updateUserContext({
updateUserContext({ databaseAccount: {
databaseAccount: { kind: "MongoDB",
kind: "MongoDB", properties: {
properties: { ipRules: [{ ipAddressOrRange: "1.1.1.1" }],
ipRules: [{ ipAddressOrRange: "1.1.1.1" }], publicNetworkAccess: "Enabled",
publicNetworkAccess: "Enabled", },
}, } as DatabaseAccount,
} as DatabaseAccount,
});
updateConfigContext({
BACKEND_ENDPOINT: endpoint,
});
let asyncWarningMessageResult: string;
const asyncWarningMessageFunc = (msg: string) => (asyncWarningMessageResult = msg);
await getNetworkSettingsWarningMessage(asyncWarningMessageFunc);
expect(asyncWarningMessageResult).toContain(accessMessagePart);
}); });
updateConfigContext({
BACKEND_ENDPOINT: legacyBackendEndpoint,
PORTAL_BACKEND_ENDPOINT: PortalBackendEndpoints.Mpac,
MONGO_PROXY_ENDPOINT: MongoProxyEndpoints.Mpac,
});
let asyncWarningMessageResult: string;
const asyncWarningMessageFunc = (msg: string) => (asyncWarningMessageResult = msg);
await getNetworkSettingsWarningMessage(asyncWarningMessageFunc);
expect(asyncWarningMessageResult).toContain(accessMessagePart);
}); });
// Postgres and vcore mongo account checks basically pass through to CheckFirewallRules so those // Postgres and vcore mongo account checks basically pass through to CheckFirewallRules so those

View File

@ -1,7 +1,13 @@
import { CassandraProxyEndpoints, MongoProxyEndpoints, PortalBackendEndpoints } from "Common/Constants";
import { configContext } from "ConfigContext"; import { configContext } from "ConfigContext";
import { checkFirewallRules } from "Explorer/Tabs/Shared/CheckFirewallRules"; import { checkFirewallRules } from "Explorer/Tabs/Shared/CheckFirewallRules";
import { userContext } from "UserContext"; import { userContext } from "UserContext";
import { PortalBackendIPs } from "Utils/EndpointUtils"; import {
CassandraProxyOutboundIPs,
MongoProxyOutboundIPs,
PortalBackendIPs,
PortalBackendOutboundIPs,
} from "Utils/EndpointUtils";
export const getNetworkSettingsWarningMessage = async ( export const getNetworkSettingsWarningMessage = async (
setStateFunc: (warningMessage: string) => void, setStateFunc: (warningMessage: string) => void,
@ -45,18 +51,53 @@ export const getNetworkSettingsWarningMessage = async (
const ipRules = accountProperties.ipRules; const ipRules = accountProperties.ipRules;
// public network access is NOT set to "All networks" // public network access is NOT set to "All networks"
if (ipRules?.length > 0) { if (ipRules?.length > 0) {
if (userContext.apiType === "Cassandra" || userContext.apiType === "Mongo") { const isProdOrMpacPortalBackendEndpoint: boolean = [
const portalIPs = PortalBackendIPs[configContext.BACKEND_ENDPOINT]; PortalBackendEndpoints.Mpac,
let numberOfMatches = 0; PortalBackendEndpoints.Prod,
ipRules.forEach((ipRule) => { ].includes(configContext.PORTAL_BACKEND_ENDPOINT);
if (portalIPs.indexOf(ipRule.ipAddressOrRange) !== -1) { const portalBackendOutboundIPs: string[] = isProdOrMpacPortalBackendEndpoint
numberOfMatches++; ? [
} ...PortalBackendOutboundIPs[PortalBackendEndpoints.Mpac],
}); ...PortalBackendOutboundIPs[PortalBackendEndpoints.Prod],
]
: PortalBackendOutboundIPs[configContext.PORTAL_BACKEND_ENDPOINT];
let portalIPs: string[] = [...portalBackendOutboundIPs, ...PortalBackendIPs[configContext.BACKEND_ENDPOINT]];
if (numberOfMatches !== portalIPs.length) { if (userContext.apiType === "Mongo") {
setStateFunc(accessMessage); const isProdOrMpacMongoProxyEndpoint: boolean = [MongoProxyEndpoints.Mpac, MongoProxyEndpoints.Prod].includes(
configContext.MONGO_PROXY_ENDPOINT,
);
const mongoProxyOutboundIPs: string[] = isProdOrMpacMongoProxyEndpoint
? [...MongoProxyOutboundIPs[MongoProxyEndpoints.Mpac], ...MongoProxyOutboundIPs[MongoProxyEndpoints.Prod]]
: MongoProxyOutboundIPs[configContext.MONGO_PROXY_ENDPOINT];
portalIPs = [...portalIPs, ...mongoProxyOutboundIPs];
} else if (userContext.apiType === "Cassandra") {
const isProdOrMpacCassandraProxyEndpoint: boolean = [
CassandraProxyEndpoints.Mpac,
CassandraProxyEndpoints.Prod,
].includes(configContext.CASSANDRA_PROXY_ENDPOINT);
const cassandraProxyOutboundIPs: string[] = isProdOrMpacCassandraProxyEndpoint
? [
...CassandraProxyOutboundIPs[CassandraProxyEndpoints.Mpac],
...CassandraProxyOutboundIPs[CassandraProxyEndpoints.Prod],
]
: CassandraProxyOutboundIPs[configContext.CASSANDRA_PROXY_ENDPOINT];
portalIPs = [...portalIPs, ...cassandraProxyOutboundIPs];
}
let numberOfMatches = 0;
ipRules.forEach((ipRule) => {
if (portalIPs.indexOf(ipRule.ipAddressOrRange) !== -1) {
numberOfMatches++;
} }
});
if (numberOfMatches !== portalIPs.length) {
setStateFunc(accessMessage);
} }
} }
} }