From 4654a17e5f6815bc8eb1fad3781f1057bc2fa723 Mon Sep 17 00:00:00 2001 From: Daniel Hougaard Date: Wed, 26 Mar 2025 05:54:33 +0400 Subject: [PATCH] feat(audit-logs): filter audit logs by secret key --- .../ee/services/audit-log/audit-log-dal.ts | 22 ++++++++++++++++-- .../services/audit-log/audit-log-service.ts | 1 + .../ee/services/audit-log/audit-log-types.ts | 1 + backend/src/lib/api-docs/constants.ts | 2 ++ .../server/routes/v1/organization-router.ts | 1 + frontend/src/hooks/api/auditLogs/types.tsx | 1 + .../AuditLogsPage/components/LogsFilter.tsx | 23 ++++++++++++++++--- .../AuditLogsPage/components/LogsSection.tsx | 3 +++ .../AuditLogsPage/components/types.tsx | 1 + 9 files changed, 50 insertions(+), 5 deletions(-) diff --git a/backend/src/ee/services/audit-log/audit-log-dal.ts b/backend/src/ee/services/audit-log/audit-log-dal.ts index 21f785835..31916ca1d 100644 --- a/backend/src/ee/services/audit-log/audit-log-dal.ts +++ b/backend/src/ee/services/audit-log/audit-log-dal.ts @@ -40,12 +40,14 @@ export const auditLogDALFactory = (db: TDbClient) => { actorId, actorType, secretPath, + secretKey, eventType, eventMetadata }: Omit & { actorId?: string; actorType?: ActorType; secretPath?: string; + secretKey?: string; eventType?: EventType[]; eventMetadata?: Record; }, @@ -90,8 +92,24 @@ export const auditLogDALFactory = (db: TDbClient) => { }); } - if (projectId && secretPath) { - void sqlQuery.whereRaw(`"eventMetadata" @> jsonb_build_object('secretPath', ?::text)`, [secretPath]); + if (projectId) { + if (secretPath) { + void sqlQuery.whereRaw(`"eventMetadata"->>'secretPath' = ?`, [secretPath]); + } + if (secretKey) { + void sqlQuery.whereRaw( + `( + "eventMetadata"->>'secretKey' = ? + OR + EXISTS ( + SELECT 1 + FROM jsonb_array_elements("eventMetadata"->'secrets') AS element + WHERE element->>'secretKey' = ? + ) + )`, + [secretKey, secretKey] + ); + } } // Filter by actor type diff --git a/backend/src/ee/services/audit-log/audit-log-service.ts b/backend/src/ee/services/audit-log/audit-log-service.ts index 295923ef5..0802d27e6 100644 --- a/backend/src/ee/services/audit-log/audit-log-service.ts +++ b/backend/src/ee/services/audit-log/audit-log-service.ts @@ -63,6 +63,7 @@ export const auditLogServiceFactory = ({ actorType: filter.actorType, eventMetadata: filter.eventMetadata, secretPath: filter.secretPath, + secretKey: filter.secretKey, ...(filter.projectId ? { projectId: filter.projectId } : { orgId: actorOrgId }) }); diff --git a/backend/src/ee/services/audit-log/audit-log-types.ts b/backend/src/ee/services/audit-log/audit-log-types.ts index eef11b8a6..2d6337cc7 100644 --- a/backend/src/ee/services/audit-log/audit-log-types.ts +++ b/backend/src/ee/services/audit-log/audit-log-types.ts @@ -36,6 +36,7 @@ export type TListProjectAuditLogDTO = { auditLogActorId?: string; actorType?: ActorType; secretPath?: string; + secretKey?: string; eventMetadata?: Record; }; } & Omit; diff --git a/backend/src/lib/api-docs/constants.ts b/backend/src/lib/api-docs/constants.ts index ec855168e..34fb41e00 100644 --- a/backend/src/lib/api-docs/constants.ts +++ b/backend/src/lib/api-docs/constants.ts @@ -843,6 +843,8 @@ export const AUDIT_LOGS = { eventType: "The type of the event to export.", secretPath: "The path of the secret to query audit logs for. Note that the projectId parameter must also be provided.", + secretKey: + "The key of the secret to query audit logs for. Note that the projectId parameter must also be provided.", userAgentType: "Choose which consuming application to export audit logs for.", eventMetadata: "Filter by event metadata key-value pairs. Formatted as `key1=value1,key2=value2`, with comma-separation.", diff --git a/backend/src/server/routes/v1/organization-router.ts b/backend/src/server/routes/v1/organization-router.ts index 21723ef58..ab628972f 100644 --- a/backend/src/server/routes/v1/organization-router.ts +++ b/backend/src/server/routes/v1/organization-router.ts @@ -118,6 +118,7 @@ export const registerOrgRouter = async (server: FastifyZodProvider) => { .optional() .transform((val) => (!val ? val : removeTrailingSlash(val))) .describe(AUDIT_LOGS.EXPORT.secretPath), + secretKey: z.string().optional().describe(AUDIT_LOGS.EXPORT.secretKey), // eventType is split with , for multiple values, we need to transform it to array eventType: z diff --git a/frontend/src/hooks/api/auditLogs/types.tsx b/frontend/src/hooks/api/auditLogs/types.tsx index 8acaa215a..e69e8a45a 100644 --- a/frontend/src/hooks/api/auditLogs/types.tsx +++ b/frontend/src/hooks/api/auditLogs/types.tsx @@ -11,6 +11,7 @@ export type TGetAuditLogsFilter = { projectId?: string; actor?: string; // user ID format secretPath?: string; + secretKey?: string; startDate?: Date; endDate?: Date; limit: number; diff --git a/frontend/src/pages/organization/AuditLogsPage/components/LogsFilter.tsx b/frontend/src/pages/organization/AuditLogsPage/components/LogsFilter.tsx index c32819cde..31db8f916 100644 --- a/frontend/src/pages/organization/AuditLogsPage/components/LogsFilter.tsx +++ b/frontend/src/pages/organization/AuditLogsPage/components/LogsFilter.tsx @@ -157,7 +157,24 @@ export const LogsFilter = ({ control={control} name="secretPath" render={({ field: { onChange, value, ...field } }) => ( - + + onChange(e.target.value)} + /> + + )} + /> + )} + + {selectedProject?.type === ProjectType.SecretManager && ( + ( + onChange(e.target.value)} /> )} @@ -289,7 +306,7 @@ export const LogsFilter = ({ control={control} render={({ field: { onChange, ...field }, fieldState: { error } }) => { return ( - + { return ( - + (secretPath!, 500); + const [debouncedSecretKey] = useDebounce(secretKey!, 500); return (
@@ -81,6 +83,7 @@ export const LogsSection = withPermission( refetchInterval={refetchInterval} filter={{ secretPath: debouncedSecretPath || undefined, + secretKey: debouncedSecretKey || undefined, eventMetadata: presets?.eventMetadata, projectId, actorType: presets?.actorType, diff --git a/frontend/src/pages/organization/AuditLogsPage/components/types.tsx b/frontend/src/pages/organization/AuditLogsPage/components/types.tsx index 17dcb80fd..ee6909b13 100644 --- a/frontend/src/pages/organization/AuditLogsPage/components/types.tsx +++ b/frontend/src/pages/organization/AuditLogsPage/components/types.tsx @@ -14,6 +14,7 @@ export const auditLogFilterFormSchema = z actor: z.string().optional(), userAgentType: z.nativeEnum(UserAgentType), secretPath: z.string().optional(), + secretKey: z.string().optional(), startDate: z.date().optional(), endDate: z.date().optional(), page: z.coerce.number().optional(),