import { FeaturesRepository } from "@calcom/features/flags/features.repository"; import { prisma } from "@calcom/prisma"; import type { MembershipRole } from "@calcom/prisma/enums"; import { PermissionMapper } from "../domain/mappers/PermissionMapper"; import type { Resource, CustomAction } from "../domain/types/permission-registry"; import { CrudAction } from "../domain/types/permission-registry"; import { PermissionCheckService } from "../services/permission-check.service"; interface RoleMapping { roles: MembershipRole[]; condition?: (userRole: MembershipRole) => boolean; } interface FallbackRoleConfig { read?: RoleMapping; create?: RoleMapping; update?: RoleMapping; delete?: RoleMapping; } interface ResourcePermissionsOptions { userId: number; teamId: number; resource: Resource; userRole: MembershipRole; fallbackRoles?: FallbackRoleConfig; } interface ResourcePermissions { canRead: boolean; canEdit: boolean; canDelete: boolean; canCreate: boolean; } const checkRoleAccess = (userRole: MembershipRole, mapping?: RoleMapping): boolean => { if (!mapping) return false; const { roles, condition } = mapping; const hasRole = roles.includes(userRole); if (condition) { return hasRole && condition(userRole); } return hasRole; }; export const getResourcePermissions = async ({ userId, teamId, resource, userRole, fallbackRoles = {}, }: ResourcePermissionsOptions): Promise => { const featureRepo = new FeaturesRepository(prisma); const permissionService = new PermissionCheckService(); const pbacEnabled = await featureRepo.checkIfTeamHasFeature(teamId, "pbac"); // If PBAC is disabled, use fallback role configuration if (!pbacEnabled) { return { canRead: checkRoleAccess(userRole, fallbackRoles.read), canCreate: checkRoleAccess(userRole, fallbackRoles.create), canEdit: checkRoleAccess(userRole, fallbackRoles.update), canDelete: checkRoleAccess(userRole, fallbackRoles.delete), }; } // PBAC is enabled, get permissions from the service const resourcePermissions = await permissionService.getResourcePermissions({ userId, teamId, resource, }); const roleActions = PermissionMapper.toActionMap(resourcePermissions, resource); return { canRead: roleActions[CrudAction.Read] ?? false, canEdit: roleActions[CrudAction.Update] ?? false, canDelete: roleActions[CrudAction.Delete] ?? false, canCreate: roleActions[CrudAction.Create] ?? false, }; }; // Enhanced function to get specific custom action permissions type ActionType = CrudAction | CustomAction; interface SpecificActionMapping { [key: string]: RoleMapping; } interface SpecificPermissionsOptions { userId: number; teamId: number; resource: Resource; userRole: MembershipRole; actions: ActionType[]; fallbackRoles?: SpecificActionMapping; } export const getSpecificPermissions = async ({ userId, teamId, resource, userRole, actions, fallbackRoles = {}, }: SpecificPermissionsOptions): Promise> => { const featureRepo = new FeaturesRepository(prisma); const permissionService = new PermissionCheckService(); const pbacEnabled = await featureRepo.checkIfTeamHasFeature(teamId, "pbac"); // If PBAC is disabled, use fallback role configuration if (!pbacEnabled) { const permissions: Record = {}; for (const action of actions) { permissions[action] = checkRoleAccess(userRole, fallbackRoles[action]); } return permissions; } // PBAC is enabled, get permissions from the service const resourcePermissions = await permissionService.getResourcePermissions({ userId, teamId, resource, }); const roleActions = PermissionMapper.toActionMap(resourcePermissions, resource); const permissions: Record = {}; for (const action of actions) { permissions[action] = roleActions[action] ?? false; } return permissions; };