mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-07-04 19:21:51 +00:00
feat(ui): add Oracle Cloud Infrastructure (OCI) provider support (#8984)
This commit is contained in:
@@ -88,7 +88,7 @@ prowler dashboard
|
||||
| Kubernetes | 83 | 7 | 5 | 7 | Official | Stable | UI, API, CLI |
|
||||
| GitHub | 17 | 2 | 1 | 0 | Official | Stable | UI, API, CLI |
|
||||
| M365 | 70 | 7 | 3 | 2 | Official | Stable | UI, API, CLI |
|
||||
| OCI | 51 | 13 | 1 | 10 | Official | Stable | API, CLI |
|
||||
| OCI | 51 | 13 | 1 | 10 | Official | Stable | UI, API, CLI |
|
||||
| IaC | [See `trivy` docs.](https://trivy.dev/latest/docs/coverage/iac/) | N/A | N/A | N/A | Official | Beta | CLI |
|
||||
| MongoDB Atlas | 10 | 3 | 0 | 0 | Official | Beta | CLI |
|
||||
| LLM | [See `promptfoo` docs.](https://www.promptfoo.dev/docs/red-team/plugins/) | N/A | N/A | N/A | Official | Beta | CLI |
|
||||
|
||||
@@ -31,7 +31,7 @@ The supported providers right now are:
|
||||
| [Kubernetes](/user-guide/providers/kubernetes/in-cluster) | Official | UI, API, CLI |
|
||||
| [M365](/user-guide/providers/microsoft365/getting-started-m365) | Official | UI, API, CLI |
|
||||
| [Github](/user-guide/providers/github/getting-started-github) | Official | UI, API, CLI |
|
||||
| [Oracle Cloud](/user-guide/providers/oci/getting-started-oci) | Official | CLI, API |
|
||||
| [Oracle Cloud](/user-guide/providers/oci/getting-started-oci) | Official | UI, API, CLI |
|
||||
| [Infra as Code](/user-guide/providers/iac/getting-started-iac) | Official | CLI |
|
||||
| [MongoDB Atlas](/user-guide/providers/mongodbatlas/getting-started-mongodbatlas) | Official | CLI |
|
||||
| [LLM](/user-guide/providers/llm/getting-started-llm) | Official | CLI |
|
||||
|
||||
@@ -7,6 +7,7 @@ All notable changes to the **Prowler UI** are documented in this file.
|
||||
### 🔄 Changed
|
||||
|
||||
- Upgrade React to version 19.2.0 [(#9039)](https://github.com/prowler-cloud/prowler/pull/9039)
|
||||
- Add support for Oracle Cloud Infrastructure (OCI) provider [(#8984)](https://github.com/prowler-cloud/prowler/pull/8984)
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -133,9 +133,17 @@ export const addCredentialsProvider = async (formData: FormData) => {
|
||||
formData,
|
||||
ProviderCredentialFields.PROVIDER_TYPE,
|
||||
) as ProviderType;
|
||||
const providerUid = getFormValue(
|
||||
formData,
|
||||
ProviderCredentialFields.PROVIDER_UID,
|
||||
) as string | undefined;
|
||||
|
||||
try {
|
||||
const { secretType, secret } = buildSecretConfig(formData, providerType);
|
||||
const { secretType, secret } = buildSecretConfig(
|
||||
formData,
|
||||
providerType,
|
||||
providerUid,
|
||||
);
|
||||
|
||||
const response = await fetch(url.toString(), {
|
||||
method: "POST",
|
||||
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
GitHubProviderBadge,
|
||||
KS8ProviderBadge,
|
||||
M365ProviderBadge,
|
||||
OracleCloudProviderBadge,
|
||||
} from "@/components/icons/providers-badge";
|
||||
import {
|
||||
Select,
|
||||
@@ -27,6 +28,7 @@ const PROVIDER_ICON: Record<ProviderType, ReactNode> = {
|
||||
kubernetes: <KS8ProviderBadge width={18} height={18} />,
|
||||
m365: <M365ProviderBadge width={18} height={18} />,
|
||||
github: <GitHubProviderBadge width={18} height={18} />,
|
||||
oci: <OracleCloudProviderBadge width={18} height={18} />,
|
||||
};
|
||||
|
||||
interface AccountsSelectorProps {
|
||||
|
||||
@@ -42,6 +42,11 @@ const GitHubProviderBadge = lazy(() =>
|
||||
default: m.GitHubProviderBadge,
|
||||
})),
|
||||
);
|
||||
const OracleCloudProviderBadge = lazy(() =>
|
||||
import("@/components/icons/providers-badge").then((m) => ({
|
||||
default: m.OracleCloudProviderBadge,
|
||||
})),
|
||||
);
|
||||
|
||||
type IconProps = { width: number; height: number };
|
||||
|
||||
@@ -77,6 +82,10 @@ const PROVIDER_DATA: Record<
|
||||
label: "GitHub",
|
||||
icon: GitHubProviderBadge,
|
||||
},
|
||||
oci: {
|
||||
label: "Oracle Cloud Infrastructure",
|
||||
icon: OracleCloudProviderBadge,
|
||||
},
|
||||
};
|
||||
|
||||
type ProviderTypeSelectorProps = {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import React from "react";
|
||||
|
||||
import { getProvider } from "@/actions/providers/providers";
|
||||
import {
|
||||
AddViaCredentialsForm,
|
||||
AddViaRoleForm,
|
||||
@@ -20,9 +21,20 @@ interface Props {
|
||||
|
||||
export default async function AddCredentialsPage({ searchParams }: Props) {
|
||||
const resolvedSearchParams = await searchParams;
|
||||
const { type: providerType, via } = resolvedSearchParams;
|
||||
const { type: providerType, via, id: providerId } = resolvedSearchParams;
|
||||
const formType = getProviderFormType(providerType, via);
|
||||
|
||||
// Fetch provider data to get the UID (needed for OCI)
|
||||
let providerUid: string | undefined;
|
||||
if (providerId) {
|
||||
const formData = new FormData();
|
||||
formData.append("id", providerId);
|
||||
const providerResponse = await getProvider(formData);
|
||||
if (providerResponse.success && providerResponse.data) {
|
||||
providerUid = providerResponse.data.attributes?.uid;
|
||||
}
|
||||
}
|
||||
|
||||
switch (formType) {
|
||||
case "selector":
|
||||
if (providerType === "aws") return <SelectViaAWS initialVia={via} />;
|
||||
@@ -33,13 +45,28 @@ export default async function AddCredentialsPage({ searchParams }: Props) {
|
||||
return null;
|
||||
|
||||
case "credentials":
|
||||
return <AddViaCredentialsForm searchParams={resolvedSearchParams} />;
|
||||
return (
|
||||
<AddViaCredentialsForm
|
||||
searchParams={resolvedSearchParams}
|
||||
providerUid={providerUid}
|
||||
/>
|
||||
);
|
||||
|
||||
case "role":
|
||||
return <AddViaRoleForm searchParams={resolvedSearchParams} />;
|
||||
return (
|
||||
<AddViaRoleForm
|
||||
searchParams={resolvedSearchParams}
|
||||
providerUid={providerUid}
|
||||
/>
|
||||
);
|
||||
|
||||
case "service-account":
|
||||
return <AddViaServiceAccountForm searchParams={resolvedSearchParams} />;
|
||||
return (
|
||||
<AddViaServiceAccountForm
|
||||
searchParams={resolvedSearchParams}
|
||||
providerUid={providerUid}
|
||||
/>
|
||||
);
|
||||
|
||||
default:
|
||||
return null;
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
GitHubProviderBadge,
|
||||
KS8ProviderBadge,
|
||||
M365ProviderBadge,
|
||||
OracleCloudProviderBadge,
|
||||
} from "../icons/providers-badge";
|
||||
|
||||
export const CustomProviderInputAWS = () => {
|
||||
@@ -62,3 +63,12 @@ export const CustomProviderInputGitHub = () => {
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const CustomProviderInputOracleCloud = () => {
|
||||
return (
|
||||
<div className="flex items-center gap-x-2">
|
||||
<OracleCloudProviderBadge width={25} height={25} />
|
||||
<p className="text-sm">Oracle Cloud Infrastructure</p>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
CustomProviderInputGitHub,
|
||||
CustomProviderInputKubernetes,
|
||||
CustomProviderInputM365,
|
||||
CustomProviderInputOracleCloud,
|
||||
} from "./custom-provider-inputs";
|
||||
|
||||
const providerDisplayData: Record<
|
||||
@@ -43,6 +44,10 @@ const providerDisplayData: Record<
|
||||
label: "GitHub",
|
||||
component: <CustomProviderInputGitHub />,
|
||||
},
|
||||
oci: {
|
||||
label: "Oracle Cloud Infrastructure",
|
||||
component: <CustomProviderInputOracleCloud />,
|
||||
},
|
||||
};
|
||||
|
||||
const dataInputsProvider = PROVIDER_TYPES.map((providerType) => ({
|
||||
|
||||
@@ -4,3 +4,4 @@ export * from "./gcp-provider-badge";
|
||||
export * from "./github-provider-badge";
|
||||
export * from "./ks8-provider-badge";
|
||||
export * from "./m365-provider-badge";
|
||||
export * from "./oraclecloud-provider-badge";
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
import * as React from "react";
|
||||
|
||||
import { IconSvgProps } from "@/types";
|
||||
|
||||
export const OracleCloudProviderBadge: 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="#f4f2ed" rx="60" />
|
||||
<path
|
||||
fill="#c74634"
|
||||
d="M 56 128
|
||||
C 56 101.5 87.2 80 128 80
|
||||
C 168.8 80 200 101.5 200 128
|
||||
C 200 154.5 168.8 176 128 176
|
||||
C 87.2 176 56 154.5 56 128 Z
|
||||
M 72 128
|
||||
C 72 145.7 96.5 160 128 160
|
||||
C 159.5 160 184 145.7 184 128
|
||||
C 184 110.3 159.5 96 128 96
|
||||
C 96.5 96 72 110.3 72 128 Z"
|
||||
fillRule="evenodd"
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
);
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
GitHubProviderBadge,
|
||||
KS8ProviderBadge,
|
||||
M365ProviderBadge,
|
||||
OracleCloudProviderBadge,
|
||||
} from "@/components/icons/providers-badge";
|
||||
import { CustomButton } from "@/components/ui/custom/custom-button";
|
||||
import { ProviderOverviewProps } from "@/types";
|
||||
@@ -37,6 +38,8 @@ export const ProvidersOverview = ({
|
||||
return <KS8ProviderBadge width={30} height={30} />;
|
||||
case "github":
|
||||
return <GitHubProviderBadge width={30} height={30} />;
|
||||
case "oci":
|
||||
return <OracleCloudProviderBadge width={30} height={30} />;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
@@ -49,6 +52,7 @@ export const ProvidersOverview = ({
|
||||
gcp: "GCP",
|
||||
kubernetes: "Kubernetes",
|
||||
github: "GitHub",
|
||||
oci: "OCI",
|
||||
};
|
||||
|
||||
const providers = PROVIDER_TYPES.map((providerType) => ({
|
||||
|
||||
@@ -17,6 +17,7 @@ const providerTypeLabels: Record<ProviderType, string> = {
|
||||
m365: "Microsoft 365",
|
||||
kubernetes: "Kubernetes",
|
||||
github: "GitHub",
|
||||
oci: "Oracle Cloud Infrastructure",
|
||||
};
|
||||
|
||||
interface EnhancedProviderSelectorProps {
|
||||
|
||||
@@ -14,6 +14,7 @@ import {
|
||||
GitHubProviderBadge,
|
||||
KS8ProviderBadge,
|
||||
M365ProviderBadge,
|
||||
OracleCloudProviderBadge,
|
||||
} from "../icons/providers-badge";
|
||||
import { CustomRadio } from "../ui/custom";
|
||||
import { FormMessage } from "../ui/form";
|
||||
@@ -78,6 +79,15 @@ export const RadioGroupProvider: React.FC<RadioGroupProviderProps> = ({
|
||||
<span className="ml-2">GitHub</span>
|
||||
</div>
|
||||
</CustomRadio>
|
||||
<CustomRadio
|
||||
description="Oracle Cloud Infrastructure"
|
||||
value="oci"
|
||||
>
|
||||
<div className="flex items-center">
|
||||
<OracleCloudProviderBadge size={26} />
|
||||
<span className="ml-2">Oracle Cloud Infrastructure</span>
|
||||
</div>
|
||||
</CustomRadio>
|
||||
</div>
|
||||
</RadioGroup>
|
||||
{errorMessage && (
|
||||
|
||||
@@ -7,8 +7,10 @@ import { BaseCredentialsForm } from "./base-credentials-form";
|
||||
|
||||
export const AddViaCredentialsForm = ({
|
||||
searchParams,
|
||||
providerUid,
|
||||
}: {
|
||||
searchParams: { type: string; id: string };
|
||||
providerUid?: string;
|
||||
}) => {
|
||||
const providerType = searchParams.type as ProviderType;
|
||||
const providerId = searchParams.id;
|
||||
@@ -23,6 +25,7 @@ export const AddViaCredentialsForm = ({
|
||||
<BaseCredentialsForm
|
||||
providerType={providerType}
|
||||
providerId={providerId}
|
||||
providerUid={providerUid}
|
||||
onSubmit={handleAddCredentials}
|
||||
successNavigationUrl={successNavigationUrl}
|
||||
submitButtonText="Next"
|
||||
|
||||
@@ -7,8 +7,10 @@ import { BaseCredentialsForm } from "./base-credentials-form";
|
||||
|
||||
export const AddViaRoleForm = ({
|
||||
searchParams,
|
||||
providerUid,
|
||||
}: {
|
||||
searchParams: { type: string; id: string };
|
||||
providerUid?: string;
|
||||
}) => {
|
||||
const providerType = searchParams.type as ProviderType;
|
||||
const providerId = searchParams.id;
|
||||
@@ -23,6 +25,7 @@ export const AddViaRoleForm = ({
|
||||
<BaseCredentialsForm
|
||||
providerType={providerType}
|
||||
providerId={providerId}
|
||||
providerUid={providerUid}
|
||||
onSubmit={handleAddCredentials}
|
||||
successNavigationUrl={successNavigationUrl}
|
||||
submitButtonText="Next"
|
||||
|
||||
@@ -19,6 +19,7 @@ import {
|
||||
KubernetesCredentials,
|
||||
M365CertificateCredentials,
|
||||
M365ClientSecretCredentials,
|
||||
OCICredentials,
|
||||
ProviderType,
|
||||
} from "@/types";
|
||||
|
||||
@@ -34,10 +35,12 @@ import {
|
||||
import { AzureCredentialsForm } from "./via-credentials/azure-credentials-form";
|
||||
import { GitHubCredentialsForm } from "./via-credentials/github-credentials-form";
|
||||
import { KubernetesCredentialsForm } from "./via-credentials/k8s-credentials-form";
|
||||
import { OracleCloudCredentialsForm } from "./via-credentials/oraclecloud-credentials-form";
|
||||
|
||||
type BaseCredentialsFormProps = {
|
||||
providerType: ProviderType;
|
||||
providerId: string;
|
||||
providerUid?: string;
|
||||
onSubmit: (formData: FormData) => Promise<any>;
|
||||
successNavigationUrl: string;
|
||||
submitButtonText?: string;
|
||||
@@ -47,6 +50,7 @@ type BaseCredentialsFormProps = {
|
||||
export const BaseCredentialsForm = ({
|
||||
providerType,
|
||||
providerId,
|
||||
providerUid,
|
||||
onSubmit,
|
||||
successNavigationUrl,
|
||||
submitButtonText = "Next",
|
||||
@@ -84,6 +88,13 @@ export const BaseCredentialsForm = ({
|
||||
name={ProviderCredentialFields.PROVIDER_TYPE}
|
||||
value={providerType}
|
||||
/>
|
||||
{providerUid && (
|
||||
<input
|
||||
type="hidden"
|
||||
name={ProviderCredentialFields.PROVIDER_UID}
|
||||
value={providerUid}
|
||||
/>
|
||||
)}
|
||||
|
||||
<ProviderTitleDocs providerType={providerType} />
|
||||
|
||||
@@ -148,6 +159,11 @@ export const BaseCredentialsForm = ({
|
||||
credentialsType={searchParamsObj.get("via") || undefined}
|
||||
/>
|
||||
)}
|
||||
{providerType === "oci" && (
|
||||
<OracleCloudCredentialsForm
|
||||
control={form.control as unknown as Control<OCICredentials>}
|
||||
/>
|
||||
)}
|
||||
|
||||
<div className="flex w-full justify-end sm:gap-6">
|
||||
{showBackButton && requiresBackButton(searchParamsObj.get("via")) && (
|
||||
|
||||
@@ -51,6 +51,11 @@ const getProviderFieldDetails = (providerType?: ProviderType) => {
|
||||
label: "Username",
|
||||
placeholder: "e.g. your-github-username",
|
||||
};
|
||||
case "oci":
|
||||
return {
|
||||
label: "Tenancy OCID",
|
||||
placeholder: "e.g. ocid1.tenancy.oc1..aaaaaaa...",
|
||||
};
|
||||
default:
|
||||
return {
|
||||
label: "Provider UID",
|
||||
|
||||
+3
@@ -7,8 +7,10 @@ import { BaseCredentialsForm } from "../../base-credentials-form";
|
||||
|
||||
export const AddViaServiceAccountForm = ({
|
||||
searchParams,
|
||||
providerUid,
|
||||
}: {
|
||||
searchParams: { type: ProviderType; id: string };
|
||||
providerUid?: string;
|
||||
}) => {
|
||||
const providerType = searchParams.type;
|
||||
const providerId = searchParams.id;
|
||||
@@ -23,6 +25,7 @@ export const AddViaServiceAccountForm = ({
|
||||
<BaseCredentialsForm
|
||||
providerType={providerType}
|
||||
providerId={providerId}
|
||||
providerUid={providerUid}
|
||||
onSubmit={handleAddCredentials}
|
||||
successNavigationUrl={successNavigationUrl}
|
||||
submitButtonText="Next"
|
||||
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
import { Control } from "react-hook-form";
|
||||
|
||||
import { CustomInput, CustomTextarea } from "@/components/ui/custom";
|
||||
import { ProviderCredentialFields } from "@/lib/provider-credentials/provider-credential-fields";
|
||||
import { OCICredentials } from "@/types";
|
||||
|
||||
export const OracleCloudCredentialsForm = ({
|
||||
control,
|
||||
}: {
|
||||
control: Control<OCICredentials>;
|
||||
}) => {
|
||||
return (
|
||||
<>
|
||||
<div className="flex flex-col">
|
||||
<div className="text-md text-default-foreground leading-9 font-bold">
|
||||
Connect via API Key
|
||||
</div>
|
||||
<div className="text-default-500 text-sm">
|
||||
Please provide your Oracle Cloud Infrastructure API key credentials.
|
||||
</div>
|
||||
</div>
|
||||
<CustomInput
|
||||
control={control}
|
||||
name={ProviderCredentialFields.OCI_USER}
|
||||
type="text"
|
||||
label="User OCID"
|
||||
labelPlacement="inside"
|
||||
placeholder="ocid1.user.oc1..aaaaaaa..."
|
||||
variant="bordered"
|
||||
isRequired
|
||||
isInvalid={!!control._formState.errors.user}
|
||||
/>
|
||||
<CustomInput
|
||||
control={control}
|
||||
name={ProviderCredentialFields.OCI_FINGERPRINT}
|
||||
type="text"
|
||||
label="Fingerprint"
|
||||
labelPlacement="inside"
|
||||
placeholder="Enter the API key fingerprint"
|
||||
variant="bordered"
|
||||
isRequired
|
||||
isInvalid={!!control._formState.errors.fingerprint}
|
||||
/>
|
||||
<CustomInput
|
||||
control={control}
|
||||
name={ProviderCredentialFields.OCI_REGION}
|
||||
type="text"
|
||||
label="Region"
|
||||
labelPlacement="inside"
|
||||
placeholder="e.g. us-ashburn-1"
|
||||
variant="bordered"
|
||||
isRequired
|
||||
isInvalid={!!control._formState.errors.region}
|
||||
/>
|
||||
<CustomTextarea
|
||||
control={control}
|
||||
name={ProviderCredentialFields.OCI_KEY_CONTENT}
|
||||
label="Private Key Content"
|
||||
labelPlacement="inside"
|
||||
placeholder="-----BEGIN RSA PRIVATE KEY----- MIIEpAIBAAKCAQEA... -----END RSA PRIVATE KEY-----"
|
||||
variant="bordered"
|
||||
minRows={6}
|
||||
isRequired
|
||||
isInvalid={!!control._formState.errors.key_content}
|
||||
/>
|
||||
<CustomInput
|
||||
control={control}
|
||||
name={ProviderCredentialFields.OCI_PASS_PHRASE}
|
||||
type="password"
|
||||
label="Passphrase (Optional)"
|
||||
labelPlacement="inside"
|
||||
placeholder="Enter passphrase if key is encrypted"
|
||||
variant="bordered"
|
||||
isRequired={false}
|
||||
isInvalid={!!control._formState.errors.pass_phrase}
|
||||
/>
|
||||
<div className="text-default-400 text-xs">
|
||||
Paste the raw content of your OCI private key file (PEM format). The key
|
||||
will be automatically encoded for secure transmission.
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
GitHubProviderBadge,
|
||||
KS8ProviderBadge,
|
||||
M365ProviderBadge,
|
||||
OracleCloudProviderBadge,
|
||||
} from "@/components/icons/providers-badge";
|
||||
import { ProviderType } from "@/types";
|
||||
|
||||
@@ -24,6 +25,8 @@ export const getProviderLogo = (provider: ProviderType) => {
|
||||
return <M365ProviderBadge width={35} height={35} />;
|
||||
case "github":
|
||||
return <GitHubProviderBadge width={35} height={35} />;
|
||||
case "oci":
|
||||
return <OracleCloudProviderBadge width={35} height={35} />;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
@@ -43,6 +46,8 @@ export const getProviderName = (provider: ProviderType): string => {
|
||||
return "Microsoft 365";
|
||||
case "github":
|
||||
return "GitHub";
|
||||
case "oci":
|
||||
return "Oracle Cloud Infrastructure";
|
||||
default:
|
||||
return "Unknown Provider";
|
||||
}
|
||||
|
||||
@@ -155,6 +155,15 @@ export const useCredentialsForm = ({
|
||||
};
|
||||
}
|
||||
return baseDefaults;
|
||||
case "oci":
|
||||
return {
|
||||
...baseDefaults,
|
||||
[ProviderCredentialFields.OCI_USER]: "",
|
||||
[ProviderCredentialFields.OCI_FINGERPRINT]: "",
|
||||
[ProviderCredentialFields.OCI_KEY_CONTENT]: "",
|
||||
[ProviderCredentialFields.OCI_REGION]: "",
|
||||
[ProviderCredentialFields.OCI_PASS_PHRASE]: "",
|
||||
};
|
||||
default:
|
||||
return baseDefaults;
|
||||
}
|
||||
|
||||
@@ -32,6 +32,11 @@ export const getProviderHelpText = (provider: string) => {
|
||||
text: "Need help connecting your GitHub account?",
|
||||
link: "https://goto.prowler.com/provider-github",
|
||||
};
|
||||
case "oci":
|
||||
return {
|
||||
text: "Need help connecting your Oracle Cloud account?",
|
||||
link: "https://goto.prowler.com/provider-oci",
|
||||
};
|
||||
default:
|
||||
return {
|
||||
text: "How to setup a provider?",
|
||||
|
||||
@@ -197,10 +197,65 @@ export const buildGitHubSecret = (formData: FormData) => {
|
||||
return {};
|
||||
};
|
||||
|
||||
/**
|
||||
* Utility function to safely encode a string to base64
|
||||
* Handles UTF-8 characters properly without using deprecated APIs
|
||||
*/
|
||||
const base64Encode = (str: string): string => {
|
||||
if (!str) return "";
|
||||
// Convert string to UTF-8 bytes, then to base64
|
||||
const utf8Bytes = new TextEncoder().encode(str);
|
||||
// Convert Uint8Array to binary string without spread operator
|
||||
let binaryString = "";
|
||||
for (let i = 0; i < utf8Bytes.length; i++) {
|
||||
binaryString += String.fromCharCode(utf8Bytes[i]);
|
||||
}
|
||||
return btoa(binaryString);
|
||||
};
|
||||
|
||||
export const buildOracleCloudSecret = (
|
||||
formData: FormData,
|
||||
providerUid?: string,
|
||||
) => {
|
||||
const keyContent = getFormValue(
|
||||
formData,
|
||||
ProviderCredentialFields.OCI_KEY_CONTENT,
|
||||
) as string;
|
||||
|
||||
// Base64 encode the key content for the backend
|
||||
// Uses modern TextEncoder API to properly handle UTF-8 characters
|
||||
const encodedKeyContent = base64Encode(keyContent);
|
||||
|
||||
const secret = {
|
||||
[ProviderCredentialFields.OCI_USER]: getFormValue(
|
||||
formData,
|
||||
ProviderCredentialFields.OCI_USER,
|
||||
),
|
||||
[ProviderCredentialFields.OCI_FINGERPRINT]: getFormValue(
|
||||
formData,
|
||||
ProviderCredentialFields.OCI_FINGERPRINT,
|
||||
),
|
||||
[ProviderCredentialFields.OCI_KEY_CONTENT]: encodedKeyContent,
|
||||
[ProviderCredentialFields.OCI_TENANCY]:
|
||||
providerUid ||
|
||||
getFormValue(formData, ProviderCredentialFields.OCI_TENANCY),
|
||||
[ProviderCredentialFields.OCI_REGION]: getFormValue(
|
||||
formData,
|
||||
ProviderCredentialFields.OCI_REGION,
|
||||
),
|
||||
[ProviderCredentialFields.OCI_PASS_PHRASE]: getFormValue(
|
||||
formData,
|
||||
ProviderCredentialFields.OCI_PASS_PHRASE,
|
||||
),
|
||||
};
|
||||
return filterEmptyValues(secret);
|
||||
};
|
||||
|
||||
// Main function to build secret configuration
|
||||
export const buildSecretConfig = (
|
||||
formData: FormData,
|
||||
providerType: ProviderType,
|
||||
providerUid?: string,
|
||||
) => {
|
||||
const isRole = formData.get(ProviderCredentialFields.ROLE_ARN) !== null;
|
||||
const isServiceAccount =
|
||||
@@ -231,6 +286,10 @@ export const buildSecretConfig = (
|
||||
secretType: "static",
|
||||
secret: buildGitHubSecret(formData),
|
||||
}),
|
||||
oci: () => ({
|
||||
secretType: "static",
|
||||
secret: buildOracleCloudSecret(formData, providerUid),
|
||||
}),
|
||||
};
|
||||
|
||||
const builder = secretBuilders[providerType];
|
||||
|
||||
@@ -13,6 +13,7 @@ export const ProviderCredentialFields = {
|
||||
PROVIDER_ID: "providerId",
|
||||
PROVIDER_TYPE: "providerType",
|
||||
PROVIDER_ALIAS: "providerAlias",
|
||||
PROVIDER_UID: "providerUid",
|
||||
|
||||
// AWS fields
|
||||
AWS_ACCESS_KEY_ID: "aws_access_key_id",
|
||||
@@ -43,6 +44,15 @@ export const ProviderCredentialFields = {
|
||||
OAUTH_APP_TOKEN: "oauth_app_token",
|
||||
GITHUB_APP_ID: "github_app_id",
|
||||
GITHUB_APP_KEY: "github_app_key_content",
|
||||
|
||||
// OCI fields
|
||||
OCI_USER: "user",
|
||||
OCI_FINGERPRINT: "fingerprint",
|
||||
OCI_KEY_FILE: "key_file",
|
||||
OCI_KEY_CONTENT: "key_content",
|
||||
OCI_TENANCY: "tenancy",
|
||||
OCI_REGION: "region",
|
||||
OCI_PASS_PHRASE: "pass_phrase",
|
||||
} as const;
|
||||
|
||||
// Type for credential field values
|
||||
@@ -72,6 +82,13 @@ export const ErrorPointers = {
|
||||
GITHUB_APP_ID: "/data/attributes/secret/github_app_id",
|
||||
GITHUB_APP_KEY: "/data/attributes/secret/github_app_key_content",
|
||||
CERTIFICATE_CONTENT: "/data/attributes/secret/certificate_content",
|
||||
OCI_USER: "/data/attributes/secret/user",
|
||||
OCI_FINGERPRINT: "/data/attributes/secret/fingerprint",
|
||||
OCI_KEY_FILE: "/data/attributes/secret/key_file",
|
||||
OCI_KEY_CONTENT: "/data/attributes/secret/key_content",
|
||||
OCI_TENANCY: "/data/attributes/secret/tenancy",
|
||||
OCI_REGION: "/data/attributes/secret/region",
|
||||
OCI_PASS_PHRASE: "/data/attributes/secret/pass_phrase",
|
||||
} as const;
|
||||
|
||||
export type ErrorPointer = (typeof ErrorPointers)[keyof typeof ErrorPointers];
|
||||
|
||||
Generated
+2
@@ -70,10 +70,12 @@
|
||||
"@iconify/react": "5.2.1",
|
||||
"@playwright/test": "1.56.1",
|
||||
"@types/d3": "7.4.3",
|
||||
"@types/geojson": "7946.0.16",
|
||||
"@types/node": "20.5.7",
|
||||
"@types/react": "19.1.13",
|
||||
"@types/react-dom": "19.1.9",
|
||||
"@types/topojson-client": "3.1.5",
|
||||
"@types/topojson-specification": "1.0.5",
|
||||
"@types/uuid": "10.0.0",
|
||||
"@typescript-eslint/eslint-plugin": "7.18.0",
|
||||
"@typescript-eslint/parser": "7.18.0",
|
||||
|
||||
+3
-1
@@ -82,12 +82,14 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@iconify/react": "5.2.1",
|
||||
"@types/d3": "7.4.3",
|
||||
"@playwright/test": "1.56.1",
|
||||
"@types/d3": "7.4.3",
|
||||
"@types/geojson": "7946.0.16",
|
||||
"@types/node": "20.5.7",
|
||||
"@types/react": "19.1.13",
|
||||
"@types/react-dom": "19.1.9",
|
||||
"@types/topojson-client": "3.1.5",
|
||||
"@types/topojson-specification": "1.0.5",
|
||||
"@types/uuid": "10.0.0",
|
||||
"@typescript-eslint/eslint-plugin": "7.18.0",
|
||||
"@typescript-eslint/parser": "7.18.0",
|
||||
|
||||
+11
-1
@@ -248,13 +248,23 @@ export type KubernetesCredentials = {
|
||||
[ProviderCredentialFields.PROVIDER_ID]: string;
|
||||
};
|
||||
|
||||
export type OCICredentials = {
|
||||
[ProviderCredentialFields.OCI_USER]: string;
|
||||
[ProviderCredentialFields.OCI_FINGERPRINT]: string;
|
||||
[ProviderCredentialFields.OCI_KEY_CONTENT]: string;
|
||||
[ProviderCredentialFields.OCI_REGION]: string;
|
||||
[ProviderCredentialFields.OCI_PASS_PHRASE]?: string;
|
||||
[ProviderCredentialFields.PROVIDER_ID]: string;
|
||||
};
|
||||
|
||||
export type CredentialsFormSchema =
|
||||
| AWSCredentials
|
||||
| AzureCredentials
|
||||
| GCPDefaultCredentials
|
||||
| GCPServiceAccountKey
|
||||
| KubernetesCredentials
|
||||
| M365Credentials;
|
||||
| M365Credentials
|
||||
| OCICredentials;
|
||||
|
||||
export interface SearchParamsProps {
|
||||
[key: string]: string | string[] | undefined;
|
||||
|
||||
+24
-1
@@ -110,6 +110,11 @@ export const addProviderFormSchema = z
|
||||
[ProviderCredentialFields.PROVIDER_ALIAS]: z.string(),
|
||||
providerUid: z.string(),
|
||||
}),
|
||||
z.object({
|
||||
providerType: z.literal("oci"),
|
||||
[ProviderCredentialFields.PROVIDER_ALIAS]: z.string(),
|
||||
providerUid: z.string(),
|
||||
}),
|
||||
]),
|
||||
);
|
||||
|
||||
@@ -191,7 +196,25 @@ export const addCredentialsFormSchema = (
|
||||
.string()
|
||||
.optional(),
|
||||
}
|
||||
: {}),
|
||||
: providerType === "oci"
|
||||
? {
|
||||
[ProviderCredentialFields.OCI_USER]: z
|
||||
.string()
|
||||
.min(1, "User OCID is required"),
|
||||
[ProviderCredentialFields.OCI_FINGERPRINT]: z
|
||||
.string()
|
||||
.min(1, "Fingerprint is required"),
|
||||
[ProviderCredentialFields.OCI_KEY_CONTENT]: z
|
||||
.string()
|
||||
.min(1, "Private Key Content is required"),
|
||||
[ProviderCredentialFields.OCI_REGION]: z
|
||||
.string()
|
||||
.min(1, "Region is required"),
|
||||
[ProviderCredentialFields.OCI_PASS_PHRASE]: z
|
||||
.union([z.string(), z.literal("")])
|
||||
.optional(),
|
||||
}
|
||||
: {}),
|
||||
})
|
||||
.superRefine((data: Record<string, any>, ctx) => {
|
||||
if (providerType === "m365") {
|
||||
|
||||
@@ -5,6 +5,7 @@ export const PROVIDER_TYPES = [
|
||||
"kubernetes",
|
||||
"m365",
|
||||
"github",
|
||||
"oci",
|
||||
] as const;
|
||||
|
||||
export type ProviderType = (typeof PROVIDER_TYPES)[number];
|
||||
|
||||
Reference in New Issue
Block a user