From ae738fae35343e43f261a5055608eb80c613f254 Mon Sep 17 00:00:00 2001 From: sean-brydon <55134778+sean-brydon@users.noreply.github.com> Date: Tue, 26 Aug 2025 12:32:49 +0100 Subject: [PATCH] revert: "fix: remove fallback permssions on organization Id. (#22769)" (#23013) * Revert "fix: remove fallback permssions on organization Id. (#22769)" This reverts commit 30061c0d58c5a23ed4c6b0e2a4654594f92b6c30. Removes the manage permission concept and fallback logic to simplify permission checking back to direct role-based checks. * remove manage from workflows * fix type error and remove manage from components --- .../__tests__/usePermissions.test.ts | 12 - .../roles/_components/usePermissions.ts | 16 +- apps/web/public/static/locales/en/common.json | 7 +- .../components/WorkflowDetailsPage.tsx | 4 +- .../pbac/domain/types/permission-registry.ts | 43 --- .../permission-check.service.test.ts | 259 +----------------- .../pbac/services/permission-check.service.ts | 84 +----- .../server/repository/workflow-permissions.ts | 7 +- .../migration.sql | 24 -- 9 files changed, 22 insertions(+), 434 deletions(-) delete mode 100644 packages/prisma/migrations/20250728091301_add_manage_permissions_to_default_roles/migration.sql diff --git a/apps/web/app/(use-page-wrapper)/settings/(settings-layout)/organizations/roles/_components/__tests__/usePermissions.test.ts b/apps/web/app/(use-page-wrapper)/settings/(settings-layout)/organizations/roles/_components/__tests__/usePermissions.test.ts index 723182ac4d..adeda4a091 100644 --- a/apps/web/app/(use-page-wrapper)/settings/(settings-layout)/organizations/roles/_components/__tests__/usePermissions.test.ts +++ b/apps/web/app/(use-page-wrapper)/settings/(settings-layout)/organizations/roles/_components/__tests__/usePermissions.test.ts @@ -44,17 +44,5 @@ describe("usePermissions", () => { expect(getResourcePermissionLevel("eventType", permissions)).toBe("all"); }); - - it("should return 'all' for resource with manage permission", () => { - const permissions = ["eventType.manage"]; - - expect(getResourcePermissionLevel("eventType", permissions)).toBe("all"); - }); - - it("should return 'all' for resource with manage permission even if other permissions are missing", () => { - const permissions = ["eventType.manage", "eventType.read"]; // Has manage and read, but missing create, update, delete - - expect(getResourcePermissionLevel("eventType", permissions)).toBe("all"); - }); }); }); diff --git a/apps/web/app/(use-page-wrapper)/settings/(settings-layout)/organizations/roles/_components/usePermissions.ts b/apps/web/app/(use-page-wrapper)/settings/(settings-layout)/organizations/roles/_components/usePermissions.ts index 7200b79475..ade70cd4e2 100644 --- a/apps/web/app/(use-page-wrapper)/settings/(settings-layout)/organizations/roles/_components/usePermissions.ts +++ b/apps/web/app/(use-page-wrapper)/settings/(settings-layout)/organizations/roles/_components/usePermissions.ts @@ -51,20 +51,14 @@ export function usePermissions(): UsePermissionsReturn { return "all"; } - // Check if user has manage permission for this resource - const hasManagePermission = permissions.includes(`${resource}.manage`); - if (hasManagePermission) { - return "all"; - } - - // Filter out internal keys like _resource and manage when checking for individual permissions - const crudPermissions = Object.keys(resourceConfig) - .filter((action) => !action.startsWith("_") && action !== "manage") + // Filter out internal keys like _resource when checking permissions + const allResourcePerms = Object.keys(resourceConfig) + .filter((action) => !action.startsWith("_")) .map((action) => `${resource}.${action}`); - const hasAllCrudPerms = crudPermissions.every((p) => permissions.includes(p)); + const hasAllPerms = allResourcePerms.every((p) => permissions.includes(p)); const hasReadPerm = permissions.includes(`${resource}.${CrudAction.Read}`); - if (hasAllCrudPerms) return "all"; + if (hasAllPerms) return "all"; if (hasReadPerm) return "read"; return "none"; }; diff --git a/apps/web/public/static/locales/en/common.json b/apps/web/public/static/locales/en/common.json index e91d1e3f5c..240781ad58 100644 --- a/apps/web/public/static/locales/en/common.json +++ b/apps/web/public/static/locales/en/common.json @@ -3336,7 +3336,6 @@ "pbac_desc_view_roles": "View roles", "pbac_desc_update_roles": "Update roles", "pbac_desc_delete_roles": "Delete roles", - "pbac_desc_manage_roles": "All actions on roles across organization teams", "pbac_desc_create_workflows": "Create and set up new workflows", "pbac_desc_view_workflows": "View existing workflows and their configurations", "pbac_desc_update_workflows": "Edit and modify workflow settings", @@ -3346,7 +3345,7 @@ "pbac_desc_view_event_types": "View event types", "pbac_desc_update_event_types": "Update event types", "pbac_desc_delete_event_types": "Delete event types", - "pbac_desc_manage_event_types": "All actions on event types across organization teams", + "pbac_desc_manage_event_types": "All actions on event types", "pbac_desc_create_teams": "Create teams", "pbac_desc_view_team_details": "View team details", "pbac_desc_update_team_settings": "Update team settings", @@ -3354,7 +3353,7 @@ "pbac_desc_invite_team_members": "Invite team members", "pbac_desc_remove_team_members": "Remove team members", "pbac_desc_change_team_member_role": "Change role of team members", - "pbac_desc_manage_teams": "All actions on teams across organization teams", + "pbac_desc_manage_teams": "All actions on teams", "pbac_desc_create_organization": "Create organization", "pbac_desc_view_organization_details": "View organization details", "pbac_desc_list_organization_members": "List organization members", @@ -3369,7 +3368,7 @@ "pbac_desc_view_organization_bookings": "View organization bookings", "pbac_desc_view_booking_recordings": "View booking recordings", "pbac_desc_update_bookings": "Update bookings", - "pbac_desc_manage_bookings": "All actions on bookings across organization teams", + "pbac_desc_manage_bookings": "All actions on bookings", "pbac_desc_view_team_insights": "View team insights", "pbac_desc_manage_team_insights": "Manage team insights", "read_permission_auto_enabled_tooltip": "Read permission is automatically enabled when creating, updating, or deleting a resource", diff --git a/packages/features/ee/workflows/components/WorkflowDetailsPage.tsx b/packages/features/ee/workflows/components/WorkflowDetailsPage.tsx index 601edfecc1..19fd718ba6 100644 --- a/packages/features/ee/workflows/components/WorkflowDetailsPage.tsx +++ b/packages/features/ee/workflows/components/WorkflowDetailsPage.tsx @@ -27,7 +27,6 @@ interface WorkflowPermissions { canView: boolean; canUpdate: boolean; canDelete: boolean; - canManage: boolean; readOnly: boolean; // Keep for backward compatibility } @@ -62,7 +61,6 @@ export default function WorkflowDetailsPage(props: Props) { canView: !teamId ? true : !props.readOnly, canUpdate: !teamId ? true : !props.readOnly, canDelete: !teamId ? true : !props.readOnly, - canManage: !teamId ? true : !props.readOnly, readOnly: !teamId ? false : props.readOnly, }; @@ -119,7 +117,7 @@ export default function WorkflowDetailsPage(props: Props) { numberVerificationPending: false, includeCalendarEvent: false, verifiedAt: SCANNING_WORKFLOW_STEPS ? null : new Date(), - agentId: null + agentId: null, }; steps?.push(step); form.setValue("steps", steps); diff --git a/packages/features/pbac/domain/types/permission-registry.ts b/packages/features/pbac/domain/types/permission-registry.ts index 1f41ce55e9..4c8ea3d76e 100644 --- a/packages/features/pbac/domain/types/permission-registry.ts +++ b/packages/features/pbac/domain/types/permission-registry.ts @@ -17,7 +17,6 @@ export enum CrudAction { Read = "read", Update = "update", Delete = "delete", - Manage = "manage", } export enum CustomAction { @@ -142,13 +141,6 @@ export const PERMISSION_REGISTRY: PermissionRegistry = { i18nKey: "pbac_action_delete", descriptionI18nKey: "pbac_desc_delete_roles", }, - [CrudAction.Manage]: { - description: "Manage roles on all sub-teams", - category: "role", - i18nKey: "pbac_action_manage", - descriptionI18nKey: "pbac_desc_manage_roles", - scope: [Scope.Organization], // Only organizations should have "Manage" permissions - }, }, [Resource.EventType]: { _resource: { @@ -178,13 +170,6 @@ export const PERMISSION_REGISTRY: PermissionRegistry = { i18nKey: "pbac_action_delete", descriptionI18nKey: "pbac_desc_delete_event_types", }, - [CrudAction.Manage]: { - description: "Manage event types", - category: "event", - i18nKey: "pbac_action_manage", - descriptionI18nKey: "pbac_desc_manage_event_types", - scope: [Scope.Organization], // Only organizations should have "Manage" permissions - }, }, [Resource.Team]: { _resource: { @@ -233,13 +218,6 @@ export const PERMISSION_REGISTRY: PermissionRegistry = { i18nKey: "pbac_action_change_member_role", descriptionI18nKey: "pbac_desc_change_team_member_role", }, - [CrudAction.Manage]: { - description: "Manage team members", - category: "team", - i18nKey: "pbac_action_manage", - descriptionI18nKey: "pbac_desc_manage_team_members", - scope: [Scope.Organization], // Only organizations should have "Manage" permissions - }, }, [Resource.Organization]: { _resource: { @@ -338,13 +316,6 @@ export const PERMISSION_REGISTRY: PermissionRegistry = { i18nKey: "pbac_action_update", descriptionI18nKey: "pbac_desc_update_bookings", }, - [CrudAction.Manage]: { - description: "Manage bookings", - category: "booking", - i18nKey: "pbac_action_manage", - descriptionI18nKey: "pbac_desc_manage_bookings", - scope: [Scope.Organization], // Only organizations should have "Manage" permissions - }, }, [Resource.Insights]: { _resource: { @@ -385,13 +356,6 @@ export const PERMISSION_REGISTRY: PermissionRegistry = { i18nKey: "pbac_action_delete", descriptionI18nKey: "pbac_desc_delete_workflows", }, - [CrudAction.Manage]: { - description: "Manage workflows", - category: "workflow", - i18nKey: "pbac_action_manage", - descriptionI18nKey: "pbac_desc_manage_workflows", - scope: [Scope.Organization], - }, }, [Resource.Attributes]: { _resource: { @@ -450,12 +414,5 @@ export const PERMISSION_REGISTRY: PermissionRegistry = { i18nKey: "pbac_action_delete", descriptionI18nKey: "pbac_desc_delete_routing_forms", }, - [CrudAction.Manage]: { - description: "Manage routing forms", - category: "routing", - i18nKey: "pbac_action_manage", - descriptionI18nKey: "pbac_desc_manage_routing_forms", - scope: [Scope.Organization], - }, }, }; diff --git a/packages/features/pbac/services/__tests__/permission-check.service.test.ts b/packages/features/pbac/services/__tests__/permission-check.service.test.ts index 96e469ecf2..c415a8dddd 100644 --- a/packages/features/pbac/services/__tests__/permission-check.service.test.ts +++ b/packages/features/pbac/services/__tests__/permission-check.service.test.ts @@ -267,6 +267,15 @@ describe("PermissionCheckService", () => { (MembershipRepository.findUniqueByUserIdAndTeamId as Mock).mockResolvedValueOnce(membership); mockFeaturesRepository.checkIfTeamHasFeature.mockResolvedValueOnce(true); + mockRepository.getMembershipByMembershipId.mockResolvedValueOnce({ + id: membership.id, + teamId: membership.teamId, + userId: membership.userId, + customRoleId: membership.customRoleId, + team: { parentId: null }, + }); + mockRepository.getOrgMembership.mockResolvedValueOnce(null); + mockRepository.checkRolePermissions.mockResolvedValueOnce(false); const result = await service.checkPermissions({ userId: 1, @@ -276,8 +285,7 @@ describe("PermissionCheckService", () => { }); expect(result).toBe(false); - // Should not call repository methods for empty permissions array (security measure) - expect(mockRepository.checkRolePermissions).not.toHaveBeenCalled(); + expect(mockRepository.checkRolePermissions).toHaveBeenCalledWith("admin_role", []); }); }); @@ -624,252 +632,5 @@ describe("PermissionCheckService", () => { Resource.EventType ); }); - - it("should expand permissions when user has manage permission", async () => { - mockFeaturesRepository.checkIfTeamHasFeature.mockResolvedValueOnce(true); - mockRepository.getMembershipByUserAndTeam.mockResolvedValueOnce({ - id: 1, - teamId: 1, - userId: 1, - customRoleId: "admin_role", - team: { parentId: null }, - }); - - // User has manage permission - mockRepository.getResourcePermissionsByRoleId.mockResolvedValueOnce(["manage", "read"]); - - const result = await service.getResourcePermissions({ - userId: 1, - teamId: 1, - resource: Resource.EventType, - }); - - // Should include all possible actions for eventType resource - expect(result).toContain("eventType.manage"); - expect(result).toContain("eventType.create"); - expect(result).toContain("eventType.read"); - expect(result).toContain("eventType.update"); - expect(result).toContain("eventType.delete"); - expect(result.length).toBeGreaterThan(2); // More than just manage and read - }); - - it("should expand permissions when user has manage permission at org level", async () => { - mockFeaturesRepository.checkIfTeamHasFeature.mockResolvedValueOnce(true); - mockRepository.getMembershipByUserAndTeam.mockResolvedValueOnce({ - id: 1, - teamId: 1, - userId: 1, - customRoleId: "team_role", - team: { parentId: 2 }, - }); - mockRepository.getOrgMembership.mockResolvedValueOnce({ - id: 2, - teamId: 2, - userId: 1, - customRoleId: "admin_role", - }); - - // Team has basic permissions, org has manage - mockRepository.getResourcePermissionsByRoleId - .mockResolvedValueOnce(["read"]) // team permissions - .mockResolvedValueOnce(["manage"]); // org permissions - - const result = await service.getResourcePermissions({ - userId: 1, - teamId: 1, - resource: Resource.Role, - }); - - // Should include all possible actions for role resource due to manage permission - expect(result).toContain("role.manage"); - expect(result).toContain("role.create"); - expect(result).toContain("role.read"); - expect(result).toContain("role.update"); - expect(result).toContain("role.delete"); - }); - }); - - describe("hasPermission with manage permissions", () => { - it("should return true when user has manage permission for the resource", async () => { - const membership = { - id: 1, - teamId: 1, - userId: 1, - accepted: true, - role: "ADMIN" as MembershipRole, - customRoleId: "admin_role", - disableImpersonation: false, - createdAt: new Date(), - updatedAt: new Date(), - }; - - (MembershipRepository.findUniqueByUserIdAndTeamId as Mock).mockResolvedValueOnce(membership); - mockFeaturesRepository.checkIfTeamHasFeature.mockResolvedValueOnce(true); - mockRepository.getMembershipByMembershipId.mockResolvedValueOnce({ - id: membership.id, - teamId: membership.teamId, - userId: membership.userId, - customRoleId: membership.customRoleId, - team: { parentId: null }, - }); - mockRepository.getOrgMembership.mockResolvedValueOnce(null); - - // User doesn't have explicit permission but has manage permission - mockRepository.checkRolePermission - .mockResolvedValueOnce(false) // explicit permission check fails - .mockResolvedValueOnce(true); // manage permission check succeeds - - const result = await service.checkPermission({ - userId: 1, - teamId: 1, - permission: "eventType.create", - fallbackRoles: ["ADMIN", "OWNER"], - }); - - expect(result).toBe(true); - expect(mockRepository.checkRolePermission).toHaveBeenCalledWith("admin_role", "eventType.create"); - expect(mockRepository.checkRolePermission).toHaveBeenCalledWith("admin_role", "eventType.manage"); - }); - - it("should return true when user has manage permission at org level", async () => { - const membership = { - id: 1, - teamId: 1, - userId: 1, - accepted: true, - role: "MEMBER" as MembershipRole, - customRoleId: "member_role", - disableImpersonation: false, - createdAt: new Date(), - updatedAt: new Date(), - }; - - (MembershipRepository.findUniqueByUserIdAndTeamId as Mock).mockResolvedValueOnce(membership); - mockFeaturesRepository.checkIfTeamHasFeature.mockResolvedValueOnce(true); - mockRepository.getMembershipByMembershipId.mockResolvedValueOnce({ - id: membership.id, - teamId: membership.teamId, - userId: membership.userId, - customRoleId: membership.customRoleId, - team: { parentId: 2 }, - }); - mockRepository.getOrgMembership.mockResolvedValueOnce({ - id: 2, - teamId: 2, - userId: 1, - customRoleId: "admin_role", - }); - - // Team level permissions fail, org level manage permission succeeds - mockRepository.checkRolePermission - .mockResolvedValueOnce(false) // team explicit permission - .mockResolvedValueOnce(false) // team manage permission - .mockResolvedValueOnce(true); // org manage permission - - const result = await service.checkPermission({ - userId: 1, - teamId: 1, - permission: "role.delete", - fallbackRoles: ["ADMIN", "OWNER"], - }); - - expect(result).toBe(true); - expect(mockRepository.checkRolePermission).toHaveBeenCalledWith("admin_role", "role.manage"); - }); - }); - - describe("hasPermissions with manage permissions", () => { - it("should return true when user has manage permissions for all requested resources", async () => { - const membership = { - id: 1, - teamId: 1, - userId: 1, - accepted: true, - role: "ADMIN" as MembershipRole, - customRoleId: "admin_role", - disableImpersonation: false, - createdAt: new Date(), - updatedAt: new Date(), - }; - - (MembershipRepository.findUniqueByUserIdAndTeamId as Mock).mockResolvedValueOnce(membership); - mockFeaturesRepository.checkIfTeamHasFeature.mockResolvedValueOnce(true); - mockRepository.getMembershipByMembershipId.mockResolvedValueOnce({ - id: membership.id, - teamId: membership.teamId, - userId: membership.userId, - customRoleId: membership.customRoleId, - team: { parentId: null }, - }); - mockRepository.getOrgMembership.mockResolvedValueOnce(null); - - // Explicit permissions check fails, but manage permissions succeed - mockRepository.checkRolePermissions.mockResolvedValueOnce(false); - mockRepository.checkRolePermission - .mockResolvedValueOnce(true) // eventType.manage - .mockResolvedValueOnce(true); // role.manage - - const result = await service.checkPermissions({ - userId: 1, - teamId: 1, - permissions: ["eventType.create", "eventType.update", "role.delete"], - fallbackRoles: ["ADMIN", "OWNER"], - }); - - expect(result).toBe(true); - expect(mockRepository.checkRolePermission).toHaveBeenCalledWith("admin_role", "eventType.manage"); - expect(mockRepository.checkRolePermission).toHaveBeenCalledWith("admin_role", "role.manage"); - }); - - it("should return false when user has manage permission for some but not all requested resources", async () => { - const membership = { - id: 1, - teamId: 1, - userId: 1, - accepted: true, - role: "ADMIN" as MembershipRole, - customRoleId: "admin_role", - disableImpersonation: false, - createdAt: new Date(), - updatedAt: new Date(), - }; - - (MembershipRepository.findUniqueByUserIdAndTeamId as Mock).mockResolvedValueOnce(membership); - mockFeaturesRepository.checkIfTeamHasFeature.mockResolvedValueOnce(true); - mockRepository.getMembershipByMembershipId.mockResolvedValueOnce({ - id: membership.id, - teamId: membership.teamId, - userId: membership.userId, - customRoleId: membership.customRoleId, - team: { parentId: 2 }, - }); - mockRepository.getOrgMembership.mockResolvedValueOnce({ - id: 2, - teamId: 2, - userId: 1, - customRoleId: "admin_role", - }); - - // All explicit permission checks fail - mockRepository.checkRolePermissions - .mockResolvedValueOnce(false) // team permissions - .mockResolvedValueOnce(false); // org permissions - - // Has manage for eventType but not for booking - mockRepository.checkRolePermission - .mockResolvedValueOnce(true) // team eventType.manage - .mockResolvedValueOnce(false) // team booking.manage - .mockResolvedValueOnce(true) // org eventType.manage (duplicate check) - .mockResolvedValueOnce(false); // org booking.manage - - const result = await service.checkPermissions({ - userId: 1, - teamId: 1, - permissions: ["eventType.create", "booking.update"], - fallbackRoles: ["ADMIN", "OWNER"], - }); - - expect(result).toBe(false); - }); }); }); diff --git a/packages/features/pbac/services/permission-check.service.ts b/packages/features/pbac/services/permission-check.service.ts index 70566db684..8b9c67d2e5 100644 --- a/packages/features/pbac/services/permission-check.service.ts +++ b/packages/features/pbac/services/permission-check.service.ts @@ -13,7 +13,6 @@ import type { CrudAction, CustomAction, } from "../domain/types/permission-registry"; -import { PERMISSION_REGISTRY, filterResourceConfig } from "../domain/types/permission-registry"; import { PermissionRepository } from "../infrastructure/repositories/PermissionRepository"; import { PermissionService } from "./permission.service"; @@ -81,19 +80,6 @@ export class PermissionCheckService { orgActions.forEach((action) => actions.add(action)); } - // Check if user has "manage" permission - if so, grant all actions for this resource - if (actions.has("manage" as CrudAction)) { - // Get all possible actions for this resource from the permission registry - const resourceConfig = PERMISSION_REGISTRY[resource]; - if (resourceConfig) { - const allActions = Object.keys(filterResourceConfig(resourceConfig)) as ( - | CrudAction - | CustomAction - )[]; - allActions.forEach((action) => actions.add(action)); - } - } - return Array.from(actions).map((action) => PermissionMapper.toPermissionString({ resource, action })); } catch (error) { this.logger.error(error); @@ -212,22 +198,11 @@ export class PermissionCheckService { permission ); if (hasTeamPermission) return true; - - // Check if user has manage permission for this resource - const [resource] = permission.split("."); - const managePermission = `${resource}.manage` as PermissionString; - const hasManagePermission = await this.repository.checkRolePermission( - membership.customRoleId, - managePermission - ); - if (hasManagePermission) return true; } // If no team permission, check org-level permissions if (orgMembership?.customRoleId) { - const [resource] = permission.split("."); - const managePermission = `${resource}.manage` as PermissionString; - return this.repository.checkRolePermission(orgMembership.customRoleId, managePermission); + return this.repository.checkRolePermission(orgMembership.customRoleId, permission); } return false; @@ -237,11 +212,6 @@ export class PermissionCheckService { * Internal method to check multiple permissions for a specific role */ private async hasPermissions(query: PermissionCheck, permissions: PermissionString[]): Promise { - // Return false for empty permissions array to prevent privilege escalation - if (permissions.length === 0) { - return false; - } - const { membership, orgMembership } = await this.getMembership(query); // First check team-level permissions @@ -251,61 +221,11 @@ export class PermissionCheckService { permissions ); if (hasTeamPermissions) return true; - - // Check if user has manage permissions for all requested resources - const resourcesWithManage = new Set(); - for (const permission of permissions) { - const [resource] = permission.split("."); - if (!resourcesWithManage.has(resource)) { - const managePermission = `${resource}.manage` as PermissionString; - const hasManagePermission = await this.repository.checkRolePermission( - membership.customRoleId, - managePermission - ); - if (hasManagePermission) { - resourcesWithManage.add(resource); - } - } - } - - // Check if all requested permissions are covered by manage permissions - const allPermissionsCovered = permissions.every((permission) => { - const [resource] = permission.split("."); - return resourcesWithManage.has(resource); - }); - - if (allPermissionsCovered) return true; } // If no team permissions, check org-level permissions if (orgMembership?.customRoleId) { - const hasOrgPermissions = await this.repository.checkRolePermissions( - orgMembership.customRoleId, - permissions - ); - if (hasOrgPermissions) return true; - - // Check if user has manage permissions for all requested resources at org level - const resourcesWithManage = new Set(); - for (const permission of permissions) { - const [resource] = permission.split("."); - if (!resourcesWithManage.has(resource)) { - const managePermission = `${resource}.manage` as PermissionString; - const hasManagePermission = await this.repository.checkRolePermission( - orgMembership.customRoleId, - managePermission - ); - if (hasManagePermission) { - resourcesWithManage.add(resource); - } - } - } - - // Check if all requested permissions are covered by manage permissions - return permissions.every((permission) => { - const [resource] = permission.split("."); - return resourcesWithManage.has(resource); - }); + return this.repository.checkRolePermissions(orgMembership.customRoleId, permissions); } return false; diff --git a/packages/lib/server/repository/workflow-permissions.ts b/packages/lib/server/repository/workflow-permissions.ts index 9ed39e3eac..d08f360750 100644 --- a/packages/lib/server/repository/workflow-permissions.ts +++ b/packages/lib/server/repository/workflow-permissions.ts @@ -6,7 +6,6 @@ export interface WorkflowPermissions { canView: boolean; canUpdate: boolean; canDelete: boolean; - canManage: boolean; readOnly: boolean; // Keep for backward compatibility } @@ -36,18 +35,16 @@ export class WorkflowPermissionsBuilder { const mockWorkflow = { id: 0, teamId, userId: null }; // Check all permissions in parallel for better performance - const [canView, canUpdate, canDelete, canManage] = await Promise.all([ + const [canView, canUpdate, canDelete] = await Promise.all([ isAuthorized(mockWorkflow, this.currentUserId, "workflow.read"), isAuthorized(mockWorkflow, this.currentUserId, "workflow.update"), isAuthorized(mockWorkflow, this.currentUserId, "workflow.delete"), - isAuthorized(mockWorkflow, this.currentUserId, "workflow.manage"), ]); const permissions = { canView, canUpdate, canDelete, - canManage, readOnly: !canUpdate, }; @@ -64,7 +61,6 @@ export class WorkflowPermissionsBuilder { canView: isOwner, canUpdate: isOwner, canDelete: isOwner, - canManage: isOwner, readOnly: !isOwner, }; } @@ -80,7 +76,6 @@ export class WorkflowPermissionsBuilder { canView: false, canUpdate: false, canDelete: false, - canManage: false, readOnly: true, }; } diff --git a/packages/prisma/migrations/20250728091301_add_manage_permissions_to_default_roles/migration.sql b/packages/prisma/migrations/20250728091301_add_manage_permissions_to_default_roles/migration.sql deleted file mode 100644 index 865bb1cf6f..0000000000 --- a/packages/prisma/migrations/20250728091301_add_manage_permissions_to_default_roles/migration.sql +++ /dev/null @@ -1,24 +0,0 @@ --- Add manage permissions to admin and owner roles --- These permissions are for organization-level management capabilities - --- Insert manage permissions for admin role -INSERT INTO "RolePermission" (id, "roleId", resource, action, "createdAt") -SELECT - gen_random_uuid(), 'admin_role', resource, action, NOW() -FROM ( - VALUES - -- Role management permissions (organization scope) - ('role', 'manage'), - - -- Event Type management permissions (organization scope) - ('eventType', 'manage'), - - -- Team management permissions (organization scope) - ('team', 'manage'), - - -- Booking management permissions (organization scope) - ('booking', 'manage') -) AS permissions(resource, action) -ON CONFLICT ("roleId", resource, action) DO NOTHING; - --- Note: Owner role already has wildcard permissions (*.*) so it inherits all manage permissions automatically \ No newline at end of file