Skip to content

Commit 23920fe

Browse files
anikdhabalcubic-dev-ai[bot]alishaz-polymath
authored
fix: Org/team admin or owner should be marked as ‘no show’ in a member’s booking (calcom#26009)
* fix: update tempOrgRediret on updating s lug * Update packages/trpc/server/routers/viewer/organizations/adminUpdate.handler.ts Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com> * refactor * fix * fix * revert * fix handle no show * revert * Remove metadata from organization query selection Removed metadata selection from organization query. * use repo * update * Update packages/features/bookings/lib/checkIfUserIsAuthorizedToManageBooking.ts Co-authored-by: Syed Ali Shahbaz <52925846+alishaz-polymath@users.noreply.github.com> * update --------- Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com> Co-authored-by: Syed Ali Shahbaz <52925846+alishaz-polymath@users.noreply.github.com>
1 parent 33eee8d commit 23920fe

3 files changed

Lines changed: 38 additions & 88 deletions

File tree

apps/api/v2/src/ee/bookings/2024-08-13/controllers/e2e/emails/confirm-emails.e2e-spec.ts

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,9 @@ import { BookingsRepositoryFixture } from "test/fixtures/repository/bookings.rep
1616
import { EventTypesRepositoryFixture } from "test/fixtures/repository/event-types.repository.fixture";
1717
import { OAuthClientRepositoryFixture } from "test/fixtures/repository/oauth-client.repository.fixture";
1818
import { TeamRepositoryFixture } from "test/fixtures/repository/team.repository.fixture";
19+
import { TokensRepositoryFixture } from "test/fixtures/repository/tokens.repository.fixture";
1920
import { UserRepositoryFixture } from "test/fixtures/repository/users.repository.fixture";
2021
import { randomString } from "test/utils/randomString";
21-
import { withApiAuth } from "test/utils/withApiAuth";
2222

2323
import { CAL_API_VERSION_HEADER, SUCCESS_STATUS, VERSION_2024_08_13 } from "@calcom/platform-constants";
2424
import {
@@ -73,6 +73,7 @@ type EmailSetup = {
7373
eventTypeId: number;
7474
createdBookingUid: string;
7575
rescheduledBookingUid: string;
76+
accessToken: string;
7677
};
7778

7879
describe("Bookings Endpoints 2024-08-13 confirm emails", () => {
@@ -85,6 +86,7 @@ describe("Bookings Endpoints 2024-08-13 confirm emails", () => {
8586
let eventTypesRepositoryFixture: EventTypesRepositoryFixture;
8687
let oauthClientRepositoryFixture: OAuthClientRepositoryFixture;
8788
let teamRepositoryFixture: TeamRepositoryFixture;
89+
let tokensRepositoryFixture: TokensRepositoryFixture;
8890

8991
let emailsEnabledSetup: EmailSetup;
9092
let emailsDisabledSetup: EmailSetup;
@@ -94,12 +96,9 @@ describe("Bookings Endpoints 2024-08-13 confirm emails", () => {
9496
const userEmailsDisabled = `confirm-emails-2024-08-13-user-${randomString()}@api.com`;
9597

9698
beforeAll(async () => {
97-
const moduleRef = await withApiAuth(
98-
authEmail,
99-
Test.createTestingModule({
100-
imports: [AppModule, PrismaModule, UsersModule, SchedulesModule_2024_04_15],
101-
})
102-
)
99+
const moduleRef = await Test.createTestingModule({
100+
imports: [AppModule, PrismaModule, UsersModule, SchedulesModule_2024_04_15],
101+
})
103102
.overrideGuard(PermissionsGuard)
104103
.useValue({
105104
canActivate: () => true,
@@ -112,6 +111,7 @@ describe("Bookings Endpoints 2024-08-13 confirm emails", () => {
112111
oauthClientRepositoryFixture = new OAuthClientRepositoryFixture(moduleRef);
113112
teamRepositoryFixture = new TeamRepositoryFixture(moduleRef);
114113
schedulesService = moduleRef.get<SchedulesService_2024_04_15>(SchedulesService_2024_04_15);
114+
tokensRepositoryFixture = new TokensRepositoryFixture(moduleRef);
115115

116116
organization = await teamRepositoryFixture.create({
117117
name: `confirm-emails-2024-08-13-organization-${randomString()}`,
@@ -152,6 +152,8 @@ describe("Bookings Endpoints 2024-08-13 confirm emails", () => {
152152
},
153153
});
154154

155+
const tokens = await tokensRepositoryFixture.createTokens(user.id, oAuthClientEmailsEnabled.id);
156+
155157
const userSchedule: CreateScheduleInput_2024_04_15 = {
156158
name: `confirm-emails-2024-08-13-schedule-${randomString()}`,
157159
timeZone: "Europe/Rome",
@@ -174,6 +176,7 @@ describe("Bookings Endpoints 2024-08-13 confirm emails", () => {
174176
eventTypeId: event.id,
175177
createdBookingUid: "",
176178
rescheduledBookingUid: "",
179+
accessToken: tokens.accessToken,
177180
};
178181
}
179182

@@ -188,6 +191,8 @@ describe("Bookings Endpoints 2024-08-13 confirm emails", () => {
188191
},
189192
},
190193
});
194+
const tokens = await tokensRepositoryFixture.createTokens(user.id, oAuthClientEmailsDisabled.id);
195+
191196
const userSchedule: CreateScheduleInput_2024_04_15 = {
192197
name: `confirm-emails-2024-08-13-schedule-${randomString()}`,
193198
timeZone: "Europe/Rome",
@@ -209,6 +214,7 @@ describe("Bookings Endpoints 2024-08-13 confirm emails", () => {
209214
eventTypeId: event.id,
210215
createdBookingUid: "",
211216
rescheduledBookingUid: "",
217+
accessToken: tokens.accessToken,
212218
};
213219
}
214220

@@ -277,6 +283,7 @@ describe("Bookings Endpoints 2024-08-13 confirm emails", () => {
277283
return request(app.getHttpServer())
278284
.post(`/v2/bookings/${emailsDisabledSetup.createdBookingUid}/confirm`)
279285
.set(CAL_API_VERSION_HEADER, VERSION_2024_08_13)
286+
.set("Authorization", `Bearer ${emailsDisabledSetup.accessToken}`)
280287
.expect(200)
281288
.then(async (response) => {
282289
const responseBody: GetBookingOutput_2024_08_13 = response.body;
@@ -331,6 +338,7 @@ describe("Bookings Endpoints 2024-08-13 confirm emails", () => {
331338
return request(app.getHttpServer())
332339
.post(`/v2/bookings/${emailsDisabledSetup.createdBookingUid}/decline`)
333340
.set(CAL_API_VERSION_HEADER, VERSION_2024_08_13)
341+
.set("Authorization", `Bearer ${emailsDisabledSetup.accessToken}`)
334342
.expect(200)
335343
.then(async (response) => {
336344
const responseBody: GetBookingOutput_2024_08_13 = response.body;
@@ -391,6 +399,7 @@ describe("Bookings Endpoints 2024-08-13 confirm emails", () => {
391399
return request(app.getHttpServer())
392400
.post(`/v2/bookings/${emailsEnabledSetup.createdBookingUid}/confirm`)
393401
.set(CAL_API_VERSION_HEADER, VERSION_2024_08_13)
402+
.set("Authorization", `Bearer ${emailsEnabledSetup.accessToken}`)
394403
.expect(200)
395404
.then(async (response) => {
396405
const responseBody: GetBookingOutput_2024_08_13 = response.body;
@@ -508,6 +517,7 @@ describe("Bookings Endpoints 2024-08-13 confirm emails", () => {
508517
return request(app.getHttpServer())
509518
.post(`/v2/bookings/${emailsEnabledSetup.createdBookingUid}/decline`)
510519
.set(CAL_API_VERSION_HEADER, VERSION_2024_08_13)
520+
.set("Authorization", `Bearer ${emailsEnabledSetup.accessToken}`)
511521
.expect(200)
512522
.then(async (response) => {
513523
const responseBody: GetBookingOutput_2024_08_13 = response.body;

packages/features/handleMarkNoShow.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { type TFunction } from "i18next";
22

33
import { BookingRepository } from "@calcom/features/bookings/repositories/BookingRepository";
4+
import { BookingAccessService } from "@calcom/features/bookings/services/BookingAccessService";
45
import { CreditService } from "@calcom/features/ee/billing/credit-service";
56
import { getBookerBaseUrl } from "@calcom/features/ee/organizations/lib/getBookerUrlServer";
67
import { workflowSelect } from "@calcom/features/ee/workflows/lib/getAllWorkflows";
@@ -423,9 +424,14 @@ const assertCanAccessBooking = async (bookingUid: string, userId?: number) => {
423424
if (!userId) throw new HttpError({ statusCode: 401 });
424425

425426
const bookingRepo = new BookingRepository(prisma);
426-
const booking = await bookingRepo.findBookingByUidAndUserId({ bookingUid, userId });
427+
const booking = await bookingRepo.findByUidIncludeEventTypeAndReferences({ bookingUid });
428+
const bookingAccessService = new BookingAccessService(prisma);
429+
const isAuthorized = await bookingAccessService.doesUserIdHaveAccessToBooking({
430+
userId,
431+
bookingUid,
432+
});
427433

428-
if (!booking)
434+
if (!isAuthorized)
429435
throw new HttpError({ statusCode: 403, message: "You are not allowed to access this booking" });
430436

431437
const isUpcoming = new Date(booking.endTime) >= new Date();

packages/trpc/server/routers/viewer/bookings/confirm.handler.ts

Lines changed: 13 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,12 @@ import { getCalEventResponses } from "@calcom/features/bookings/lib/getCalEventR
77
import { handleConfirmation } from "@calcom/features/bookings/lib/handleConfirmation";
88
import { handleWebhookTrigger } from "@calcom/features/bookings/lib/handleWebhookTrigger";
99
import { processPaymentRefund } from "@calcom/features/bookings/lib/payment/processPaymentRefund";
10+
import { BookingAccessService } from "@calcom/features/bookings/services/BookingAccessService";
1011
import { CreditService } from "@calcom/features/ee/billing/credit-service";
1112
import { getBookerBaseUrl } from "@calcom/features/ee/organizations/lib/getBookerUrlServer";
1213
import { workflowSelect } from "@calcom/features/ee/workflows/lib/getAllWorkflows";
1314
import { getAllWorkflowsFromEventType } from "@calcom/features/ee/workflows/lib/getAllWorkflowsFromEventType";
1415
import { WorkflowService } from "@calcom/features/ee/workflows/lib/service/WorkflowService";
15-
import { PrismaOrgMembershipRepository } from "@calcom/features/membership/repositories/PrismaOrgMembershipRepository";
1616
import type { GetSubscriberOptions } from "@calcom/features/webhooks/lib/getWebhooks";
1717
import type { EventPayloadType, EventTypeInfo } from "@calcom/features/webhooks/lib/sendPayload";
1818
import getOrgIdFromMemberOrTeamId from "@calcom/lib/getOrgIdFromMemberOrTeamId";
@@ -24,13 +24,7 @@ import { getTimeFormatStringFromUserTimeFormat } from "@calcom/lib/timeFormat";
2424
import type { TraceContext } from "@calcom/lib/tracing";
2525
import { prisma } from "@calcom/prisma";
2626
import { Prisma } from "@calcom/prisma/client";
27-
import {
28-
BookingStatus,
29-
MembershipRole,
30-
WebhookTriggerEvents,
31-
WorkflowTriggerEvents,
32-
UserPermissionRole,
33-
} from "@calcom/prisma/enums";
27+
import { BookingStatus, WebhookTriggerEvents, WorkflowTriggerEvents } from "@calcom/prisma/enums";
3428
import type { EventTypeMetadata } from "@calcom/prisma/zod-utils";
3529
import type { CalendarEvent } from "@calcom/types/Calendar";
3630

@@ -149,14 +143,19 @@ export const confirmHandler = async ({ ctx, input }: ConfirmOptions) => {
149143
throw new TRPCError({ code: "BAD_REQUEST", message: "Booking must have an organizer" });
150144
}
151145

152-
await checkIfUserIsAuthorizedToConfirmBooking({
153-
eventTypeId: booking.eventTypeId,
154-
loggedInUserId: ctx.user.id,
155-
teamId: booking.eventType?.teamId || booking.eventType?.parent?.teamId,
156-
bookingUserId: booking.userId,
157-
userRole: ctx.user.role,
146+
const bookingAccessService = new BookingAccessService(prisma);
147+
const isUserAuthorizedToConfirmBooking = await bookingAccessService.doesUserIdHaveAccessToBooking({
148+
userId: ctx.user.id,
149+
bookingId: bookingId,
158150
});
159151

152+
if (!isUserAuthorizedToConfirmBooking) {
153+
throw new TRPCError({
154+
code: "UNAUTHORIZED",
155+
message: "User is not authorized to confirm this booking",
156+
});
157+
}
158+
160159
// Do not move this before authorization check.
161160
// This is done to avoid exposing extra information to the requester.
162161
if (booking.status === BookingStatus.ACCEPTED) {
@@ -443,68 +442,3 @@ export const confirmHandler = async ({ ctx, input }: ConfirmOptions) => {
443442

444443
return { message, status };
445444
};
446-
447-
const checkIfUserIsAuthorizedToConfirmBooking = async ({
448-
eventTypeId,
449-
loggedInUserId,
450-
teamId,
451-
bookingUserId,
452-
userRole,
453-
}: {
454-
eventTypeId: number | null;
455-
loggedInUserId: number;
456-
teamId?: number | null;
457-
bookingUserId: number | null;
458-
userRole: string;
459-
}): Promise<void> => {
460-
// check system wide admin
461-
if (userRole === UserPermissionRole.ADMIN) return;
462-
463-
// Check if the user is the owner of the event type
464-
if (bookingUserId === loggedInUserId) return;
465-
466-
// Check if user is associated with the event type
467-
if (eventTypeId) {
468-
const [loggedInUserAsHostOfEventType, loggedInUserAsUserOfEventType] = await Promise.all([
469-
prisma.eventType.findUnique({
470-
where: {
471-
id: eventTypeId,
472-
hosts: { some: { userId: loggedInUserId } },
473-
},
474-
select: { id: true },
475-
}),
476-
prisma.eventType.findUnique({
477-
where: {
478-
id: eventTypeId,
479-
users: { some: { id: loggedInUserId } },
480-
},
481-
select: { id: true },
482-
}),
483-
]);
484-
485-
if (loggedInUserAsHostOfEventType || loggedInUserAsUserOfEventType) return;
486-
}
487-
488-
// Check if the user is an admin/owner of the team the booking belongs to
489-
if (teamId) {
490-
const membership = await prisma.membership.findFirst({
491-
where: {
492-
userId: loggedInUserId,
493-
teamId: teamId,
494-
role: {
495-
in: [MembershipRole.OWNER, MembershipRole.ADMIN],
496-
},
497-
},
498-
});
499-
if (membership) return;
500-
}
501-
502-
if (
503-
bookingUserId &&
504-
(await PrismaOrgMembershipRepository.isLoggedInUserOrgAdminOfBookingHost(loggedInUserId, bookingUserId))
505-
) {
506-
return;
507-
}
508-
509-
throw new TRPCError({ code: "UNAUTHORIZED", message: "User is not authorized to confirm this booking" });
510-
};

0 commit comments

Comments
 (0)