mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-07 00:27:30 +00:00
feat(audit-logs): filter audit logs by secret key
This commit is contained in:
@@ -40,12 +40,14 @@ export const auditLogDALFactory = (db: TDbClient) => {
|
|||||||
actorId,
|
actorId,
|
||||||
actorType,
|
actorType,
|
||||||
secretPath,
|
secretPath,
|
||||||
|
secretKey,
|
||||||
eventType,
|
eventType,
|
||||||
eventMetadata
|
eventMetadata
|
||||||
}: Omit<TFindQuery, "actor" | "eventType"> & {
|
}: Omit<TFindQuery, "actor" | "eventType"> & {
|
||||||
actorId?: string;
|
actorId?: string;
|
||||||
actorType?: ActorType;
|
actorType?: ActorType;
|
||||||
secretPath?: string;
|
secretPath?: string;
|
||||||
|
secretKey?: string;
|
||||||
eventType?: EventType[];
|
eventType?: EventType[];
|
||||||
eventMetadata?: Record<string, string>;
|
eventMetadata?: Record<string, string>;
|
||||||
},
|
},
|
||||||
@@ -90,8 +92,24 @@ export const auditLogDALFactory = (db: TDbClient) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (projectId && secretPath) {
|
if (projectId) {
|
||||||
void sqlQuery.whereRaw(`"eventMetadata" @> jsonb_build_object('secretPath', ?::text)`, [secretPath]);
|
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
|
// Filter by actor type
|
||||||
|
|||||||
@@ -63,6 +63,7 @@ export const auditLogServiceFactory = ({
|
|||||||
actorType: filter.actorType,
|
actorType: filter.actorType,
|
||||||
eventMetadata: filter.eventMetadata,
|
eventMetadata: filter.eventMetadata,
|
||||||
secretPath: filter.secretPath,
|
secretPath: filter.secretPath,
|
||||||
|
secretKey: filter.secretKey,
|
||||||
...(filter.projectId ? { projectId: filter.projectId } : { orgId: actorOrgId })
|
...(filter.projectId ? { projectId: filter.projectId } : { orgId: actorOrgId })
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ export type TListProjectAuditLogDTO = {
|
|||||||
auditLogActorId?: string;
|
auditLogActorId?: string;
|
||||||
actorType?: ActorType;
|
actorType?: ActorType;
|
||||||
secretPath?: string;
|
secretPath?: string;
|
||||||
|
secretKey?: string;
|
||||||
eventMetadata?: Record<string, string>;
|
eventMetadata?: Record<string, string>;
|
||||||
};
|
};
|
||||||
} & Omit<TProjectPermission, "projectId">;
|
} & Omit<TProjectPermission, "projectId">;
|
||||||
|
|||||||
@@ -843,6 +843,8 @@ export const AUDIT_LOGS = {
|
|||||||
eventType: "The type of the event to export.",
|
eventType: "The type of the event to export.",
|
||||||
secretPath:
|
secretPath:
|
||||||
"The path of the secret to query audit logs for. Note that the projectId parameter must also be provided.",
|
"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.",
|
userAgentType: "Choose which consuming application to export audit logs for.",
|
||||||
eventMetadata:
|
eventMetadata:
|
||||||
"Filter by event metadata key-value pairs. Formatted as `key1=value1,key2=value2`, with comma-separation.",
|
"Filter by event metadata key-value pairs. Formatted as `key1=value1,key2=value2`, with comma-separation.",
|
||||||
|
|||||||
@@ -118,6 +118,7 @@ export const registerOrgRouter = async (server: FastifyZodProvider) => {
|
|||||||
.optional()
|
.optional()
|
||||||
.transform((val) => (!val ? val : removeTrailingSlash(val)))
|
.transform((val) => (!val ? val : removeTrailingSlash(val)))
|
||||||
.describe(AUDIT_LOGS.EXPORT.secretPath),
|
.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 is split with , for multiple values, we need to transform it to array
|
||||||
eventType: z
|
eventType: z
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ export type TGetAuditLogsFilter = {
|
|||||||
projectId?: string;
|
projectId?: string;
|
||||||
actor?: string; // user ID format
|
actor?: string; // user ID format
|
||||||
secretPath?: string;
|
secretPath?: string;
|
||||||
|
secretKey?: string;
|
||||||
startDate?: Date;
|
startDate?: Date;
|
||||||
endDate?: Date;
|
endDate?: Date;
|
||||||
limit: number;
|
limit: number;
|
||||||
|
|||||||
@@ -157,7 +157,24 @@ export const LogsFilter = ({
|
|||||||
control={control}
|
control={control}
|
||||||
name="secretPath"
|
name="secretPath"
|
||||||
render={({ field: { onChange, value, ...field } }) => (
|
render={({ field: { onChange, value, ...field } }) => (
|
||||||
<FormControl label="Secret path" className="w-40">
|
<FormControl label="Secret Path" className="w-40">
|
||||||
|
<Input
|
||||||
|
placeholder="/folder"
|
||||||
|
{...field}
|
||||||
|
value={value}
|
||||||
|
onChange={(e) => onChange(e.target.value)}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{selectedProject?.type === ProjectType.SecretManager && (
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name="secretKey"
|
||||||
|
render={({ field: { onChange, value, ...field } }) => (
|
||||||
|
<FormControl label="Secret Key" className="w-40">
|
||||||
<Input {...field} value={value} onChange={(e) => onChange(e.target.value)} />
|
<Input {...field} value={value} onChange={(e) => onChange(e.target.value)} />
|
||||||
</FormControl>
|
</FormControl>
|
||||||
)}
|
)}
|
||||||
@@ -289,7 +306,7 @@ export const LogsFilter = ({
|
|||||||
control={control}
|
control={control}
|
||||||
render={({ field: { onChange, ...field }, fieldState: { error } }) => {
|
render={({ field: { onChange, ...field }, fieldState: { error } }) => {
|
||||||
return (
|
return (
|
||||||
<FormControl label="Start date" errorText={error?.message} isError={Boolean(error)}>
|
<FormControl label="Start Date" errorText={error?.message} isError={Boolean(error)}>
|
||||||
<DatePicker
|
<DatePicker
|
||||||
value={field.value || undefined}
|
value={field.value || undefined}
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
@@ -309,7 +326,7 @@ export const LogsFilter = ({
|
|||||||
control={control}
|
control={control}
|
||||||
render={({ field: { onChange, ...field }, fieldState: { error } }) => {
|
render={({ field: { onChange, ...field }, fieldState: { error } }) => {
|
||||||
return (
|
return (
|
||||||
<FormControl label="End date" errorText={error?.message} isError={Boolean(error)}>
|
<FormControl label="End Date" errorText={error?.message} isError={Boolean(error)}>
|
||||||
<DatePicker
|
<DatePicker
|
||||||
value={field.value || undefined}
|
value={field.value || undefined}
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
|
|||||||
@@ -58,11 +58,13 @@ export const LogsSection = withPermission(
|
|||||||
const actor = watch("actor");
|
const actor = watch("actor");
|
||||||
const projectId = watch("project")?.id;
|
const projectId = watch("project")?.id;
|
||||||
const secretPath = watch("secretPath");
|
const secretPath = watch("secretPath");
|
||||||
|
const secretKey = watch("secretKey");
|
||||||
|
|
||||||
const startDate = watch("startDate");
|
const startDate = watch("startDate");
|
||||||
const endDate = watch("endDate");
|
const endDate = watch("endDate");
|
||||||
|
|
||||||
const [debouncedSecretPath] = useDebounce<string>(secretPath!, 500);
|
const [debouncedSecretPath] = useDebounce<string>(secretPath!, 500);
|
||||||
|
const [debouncedSecretKey] = useDebounce<string>(secretKey!, 500);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
@@ -81,6 +83,7 @@ export const LogsSection = withPermission(
|
|||||||
refetchInterval={refetchInterval}
|
refetchInterval={refetchInterval}
|
||||||
filter={{
|
filter={{
|
||||||
secretPath: debouncedSecretPath || undefined,
|
secretPath: debouncedSecretPath || undefined,
|
||||||
|
secretKey: debouncedSecretKey || undefined,
|
||||||
eventMetadata: presets?.eventMetadata,
|
eventMetadata: presets?.eventMetadata,
|
||||||
projectId,
|
projectId,
|
||||||
actorType: presets?.actorType,
|
actorType: presets?.actorType,
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ export const auditLogFilterFormSchema = z
|
|||||||
actor: z.string().optional(),
|
actor: z.string().optional(),
|
||||||
userAgentType: z.nativeEnum(UserAgentType),
|
userAgentType: z.nativeEnum(UserAgentType),
|
||||||
secretPath: z.string().optional(),
|
secretPath: z.string().optional(),
|
||||||
|
secretKey: z.string().optional(),
|
||||||
startDate: z.date().optional(),
|
startDate: z.date().optional(),
|
||||||
endDate: z.date().optional(),
|
endDate: z.date().optional(),
|
||||||
page: z.coerce.number().optional(),
|
page: z.coerce.number().optional(),
|
||||||
|
|||||||
Reference in New Issue
Block a user