feat(ui): add MongoDB Atlas provider support (#9253)

Co-authored-by: Alan Buscaglia <gentlemanprogramming@gmail.com>
This commit is contained in:
Daniel Barranquero
2025-11-27 12:37:20 +01:00
committed by GitHub
parent 6e135abaa0
commit ed5f6b3af6
31 changed files with 256 additions and 179 deletions

View File

@@ -8,6 +8,7 @@ import {
IacProviderBadge,
KS8ProviderBadge,
M365ProviderBadge,
MongoDBAtlasProviderBadge,
OracleCloudProviderBadge,
} from "../icons/providers-badge";
@@ -38,6 +39,15 @@ export const CustomProviderInputM365 = () => {
);
};
export const CustomProviderInputMongoDBAtlas = () => {
return (
<div className="flex items-center gap-x-2">
<MongoDBAtlasProviderBadge width={25} height={25} />
<p className="text-sm">MongoDB Atlas</p>
</div>
);
};
export const CustomProviderInputGCP = () => {
return (
<div className="flex items-center gap-x-2">

View File

@@ -14,6 +14,7 @@ import {
CustomProviderInputIac,
CustomProviderInputKubernetes,
CustomProviderInputM365,
CustomProviderInputMongoDBAtlas,
CustomProviderInputOracleCloud,
} from "./custom-provider-inputs";
@@ -25,21 +26,13 @@ const providerDisplayData: Record<
label: "Amazon Web Services",
component: <CustomProviderInputAWS />,
},
gcp: {
label: "Google Cloud Platform",
component: <CustomProviderInputGCP />,
},
azure: {
label: "Microsoft Azure",
component: <CustomProviderInputAzure />,
},
m365: {
label: "Microsoft 365",
component: <CustomProviderInputM365 />,
},
kubernetes: {
label: "Kubernetes",
component: <CustomProviderInputKubernetes />,
gcp: {
label: "Google Cloud Platform",
component: <CustomProviderInputGCP />,
},
github: {
label: "GitHub",
@@ -49,6 +42,18 @@ const providerDisplayData: Record<
label: "Infrastructure as Code",
component: <CustomProviderInputIac />,
},
kubernetes: {
label: "Kubernetes",
component: <CustomProviderInputKubernetes />,
},
m365: {
label: "Microsoft 365",
component: <CustomProviderInputM365 />,
},
mongodbatlas: {
label: "MongoDB Atlas",
component: <CustomProviderInputMongoDBAtlas />,
},
oraclecloud: {
label: "Oracle Cloud Infrastructure",
component: <CustomProviderInputOracleCloud />,

View File

@@ -1,6 +1,19 @@
import { CONNECTION_STATUS_MAPPING } from "@/lib/helper-filters";
import { FilterOption, FilterType } from "@/types/filters";
import { PROVIDER_TYPES } from "@/types/providers";
import {
PROVIDER_DISPLAY_NAMES,
PROVIDER_TYPES,
ProviderType,
} from "@/types/providers";
// Create a mapping for provider types to display with icons and labels
const PROVIDER_TYPE_MAPPING = PROVIDER_TYPES.map((providerType) => ({
[providerType]: {
provider: providerType as ProviderType,
uid: "",
alias: PROVIDER_DISPLAY_NAMES[providerType],
},
}));
export const filterProviders: FilterOption[] = [
{
@@ -13,6 +26,7 @@ export const filterProviders: FilterOption[] = [
key: "provider__in",
labelCheckboxGroup: "Cloud Provider",
values: [...PROVIDER_TYPES],
valueLabelMapping: PROVIDER_TYPE_MAPPING,
},
// Add more filter categories as needed
];
@@ -22,6 +36,7 @@ export const filterScans = [
key: "provider_type__in",
labelCheckboxGroup: "Cloud Provider",
values: [...PROVIDER_TYPES],
valueLabelMapping: PROVIDER_TYPE_MAPPING,
index: 0,
},
{
@@ -64,6 +79,7 @@ export const filterFindings = [
key: FilterType.PROVIDER_TYPE,
labelCheckboxGroup: "Cloud Provider",
values: [...PROVIDER_TYPES],
valueLabelMapping: PROVIDER_TYPE_MAPPING,
index: 5,
},
{

View File

@@ -7,6 +7,7 @@ import { GitHubProviderBadge } from "./github-provider-badge";
import { IacProviderBadge } from "./iac-provider-badge";
import { KS8ProviderBadge } from "./ks8-provider-badge";
import { M365ProviderBadge } from "./m365-provider-badge";
import { MongoDBAtlasProviderBadge } from "./mongodbatlas-provider-badge";
import { OracleCloudProviderBadge } from "./oraclecloud-provider-badge";
export {
@@ -17,6 +18,7 @@ export {
IacProviderBadge,
KS8ProviderBadge,
M365ProviderBadge,
MongoDBAtlasProviderBadge,
OracleCloudProviderBadge,
};
@@ -30,4 +32,5 @@ export const PROVIDER_ICONS: Record<string, React.FC<IconSvgProps>> = {
GitHub: GitHubProviderBadge,
"Infrastructure as Code": IacProviderBadge,
"Oracle Cloud Infrastructure": OracleCloudProviderBadge,
"MongoDB Atlas": MongoDBAtlasProviderBadge,
};

View File

@@ -0,0 +1,40 @@
import * as React from "react";
import { IconSvgProps } from "@/types";
export const MongoDBAtlasProviderBadge: React.FC<IconSvgProps> = ({
size,
width,
height,
...props
}) => (
<svg
xmlns="http://www.w3.org/2000/svg"
aria-hidden="true"
fill="none"
focusable="false"
height={size || height}
role="presentation"
viewBox="0 0 256 256"
width={size || width}
{...props}
>
<g fill="none">
<rect width="256" height="256" fill="#e6f4ec" rx="60" />
<g transform="translate(128 128) scale(7.25) translate(-16 -16)">
<path
d="M15.9.087l.854 1.604c.192.296.4.558.645.802.715.715 1.394 1.464 2.004 2.266 1.447 1.9 2.423 4.01 3.12 6.292.418 1.394.645 2.824.662 4.27.07 4.323-1.412 8.035-4.4 11.12-.488.488-1.01.94-1.57 1.342-.296 0-.436-.227-.558-.436-.227-.383-.366-.82-.436-1.255-.105-.523-.174-1.046-.14-1.586v-.244C16.057 24.21 15.796.21 15.9.087z"
fill="#599636"
/>
<path
d="M15.9.034c-.035-.07-.07-.017-.105.017.017.35-.105.662-.296.96-.21.296-.488.523-.767.767-1.55 1.342-2.77 2.963-3.747 4.776-1.3 2.44-1.97 5.055-2.16 7.808-.087.993.314 4.497.627 5.508.854 2.684 2.388 4.933 4.375 6.885.488.47 1.01.906 1.55 1.325.157 0 .174-.14.21-.244a4.78 4.78 0 0 0 .157-.68l.35-2.614L15.9.034z"
fill="#6cac48"
/>
<path
d="M16.754 28.845c.035-.4.227-.732.436-1.063-.21-.087-.366-.26-.488-.453-.105-.174-.192-.383-.26-.575-.244-.732-.296-1.5-.366-2.248v-.453c-.087.07-.105.662-.105.75a17.37 17.37 0 0 1-.314 2.353c-.052.314-.087.627-.28.906 0 .035 0 .07.017.122.314.924.4 1.865.453 2.824v.35c0 .418-.017.33.33.47.14.052.296.07.436.174.105 0 .122-.087.122-.157l-.052-.575v-1.604c-.017-.28.035-.558.07-.82z"
fill="#c2bfbf"
/>
</g>
</g>
</svg>
);

View File

@@ -19,6 +19,7 @@ const providerTypeLabels: Record<ProviderType, string> = {
github: "GitHub",
iac: "Infrastructure as Code",
oraclecloud: "Oracle Cloud Infrastructure",
mongodbatlas: "MongoDB Atlas",
};
interface EnhancedProviderSelectorProps {

View File

@@ -1,10 +1,10 @@
import { Tooltip } from "@heroui/tooltip";
import React from "react";
import { ProviderType } from "@/types";
import { ConnectionFalse, ConnectionPending, ConnectionTrue } from "../icons";
import { getProviderLogo } from "../ui/entities";
interface ProviderInfoProps {
connected: boolean | null;
provider: ProviderType;
@@ -12,12 +12,12 @@ interface ProviderInfoProps {
providerUID?: string;
}
export const ProviderInfo: React.FC<ProviderInfoProps> = ({
export const ProviderInfo = ({
connected,
provider,
providerAlias,
providerUID,
}) => {
}: ProviderInfoProps) => {
const getIcon = () => {
switch (connected) {
case true:
@@ -31,8 +31,8 @@ export const ProviderInfo: React.FC<ProviderInfoProps> = ({
case false:
return (
<Tooltip content="Provider connection failed" className="text-xs">
<div className="rounded-medium border-danger bg-system-error-lighter flex items-center justify-center border-2 p-1">
<ConnectionFalse className="text-text-error" size={24} />
<div className="rounded-medium border-border-error flex items-center justify-center border-2 p-1">
<ConnectionFalse className="text-text-error-primary" size={24} />
</div>
</Tooltip>
);

View File

@@ -15,6 +15,7 @@ import {
IacProviderBadge,
KS8ProviderBadge,
M365ProviderBadge,
MongoDBAtlasProviderBadge,
OracleCloudProviderBadge,
} from "../icons/providers-badge";
import { CustomRadio } from "../ui/custom";
@@ -68,6 +69,12 @@ export const RadioGroupProvider: React.FC<RadioGroupProviderProps> = ({
<span className="ml-2">Microsoft 365</span>
</div>
</CustomRadio>
<CustomRadio description="MongoDB Atlas" value="mongodbatlas">
<div className="flex items-center">
<MongoDBAtlasProviderBadge size={26} />
<span className="ml-2">MongoDB Atlas</span>
</div>
</CustomRadio>
<CustomRadio description="Kubernetes" value="kubernetes">
<div className="flex items-center">
<KS8ProviderBadge size={26} />

View File

@@ -20,6 +20,7 @@ import {
KubernetesCredentials,
M365CertificateCredentials,
M365ClientSecretCredentials,
MongoDBAtlasCredentials,
OCICredentials,
ProviderType,
} from "@/types";
@@ -37,6 +38,7 @@ import { AzureCredentialsForm } from "./via-credentials/azure-credentials-form";
import { GitHubCredentialsForm } from "./via-credentials/github-credentials-form";
import { IacCredentialsForm } from "./via-credentials/iac-credentials-form";
import { KubernetesCredentialsForm } from "./via-credentials/k8s-credentials-form";
import { MongoDBAtlasCredentialsForm } from "./via-credentials/mongodbatlas-credentials-form";
import { OracleCloudCredentialsForm } from "./via-credentials/oraclecloud-credentials-form";
type BaseCredentialsFormProps = {
@@ -172,6 +174,13 @@ export const BaseCredentialsForm = ({
control={form.control as unknown as Control<OCICredentials>}
/>
)}
{providerType === "mongodbatlas" && (
<MongoDBAtlasCredentialsForm
control={
form.control as unknown as Control<MongoDBAtlasCredentials>
}
/>
)}
<div className="flex w-full justify-end gap-4">
{showBackButton && requiresBackButton(searchParamsObj.get("via")) && (

View File

@@ -62,6 +62,11 @@ const getProviderFieldDetails = (providerType?: ProviderType) => {
label: "Tenancy OCID",
placeholder: "e.g. ocid1.tenancy.oc1..aaaaaaa...",
};
case "mongodbatlas":
return {
label: "Organization ID",
placeholder: "e.g. 5f43a8c4e1234567890abcde",
};
default:
return {
label: "Provider UID",

View File

@@ -45,7 +45,7 @@ export const TestConnectionForm = ({
};
provider: ProviderType;
alias: string;
scanner_args: Record<string, any>;
scanner_args: Record<string, unknown>;
};
relationships: {
secret: {
@@ -239,27 +239,27 @@ export const TestConnectionForm = ({
</div>
{apiErrorMessage && (
<div className="text-text-error mt-4 rounded-md bg-red-100 p-3">
<div className="text-text-error-primary mt-4 rounded-md p-3">
<p>{`Provider ID ${apiErrorMessage?.toLowerCase()}. Please check and try again.`}</p>
</div>
)}
{connectionStatus && !connectionStatus.connected && (
<>
<div className="flex items-center gap-4 rounded-lg border border-red-200 bg-red-50 p-4">
<div className="flex items-center">
<div className="border-border-error flex items-start gap-4 rounded-lg border p-4">
<div className="flex shrink-0 items-center">
<Icon
icon="heroicons:exclamation-circle"
className="text-text-error h-5 w-5"
className="text-text-error-primary h-5 w-5"
/>
</div>
<div className="flex items-center">
<p className="text-small text-text-error">
<div className="min-w-0 flex-1">
<p className="text-small text-text-error-primary break-words">
{connectionStatus.error || "Unknown error"}
</p>
</div>
</div>
<p className="text-small text-text-error">
<p className="text-small text-text-error-primary">
It seems there was an issue with your credentials. Please review
your credentials and try again.
</p>

View File

@@ -2,3 +2,4 @@ export * from "./azure-credentials-form";
export * from "./github-credentials-form";
export * from "./iac-credentials-form";
export * from "./k8s-credentials-form";
export * from "./mongodbatlas-credentials-form";

View File

@@ -0,0 +1,49 @@
import { Control } from "react-hook-form";
import { CustomInput } from "@/components/ui/custom";
import { ProviderCredentialFields } from "@/lib/provider-credentials/provider-credential-fields";
import { MongoDBAtlasCredentials } from "@/types";
export const MongoDBAtlasCredentialsForm = ({
control,
}: {
control: Control<MongoDBAtlasCredentials>;
}) => {
return (
<>
<div className="flex flex-col">
<div className="text-md text-default-foreground leading-9 font-bold">
Connect via API Keys
</div>
<div className="text-default-500 text-sm">
Provide an organization-level MongoDB Atlas API public and private key
with read access to the resources you want Prowler to assess.
</div>
</div>
<CustomInput
control={control}
name={ProviderCredentialFields.ATLAS_PUBLIC_KEY}
type="text"
label="Atlas Public Key"
labelPlacement="inside"
placeholder="e.g. abcdefgh"
variant="bordered"
isRequired
/>
<CustomInput
control={control}
name={ProviderCredentialFields.ATLAS_PRIVATE_KEY}
type="password"
label="Atlas Private Key"
labelPlacement="inside"
placeholder="Enter the private key"
variant="bordered"
isRequired
/>
<div className="text-default-400 text-xs">
Keys never leave your browser unencrypted and are stored as secrets in
the backend. Rotate the key from MongoDB Atlas anytime if needed.
</div>
</>
);
};

View File

@@ -8,6 +8,7 @@ import {
IacProviderBadge,
KS8ProviderBadge,
M365ProviderBadge,
MongoDBAtlasProviderBadge,
OracleCloudProviderBadge,
} from "@/components/icons/providers-badge";
import { ProviderType } from "@/types";
@@ -30,6 +31,8 @@ export const getProviderLogo = (provider: ProviderType) => {
return <IacProviderBadge width={35} height={35} />;
case "oraclecloud":
return <OracleCloudProviderBadge width={35} height={35} />;
case "mongodbatlas":
return <MongoDBAtlasProviderBadge width={35} height={35} />;
default:
return null;
}
@@ -53,6 +56,8 @@ export const getProviderName = (provider: ProviderType): string => {
return "Infrastructure as Code";
case "oraclecloud":
return "Oracle Cloud Infrastructure";
case "mongodbatlas":
return "MongoDB Atlas";
default:
return "Unknown Provider";
}