diff --git a/packages/app-store/routing-forms/api/responses/[formId].ts b/packages/app-store/routing-forms/api/responses/[formId].ts index 8b8994a689..19af9ce706 100644 --- a/packages/app-store/routing-forms/api/responses/[formId].ts +++ b/packages/app-store/routing-forms/api/responses/[formId].ts @@ -87,20 +87,13 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) id: formId, ...entityPrismaWhereClause({ userId: user.id }), }, - include: { - team: { - select: { - members: true, - }, - }, - }, }); if (!form) { return res.status(404).json({ message: "Form not found or unauthorized" }); } - if (!canEditEntity(form, user.id)) { + if (!(await canEditEntity(form, user.id))) { return res.status(404).json({ message: "Form not found or unauthorized" }); } diff --git a/packages/app-store/routing-forms/lib/isFormCreateEditAllowed.ts b/packages/app-store/routing-forms/lib/isFormCreateEditAllowed.ts index 2c514fdfb0..a454da3645 100644 --- a/packages/app-store/routing-forms/lib/isFormCreateEditAllowed.ts +++ b/packages/app-store/routing-forms/lib/isFormCreateEditAllowed.ts @@ -22,11 +22,6 @@ export async function isFormCreateEditAllowed({ select: { userId: true, teamId: true, - team: { - select: { - members: true, - }, - }, }, }); @@ -37,5 +32,5 @@ export async function isFormCreateEditAllowed({ }); } - return canEditEntity(form, userId); + return await canEditEntity(form, userId); } diff --git a/packages/app-store/routing-forms/trpc/formMutation.handler.ts b/packages/app-store/routing-forms/trpc/formMutation.handler.ts index 84350165fb..08299432b7 100644 --- a/packages/app-store/routing-forms/trpc/formMutation.handler.ts +++ b/packages/app-store/routing-forms/trpc/formMutation.handler.ts @@ -367,7 +367,7 @@ export const formMutationHandler = async ({ ctx, input }: FormMutationHandlerOpt }); } - if (!canEditEntity(sourceForm, userId)) { + if (!(await canEditEntity(sourceForm, userId))) { throw new TRPCError({ code: "NOT_FOUND", message: `Form to duplicate: ${duplicateFrom} not found or you are unauthorized`, diff --git a/packages/app-store/routing-forms/trpc/forms.handler.ts b/packages/app-store/routing-forms/trpc/forms.handler.ts index 30e91677b5..5b5b237935 100644 --- a/packages/app-store/routing-forms/trpc/forms.handler.ts +++ b/packages/app-store/routing-forms/trpc/forms.handler.ts @@ -36,8 +36,9 @@ export const formsHandler = async ({ ctx, input }: FormsHandlerOptions) => { ], include: { team: { - include: { - members: true, + select: { + id: true, + name: true, }, }, _count: { @@ -54,15 +55,19 @@ export const formsHandler = async ({ ctx, input }: FormsHandlerOptions) => { }), }); - const serializableForms = []; - for (let i = 0; i < forms.length; i++) { - const form = forms[i]; - const hasWriteAccess = canEditEntity(form, user.id); - serializableForms.push({ - form: await getSerializableForm({ form: forms[i] }), - readOnly: !hasWriteAccess, - }); - } + const serializableForms = await Promise.all( + forms.map(async (form) => { + const [hasWriteAccess, serializedForm] = await Promise.all([ + canEditEntity(form, user.id), + getSerializableForm({ form }), + ]); + + return { + form: serializedForm, + readOnly: !hasWriteAccess, + }; + }) + ); return { filtered: serializableForms, diff --git a/packages/app-store/routing-forms/trpc/getResponseWithFormFields.handler.ts b/packages/app-store/routing-forms/trpc/getResponseWithFormFields.handler.ts index 76602ff5f7..06f5375873 100644 --- a/packages/app-store/routing-forms/trpc/getResponseWithFormFields.handler.ts +++ b/packages/app-store/routing-forms/trpc/getResponseWithFormFields.handler.ts @@ -49,6 +49,7 @@ async function getResponseWithFormFieldsHandler({ ctx, input }: GetResponseWithF }, team: { select: { + id: true, members: true, slug: true, parent: { @@ -74,7 +75,7 @@ async function getResponseWithFormFieldsHandler({ ctx, input }: GetResponseWithF // TODO: To make the check stricter, we could check if the user is admin/owner of the team or a member that is the organizer. // But the exact criteria of showing a booking to the user could be trickier. Maybe we allow hosts as well to access the booking and thus should allow them as well to reroute - if (!canAccessEntity(form, user.id)) { + if (!(await canAccessEntity(form, user.id))) { throw new TRPCError({ code: "FORBIDDEN", message: translate("you_dont_have_access_to_reroute_this_booking"), diff --git a/packages/lib/entityPermissionUtils.ts b/packages/lib/entityPermissionUtils.ts index 1a26a9c967..f64ab7313d 100644 --- a/packages/lib/entityPermissionUtils.ts +++ b/packages/lib/entityPermissionUtils.ts @@ -1,4 +1,3 @@ -import type { Membership } from "@calcom/prisma/client"; import { MembershipRole } from "@calcom/prisma/enums"; export const enum ENTITY_PERMISSION_LEVEL { @@ -11,22 +10,22 @@ export const enum ENTITY_PERMISSION_LEVEL { TEAM_WRITE, } -export function canEditEntity( +export async function canEditEntity( entity: Parameters[0], userId: Parameters[1] ) { - const permissionLevel = getEntityPermissionLevel(entity, userId); + const permissionLevel = await getEntityPermissionLevel(entity, userId); return ( permissionLevel === ENTITY_PERMISSION_LEVEL.TEAM_WRITE || permissionLevel === ENTITY_PERMISSION_LEVEL.USER_ONLY_WRITE ); } -export function canAccessEntity( +export async function canAccessEntity( entity: Parameters[0], userId: Parameters[1] ) { - const permissionLevel = getEntityPermissionLevel(entity, userId); + const permissionLevel = await getEntityPermissionLevel(entity, userId); return ( permissionLevel === ENTITY_PERMISSION_LEVEL.TEAM_WRITE || permissionLevel === ENTITY_PERMISSION_LEVEL.USER_ONLY_WRITE || @@ -34,15 +33,24 @@ export function canAccessEntity( ); } -export function getEntityPermissionLevel( +export async function getEntityPermissionLevel( entity: { userId: number | null; - team: { members: Membership[] } | null; + teamId: number | null; }, userId: number ) { - if (entity.team) { - const roleForTeamMember = entity.team.members.find((member) => member.userId === userId)?.role; + if (entity.teamId) { + const { prisma } = await import("@calcom/prisma"); + const membership = await prisma.membership.findFirst({ + where: { + teamId: entity.teamId, + userId, + accepted: true, + }, + }); + const roleForTeamMember = membership?.role; + if (roleForTeamMember) { //TODO: Remove type assertion const hasWriteAccessToTeam = (