mirror of
https://github.com/prowler-cloud/prowler.git
synced 2025-12-19 05:17:47 +00:00
feat(ui): add MongoDB Atlas provider support (#9253)
Co-authored-by: Alan Buscaglia <gentlemanprogramming@gmail.com>
This commit is contained in:
committed by
GitHub
parent
6e135abaa0
commit
ed5f6b3af6
@@ -90,7 +90,7 @@ prowler dashboard
|
||||
| M365 | 70 | 7 | 3 | 2 | Official | UI, API, CLI |
|
||||
| OCI | 51 | 13 | 1 | 10 | Official | UI, API, CLI |
|
||||
| IaC | [See `trivy` docs.](https://trivy.dev/latest/docs/coverage/iac/) | N/A | N/A | N/A | Official | UI, API, CLI |
|
||||
| MongoDB Atlas | 10 | 3 | 0 | 0 | Official | CLI, API |
|
||||
| MongoDB Atlas | 10 | 3 | 0 | 0 | Official | UI, API, CLI |
|
||||
| LLM | [See `promptfoo` docs.](https://www.promptfoo.dev/docs/red-team/plugins/) | N/A | N/A | N/A | Official | CLI |
|
||||
| NHN | 6 | 2 | 1 | 0 | Unofficial | CLI |
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ The supported providers right now are:
|
||||
| [Github](/user-guide/providers/github/getting-started-github) | Official | UI, API, CLI |
|
||||
| [Oracle Cloud](/user-guide/providers/oci/getting-started-oci) | Official | UI, API, CLI |
|
||||
| [Infra as Code](/user-guide/providers/iac/getting-started-iac) | Official | UI, API, CLI |
|
||||
| [MongoDB Atlas](/user-guide/providers/mongodbatlas/getting-started-mongodbatlas) | Official | CLI, API |
|
||||
| [MongoDB Atlas](/user-guide/providers/mongodbatlas/getting-started-mongodbatlas) | Official | UI, API, CLI |
|
||||
| [LLM](/user-guide/providers/llm/getting-started-llm) | Official | CLI |
|
||||
| **NHN** | Unofficial | CLI |
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ All notable changes to the **Prowler UI** are documented in this file.
|
||||
- IaC (Infrastructure as Code) provider support for scanning remote repositories [(#8751)](https://github.com/prowler-cloud/prowler/pull/8751)
|
||||
- PDF reporting for NIS2 compliance framework [(#9170)](https://github.com/prowler-cloud/prowler/pull/9170)
|
||||
- External resource link to IaC findings for direct navigation to source code in Git repositories [(#9151)](https://github.com/prowler-cloud/prowler/pull/9151)
|
||||
- MongoDB Atlas provider support [(#9253)](https://github.com/prowler-cloud/prowler/pull/9253)
|
||||
- New Overview page and new app styles [(#9234)](https://github.com/prowler-cloud/prowler/pull/9234)
|
||||
- Use branch name as region for IaC findings [(#9296)](https://github.com/prowler-cloud/prowler/pull/9296)
|
||||
|
||||
|
||||
@@ -4,62 +4,6 @@ import { redirect } from "next/navigation";
|
||||
|
||||
import { apiBaseUrl, getAuthHeaders } from "@/lib";
|
||||
import { handleApiResponse } from "@/lib/server-actions-helper";
|
||||
import { FindingsResponse } from "@/types";
|
||||
|
||||
interface IncludedItem {
|
||||
type: string;
|
||||
id: string;
|
||||
attributes?: { provider?: string };
|
||||
relationships?: { provider?: { data?: { id: string } } };
|
||||
}
|
||||
|
||||
type FindingsApiResponse = FindingsResponse & {
|
||||
included?: IncludedItem[];
|
||||
};
|
||||
|
||||
const filterMongoFindings = <T extends FindingsApiResponse | null | undefined>(
|
||||
result: T,
|
||||
): T => {
|
||||
if (!result?.data) return result;
|
||||
|
||||
const included = (result as FindingsApiResponse).included || [];
|
||||
|
||||
// Get IDs of providers containing "mongo" in included items
|
||||
const mongoProviderIds = new Set(
|
||||
included
|
||||
.filter(
|
||||
(item) =>
|
||||
item.type === "providers" &&
|
||||
item.attributes?.provider?.toLowerCase().includes("mongo"),
|
||||
)
|
||||
.map((item) => item.id),
|
||||
);
|
||||
|
||||
// Filter out findings associated with mongo providers
|
||||
result.data = result.data.filter((finding) => {
|
||||
const scanId = finding.relationships?.scan?.data?.id;
|
||||
// Find the scan in included items
|
||||
const scan = included.find(
|
||||
(item) => item.type === "scans" && item.id === scanId,
|
||||
);
|
||||
const providerId = scan?.relationships?.provider?.data?.id;
|
||||
return !providerId || !mongoProviderIds.has(providerId);
|
||||
});
|
||||
|
||||
// Filter out mongo-related included items
|
||||
if ((result as FindingsApiResponse).included) {
|
||||
(result as FindingsApiResponse).included = included.filter(
|
||||
(item) =>
|
||||
!(
|
||||
item.type === "providers" &&
|
||||
item.attributes?.provider?.toLowerCase().includes("mongo")
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
export const getFindings = async ({
|
||||
page = 1,
|
||||
pageSize = 10,
|
||||
@@ -89,9 +33,7 @@ export const getFindings = async ({
|
||||
headers,
|
||||
});
|
||||
|
||||
const result = await handleApiResponse(findings);
|
||||
|
||||
return filterMongoFindings(result);
|
||||
return handleApiResponse(findings);
|
||||
} catch (error) {
|
||||
console.error("Error fetching findings:", error);
|
||||
return undefined;
|
||||
@@ -129,9 +71,7 @@ export const getLatestFindings = async ({
|
||||
headers,
|
||||
});
|
||||
|
||||
const result = await handleApiResponse(findings);
|
||||
|
||||
return filterMongoFindings(result);
|
||||
return handleApiResponse(findings);
|
||||
} catch (error) {
|
||||
console.error("Error fetching findings:", error);
|
||||
return undefined;
|
||||
|
||||
@@ -28,8 +28,6 @@ interface AggregatedProvider {
|
||||
fail: number;
|
||||
}
|
||||
|
||||
const EXCLUDED_PROVIDERS = new Set(["mongo", "mongodb", "mongodbatlas"]);
|
||||
|
||||
// API can return multiple entries for the same provider type, so we sum their findings
|
||||
function aggregateProvidersByType(
|
||||
providers: ProviderOverview[],
|
||||
@@ -39,8 +37,6 @@ function aggregateProvidersByType(
|
||||
for (const provider of providers) {
|
||||
const { id, attributes } = provider;
|
||||
|
||||
if (EXCLUDED_PROVIDERS.has(id)) continue;
|
||||
|
||||
const existing = aggregated.get(id);
|
||||
|
||||
if (existing) {
|
||||
|
||||
@@ -39,26 +39,9 @@ export const getProviders = async ({
|
||||
headers,
|
||||
});
|
||||
|
||||
const result = (await handleApiResponse(response)) as
|
||||
return (await handleApiResponse(response)) as
|
||||
| ProvidersApiResponse
|
||||
| undefined;
|
||||
|
||||
if (result?.data) {
|
||||
// Filter out providers with provider type containing "mongo"
|
||||
result.data = result.data.filter(
|
||||
(provider) =>
|
||||
!provider.attributes?.provider?.toLowerCase().includes("mongo"),
|
||||
);
|
||||
|
||||
// Also filter out mongo-related included items if present
|
||||
if (result.included) {
|
||||
result.included = result.included.filter(
|
||||
(item) => !item.type.toLowerCase().includes("mongo"),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
} catch (error) {
|
||||
console.error("Error fetching providers:", error);
|
||||
return undefined;
|
||||
|
||||
@@ -9,47 +9,6 @@ import {
|
||||
} from "@/lib/compliance/compliance-report-types";
|
||||
import { addScanOperation } from "@/lib/sentry-breadcrumbs";
|
||||
import { handleApiError, handleApiResponse } from "@/lib/server-actions-helper";
|
||||
import { ScansApiResponse } from "@/types";
|
||||
|
||||
const filterMongoScans = (result: ScansApiResponse | undefined) => {
|
||||
if (!result?.data) return result;
|
||||
|
||||
const included = result.included || [];
|
||||
|
||||
// Get IDs of providers containing "mongo"
|
||||
const mongoProviderIds = new Set(
|
||||
included
|
||||
.filter(
|
||||
(item) =>
|
||||
item.type === "providers" &&
|
||||
item.attributes?.provider?.toLowerCase().includes("mongo"),
|
||||
)
|
||||
.map((item) => item.id),
|
||||
);
|
||||
|
||||
// If no mongo providers found, return as-is
|
||||
if (mongoProviderIds.size === 0) return result;
|
||||
|
||||
// Filter out scans associated with mongo providers
|
||||
result.data = result.data.filter((scan) => {
|
||||
const providerId = scan.relationships?.provider?.data?.id;
|
||||
return !providerId || !mongoProviderIds.has(providerId);
|
||||
});
|
||||
|
||||
// Filter out mongo-related included items
|
||||
if (result.included) {
|
||||
result.included = included.filter(
|
||||
(item) =>
|
||||
!(
|
||||
item.type === "providers" &&
|
||||
item.attributes?.provider?.toLowerCase().includes("mongo")
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
export const getScans = async ({
|
||||
page = 1,
|
||||
query = "",
|
||||
@@ -84,10 +43,7 @@ export const getScans = async ({
|
||||
try {
|
||||
const response = await fetch(url.toString(), { headers });
|
||||
|
||||
const result = await handleApiResponse(response);
|
||||
|
||||
// Filter out mongo-related scans when provider is included
|
||||
return filterMongoScans(result);
|
||||
return handleApiResponse(response);
|
||||
} catch (error) {
|
||||
console.error("Error fetching scans:", error);
|
||||
return undefined;
|
||||
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
IacProviderBadge,
|
||||
KS8ProviderBadge,
|
||||
M365ProviderBadge,
|
||||
MongoDBAtlasProviderBadge,
|
||||
OracleCloudProviderBadge,
|
||||
} from "@/components/icons/providers-badge";
|
||||
import {
|
||||
@@ -31,6 +32,7 @@ const PROVIDER_ICON: Record<ProviderType, ReactNode> = {
|
||||
github: <GitHubProviderBadge width={18} height={18} />,
|
||||
iac: <IacProviderBadge width={18} height={18} />,
|
||||
oraclecloud: <OracleCloudProviderBadge width={18} height={18} />,
|
||||
mongodbatlas: <MongoDBAtlasProviderBadge width={18} height={18} />,
|
||||
};
|
||||
|
||||
interface AccountsSelectorProps {
|
||||
|
||||
@@ -52,6 +52,11 @@ const OracleCloudProviderBadge = lazy(() =>
|
||||
default: m.OracleCloudProviderBadge,
|
||||
})),
|
||||
);
|
||||
const MongoDBAtlasProviderBadge = lazy(() =>
|
||||
import("@/components/icons/providers-badge").then((m) => ({
|
||||
default: m.MongoDBAtlasProviderBadge,
|
||||
})),
|
||||
);
|
||||
|
||||
type IconProps = { width: number; height: number };
|
||||
|
||||
@@ -95,6 +100,10 @@ const PROVIDER_DATA: Record<
|
||||
label: "Oracle Cloud Infrastructure",
|
||||
icon: OracleCloudProviderBadge,
|
||||
},
|
||||
mongodbatlas: {
|
||||
label: "MongoDB Atlas",
|
||||
icon: MongoDBAtlasProviderBadge,
|
||||
},
|
||||
};
|
||||
|
||||
type ProviderTypeSelectorProps = {
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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 />,
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
{
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
@@ -19,6 +19,7 @@ const providerTypeLabels: Record<ProviderType, string> = {
|
||||
github: "GitHub",
|
||||
iac: "Infrastructure as Code",
|
||||
oraclecloud: "Oracle Cloud Infrastructure",
|
||||
mongodbatlas: "MongoDB Atlas",
|
||||
};
|
||||
|
||||
interface EnhancedProviderSelectorProps {
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
|
||||
@@ -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} />
|
||||
|
||||
@@ -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")) && (
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -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";
|
||||
}
|
||||
|
||||
@@ -167,6 +167,12 @@ export const useCredentialsForm = ({
|
||||
[ProviderCredentialFields.OCI_REGION]: "",
|
||||
[ProviderCredentialFields.OCI_PASS_PHRASE]: "",
|
||||
};
|
||||
case "mongodbatlas":
|
||||
return {
|
||||
...baseDefaults,
|
||||
[ProviderCredentialFields.ATLAS_PUBLIC_KEY]: "",
|
||||
[ProviderCredentialFields.ATLAS_PRIVATE_KEY]: "",
|
||||
};
|
||||
default:
|
||||
return baseDefaults;
|
||||
}
|
||||
|
||||
@@ -28,4 +28,6 @@ export const PROVIDER_CREDENTIALS_ERROR_MAPPING: Record<string, string> = {
|
||||
[ErrorPointers.ROLE_SESSION_NAME]: ProviderCredentialFields.ROLE_SESSION_NAME,
|
||||
[ErrorPointers.SERVICE_ACCOUNT_KEY]:
|
||||
ProviderCredentialFields.SERVICE_ACCOUNT_KEY,
|
||||
[ErrorPointers.ATLAS_PUBLIC_KEY]: ProviderCredentialFields.ATLAS_PUBLIC_KEY,
|
||||
[ErrorPointers.ATLAS_PRIVATE_KEY]: ProviderCredentialFields.ATLAS_PRIVATE_KEY,
|
||||
};
|
||||
|
||||
@@ -42,6 +42,11 @@ export const getProviderHelpText = (provider: string) => {
|
||||
text: "Need help connecting your Oracle Cloud account?",
|
||||
link: "https://goto.prowler.com/provider-oraclecloud",
|
||||
};
|
||||
case "mongodbatlas":
|
||||
return {
|
||||
text: "Need help connecting your MongoDB Atlas organization?",
|
||||
link: "https://goto.prowler.com/provider-mongodbatlas",
|
||||
};
|
||||
default:
|
||||
return {
|
||||
text: "How to setup a provider?",
|
||||
|
||||
@@ -197,6 +197,20 @@ export const buildGitHubSecret = (formData: FormData) => {
|
||||
return {};
|
||||
};
|
||||
|
||||
export const buildMongoDBAtlasSecret = (formData: FormData) => {
|
||||
const secret = {
|
||||
[ProviderCredentialFields.ATLAS_PUBLIC_KEY]: getFormValue(
|
||||
formData,
|
||||
ProviderCredentialFields.ATLAS_PUBLIC_KEY,
|
||||
),
|
||||
[ProviderCredentialFields.ATLAS_PRIVATE_KEY]: getFormValue(
|
||||
formData,
|
||||
ProviderCredentialFields.ATLAS_PRIVATE_KEY,
|
||||
),
|
||||
};
|
||||
return filterEmptyValues(secret);
|
||||
};
|
||||
|
||||
export const buildIacSecret = (formData: FormData) => {
|
||||
const secret = {
|
||||
[ProviderCredentialFields.REPOSITORY_URL]: getFormValue(
|
||||
@@ -308,6 +322,10 @@ export const buildSecretConfig = (
|
||||
secretType: "static",
|
||||
secret: buildOracleCloudSecret(formData, providerUid),
|
||||
}),
|
||||
mongodbatlas: () => ({
|
||||
secretType: "static",
|
||||
secret: buildMongoDBAtlasSecret(formData),
|
||||
}),
|
||||
};
|
||||
|
||||
const builder = secretBuilders[providerType];
|
||||
|
||||
@@ -45,6 +45,10 @@ export const ProviderCredentialFields = {
|
||||
GITHUB_APP_ID: "github_app_id",
|
||||
GITHUB_APP_KEY: "github_app_key_content",
|
||||
|
||||
// MongoDB Atlas fields
|
||||
ATLAS_PUBLIC_KEY: "atlas_public_key",
|
||||
ATLAS_PRIVATE_KEY: "atlas_private_key",
|
||||
|
||||
// IaC fields
|
||||
REPOSITORY_URL: "repository_url",
|
||||
ACCESS_TOKEN: "access_token",
|
||||
@@ -95,6 +99,8 @@ export const ErrorPointers = {
|
||||
OCI_TENANCY: "/data/attributes/secret/tenancy",
|
||||
OCI_REGION: "/data/attributes/secret/region",
|
||||
OCI_PASS_PHRASE: "/data/attributes/secret/pass_phrase",
|
||||
ATLAS_PUBLIC_KEY: "/data/attributes/secret/atlas_public_key",
|
||||
ATLAS_PRIVATE_KEY: "/data/attributes/secret/atlas_private_key",
|
||||
} as const;
|
||||
|
||||
export type ErrorPointer = (typeof ErrorPointers)[keyof typeof ErrorPointers];
|
||||
|
||||
@@ -264,6 +264,12 @@ export type OCICredentials = {
|
||||
[ProviderCredentialFields.PROVIDER_ID]: string;
|
||||
};
|
||||
|
||||
export type MongoDBAtlasCredentials = {
|
||||
[ProviderCredentialFields.ATLAS_PUBLIC_KEY]: string;
|
||||
[ProviderCredentialFields.ATLAS_PRIVATE_KEY]: string;
|
||||
[ProviderCredentialFields.PROVIDER_ID]: string;
|
||||
};
|
||||
|
||||
export type CredentialsFormSchema =
|
||||
| AWSCredentials
|
||||
| AzureCredentials
|
||||
@@ -272,7 +278,8 @@ export type CredentialsFormSchema =
|
||||
| KubernetesCredentials
|
||||
| IacCredentials
|
||||
| M365Credentials
|
||||
| OCICredentials;
|
||||
| OCICredentials
|
||||
| MongoDBAtlasCredentials;
|
||||
|
||||
export interface SearchParamsProps {
|
||||
[key: string]: string | string[] | undefined;
|
||||
|
||||
@@ -120,6 +120,11 @@ export const addProviderFormSchema = z
|
||||
[ProviderCredentialFields.PROVIDER_ALIAS]: z.string(),
|
||||
providerUid: z.string(),
|
||||
}),
|
||||
z.object({
|
||||
providerType: z.literal("mongodbatlas"),
|
||||
[ProviderCredentialFields.PROVIDER_ALIAS]: z.string(),
|
||||
providerUid: z.string(),
|
||||
}),
|
||||
]),
|
||||
);
|
||||
|
||||
@@ -231,7 +236,16 @@ export const addCredentialsFormSchema = (
|
||||
.union([z.string(), z.literal("")])
|
||||
.optional(),
|
||||
}
|
||||
: {}),
|
||||
: providerType === "mongodbatlas"
|
||||
? {
|
||||
[ProviderCredentialFields.ATLAS_PUBLIC_KEY]: z
|
||||
.string()
|
||||
.min(1, "Atlas Public Key is required"),
|
||||
[ProviderCredentialFields.ATLAS_PRIVATE_KEY]: z
|
||||
.string()
|
||||
.min(1, "Atlas Private Key is required"),
|
||||
}
|
||||
: {}),
|
||||
})
|
||||
.superRefine((data: Record<string, any>, ctx) => {
|
||||
if (providerType === "m365") {
|
||||
|
||||
@@ -4,6 +4,7 @@ export const PROVIDER_TYPES = [
|
||||
"gcp",
|
||||
"kubernetes",
|
||||
"m365",
|
||||
"mongodbatlas",
|
||||
"github",
|
||||
"iac",
|
||||
"oraclecloud",
|
||||
@@ -17,6 +18,7 @@ export const PROVIDER_DISPLAY_NAMES: Record<ProviderType, string> = {
|
||||
gcp: "Google Cloud",
|
||||
kubernetes: "Kubernetes",
|
||||
m365: "Microsoft 365",
|
||||
mongodbatlas: "MongoDB Atlas",
|
||||
github: "GitHub",
|
||||
iac: "Infrastructure as Code",
|
||||
oraclecloud: "Oracle Cloud Infrastructure",
|
||||
@@ -85,27 +87,6 @@ export interface ProviderConnectionStatus {
|
||||
value: string;
|
||||
}
|
||||
|
||||
export interface ProviderOverviewProps {
|
||||
data: {
|
||||
type: "provider-overviews";
|
||||
id: ProviderType;
|
||||
attributes: {
|
||||
findings: {
|
||||
pass: number;
|
||||
fail: number;
|
||||
manual: number;
|
||||
total: number;
|
||||
};
|
||||
resources: {
|
||||
total: number;
|
||||
};
|
||||
};
|
||||
}[];
|
||||
meta: {
|
||||
version: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface ProvidersApiResponse {
|
||||
links: {
|
||||
first: string;
|
||||
|
||||
Reference in New Issue
Block a user