feat: implement audit logs and custom role management components

- Added new components for displaying and managing audit logs, including a data table and filters for user actions.
- Introduced a custom roles management interface, allowing users to create and modify roles with specific permissions.
- Updated permission checks to ensure proper access control for audit logs and custom roles.
- Refactored existing components to integrate new functionality and improve user experience.
This commit is contained in:
Mauricio Siu
2026-03-16 11:13:24 -06:00
parent 72fb85f616
commit a4e9c6e890
9 changed files with 38 additions and 23 deletions
@@ -88,16 +88,28 @@ describe("enterprise resources for static roles", () => {
}
});
it("member gets true for all enterprise resources", async () => {
it("member gets true for service-level enterprise resources", async () => {
memberToReturn = mockMemberData("member");
const perms = await resolvePermissions(ctx);
for (const resource of enterpriseOnlyResources) {
const actions = statements[resource as keyof typeof statements];
for (const action of actions) {
expect((perms as any)[resource][action]).toBe(true);
}
}
expect(perms.deployment.read).toBe(true);
expect(perms.deployment.create).toBe(true);
expect(perms.domain.read).toBe(true);
expect(perms.backup.read).toBe(true);
expect(perms.logs.read).toBe(true);
expect(perms.monitoring.read).toBe(true);
});
it("member gets false for org-level enterprise resources", async () => {
memberToReturn = mockMemberData("member");
const perms = await resolvePermissions(ctx);
expect(perms.server.read).toBe(false);
expect(perms.registry.read).toBe(false);
expect(perms.certificate.read).toBe(false);
expect(perms.destination.read).toBe(false);
expect(perms.notification.read).toBe(false);
expect(perms.auditLog.read).toBe(false);
});
});
@@ -3,8 +3,8 @@ import { createServerSideHelpers } from "@trpc/react-query/server";
import type { GetServerSidePropsContext } from "next";
import type { ReactElement } from "react";
import superjson from "superjson";
import { ShowAuditLogs } from "@/components/dashboard/settings/audit-logs/show-audit-logs";
import { DashboardLayout } from "@/components/layouts/dashboard-layout";
import { ShowAuditLogs } from "@/components/proprietary/audit-logs/show-audit-logs";
import { appRouter } from "@/server/api/root";
const Page = () => {
@@ -3,10 +3,10 @@ import { createServerSideHelpers } from "@trpc/react-query/server";
import type { GetServerSidePropsContext } from "next";
import type { ReactElement } from "react";
import superjson from "superjson";
import { ManageCustomRoles } from "@/components/dashboard/settings/users/manage-custom-roles";
import { DashboardLayout } from "@/components/layouts/dashboard-layout";
import { ManageCustomRoles } from "@/components/proprietary/roles/manage-custom-roles";
import { ShowInvitations } from "@/components/dashboard/settings/users/show-invitations";
import { ShowUsers } from "@/components/dashboard/settings/users/show-users";
import { DashboardLayout } from "@/components/layouts/dashboard-layout";
import { appRouter } from "@/server/api/root";
import { api } from "@/utils/api";
+13 -11
View File
@@ -168,21 +168,23 @@ export const memberRole = ac.newRole({
gitProviders: [],
traefikFiles: [],
api: [],
volume: [],
deployment: [],
envVars: [],
projectEnvVars: [],
environmentEnvVars: [],
// Service-level enterprise resources — member can do everything within services they have access to
volume: ["read", "create", "delete"],
deployment: ["read", "create", "cancel"],
envVars: ["read", "write"],
projectEnvVars: ["read", "write"],
environmentEnvVars: ["read", "write"],
backup: ["read", "create", "update", "delete", "restore"],
volumeBackup: ["read", "create", "update", "delete", "restore"],
schedule: ["read", "create", "update", "delete"],
domain: ["read", "create", "delete"],
logs: ["read"],
monitoring: ["read"],
// Org-level enterprise resources — member cannot manage these
server: [],
registry: [],
certificate: [],
backup: [],
volumeBackup: [],
schedule: [],
domain: [],
destination: [],
notification: [],
logs: [],
monitoring: [],
auditLog: [],
});
+3 -2
View File
@@ -182,13 +182,14 @@ export const resolvePermissions = async (
const legacyOverrides =
memberRecord.role === "member" ? getLegacyOverrides(memberRecord) : {};
const isStaticRole = memberRecord.role in staticRoles;
const isPrivilegedRole =
memberRecord.role === "owner" || memberRecord.role === "admin";
const result = {} as ResolvedPermissions;
for (const [resource, actions] of Object.entries(statements)) {
const resourcePerms = {} as Record<string, boolean>;
for (const action of actions) {
if (isStaticRole && enterpriseOnlyResources.has(resource)) {
if (isPrivilegedRole && enterpriseOnlyResources.has(resource)) {
resourcePerms[action] = true;
continue;
}