From b5660c87a033bfe89e130ab98cf269d8f8c572d3 Mon Sep 17 00:00:00 2001 From: Daniel Hougaard Date: Wed, 18 Sep 2024 14:36:28 +0400 Subject: [PATCH 01/14] feat(dashboard): organization-level audit logs --- .../ee/services/audit-log/audit-log-dal.ts | 52 +++++++++----- frontend/src/hooks/api/auditLogs/types.tsx | 1 + frontend/src/layouts/AppLayout/AppLayout.tsx | 10 +++ .../src/pages/org/[id]/audit-logs/index.tsx | 20 ++++++ .../views/Org/AuditLogsPage/AuditLogsPage.tsx | 20 ++++++ .../src/views/Org/AuditLogsPage/index.tsx | 1 + .../AuditLogsPage/components/LogsFilter.tsx | 69 +++++++++++++++++-- .../AuditLogsPage/components/LogsSection.tsx | 3 + .../AuditLogsPage/components/LogsTable.tsx | 5 +- .../AuditLogsPage/components/types.tsx | 1 + 10 files changed, 156 insertions(+), 26 deletions(-) create mode 100644 frontend/src/pages/org/[id]/audit-logs/index.tsx create mode 100644 frontend/src/views/Org/AuditLogsPage/AuditLogsPage.tsx create mode 100644 frontend/src/views/Org/AuditLogsPage/index.tsx 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 ce345766b..4b967bd38 100644 --- a/backend/src/ee/services/audit-log/audit-log-dal.ts +++ b/backend/src/ee/services/audit-log/audit-log-dal.ts @@ -3,7 +3,7 @@ import { Knex } from "knex"; import { TDbClient } from "@app/db"; import { AuditLogsSchema, TableName } from "@app/db/schemas"; import { DatabaseError } from "@app/lib/errors"; -import { ormify, selectAllTableCols, stripUndefinedInWhere } from "@app/lib/knex"; +import { ormify, selectAllTableCols } from "@app/lib/knex"; import { logger } from "@app/lib/logger"; import { QueueName } from "@app/queue"; import { ActorType } from "@app/services/auth/auth-type"; @@ -49,46 +49,56 @@ export const auditLogDALFactory = (db: TDbClient) => { tx?: Knex ) => { try { + // Find statements const sqlQuery = (tx || db.replicaNode())(TableName.AuditLog) - .where( - stripUndefinedInWhere({ - projectId, - [`${TableName.AuditLog}.orgId`]: orgId, - userAgentType - }) - ) - .leftJoin(TableName.Project, `${TableName.AuditLog}.projectId`, `${TableName.Project}.id`) + // eslint-disable-next-line func-names + .where(function () { + if (orgId) { + void this.where(`${TableName.Project}.orgId`, orgId); + // .orWhere(`${TableName.AuditLog}.orgId`, orgId); + } else if (projectId) { + void this.where(`${TableName.AuditLog}.projectId`, projectId); + } + if (userAgentType) { + void this.where(`${TableName.AuditLog}.userAgentType`, userAgentType); + } + }); + // Select statements + void sqlQuery .select(selectAllTableCols(TableName.AuditLog)) - .select( db.ref("name").withSchema(TableName.Project).as("projectName"), db.ref("slug").withSchema(TableName.Project).as("projectSlug") ) - .limit(limit) .offset(offset) .orderBy(`${TableName.AuditLog}.createdAt`, "desc"); + // Special case: Filter by actor ID if (actorId) { void sqlQuery.whereRaw(`"actorMetadata"->>'userId' = ?`, [actorId]); } + // Special case: Filter by key/value pairs in eventMetadata field if (eventMetadata && Object.keys(eventMetadata).length) { Object.entries(eventMetadata).forEach(([key, value]) => { void sqlQuery.whereRaw(`"eventMetadata"->>'${key}' = ?`, [value]); }); } + // Filter by actor type if (actorType) { void sqlQuery.where("actor", actorType); } + // Filter by event types if (eventType?.length) { void sqlQuery.whereIn("eventType", eventType); } + // Filter by date range if (startDate) { void sqlQuery.where(`${TableName.AuditLog}.createdAt`, ">=", startDate); } @@ -97,13 +107,19 @@ export const auditLogDALFactory = (db: TDbClient) => { } const docs = await sqlQuery; - return docs.map((doc) => ({ - ...AuditLogsSchema.parse(doc), - project: { - name: doc.projectName, - slug: doc.projectSlug - } - })); + return docs.map((doc) => { + // Our type system refuses to acknowledge that the project name and slug are present in the doc, due to the disjointed query structure above. + // This is a quick and dirty way to get around the types. + const projectDoc = doc as unknown as { projectName: string; projectSlug: string }; + + return { + ...AuditLogsSchema.parse(doc), + project: { + name: projectDoc.projectName, + slug: projectDoc.projectSlug + } + }; + }); } catch (error) { throw new DatabaseError({ error }); } diff --git a/frontend/src/hooks/api/auditLogs/types.tsx b/frontend/src/hooks/api/auditLogs/types.tsx index fc616321a..80a39acae 100644 --- a/frontend/src/hooks/api/auditLogs/types.tsx +++ b/frontend/src/hooks/api/auditLogs/types.tsx @@ -8,6 +8,7 @@ export type TGetAuditLogsFilter = { userAgentType?: UserAgentType; eventMetadata?: Record; actorType?: ActorType; + projectId?: string; actorId?: string; // user ID format startDate?: Date; endDate?: Date; diff --git a/frontend/src/layouts/AppLayout/AppLayout.tsx b/frontend/src/layouts/AppLayout/AppLayout.tsx index 4e3602686..3f7f1a7e0 100644 --- a/frontend/src/layouts/AppLayout/AppLayout.tsx +++ b/frontend/src/layouts/AppLayout/AppLayout.tsx @@ -755,6 +755,16 @@ export const AppLayout = ({ children }: LayoutProps) => { )} + + + + Audit Logs + + + { + return ( +
+ + Infisical | Audit Logs + + + + +
+ ); +}; + +export default Logs; + +Logs.requireAuth = true; diff --git a/frontend/src/views/Org/AuditLogsPage/AuditLogsPage.tsx b/frontend/src/views/Org/AuditLogsPage/AuditLogsPage.tsx new file mode 100644 index 000000000..239ec7ff7 --- /dev/null +++ b/frontend/src/views/Org/AuditLogsPage/AuditLogsPage.tsx @@ -0,0 +1,20 @@ +import { OrgPermissionActions, OrgPermissionSubjects } from "@app/context"; +import { withPermission } from "@app/hoc"; +import { LogsSection } from "@app/views/Project/AuditLogsPage/components"; + +export const AuditLogsPage = withPermission( + () => { + return ( +
+
+
+

Audit Logs

+
+
+ +
+
+ ); + }, + { action: OrgPermissionActions.Read, subject: OrgPermissionSubjects.Member } // TODO(Daniel): Create a permission for org audit logs +); diff --git a/frontend/src/views/Org/AuditLogsPage/index.tsx b/frontend/src/views/Org/AuditLogsPage/index.tsx new file mode 100644 index 000000000..3864b1435 --- /dev/null +++ b/frontend/src/views/Org/AuditLogsPage/index.tsx @@ -0,0 +1 @@ +export { AuditLogsPage } from "./AuditLogsPage"; diff --git a/frontend/src/views/Project/AuditLogsPage/components/LogsFilter.tsx b/frontend/src/views/Project/AuditLogsPage/components/LogsFilter.tsx index 619b0503a..ba1b91a85 100644 --- a/frontend/src/views/Project/AuditLogsPage/components/LogsFilter.tsx +++ b/frontend/src/views/Project/AuditLogsPage/components/LogsFilter.tsx @@ -40,16 +40,24 @@ type Props = { eventType?: EventType[]; }; className?: string; + isOrgAuditLogs?: boolean; control: Control; reset: UseFormReset; watch: UseFormWatch; }; -export const LogsFilter = ({ presets, className, control, reset, watch }: Props) => { +export const LogsFilter = ({ + presets, + isOrgAuditLogs, + className, + control, + reset, + watch +}: Props) => { const [isStartDatePickerOpen, setIsStartDatePickerOpen] = useState(false); const [isEndDatePickerOpen, setIsEndDatePickerOpen] = useState(false); - const { currentWorkspace } = useWorkspace(); + const { currentWorkspace, workspaces } = useWorkspace(); const { data, isLoading } = useGetAuditLogActorFilterOpts(currentWorkspace?.id ?? ""); const renderActorSelectItem = (actor: Actor) => { @@ -112,7 +120,7 @@ export const LogsFilter = ({ presets, className, control, reset, watch }: Props) ? eventTypes.find((eventType) => eventType.value === selectedEventTypes[0]) ?.label : selectedEventTypes?.length === 0 - ? "Select event types" + ? "All events" : `${selectedEventTypes?.length} events selected`}
@@ -199,11 +207,20 @@ export const LogsFilter = ({ presets, className, control, reset, watch }: Props) className="w-40" > { + if (e === "all") onChange(undefined); + else onChange(e); + }} + className={twMerge( + "w-full border border-mineshaft-500 bg-mineshaft-700 text-mineshaft-100", + (field.value === "all" || field.value === undefined) && "text-mineshaft-400" + )} + > + + All projects + + {workspaces.map((project) => ( + + {project.name} + + ))} + + + )} + /> + )} diff --git a/frontend/src/views/Project/AuditLogsPage/components/LogsSection.tsx b/frontend/src/views/Project/AuditLogsPage/components/LogsSection.tsx index 68fd354e3..83ae4d1df 100644 --- a/frontend/src/views/Project/AuditLogsPage/components/LogsSection.tsx +++ b/frontend/src/views/Project/AuditLogsPage/components/LogsSection.tsx @@ -65,6 +65,7 @@ export const LogsSection = ({ const eventType = watch("eventType") as EventType[] | undefined; const userAgentType = watch("userAgentType") as UserAgentType | undefined; const actor = watch("actor"); + const projectId = watch("projectId"); const startDate = watch("startDate"); const endDate = watch("endDate"); @@ -73,6 +74,7 @@ export const LogsSection = ({
{showFilters && ( { const { currentWorkspace } = useWorkspace(); + const filterProjectId = + filter?.projectId ?? (!isOrgAuditLogs ? currentWorkspace?.id ?? "" : null); + const { data, isLoading, isFetchingNextPage, hasNextPage, fetchNextPage } = useGetAuditLogs( { ...filter, limit: AUDIT_LOG_LIMIT }, - !isOrgAuditLogs ? currentWorkspace?.id ?? "" : null, + filterProjectId, { refetchInterval } diff --git a/frontend/src/views/Project/AuditLogsPage/components/types.tsx b/frontend/src/views/Project/AuditLogsPage/components/types.tsx index f1165c80f..12afd0779 100644 --- a/frontend/src/views/Project/AuditLogsPage/components/types.tsx +++ b/frontend/src/views/Project/AuditLogsPage/components/types.tsx @@ -5,6 +5,7 @@ import { EventType, UserAgentType } from "@app/hooks/api/auditLogs/enums"; export const auditLogFilterFormSchema = yup .object({ eventMetadata: yup.object({}).optional(), + projectId: yup.string().optional(), eventType: yup.array(yup.string().oneOf(Object.values(EventType), "Invalid event type")), actor: yup.string(), userAgentType: yup.string().oneOf(Object.values(UserAgentType), "Invalid user agent type"), From 0f70c3ea9a905ee7d93e82f39a07766f73572aa5 Mon Sep 17 00:00:00 2001 From: Daniel Hougaard Date: Wed, 18 Sep 2024 16:52:54 +0400 Subject: [PATCH 02/14] Moved audit logs to org-level entirely --- .../views/Org/AuditLogsPage/AuditLogsPage.tsx | 5 +++-- .../AuditLogsPage/components/LogsFilter.tsx | 16 +++++++------- .../AuditLogsPage/components/LogsSection.tsx | 1 + .../AuditLogsPage/components/LogsTable.tsx | 0 .../AuditLogsPage/components/LogsTableRow.tsx | 0 .../AuditLogsPage/components/index.tsx | 0 .../AuditLogsPage/components/types.tsx | 0 .../Project/AuditLogsPage/AuditLogsPage.tsx | 21 ------------------- .../src/views/Project/AuditLogsPage/index.tsx | 1 - 9 files changed, 12 insertions(+), 32 deletions(-) rename frontend/src/views/{Project => Org}/AuditLogsPage/components/LogsFilter.tsx (94%) rename frontend/src/views/{Project => Org}/AuditLogsPage/components/LogsSection.tsx (99%) rename frontend/src/views/{Project => Org}/AuditLogsPage/components/LogsTable.tsx (100%) rename frontend/src/views/{Project => Org}/AuditLogsPage/components/LogsTableRow.tsx (100%) rename frontend/src/views/{Project => Org}/AuditLogsPage/components/index.tsx (100%) rename frontend/src/views/{Project => Org}/AuditLogsPage/components/types.tsx (100%) delete mode 100644 frontend/src/views/Project/AuditLogsPage/AuditLogsPage.tsx delete mode 100644 frontend/src/views/Project/AuditLogsPage/index.tsx diff --git a/frontend/src/views/Org/AuditLogsPage/AuditLogsPage.tsx b/frontend/src/views/Org/AuditLogsPage/AuditLogsPage.tsx index 239ec7ff7..2b6ec6744 100644 --- a/frontend/src/views/Org/AuditLogsPage/AuditLogsPage.tsx +++ b/frontend/src/views/Org/AuditLogsPage/AuditLogsPage.tsx @@ -1,6 +1,7 @@ import { OrgPermissionActions, OrgPermissionSubjects } from "@app/context"; import { withPermission } from "@app/hoc"; -import { LogsSection } from "@app/views/Project/AuditLogsPage/components"; + +import { LogsSection } from "./components"; export const AuditLogsPage = withPermission( () => { @@ -16,5 +17,5 @@ export const AuditLogsPage = withPermission(
); }, - { action: OrgPermissionActions.Read, subject: OrgPermissionSubjects.Member } // TODO(Daniel): Create a permission for org audit logs + { action: OrgPermissionActions.Read, subject: OrgPermissionSubjects.AuditLogs } ); diff --git a/frontend/src/views/Project/AuditLogsPage/components/LogsFilter.tsx b/frontend/src/views/Org/AuditLogsPage/components/LogsFilter.tsx similarity index 94% rename from frontend/src/views/Project/AuditLogsPage/components/LogsFilter.tsx rename to frontend/src/views/Org/AuditLogsPage/components/LogsFilter.tsx index ba1b91a85..cf00c9b0a 100644 --- a/frontend/src/views/Project/AuditLogsPage/components/LogsFilter.tsx +++ b/frontend/src/views/Org/AuditLogsPage/components/LogsFilter.tsx @@ -199,7 +199,7 @@ export const LogsFilter = ({ ( + render={({ field: { onChange, value, ...field }, fieldState: { error } }) => ( { if (e === "all") onChange(undefined); @@ -251,7 +251,7 @@ export const LogsFilter = ({ }} className={twMerge( "w-full border border-mineshaft-500 bg-mineshaft-700 text-mineshaft-100", - (field.value === "all" || field.value === undefined) && "text-mineshaft-400" + value === undefined && "text-mineshaft-400" )} > diff --git a/frontend/src/views/Project/AuditLogsPage/components/LogsSection.tsx b/frontend/src/views/Org/AuditLogsPage/components/LogsSection.tsx similarity index 99% rename from frontend/src/views/Project/AuditLogsPage/components/LogsSection.tsx rename to frontend/src/views/Org/AuditLogsPage/components/LogsSection.tsx index 83ae4d1df..a184b0f5c 100644 --- a/frontend/src/views/Project/AuditLogsPage/components/LogsSection.tsx +++ b/frontend/src/views/Org/AuditLogsPage/components/LogsSection.tsx @@ -47,6 +47,7 @@ export const LogsSection = ({ const { control, reset, watch } = useForm({ resolver: yupResolver(auditLogFilterFormSchema), defaultValues: { + projectId: undefined, actor: presets?.actorId, eventType: presets?.eventType || [], page: 1, diff --git a/frontend/src/views/Project/AuditLogsPage/components/LogsTable.tsx b/frontend/src/views/Org/AuditLogsPage/components/LogsTable.tsx similarity index 100% rename from frontend/src/views/Project/AuditLogsPage/components/LogsTable.tsx rename to frontend/src/views/Org/AuditLogsPage/components/LogsTable.tsx diff --git a/frontend/src/views/Project/AuditLogsPage/components/LogsTableRow.tsx b/frontend/src/views/Org/AuditLogsPage/components/LogsTableRow.tsx similarity index 100% rename from frontend/src/views/Project/AuditLogsPage/components/LogsTableRow.tsx rename to frontend/src/views/Org/AuditLogsPage/components/LogsTableRow.tsx diff --git a/frontend/src/views/Project/AuditLogsPage/components/index.tsx b/frontend/src/views/Org/AuditLogsPage/components/index.tsx similarity index 100% rename from frontend/src/views/Project/AuditLogsPage/components/index.tsx rename to frontend/src/views/Org/AuditLogsPage/components/index.tsx diff --git a/frontend/src/views/Project/AuditLogsPage/components/types.tsx b/frontend/src/views/Org/AuditLogsPage/components/types.tsx similarity index 100% rename from frontend/src/views/Project/AuditLogsPage/components/types.tsx rename to frontend/src/views/Org/AuditLogsPage/components/types.tsx diff --git a/frontend/src/views/Project/AuditLogsPage/AuditLogsPage.tsx b/frontend/src/views/Project/AuditLogsPage/AuditLogsPage.tsx deleted file mode 100644 index e9b8504a2..000000000 --- a/frontend/src/views/Project/AuditLogsPage/AuditLogsPage.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import { ProjectPermissionActions, ProjectPermissionSub } from "@app/context"; -import { withProjectPermission } from "@app/hoc"; - -import { LogsSection } from "./components"; - -export const AuditLogsPage = withProjectPermission( - () => { - return ( -
-
-
-

Audit Logs

-
-
- -
-
- ); - }, - { action: ProjectPermissionActions.Read, subject: ProjectPermissionSub.AuditLogs } -); diff --git a/frontend/src/views/Project/AuditLogsPage/index.tsx b/frontend/src/views/Project/AuditLogsPage/index.tsx deleted file mode 100644 index 3864b1435..000000000 --- a/frontend/src/views/Project/AuditLogsPage/index.tsx +++ /dev/null @@ -1 +0,0 @@ -export { AuditLogsPage } from "./AuditLogsPage"; From 69311f058bccc54ace46feacfb1276d0fd14da77 Mon Sep 17 00:00:00 2001 From: Daniel Hougaard Date: Wed, 18 Sep 2024 16:54:19 +0400 Subject: [PATCH 03/14] Update BackfillSecretReferenceSection.tsx --- .../BackfillSecretReferenceSection.tsx | 65 ++++++++++--------- 1 file changed, 33 insertions(+), 32 deletions(-) diff --git a/frontend/src/views/Settings/ProjectSettingsPage/components/BackfillSecretReferenceSection/BackfillSecretReferenceSection.tsx b/frontend/src/views/Settings/ProjectSettingsPage/components/BackfillSecretReferenceSection/BackfillSecretReferenceSection.tsx index 91f71f5eb..6b1da60b3 100644 --- a/frontend/src/views/Settings/ProjectSettingsPage/components/BackfillSecretReferenceSection/BackfillSecretReferenceSection.tsx +++ b/frontend/src/views/Settings/ProjectSettingsPage/components/BackfillSecretReferenceSection/BackfillSecretReferenceSection.tsx @@ -5,39 +5,40 @@ import { useBackfillSecretReference } from "@app/hooks/api"; import { ProjectMembershipRole } from "@app/hooks/api/roles/types"; export const BackfillSecretReferenceSecretion = () => { - const { currentWorkspace } = useWorkspace(); - const { membership } = useProjectPermission(); - const backfillSecretReferences = useBackfillSecretReference(); + const { currentWorkspace } = useWorkspace(); + const { membership } = useProjectPermission(); + const backfillSecretReferences = useBackfillSecretReference(); - if (!currentWorkspace) return null; + if (!currentWorkspace) return null; - const handleBackfill = async () => { - if (backfillSecretReferences.isLoading) return; - try { - await backfillSecretReferences.mutateAsync({ projectId: currentWorkspace.id || "" }); - createNotification({ text: "Successfully re-indexed secret references", type: "success" }); - } catch { - createNotification({ text: "Failed to re-index secret references", type: "error" }); - } - }; + const handleBackfill = async () => { + if (backfillSecretReferences.isLoading) return; + try { + await backfillSecretReferences.mutateAsync({ projectId: currentWorkspace.id || "" }); + createNotification({ text: "Successfully re-indexed secret references", type: "success" }); + } catch { + createNotification({ text: "Failed to re-index secret references", type: "error" }); + } + }; - const isAdmin = membership.roles.includes(ProjectMembershipRole.Admin); - return ( -
-
-

Index Secret References

-
-

- This will index all secret references, enabling integrations to be triggered when their values change going forward. -

- -
- ); + const isAdmin = membership.roles.includes(ProjectMembershipRole.Admin); + return ( +
+
+

Index Secret References

+
+

+ This will index all secret references, enabling integrations to be triggered when their + values change going forward. This happens automatically when secrets are created or updated. +

+ +
+ ); }; From 51c0598b50cb57344ffb85f75877089349a52606 Mon Sep 17 00:00:00 2001 From: Daniel Hougaard Date: Wed, 18 Sep 2024 16:54:51 +0400 Subject: [PATCH 04/14] feat: audit log permissions --- backend/src/ee/services/audit-log/audit-log-service.ts | 7 ++++--- backend/src/ee/services/permission/org-permission.ts | 9 ++++++++- frontend/src/context/OrgPermissionContext/types.ts | 6 ++++-- .../RolePage/components/OrgRoleModifySection.utils.ts | 2 ++ .../RolePermissionsSection/RolePermissionsSection.tsx | 4 ++++ 5 files changed, 22 insertions(+), 6 deletions(-) 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 a93b2a6e1..747c53c1a 100644 --- a/backend/src/ee/services/audit-log/audit-log-service.ts +++ b/backend/src/ee/services/audit-log/audit-log-service.ts @@ -24,6 +24,7 @@ export const auditLogServiceFactory = ({ permissionService }: TAuditLogServiceFactoryDep) => { const listAuditLogs = async ({ actorAuthMethod, actorId, actorOrgId, actor, filter }: TListProjectAuditLogDTO) => { + // Filter logs for specific project if (filter.projectId) { const { permission } = await permissionService.getProjectPermission( actor, @@ -34,6 +35,7 @@ export const auditLogServiceFactory = ({ ); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.AuditLogs); } else { + // Organization-wide logs const { permission } = await permissionService.getOrgPermission( actor, actorId, @@ -44,13 +46,12 @@ export const auditLogServiceFactory = ({ /** * NOTE (dangtony98): Update this to organization-level audit log permission check once audit logs are moved - * to the organization level + * to the organization level ✅ */ - ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Member); + ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.AuditLogs); } // If project ID is not provided, then we need to return all the audit logs for the organization itself. - const auditLogs = await auditLogDAL.find({ startDate: filter.startDate, endDate: filter.endDate, diff --git a/backend/src/ee/services/permission/org-permission.ts b/backend/src/ee/services/permission/org-permission.ts index 64ca8ad41..075037574 100644 --- a/backend/src/ee/services/permission/org-permission.ts +++ b/backend/src/ee/services/permission/org-permission.ts @@ -25,7 +25,8 @@ export enum OrgPermissionSubjects { SecretScanning = "secret-scanning", Identity = "identity", Kms = "kms", - AdminConsole = "organization-admin-console" + AdminConsole = "organization-admin-console", + AuditLogs = "audit-logs" } export type OrgPermissionSet = @@ -43,6 +44,7 @@ export type OrgPermissionSet = | [OrgPermissionActions, OrgPermissionSubjects.Billing] | [OrgPermissionActions, OrgPermissionSubjects.Identity] | [OrgPermissionActions, OrgPermissionSubjects.Kms] + | [OrgPermissionActions, OrgPermissionSubjects.AuditLogs] | [OrgPermissionAdminConsoleAction, OrgPermissionSubjects.AdminConsole]; const buildAdminPermission = () => { @@ -111,6 +113,11 @@ const buildAdminPermission = () => { can(OrgPermissionActions.Edit, OrgPermissionSubjects.Kms); can(OrgPermissionActions.Delete, OrgPermissionSubjects.Kms); + can(OrgPermissionActions.Read, OrgPermissionSubjects.AuditLogs); + can(OrgPermissionActions.Create, OrgPermissionSubjects.AuditLogs); + can(OrgPermissionActions.Edit, OrgPermissionSubjects.AuditLogs); + can(OrgPermissionActions.Delete, OrgPermissionSubjects.AuditLogs); + can(OrgPermissionAdminConsoleAction.AccessAllProjects, OrgPermissionSubjects.AdminConsole); return rules; diff --git a/frontend/src/context/OrgPermissionContext/types.ts b/frontend/src/context/OrgPermissionContext/types.ts index 5b7ef0174..c950ec179 100644 --- a/frontend/src/context/OrgPermissionContext/types.ts +++ b/frontend/src/context/OrgPermissionContext/types.ts @@ -21,7 +21,8 @@ export enum OrgPermissionSubjects { SecretScanning = "secret-scanning", Identity = "identity", Kms = "kms", - AdminConsole = "organization-admin-console" + AdminConsole = "organization-admin-console", + AuditLogs = "audit-logs" } export enum OrgPermissionAdminConsoleAction { @@ -43,6 +44,7 @@ export type OrgPermissionSet = | [OrgPermissionActions, OrgPermissionSubjects.Billing] | [OrgPermissionActions, OrgPermissionSubjects.Identity] | [OrgPermissionActions, OrgPermissionSubjects.Kms] - | [OrgPermissionAdminConsoleAction, OrgPermissionSubjects.AdminConsole]; + | [OrgPermissionAdminConsoleAction, OrgPermissionSubjects.AdminConsole] + | [OrgPermissionActions, OrgPermissionSubjects.AuditLogs]; export type TOrgPermission = MongoAbility; diff --git a/frontend/src/views/Org/RolePage/components/OrgRoleModifySection.utils.ts b/frontend/src/views/Org/RolePage/components/OrgRoleModifySection.utils.ts index 13cf2316b..027ac1bfe 100644 --- a/frontend/src/views/Org/RolePage/components/OrgRoleModifySection.utils.ts +++ b/frontend/src/views/Org/RolePage/components/OrgRoleModifySection.utils.ts @@ -32,6 +32,8 @@ export const formSchema = z.object({ create: z.boolean().optional() }) .optional(), + + "audit-logs": generalPermissionSchema, member: generalPermissionSchema, groups: generalPermissionSchema, role: generalPermissionSchema, diff --git a/frontend/src/views/Org/RolePage/components/RolePermissionsSection/RolePermissionsSection.tsx b/frontend/src/views/Org/RolePage/components/RolePermissionsSection/RolePermissionsSection.tsx index f4b237cfe..54bb903c6 100644 --- a/frontend/src/views/Org/RolePage/components/RolePermissionsSection/RolePermissionsSection.tsx +++ b/frontend/src/views/Org/RolePage/components/RolePermissionsSection/RolePermissionsSection.tsx @@ -41,6 +41,10 @@ const SIMPLE_PERMISSION_OPTIONS = [ title: "Incident Contacts", formName: "incident-contact" }, + { + title: "Audit Logs", + formName: "audit-logs" + }, { title: "Organization Profile", formName: "settings" From eee4d00a082d46417a1a645bdfe5e1afa8fcc991 Mon Sep 17 00:00:00 2001 From: Daniel Hougaard Date: Wed, 18 Sep 2024 16:55:29 +0400 Subject: [PATCH 05/14] fix: removed audit logs from project-level --- frontend/src/layouts/AppLayout/AppLayout.tsx | 12 ---------- .../pages/project/[id]/audit-logs/index.tsx | 23 ------------------- 2 files changed, 35 deletions(-) delete mode 100644 frontend/src/pages/project/[id]/audit-logs/index.tsx diff --git a/frontend/src/layouts/AppLayout/AppLayout.tsx b/frontend/src/layouts/AppLayout/AppLayout.tsx index 3f7f1a7e0..9f5e9897f 100644 --- a/frontend/src/layouts/AppLayout/AppLayout.tsx +++ b/frontend/src/layouts/AppLayout/AppLayout.tsx @@ -675,18 +675,6 @@ export const AppLayout = ({ children }: LayoutProps) => {
- - - - Audit Logs - - - { - const { t } = useTranslation(); - - return ( -
- - {t("common.head-title", { title: t("settings.project.title") })} - - - - -
- ); -}; - -export default Logs; - -Logs.requireAuth = true; From 59b8e834769b8c62c17e749f2f970c0c683e6e2c Mon Sep 17 00:00:00 2001 From: Daniel Hougaard Date: Wed, 18 Sep 2024 16:55:49 +0400 Subject: [PATCH 06/14] updated imports --- .../components/IntegrationAuditLogsSection.tsx | 2 +- .../components/UserProjectsSection/UserAuditLogsSection.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/views/IntegrationsPage/IntegrationDetailsPage/components/IntegrationAuditLogsSection.tsx b/frontend/src/views/IntegrationsPage/IntegrationDetailsPage/components/IntegrationAuditLogsSection.tsx index c90f5fef7..e7aa5b2f5 100644 --- a/frontend/src/views/IntegrationsPage/IntegrationDetailsPage/components/IntegrationAuditLogsSection.tsx +++ b/frontend/src/views/IntegrationsPage/IntegrationDetailsPage/components/IntegrationAuditLogsSection.tsx @@ -4,7 +4,7 @@ import { EmptyState } from "@app/components/v2"; import { useSubscription } from "@app/context"; import { EventType } from "@app/hooks/api/auditLogs/enums"; import { TIntegrationWithEnv } from "@app/hooks/api/integrations/types"; -import { LogsSection } from "@app/views/Project/AuditLogsPage/components"; +import { LogsSection } from "@app/views/Org/AuditLogsPage/components"; // Add more events if needed const INTEGRATION_EVENTS = [EventType.INTEGRATION_SYNCED]; diff --git a/frontend/src/views/Org/UserPage/components/UserProjectsSection/UserAuditLogsSection.tsx b/frontend/src/views/Org/UserPage/components/UserProjectsSection/UserAuditLogsSection.tsx index 9ed698bb1..a77e5eb4f 100644 --- a/frontend/src/views/Org/UserPage/components/UserProjectsSection/UserAuditLogsSection.tsx +++ b/frontend/src/views/Org/UserPage/components/UserProjectsSection/UserAuditLogsSection.tsx @@ -7,7 +7,7 @@ import { EmptyState, IconButton, Tooltip } from "@app/components/v2"; import { OrgPermissionActions, OrgPermissionSubjects, useSubscription } from "@app/context"; import { withPermission } from "@app/hoc"; import { OrgUser } from "@app/hooks/api/types"; -import { LogsSection } from "@app/views/Project/AuditLogsPage/components"; +import { LogsSection } from "@app/views/Org/AuditLogsPage/components"; type Props = { orgMembership: OrgUser; From 9f61177b62ff8ce80aeee2903718d2d0455c5034 Mon Sep 17 00:00:00 2001 From: Daniel Hougaard Date: Wed, 18 Sep 2024 17:07:00 +0400 Subject: [PATCH 07/14] feat: project-independent log support --- backend/src/ee/routes/v1/project-router.ts | 10 ++++++---- backend/src/ee/services/audit-log/audit-log-dal.ts | 13 +++++++------ backend/src/server/routes/v1/organization-router.ts | 10 ++++++---- frontend/src/hooks/api/auditLogs/types.tsx | 2 +- .../Org/AuditLogsPage/components/LogsTableRow.tsx | 2 +- 5 files changed, 21 insertions(+), 16 deletions(-) diff --git a/backend/src/ee/routes/v1/project-router.ts b/backend/src/ee/routes/v1/project-router.ts index 51a04b783..af8d9579d 100644 --- a/backend/src/ee/routes/v1/project-router.ts +++ b/backend/src/ee/routes/v1/project-router.ts @@ -122,10 +122,12 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => { }) .merge( z.object({ - project: z.object({ - name: z.string(), - slug: z.string() - }), + project: z + .object({ + name: z.string(), + slug: z.string() + }) + .optional(), event: z.object({ type: z.string(), metadata: z.any() 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 4b967bd38..18eb1a8cb 100644 --- a/backend/src/ee/services/audit-log/audit-log-dal.ts +++ b/backend/src/ee/services/audit-log/audit-log-dal.ts @@ -55,8 +55,7 @@ export const auditLogDALFactory = (db: TDbClient) => { // eslint-disable-next-line func-names .where(function () { if (orgId) { - void this.where(`${TableName.Project}.orgId`, orgId); - // .orWhere(`${TableName.AuditLog}.orgId`, orgId); + void this.where(`${TableName.Project}.orgId`, orgId).orWhere(`${TableName.AuditLog}.orgId`, orgId); } else if (projectId) { void this.where(`${TableName.AuditLog}.projectId`, projectId); } @@ -114,10 +113,12 @@ export const auditLogDALFactory = (db: TDbClient) => { return { ...AuditLogsSchema.parse(doc), - project: { - name: projectDoc.projectName, - slug: projectDoc.projectSlug - } + ...(projectDoc?.projectSlug && { + project: { + name: projectDoc.projectName, + slug: projectDoc.projectSlug + } + }) }; }); } catch (error) { diff --git a/backend/src/server/routes/v1/organization-router.ts b/backend/src/server/routes/v1/organization-router.ts index bb12a5151..dd0221737 100644 --- a/backend/src/server/routes/v1/organization-router.ts +++ b/backend/src/server/routes/v1/organization-router.ts @@ -120,10 +120,12 @@ export const registerOrgRouter = async (server: FastifyZodProvider) => { }) .merge( z.object({ - project: z.object({ - name: z.string(), - slug: z.string() - }), + project: z + .object({ + name: z.string(), + slug: z.string() + }) + .optional(), event: z.object({ type: z.string(), metadata: z.any() diff --git a/frontend/src/hooks/api/auditLogs/types.tsx b/frontend/src/hooks/api/auditLogs/types.tsx index 80a39acae..764d0b2a5 100644 --- a/frontend/src/hooks/api/auditLogs/types.tsx +++ b/frontend/src/hooks/api/auditLogs/types.tsx @@ -886,7 +886,7 @@ export type AuditLog = { userAgentType: UserAgentType; createdAt: string; updatedAt: string; - project: { + project?: { name: string; slug: string; }; diff --git a/frontend/src/views/Org/AuditLogsPage/components/LogsTableRow.tsx b/frontend/src/views/Org/AuditLogsPage/components/LogsTableRow.tsx index 7872f9f7d..33b634d46 100644 --- a/frontend/src/views/Org/AuditLogsPage/components/LogsTableRow.tsx +++ b/frontend/src/views/Org/AuditLogsPage/components/LogsTableRow.tsx @@ -531,7 +531,7 @@ export const LogsTableRow = ({ auditLog, isOrgAuditLogs, showActorColumn }: Prop {formatDate(auditLog.createdAt)} {`${eventToNameMap[auditLog.event.type]}`} - {isOrgAuditLogs && {auditLog.project.name}} + {isOrgAuditLogs && {auditLog?.project?.name ?? "N/A"}} {showActorColumn && renderActor(auditLog.actor)} {renderSource()} {renderMetadata(auditLog.event)} From 1cf8d1e3fab0ae85fc4ec335b70680e163ebc4f1 Mon Sep 17 00:00:00 2001 From: Daniel Hougaard Date: Wed, 18 Sep 2024 18:37:46 +0400 Subject: [PATCH 08/14] Fix: Added missing event cases --- .../AuditLogsPage/components/LogsTableRow.tsx | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/frontend/src/views/Org/AuditLogsPage/components/LogsTableRow.tsx b/frontend/src/views/Org/AuditLogsPage/components/LogsTableRow.tsx index 33b634d46..014301c56 100644 --- a/frontend/src/views/Org/AuditLogsPage/components/LogsTableRow.tsx +++ b/frontend/src/views/Org/AuditLogsPage/components/LogsTableRow.tsx @@ -39,6 +39,8 @@ export const LogsTableRow = ({ auditLog, isOrgAuditLogs, showActorColumn }: Prop }; const renderMetadata = (event: Event) => { + const metadataKeys = Object.keys(event.metadata); + switch (event.type) { case EventType.GET_SECRETS: return ( @@ -476,7 +478,47 @@ export const LogsTableRow = ({ auditLog, isOrgAuditLogs, showActorColumn }: Prop ); + + case EventType.GET_WORKSPACE_KEY: + return ( + +

{`Key ID: ${event.metadata.keyId}`}

+ + ); + + case EventType.LOGIN_IDENTITY_UNIVERSAL_AUTH: + case EventType.ADD_IDENTITY_UNIVERSAL_AUTH: + case EventType.UPDATE_IDENTITY_UNIVERSAL_AUTH: + case EventType.GET_IDENTITY_UNIVERSAL_AUTH_CLIENT_SECRETS: + return ( + +

{`Identity ID: ${event.metadata.identityId}`}

+ + ); + + case EventType.CREATE_IDENTITY_UNIVERSAL_AUTH_CLIENT_SECRET: + case EventType.REVOKE_IDENTITY_UNIVERSAL_AUTH_CLIENT_SECRET: + return ( + +

{`Identity ID: ${event.metadata.identityId}`}

+

{`Client Secret ID: ${event.metadata.clientSecretId}`}

+ + ); + + // ? If for some reason non the above events are matched, we will display the first 3 metadata items in the metadata object. default: + if (metadataKeys.length) { + const maxMetadataLength = metadataKeys.length > 3 ? 3 : metadataKeys.length; + return ( + + {Object.entries(event.metadata) + .slice(0, maxMetadataLength) + .map(([key, value]) => { + return

{`${key}: ${value}`}

; + })} + + ); + } return ; } }; From 8fa9f476e37fcc7dc223e4f28d20fb697cd8b994 Mon Sep 17 00:00:00 2001 From: Daniel Hougaard Date: Wed, 18 Sep 2024 18:40:54 +0400 Subject: [PATCH 09/14] fix: allow org members to read audit logs --- backend/src/ee/services/permission/org-permission.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/backend/src/ee/services/permission/org-permission.ts b/backend/src/ee/services/permission/org-permission.ts index 075037574..b9a4980be 100644 --- a/backend/src/ee/services/permission/org-permission.ts +++ b/backend/src/ee/services/permission/org-permission.ts @@ -147,6 +147,8 @@ const buildMemberPermission = () => { can(OrgPermissionActions.Edit, OrgPermissionSubjects.Identity); can(OrgPermissionActions.Delete, OrgPermissionSubjects.Identity); + can(OrgPermissionActions.Read, OrgPermissionSubjects.AuditLogs); + return rules; }; From 6885ef2e5434b6cd538a084524047572f26ec2a9 Mon Sep 17 00:00:00 2001 From: Daniel Hougaard Date: Wed, 18 Sep 2024 18:50:22 +0400 Subject: [PATCH 10/14] docs(api-reference): updated audit log endpoint --- backend/src/lib/api-docs/constants.ts | 5 ++++- backend/src/server/routes/v1/organization-router.ts | 5 +++-- docs/api-reference/endpoints/audit-logs/export-audit-log.mdx | 2 +- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/backend/src/lib/api-docs/constants.ts b/backend/src/lib/api-docs/constants.ts index 5ed7ed8f2..0740912a7 100644 --- a/backend/src/lib/api-docs/constants.ts +++ b/backend/src/lib/api-docs/constants.ts @@ -699,9 +699,12 @@ export const SECRET_IMPORTS = { export const AUDIT_LOGS = { EXPORT: { - workspaceId: "The ID of the project to export audit logs from.", + projectId: + "Optionally filter logs by project ID. If not provided, logs from the entire organization will be returned.", eventType: "The type of the event to export.", 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.", startDate: "The date to start the export from.", endDate: "The date to end the export at.", offset: "The offset to start from. If you enter 10, it will start from the 10th audit log.", diff --git a/backend/src/server/routes/v1/organization-router.ts b/backend/src/server/routes/v1/organization-router.ts index dd0221737..8170fe04a 100644 --- a/backend/src/server/routes/v1/organization-router.ts +++ b/backend/src/server/routes/v1/organization-router.ts @@ -74,7 +74,7 @@ export const registerOrgRouter = async (server: FastifyZodProvider) => { schema: { description: "Get all audit logs for an organization", querystring: z.object({ - projectId: z.string().optional(), + projectId: z.string().optional().describe(AUDIT_LOGS.EXPORT.projectId), actorType: z.nativeEnum(ActorType).optional(), // eventType is split with , for multiple values, we need to transform it to array eventType: z @@ -102,7 +102,8 @@ export const registerOrgRouter = async (server: FastifyZodProvider) => { }, {} as Record ); - }), + }) + .describe(AUDIT_LOGS.EXPORT.eventMetadata), startDate: z.string().datetime().optional().describe(AUDIT_LOGS.EXPORT.startDate), endDate: z.string().datetime().optional().describe(AUDIT_LOGS.EXPORT.endDate), offset: z.coerce.number().default(0).describe(AUDIT_LOGS.EXPORT.offset), diff --git a/docs/api-reference/endpoints/audit-logs/export-audit-log.mdx b/docs/api-reference/endpoints/audit-logs/export-audit-log.mdx index aa5adb004..d39cbe3d1 100644 --- a/docs/api-reference/endpoints/audit-logs/export-audit-log.mdx +++ b/docs/api-reference/endpoints/audit-logs/export-audit-log.mdx @@ -1,4 +1,4 @@ --- title: "Export" -openapi: "GET /api/v1/workspace/{workspaceId}/audit-logs" +openapi: "GET /api/v1/organization/audit-logs" --- From e2ea84f28a6e6166c4fd7e7170200bb2809aee82 Mon Sep 17 00:00:00 2001 From: Daniel Hougaard Date: Wed, 18 Sep 2024 18:54:01 +0400 Subject: [PATCH 11/14] Update project-router.ts --- backend/src/ee/routes/v1/project-router.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/ee/routes/v1/project-router.ts b/backend/src/ee/routes/v1/project-router.ts index af8d9579d..9d9280192 100644 --- a/backend/src/ee/routes/v1/project-router.ts +++ b/backend/src/ee/routes/v1/project-router.ts @@ -101,7 +101,7 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => { } ], params: z.object({ - workspaceId: z.string().trim().describe(AUDIT_LOGS.EXPORT.workspaceId) + workspaceId: z.string().trim().describe(AUDIT_LOGS.EXPORT.projectId) }), querystring: z.object({ eventType: z.nativeEnum(EventType).optional().describe(AUDIT_LOGS.EXPORT.eventType), From cfcc32271f82c19c320aadcb3b58427d2f0ed4a5 Mon Sep 17 00:00:00 2001 From: Daniel Hougaard Date: Wed, 18 Sep 2024 18:56:41 +0400 Subject: [PATCH 12/14] Update project-router.ts --- backend/src/ee/routes/v1/project-router.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/backend/src/ee/routes/v1/project-router.ts b/backend/src/ee/routes/v1/project-router.ts index 9d9280192..fccfbd158 100644 --- a/backend/src/ee/routes/v1/project-router.ts +++ b/backend/src/ee/routes/v1/project-router.ts @@ -87,6 +87,12 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => { } }); + /* + * Daniel: This endpoint is no longer is use. + * We are keeping it for now because it has been exposed in our public api docs for a while, so by removing it we are likely to break users workflows. + * + * Please refer to the new endpoint, GET /api/v1/organization/audit-logs, for the same (and more) functionality. + */ server.route({ method: "GET", url: "/:workspaceId/audit-logs", From 31ff6d3c17c7df6c6bcffc4ae9067933b3707817 Mon Sep 17 00:00:00 2001 From: Daniel Hougaard Date: Wed, 18 Sep 2024 20:33:35 +0400 Subject: [PATCH 13/14] Cleanup --- backend/src/ee/services/permission/project-permission.ts | 2 ++ .../RolePermissionsSection/ProjectRoleModifySection.utils.ts | 1 - .../RolePermissionsSection/RolePermissionsSection.tsx | 4 ---- 3 files changed, 2 insertions(+), 5 deletions(-) diff --git a/backend/src/ee/services/permission/project-permission.ts b/backend/src/ee/services/permission/project-permission.ts index 2cafc8b64..60daa14c4 100644 --- a/backend/src/ee/services/permission/project-permission.ts +++ b/backend/src/ee/services/permission/project-permission.ts @@ -145,6 +145,8 @@ export const fullProjectPermissionSet: [ProjectPermissionActions, ProjectPermiss [ProjectPermissionActions.Edit, ProjectPermissionSub.Tags], [ProjectPermissionActions.Delete, ProjectPermissionSub.Tags], + // TODO(Daniel): Remove the audit logs permissions from project-level permissions. + // TODO: We haven't done this yet because it might break existing roles, since those roles will become "invalid" since the audit log permission defined on those roles, no longer exist in the project-level defined permissions. [ProjectPermissionActions.Read, ProjectPermissionSub.AuditLogs], [ProjectPermissionActions.Create, ProjectPermissionSub.AuditLogs], [ProjectPermissionActions.Edit, ProjectPermissionSub.AuditLogs], diff --git a/frontend/src/views/Project/RolePage/components/RolePermissionsSection/ProjectRoleModifySection.utils.ts b/frontend/src/views/Project/RolePage/components/RolePermissionsSection/ProjectRoleModifySection.utils.ts index e3b05a41f..8b4958ee0 100644 --- a/frontend/src/views/Project/RolePage/components/RolePermissionsSection/ProjectRoleModifySection.utils.ts +++ b/frontend/src/views/Project/RolePage/components/RolePermissionsSection/ProjectRoleModifySection.utils.ts @@ -47,7 +47,6 @@ export const formSchema = z.object({ settings: generalPermissionSchema, environments: generalPermissionSchema, tags: generalPermissionSchema, - "audit-logs": generalPermissionSchema, "ip-allowlist": generalPermissionSchema, "certificate-authorities": generalPermissionSchema, certificates: generalPermissionSchema, diff --git a/frontend/src/views/Project/RolePage/components/RolePermissionsSection/RolePermissionsSection.tsx b/frontend/src/views/Project/RolePage/components/RolePermissionsSection/RolePermissionsSection.tsx index 1de24ae71..814994f90 100644 --- a/frontend/src/views/Project/RolePage/components/RolePermissionsSection/RolePermissionsSection.tsx +++ b/frontend/src/views/Project/RolePage/components/RolePermissionsSection/RolePermissionsSection.tsx @@ -65,10 +65,6 @@ const SINGLE_PERMISSION_LIST = [ title: "Tags", formName: "tags" }, - { - title: "Audit Logs", - formName: "audit-logs" - }, { title: "IP Allowlist", formName: "ip-allowlist" From 449e7672f9f8d71855967f231d9032fd7211c7b6 Mon Sep 17 00:00:00 2001 From: Daniel Hougaard Date: Fri, 20 Sep 2024 22:51:44 +0400 Subject: [PATCH 14/14] Requested changes --- backend/src/ee/services/audit-log/audit-log-dal.ts | 11 ++++++++--- .../views/Org/AuditLogsPage/components/LogsFilter.tsx | 2 +- .../views/Org/AuditLogsPage/components/LogsTable.tsx | 10 +++++++++- 3 files changed, 18 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 18eb1a8cb..5e5e6872b 100644 --- a/backend/src/ee/services/audit-log/audit-log-dal.ts +++ b/backend/src/ee/services/audit-log/audit-log-dal.ts @@ -48,6 +48,10 @@ export const auditLogDALFactory = (db: TDbClient) => { }, tx?: Knex ) => { + if (!orgId && !projectId) { + throw new Error("Either orgId or projectId must be provided"); + } + try { // Find statements const sqlQuery = (tx || db.replicaNode())(TableName.AuditLog) @@ -59,11 +63,12 @@ export const auditLogDALFactory = (db: TDbClient) => { } else if (projectId) { void this.where(`${TableName.AuditLog}.projectId`, projectId); } - if (userAgentType) { - void this.where(`${TableName.AuditLog}.userAgentType`, userAgentType); - } }); + if (userAgentType) { + void sqlQuery.where("userAgentType", userAgentType); + } + // Select statements void sqlQuery .select(selectAllTableCols(TableName.AuditLog)) diff --git a/frontend/src/views/Org/AuditLogsPage/components/LogsFilter.tsx b/frontend/src/views/Org/AuditLogsPage/components/LogsFilter.tsx index cf00c9b0a..9ea92f9a9 100644 --- a/frontend/src/views/Org/AuditLogsPage/components/LogsFilter.tsx +++ b/frontend/src/views/Org/AuditLogsPage/components/LogsFilter.tsx @@ -231,7 +231,7 @@ export const LogsFilter = ({ )} /> - {isOrgAuditLogs && workspaces.length && ( + {isOrgAuditLogs && workspaces.length > 0 && ( { const { currentWorkspace } = useWorkspace(); + // Determine the project ID for filtering const filterProjectId = - filter?.projectId ?? (!isOrgAuditLogs ? currentWorkspace?.id ?? "" : null); + // Use the projectId from the filter if it exists + filter?.projectId ?? + // Otherwise, if we're not looking at org-wide audit logs + (!isOrgAuditLogs + ? // Use the current workspace ID (or an empty string if that's null) + currentWorkspace?.id ?? "" + : // For org-wide audit logs, use null (no specific project filter) + null); const { data, isLoading, isFetchingNextPage, hasNextPage, fetchNextPage } = useGetAuditLogs( {