diff --git a/backend/src/ee/routes/v1/audit-log-stream-routers/audit-log-stream-router.ts b/backend/src/ee/routes/v1/audit-log-stream-routers/audit-log-stream-router.ts index e0ac0b6af..48eed14c9 100644 --- a/backend/src/ee/routes/v1/audit-log-stream-routers/audit-log-stream-router.ts +++ b/backend/src/ee/routes/v1/audit-log-stream-routers/audit-log-stream-router.ts @@ -1,5 +1,9 @@ import { z } from "zod"; +import { + AzureProviderListItemSchema, + SanitizedAzureProviderSchema +} from "@app/ee/services/audit-log-stream/azure/azure-provider-schemas"; import { CriblProviderListItemSchema, SanitizedCriblProviderSchema @@ -24,6 +28,7 @@ const SanitizedAuditLogStreamSchema = z.union([ SanitizedCustomProviderSchema, SanitizedDatadogProviderSchema, SanitizedSplunkProviderSchema, + SanitizedAzureProviderSchema, SanitizedCriblProviderSchema ]); @@ -31,6 +36,7 @@ const ProviderOptionsSchema = z.discriminatedUnion("provider", [ CustomProviderListItemSchema, DatadogProviderListItemSchema, SplunkProviderListItemSchema, + AzureProviderListItemSchema, CriblProviderListItemSchema ]); diff --git a/backend/src/ee/routes/v1/audit-log-stream-routers/index.ts b/backend/src/ee/routes/v1/audit-log-stream-routers/index.ts index f40a82d89..ad338c801 100644 --- a/backend/src/ee/routes/v1/audit-log-stream-routers/index.ts +++ b/backend/src/ee/routes/v1/audit-log-stream-routers/index.ts @@ -1,4 +1,9 @@ import { LogProvider } from "@app/ee/services/audit-log-stream/audit-log-stream-enums"; +import { + CreateAzureProviderLogStreamSchema, + SanitizedAzureProviderSchema, + UpdateAzureProviderLogStreamSchema +} from "@app/ee/services/audit-log-stream/azure/azure-provider-schemas"; import { CreateCriblProviderLogStreamSchema, SanitizedCriblProviderSchema, @@ -26,6 +31,15 @@ export * from "./audit-log-stream-router"; export const AUDIT_LOG_STREAM_REGISTER_ROUTER_MAP: Record Promise> = { + [LogProvider.Azure]: async (server: FastifyZodProvider) => { + registerAuditLogStreamEndpoints({ + server, + provider: LogProvider.Azure, + sanitizedResponseSchema: SanitizedAzureProviderSchema, + createSchema: CreateAzureProviderLogStreamSchema, + updateSchema: UpdateAzureProviderLogStreamSchema + }); + }, [LogProvider.Custom]: async (server: FastifyZodProvider) => { registerAuditLogStreamEndpoints({ server, diff --git a/backend/src/ee/services/audit-log-stream/audit-log-stream-enums.ts b/backend/src/ee/services/audit-log-stream/audit-log-stream-enums.ts index 78233f774..ebef18574 100644 --- a/backend/src/ee/services/audit-log-stream/audit-log-stream-enums.ts +++ b/backend/src/ee/services/audit-log-stream/audit-log-stream-enums.ts @@ -1,4 +1,5 @@ export enum LogProvider { + Azure = "azure", Cribl = "cribl", Custom = "custom", Datadog = "datadog", diff --git a/backend/src/ee/services/audit-log-stream/audit-log-stream-factory.ts b/backend/src/ee/services/audit-log-stream/audit-log-stream-factory.ts index 21d629a53..8dde0e079 100644 --- a/backend/src/ee/services/audit-log-stream/audit-log-stream-factory.ts +++ b/backend/src/ee/services/audit-log-stream/audit-log-stream-factory.ts @@ -1,5 +1,6 @@ import { LogProvider } from "./audit-log-stream-enums"; import { TAuditLogStreamCredentials, TLogStreamFactory } from "./audit-log-stream-types"; +import { AzureProviderFactory } from "./azure/azure-provider-factory"; import { CriblProviderFactory } from "./cribl/cribl-provider-factory"; import { CustomProviderFactory } from "./custom/custom-provider-factory"; import { DatadogProviderFactory } from "./datadog/datadog-provider-factory"; @@ -8,6 +9,7 @@ import { SplunkProviderFactory } from "./splunk/splunk-provider-factory"; type TLogStreamFactoryImplementation = TLogStreamFactory; export const LOG_STREAM_FACTORY_MAP: Record = { + [LogProvider.Azure]: AzureProviderFactory as TLogStreamFactoryImplementation, [LogProvider.Datadog]: DatadogProviderFactory as TLogStreamFactoryImplementation, [LogProvider.Splunk]: SplunkProviderFactory as TLogStreamFactoryImplementation, [LogProvider.Custom]: CustomProviderFactory as TLogStreamFactoryImplementation, diff --git a/backend/src/ee/services/audit-log-stream/audit-log-stream-fns.ts b/backend/src/ee/services/audit-log-stream/audit-log-stream-fns.ts index d03a5c8a7..07d833030 100644 --- a/backend/src/ee/services/audit-log-stream/audit-log-stream-fns.ts +++ b/backend/src/ee/services/audit-log-stream/audit-log-stream-fns.ts @@ -3,6 +3,7 @@ import { TKmsServiceFactory } from "@app/services/kms/kms-service"; import { KmsDataKey } from "@app/services/kms/kms-types"; import { TAuditLogStream, TAuditLogStreamCredentials } from "./audit-log-stream-types"; +import { getAzureProviderListItem } from "./azure/azure-provider-fns"; import { getCriblProviderListItem } from "./cribl/cribl-provider-fns"; import { getCustomProviderListItem } from "./custom/custom-provider-fns"; import { getDatadogProviderListItem } from "./datadog/datadog-provider-fns"; @@ -13,6 +14,7 @@ export const listProviderOptions = () => { getDatadogProviderListItem(), getSplunkProviderListItem(), getCustomProviderListItem(), + getAzureProviderListItem(), getCriblProviderListItem() ].sort((a, b) => a.name.localeCompare(b.name)); }; diff --git a/backend/src/ee/services/audit-log-stream/audit-log-stream-types.ts b/backend/src/ee/services/audit-log-stream/audit-log-stream-types.ts index 1ef33befe..5983e50bf 100644 --- a/backend/src/ee/services/audit-log-stream/audit-log-stream-types.ts +++ b/backend/src/ee/services/audit-log-stream/audit-log-stream-types.ts @@ -1,17 +1,19 @@ import { TAuditLogs } from "@app/db/schemas"; import { LogProvider } from "./audit-log-stream-enums"; +import { TAzureProvider, TAzureProviderCredentials } from "./azure/azure-provider-types"; import { TCriblProvider, TCriblProviderCredentials } from "./cribl/cribl-provider-types"; import { TCustomProvider, TCustomProviderCredentials } from "./custom/custom-provider-types"; import { TDatadogProvider, TDatadogProviderCredentials } from "./datadog/datadog-provider-types"; import { TSplunkProvider, TSplunkProviderCredentials } from "./splunk/splunk-provider-types"; -export type TAuditLogStream = TDatadogProvider | TSplunkProvider | TCustomProvider | TCriblProvider; +export type TAuditLogStream = TDatadogProvider | TSplunkProvider | TCustomProvider | TAzureProvider | TCriblProvider; export type TAuditLogStreamCredentials = | TDatadogProviderCredentials | TSplunkProviderCredentials | TCustomProviderCredentials + | TAzureProviderCredentials | TCriblProviderCredentials; export type TCreateAuditLogStreamDTO = { diff --git a/backend/src/ee/services/audit-log-stream/azure/azure-provider-factory.ts b/backend/src/ee/services/audit-log-stream/azure/azure-provider-factory.ts new file mode 100644 index 000000000..9a6157666 --- /dev/null +++ b/backend/src/ee/services/audit-log-stream/azure/azure-provider-factory.ts @@ -0,0 +1,98 @@ +import { RawAxiosRequestHeaders } from "axios"; + +import { request } from "@app/lib/config/request"; +import { BadRequestError } from "@app/lib/errors"; +import { blockLocalAndPrivateIpAddresses } from "@app/lib/validator"; + +import { AUDIT_LOG_STREAM_TIMEOUT } from "../../audit-log/audit-log-queue"; +import { TLogStreamFactoryStreamLog, TLogStreamFactoryValidateCredentials } from "../audit-log-stream-types"; +import { TAzureProviderCredentials } from "./azure-provider-types"; + +function createPayload(event: { createdAt?: Date | string } & Record) { + return [ + { + ...event, + TimeGenerated: (event.createdAt ? new Date(event.createdAt) : new Date()).toISOString() + } + ]; +} + +async function getAzureToken(tenantId: string, clientId: string, clientSecret: string) { + const { data } = await request.post<{ access_token: string }>( + `https://login.microsoftonline.com/${tenantId}/oauth2/v2.0/token`, + new URLSearchParams({ + grant_type: "client_credentials", + client_id: clientId, + client_secret: clientSecret, + scope: "https://monitor.azure.com/.default" + }), + { + headers: { + "Content-Type": "application/x-www-form-urlencoded" + } + } + ); + + return data.access_token; +} + +export const AzureProviderFactory = () => { + const validateCredentials: TLogStreamFactoryValidateCredentials = async ({ + credentials + }) => { + const { tenantId, clientId, clientSecret, dceUrl, dcrId, cltName } = credentials; + + await blockLocalAndPrivateIpAddresses(dceUrl); + + const token = await getAzureToken(tenantId, clientId, clientSecret); + + const streamHeaders: RawAxiosRequestHeaders = { + "Content-Type": "application/json", + Authorization: `Bearer ${token}` + }; + + await request + .post( + `${dceUrl}/dataCollectionRules/${dcrId}/streams/Custom-${cltName}_CL?api-version=2023-01-01`, + createPayload({ ping: "ok" }), + { + headers: streamHeaders, + timeout: AUDIT_LOG_STREAM_TIMEOUT, + signal: AbortSignal.timeout(AUDIT_LOG_STREAM_TIMEOUT) + } + ) + .catch((err) => { + throw new BadRequestError({ message: `Failed to connect with Azure: ${(err as Error)?.message}` }); + }); + + return credentials; + }; + + const streamLog: TLogStreamFactoryStreamLog = async ({ credentials, auditLog }) => { + const { tenantId, clientId, clientSecret, dceUrl, dcrId, cltName } = credentials; + + await blockLocalAndPrivateIpAddresses(dceUrl); + + const token = await getAzureToken(tenantId, clientId, clientSecret); + + const streamHeaders: RawAxiosRequestHeaders = { + "Content-Type": "application/json", + Authorization: `Bearer ${token}` + }; + + await request.post( + `${dceUrl}/dataCollectionRules/${dcrId}/streams/Custom-${cltName}_CL?api-version=2023-01-01`, + createPayload(auditLog), + { + headers: streamHeaders, + timeout: AUDIT_LOG_STREAM_TIMEOUT, + signal: AbortSignal.timeout(AUDIT_LOG_STREAM_TIMEOUT) + } + ); + }; + + return { + validateCredentials, + streamLog + }; +}; diff --git a/backend/src/ee/services/audit-log-stream/azure/azure-provider-fns.ts b/backend/src/ee/services/audit-log-stream/azure/azure-provider-fns.ts new file mode 100644 index 000000000..e558b69e2 --- /dev/null +++ b/backend/src/ee/services/audit-log-stream/azure/azure-provider-fns.ts @@ -0,0 +1,8 @@ +import { LogProvider } from "../audit-log-stream-enums"; + +export const getAzureProviderListItem = () => { + return { + name: "Azure" as const, + provider: LogProvider.Azure as const + }; +}; diff --git a/backend/src/ee/services/audit-log-stream/azure/azure-provider-schemas.ts b/backend/src/ee/services/audit-log-stream/azure/azure-provider-schemas.ts new file mode 100644 index 000000000..50def1d79 --- /dev/null +++ b/backend/src/ee/services/audit-log-stream/azure/azure-provider-schemas.ts @@ -0,0 +1,52 @@ +import RE2 from "re2"; +import { z } from "zod"; + +import { LogProvider } from "../audit-log-stream-enums"; +import { BaseProviderSchema } from "../audit-log-stream-schemas"; + +export const AzureProviderCredentialsSchema = z.object({ + tenantId: z.string().trim().uuid(), + clientId: z.string().trim().uuid(), + clientSecret: z.string().trim().length(40), + + // Data Collection Endpoint URL + dceUrl: z.string().trim().url().min(1).max(255), + + // Data Collection Rule Immutable ID + dcrId: z + .string() + .trim() + .refine((val) => new RE2(/^dcr-[0-9a-f]{32}$/).test(val), "DCR ID must be in dcr-*** format"), + + // Custom Log Table Name + cltName: z.string().trim().min(1).max(255) +}); + +const BaseAzureProviderSchema = BaseProviderSchema.extend({ provider: z.literal(LogProvider.Azure) }); + +export const AzureProviderSchema = BaseAzureProviderSchema.extend({ + credentials: AzureProviderCredentialsSchema +}); + +export const SanitizedAzureProviderSchema = BaseAzureProviderSchema.extend({ + credentials: AzureProviderCredentialsSchema.pick({ + tenantId: true, + clientId: true, + dceUrl: true, + dcrId: true, + cltName: true + }) +}); + +export const AzureProviderListItemSchema = z.object({ + name: z.literal("Azure"), + provider: z.literal(LogProvider.Azure) +}); + +export const CreateAzureProviderLogStreamSchema = z.object({ + credentials: AzureProviderCredentialsSchema +}); + +export const UpdateAzureProviderLogStreamSchema = z.object({ + credentials: AzureProviderCredentialsSchema +}); diff --git a/backend/src/ee/services/audit-log-stream/azure/azure-provider-types.ts b/backend/src/ee/services/audit-log-stream/azure/azure-provider-types.ts new file mode 100644 index 000000000..0ba5f120d --- /dev/null +++ b/backend/src/ee/services/audit-log-stream/azure/azure-provider-types.ts @@ -0,0 +1,7 @@ +import { z } from "zod"; + +import { AzureProviderCredentialsSchema, AzureProviderSchema } from "./azure-provider-schemas"; + +export type TAzureProvider = z.infer; + +export type TAzureProviderCredentials = z.infer; diff --git a/docs/documentation/platform/audit-log-streams/audit-log-streams.mdx b/docs/documentation/platform/audit-log-streams/audit-log-streams.mdx index 50fa75e92..ad030ec26 100644 --- a/docs/documentation/platform/audit-log-streams/audit-log-streams.mdx +++ b/docs/documentation/platform/audit-log-streams/audit-log-streams.mdx @@ -45,6 +45,116 @@ Infisical Audit Log Streaming enables you to transmit your organization's audit ## Example Providers + + Infisical offers a dedicated **Azure** provider to stream your audit logs, enabling seamless integration with services like Microsoft Sentinel. + + + After setting up all Azure resources, it may take 10-20 minutes for logs to begin streaming. + + + + + Navigate to [Data Collection Endpoints](https://portal.azure.com/#view/HubsExtension/BrowseResource.ReactView/resourceType/microsoft.insights%2Fdatacollectionendpoints) and click **Create**. + + ![azure create dce](/images/platform/audit-log-streams/azure-create-dce.png) + + Configure your Data Collection Endpoint by providing an **Endpoint Name**, **Subscription**, and a **Resource group**. Then click **Review + Create**. + + ![azure configure dce](/images/platform/audit-log-streams/azure-configure-dce.png) + + After creation, it may take a few minutes for the Data Collection Endpoint to appear. Once visible, click on it and copy the **Logs Ingestion** URL. You will need this URL in later steps. + + ![azure dce url](/images/platform/audit-log-streams/azure-dce-url.png) + + + + If you already have a Log Analytics Workspace, you may skip this step. + + + Navigate to [Log Analytics Workspaces](https://portal.azure.com/#browse/Microsoft.OperationalInsights%2Fworkspaces) and click **Create**. + + ![azure create law](/images/platform/audit-log-streams/azure-create-law.png) + + Configure your Log Analytics Workspace by providing a **Subscription**, **Resource group**, and a **Name**. Then click **Review + Create**. + + ![azure configure law](/images/platform/audit-log-streams/azure-configure-law.png) + + Once the workspace is deployed, click **Go to resource** to access it. + + ![azure go to resource](/images/platform/audit-log-streams/azure-go-to-resource.png) + + + Within your Log Analytics Workspace, navigate to **Tables** and click **Create**. Select **New custom log (DCR-based)** from the dropdown. + + ![azure new table](/images/platform/audit-log-streams/azure-new-table.png) + + Configure the Custom Log Table: Provide a **Table name** (e.g., `InfisicalLogs`), select the **Data collection endpoint** created in Step 1, and create a new **Data collection rule** as illustrated in the image below. Then, click **Next**. + + ![azure configure table](/images/platform/audit-log-streams/azure-configure-table.png) + + On the **Schema and transformation** page, you'll be prompted to upload a **Log Sample**. Create a `.json` file with the following content and upload it: + + ```json + { + "id": "00000000-0000-0000-0000-000000000000", + "actor": "user", + "actorMetadata": { + "email": "user@example.com", + "userId": "00000000-0000-0000-0000-000000000000", + "username": "user@example.com" + }, + "ipAddress": "0.0.0.0", + "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36", + "userAgentType": "web", + "eventType": "get-secrets", + "eventMetadata": {}, + "projectName": "MyProject", + "orgId": "00000000-0000-0000-0000-000000000000", + "projectId": "00000000-0000-0000-0000-000000000000", + "TimeGenerated": "2025-01-01T00:00:00.000Z" + } + ``` + + Optionally, you can add **Transformations** to further destructure the data. For example, to extract actor email and userId: + + ``` + source + | extend + ActorEmail = tostring(actorMetadata.email), + ActorUserId = tostring(actorMetadata.userId) + ``` + + On the final step, click **Create**. + + + It may take a few minutes for your Custom Log Table to be created and appear under Tables. + + + + After creating your Data Collection Rule, you'll need its **Immutable ID**. + + Navigate to [Data collection rules](https://portal.azure.com/#view/HubsExtension/BrowseResource.ReactView/resourceType/microsoft.insights%2Fdatacollectionrules). Click on your newly created DCR and copy its **Immutable ID** for the next step. + + ![azure dcr](/images/platform/audit-log-streams/azure-dcr.png) + + + In Infisical, create a new audit log stream and select the **Azure** provider. Input the following details: + + - **Tenant ID**: Your Tenant ID + - **Client ID**: The Client ID of an App Registration + - **Client Secret**: The Client Secret of an App Registration + - **Data Collection Endpoint URL**: Obtained from Step 1 + - **Data Collection Rule Immutable ID**: Obtained from Step 4 + - **Custom Log Table Name**: Defined in Step 3 + + ![azure create als](/images/platform/audit-log-streams/azure-create-als.png) + + + The App Registration used for authentication must have the **Monitoring Metrics Publisher** role assigned on the **Data Collection Rule** created in Step 3. [See Microsoft Guide](https://learn.microsoft.com/en-us/azure/azure-monitor/logs/tutorial-logs-ingestion-portal#assign-permissions-to-the-dcr). + + + + You can stream to Better Stack using a **Custom** log stream. diff --git a/docs/images/platform/audit-log-streams/azure-configure-dce.png b/docs/images/platform/audit-log-streams/azure-configure-dce.png new file mode 100644 index 000000000..b0b32f0b2 Binary files /dev/null and b/docs/images/platform/audit-log-streams/azure-configure-dce.png differ diff --git a/docs/images/platform/audit-log-streams/azure-configure-law.png b/docs/images/platform/audit-log-streams/azure-configure-law.png new file mode 100644 index 000000000..11ec8d86f Binary files /dev/null and b/docs/images/platform/audit-log-streams/azure-configure-law.png differ diff --git a/docs/images/platform/audit-log-streams/azure-configure-table.png b/docs/images/platform/audit-log-streams/azure-configure-table.png new file mode 100644 index 000000000..7dba237b9 Binary files /dev/null and b/docs/images/platform/audit-log-streams/azure-configure-table.png differ diff --git a/docs/images/platform/audit-log-streams/azure-create-als.png b/docs/images/platform/audit-log-streams/azure-create-als.png new file mode 100644 index 000000000..bdfb2f8d6 Binary files /dev/null and b/docs/images/platform/audit-log-streams/azure-create-als.png differ diff --git a/docs/images/platform/audit-log-streams/azure-create-dce.png b/docs/images/platform/audit-log-streams/azure-create-dce.png new file mode 100644 index 000000000..00a546c30 Binary files /dev/null and b/docs/images/platform/audit-log-streams/azure-create-dce.png differ diff --git a/docs/images/platform/audit-log-streams/azure-create-law.png b/docs/images/platform/audit-log-streams/azure-create-law.png new file mode 100644 index 000000000..1150f64ff Binary files /dev/null and b/docs/images/platform/audit-log-streams/azure-create-law.png differ diff --git a/docs/images/platform/audit-log-streams/azure-dce-url.png b/docs/images/platform/audit-log-streams/azure-dce-url.png new file mode 100644 index 000000000..d8c1dc96d Binary files /dev/null and b/docs/images/platform/audit-log-streams/azure-dce-url.png differ diff --git a/docs/images/platform/audit-log-streams/azure-dcr.png b/docs/images/platform/audit-log-streams/azure-dcr.png new file mode 100644 index 000000000..441808947 Binary files /dev/null and b/docs/images/platform/audit-log-streams/azure-dcr.png differ diff --git a/docs/images/platform/audit-log-streams/azure-go-to-resource.png b/docs/images/platform/audit-log-streams/azure-go-to-resource.png new file mode 100644 index 000000000..ef197517d Binary files /dev/null and b/docs/images/platform/audit-log-streams/azure-go-to-resource.png differ diff --git a/docs/images/platform/audit-log-streams/azure-new-table.png b/docs/images/platform/audit-log-streams/azure-new-table.png new file mode 100644 index 000000000..829af3e53 Binary files /dev/null and b/docs/images/platform/audit-log-streams/azure-new-table.png differ diff --git a/frontend/src/helpers/auditLogStreams.ts b/frontend/src/helpers/auditLogStreams.ts index eee2fc8da..faa132dd2 100644 --- a/frontend/src/helpers/auditLogStreams.ts +++ b/frontend/src/helpers/auditLogStreams.ts @@ -8,6 +8,7 @@ export const AUDIT_LOG_STREAM_PROVIDER_MAP: Record< LogProvider, { name: string; image?: string; icon?: IconDefinition; size?: number } > = { + [LogProvider.Azure]: { name: "Azure", image: "Microsoft Azure.png", size: 60 }, [LogProvider.Cribl]: { name: "Cribl", image: "Cribl.png", size: 60 }, [LogProvider.Custom]: { name: "Custom", icon: faCode }, [LogProvider.Datadog]: { name: "Datadog", image: "Datadog.png" }, @@ -25,6 +26,8 @@ export function getProviderUrl( return logStream.credentials.url; case LogProvider.Splunk: return `https://${logStream.credentials.hostname}:8088/services/collector/event`; + case LogProvider.Azure: + return `${logStream.credentials.dceUrl}/dataCollectionRules/${logStream.credentials.dcrId}/streams/Custom-${logStream.credentials.cltName}_CL`; default: throw new Error( `Unhandled provider in getProviderUrl: ${(logStream as TAuditLogStream).provider}` diff --git a/frontend/src/hooks/api/auditLogStreams/enums.ts b/frontend/src/hooks/api/auditLogStreams/enums.ts index 78233f774..ebef18574 100644 --- a/frontend/src/hooks/api/auditLogStreams/enums.ts +++ b/frontend/src/hooks/api/auditLogStreams/enums.ts @@ -1,4 +1,5 @@ export enum LogProvider { + Azure = "azure", Cribl = "cribl", Custom = "custom", Datadog = "datadog", diff --git a/frontend/src/hooks/api/auditLogStreams/types/index.ts b/frontend/src/hooks/api/auditLogStreams/types/index.ts index a360cd677..f780510c2 100644 --- a/frontend/src/hooks/api/auditLogStreams/types/index.ts +++ b/frontend/src/hooks/api/auditLogStreams/types/index.ts @@ -1,4 +1,5 @@ import { LogProvider } from "../enums"; +import { TAzureProviderLogStream } from "./providers/azure-provider"; import { TCriblProviderLogStream } from "./providers/cribl-provider"; import { TCustomProviderLogStream } from "./providers/custom-provider"; import { TDatadogProviderLogStream } from "./providers/datadog-provider"; @@ -8,9 +9,11 @@ export type TAuditLogStream = | TCustomProviderLogStream | TDatadogProviderLogStream | TSplunkProviderLogStream + | TAzureProviderLogStream | TCriblProviderLogStream; export type TAuditLogStreamProviderMap = { + [LogProvider.Azure]: TAzureProviderLogStream; [LogProvider.Cribl]: TCriblProviderLogStream; [LogProvider.Custom]: TCustomProviderLogStream; [LogProvider.Datadog]: TDatadogProviderLogStream; diff --git a/frontend/src/hooks/api/auditLogStreams/types/providers/azure-provider.ts b/frontend/src/hooks/api/auditLogStreams/types/providers/azure-provider.ts new file mode 100644 index 000000000..3086fd7cc --- /dev/null +++ b/frontend/src/hooks/api/auditLogStreams/types/providers/azure-provider.ts @@ -0,0 +1,14 @@ +import { LogProvider } from "../../enums"; +import { TRootProviderLogStream } from "./root-provider"; + +export type TAzureProviderLogStream = TRootProviderLogStream & { + provider: LogProvider.Azure; + credentials: { + tenantId: string; + clientId: string; + clientSecret: string; + dceUrl: string; + dcrId: string; + cltName: string; + }; +}; diff --git a/frontend/src/pages/organization/SettingsPage/components/AuditLogStreamTab/AuditLogStreamForm/AuditLogStreamForm.tsx b/frontend/src/pages/organization/SettingsPage/components/AuditLogStreamTab/AuditLogStreamForm/AuditLogStreamForm.tsx index 6de5a0ecf..f59ae23aa 100644 --- a/frontend/src/pages/organization/SettingsPage/components/AuditLogStreamTab/AuditLogStreamForm/AuditLogStreamForm.tsx +++ b/frontend/src/pages/organization/SettingsPage/components/AuditLogStreamTab/AuditLogStreamForm/AuditLogStreamForm.tsx @@ -6,6 +6,7 @@ import { TAuditLogStream } from "@app/hooks/api/types"; import { DiscriminativePick } from "@app/types"; import { AuditLogStreamHeader } from "../components/AuditLogStreamHeader"; +import { AzureProviderAuditLogStreamForm } from "./AzureProviderAuditLogStreamForm"; import { CriblProviderAuditLogStreamForm } from "./CriblProviderAuditLogStreamForm"; import { CustomProviderAuditLogStreamForm } from "./CustomProviderAuditLogStreamForm"; import { DatadogProviderAuditLogStreamForm } from "./DatadogProviderAuditLogStreamForm"; @@ -45,6 +46,8 @@ const CreateForm = ({ provider, onComplete }: CreateFormProps) => { }; switch (provider) { + case LogProvider.Azure: + return ; case LogProvider.Cribl: return ; case LogProvider.Custom: @@ -86,6 +89,10 @@ const UpdateForm = ({ auditLogStream, onComplete }: UpdateFormProps) => { }; switch (auditLogStream.provider) { + case LogProvider.Azure: + return ( + + ); case LogProvider.Cribl: return ( diff --git a/frontend/src/pages/organization/SettingsPage/components/AuditLogStreamTab/AuditLogStreamForm/AzureProviderAuditLogStreamForm.tsx b/frontend/src/pages/organization/SettingsPage/components/AuditLogStreamTab/AuditLogStreamForm/AzureProviderAuditLogStreamForm.tsx new file mode 100644 index 000000000..131ea1c85 --- /dev/null +++ b/frontend/src/pages/organization/SettingsPage/components/AuditLogStreamTab/AuditLogStreamForm/AzureProviderAuditLogStreamForm.tsx @@ -0,0 +1,158 @@ +import { Controller, FormProvider, useForm } from "react-hook-form"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { z } from "zod"; + +import { Button, FormControl, Input, ModalClose, SecretInput } from "@app/components/v2"; +import { LogProvider } from "@app/hooks/api/auditLogStreams/enums"; +import { TAzureProviderLogStream } from "@app/hooks/api/auditLogStreams/types/providers/azure-provider"; + +type Props = { + auditLogStream?: TAzureProviderLogStream; + onSubmit: (formData: FormData) => void; +}; + +const formSchema = z.object({ + provider: z.literal(LogProvider.Azure), + credentials: z.object({ + tenantId: z.string().trim().uuid(), + clientId: z.string().trim().uuid(), + clientSecret: z.string().trim().length(40), + dceUrl: z.string().trim().url().min(1).max(255), + dcrId: z + .string() + .trim() + .regex(/^dcr-[0-9a-f]{32}$/, "DCR ID must be in dcr-*** format"), + cltName: z.string().trim().min(1).max(255) + }) +}); + +type FormData = z.infer; + +export const AzureProviderAuditLogStreamForm = ({ auditLogStream, onSubmit }: Props) => { + const isUpdate = Boolean(auditLogStream); + + const form = useForm({ + resolver: zodResolver(formSchema), + defaultValues: auditLogStream ?? { + provider: LogProvider.Azure + } + }); + + const { + handleSubmit, + control, + formState: { isSubmitting, isDirty } + } = form; + + return ( + +
+ ( + + + + )} + /> + ( + + + + )} + /> + ( + + onChange(e.target.value)} + /> + + )} + /> + ( + + + + )} + /> + ( + + + + )} + /> + ( + + + + )} + /> +
+ + + + +
+ +
+ ); +};