-
Notifications
You must be signed in to change notification settings - Fork 11
[CORRUPTED] Synthetic Benchmark PR #24640 - fix: allow org admin to cancel and reschedule seated bookings #22
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
e8d3e34
c6858f3
28dfa8b
f4f2de4
a1d3571
5d67b09
9b46778
426e8ad
9e87e85
c1b34ea
fc4849b
bc977d4
771afe9
5fa1053
6748d52
fb536c3
766c36d
2dac4fe
7c4189b
ae8ffa3
375b1d0
2597170
46857d4
8c50f68
0e70429
cf52157
9533cf3
415a952
8638c10
a1f0ad3
696754d
202d468
6f57ff8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -49,6 +49,7 @@ import { | |
| confirmBookingHandler, | ||
| getCalendarLinks, | ||
| } from "@calcom/platform-libraries"; | ||
| import { PrismaOrgMembershipRepository } from "@calcom/platform-libraries/bookings"; | ||
| import { | ||
| CreateBookingInput_2024_08_13, | ||
| CreateBookingInput, | ||
|
|
@@ -64,6 +65,7 @@ import { | |
| RescheduleBookingInput, | ||
| CancelBookingInput, | ||
| } from "@calcom/platform-types"; | ||
| import type { RescheduleSeatedBookingInput_2024_08_13 } from "@calcom/platform-types"; | ||
| import type { PrismaClient } from "@calcom/prisma"; | ||
| import type { EventType, User, Team } from "@calcom/prisma/client"; | ||
|
|
||
|
|
@@ -762,16 +764,26 @@ export class BookingsService_2024_08_13 { | |
| authUser: AuthOptionalUser | ||
| ) { | ||
| try { | ||
| await this.canRescheduleBooking(bookingUid); | ||
| const isIndividualSeatRequest = this.isRescheduleSeatedBody(body); | ||
| const isIndividualSeatReschedule = await this.shouldRescheduleIndividualSeat( | ||
| bookingUid, | ||
| isIndividualSeatRequest, | ||
| authUser | ||
| ); | ||
|
|
||
| const bookingRequest = await this.inputService.createRescheduleBookingRequest( | ||
| request, | ||
| bookingUid, | ||
| body | ||
| body, | ||
| isIndividualSeatReschedule | ||
| ); | ||
|
|
||
| await this.canRescheduleBooking(bookingUid); | ||
|
|
||
| const booking = await this.regularBookingService.createBooking({ | ||
| bookingData: bookingRequest.body, | ||
| bookingMeta: { | ||
| userId: bookingRequest.userId, | ||
| userId: bookingRequest.userId ?? authUser?.id, | ||
| hostname: bookingRequest.headers?.host || "", | ||
| platformClientId: bookingRequest.platformClientId, | ||
| platformRescheduleUrl: bookingRequest.platformRescheduleUrl, | ||
|
|
@@ -840,6 +852,57 @@ export class BookingsService_2024_08_13 { | |
| return booking; | ||
| } | ||
|
|
||
| async shouldRescheduleIndividualSeat( | ||
| bookingUid: string, | ||
| isIndividualSeatReschedule: boolean, | ||
| authUser: AuthOptionalUser | ||
| ) { | ||
| const booking = await this.bookingsRepository.getByUidWithUserIdAndSeatsReferencesCount(bookingUid); | ||
|
|
||
| if (!booking) { | ||
| throw new NotFoundException(`Booking with uid=${bookingUid} was not found in the database`); | ||
| } | ||
|
|
||
| const hasSeatsPresent = booking.seatsReferences.length > 0; | ||
|
|
||
| if (!hasSeatsPresent) return false; | ||
|
|
||
| return this.isIndividualSeatOrOrgAdminReschedule( | ||
| isIndividualSeatReschedule, | ||
| booking.userId, | ||
| authUser?.id | ||
| ); | ||
| } | ||
|
|
||
| async isIndividualSeatOrOrgAdminReschedule( | ||
| isIndividualSeatReschedule: boolean, | ||
| bookingUserId: number | null, | ||
| authUserId?: number | null | ||
| ) { | ||
| if (isIndividualSeatReschedule) { | ||
| return true; | ||
| } | ||
|
|
||
| if (!authUserId) { | ||
| throw new Error(`No auth user found`); | ||
| } | ||
|
|
||
| if (!bookingUserId) { | ||
| throw new Error(`No user found for booking`); | ||
| } | ||
|
|
||
| const isOrgAdmin = await PrismaOrgMembershipRepository.isLoggedInUserOrgAdminOfBookingHost( | ||
| bookingUserId, | ||
| authUserId | ||
| ); | ||
|
Comment on lines
+894
to
+897
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [breaking-issue] [Critical Bug: Swapped Parameters in Org Admin Authorization Check] >� Agent PromptCopy this prompt and use it to remediate the issue with your preferred AI generation tools
Comment on lines
+894
to
+897
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [action-required] [Swapped Parameters in Org Admin Function Call] >� Agent PromptCopy this prompt and use it to remediate the issue with your preferred AI generation tools
Comment on lines
+894
to
+897
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [action-required] [Swapped Parameters in Org Admin Check] >Agent PromptCopy this prompt and use it to remediate the issue with your preferred AI generation tools
Comment on lines
+894
to
+897
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 2. Org admin args swapped BookingsService_2024_08_13 calls isLoggedInUserOrgAdminOfBookingHost with `(bookingUserId, authUserId) instead of (authUserId, bookingUserId)`, so org-admin authorization is evaluated against the wrong identities. Agent Prompt
|
||
|
|
||
| return isOrgAdmin; | ||
| } | ||
|
|
||
| isRescheduleSeatedBody(body: RescheduleBookingInput): body is RescheduleSeatedBookingInput_2024_08_13 { | ||
| return "seatUid" in body; | ||
| } | ||
|
|
||
| async cancelBooking( | ||
| request: Request, | ||
| bookingUid: string, | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -507,11 +507,13 @@ export class InputBookingsService_2024_08_13 { | |
| async createRescheduleBookingRequest( | ||
| request: Request, | ||
| bookingUid: string, | ||
| body: RescheduleBookingInput | ||
| body: RescheduleBookingInput, | ||
| isIndividualSeatReschedule: boolean | ||
| ): Promise<BookingRequest> { | ||
| const bodyTransformed = this.isRescheduleSeatedBody(body) | ||
| ? await this.transformInputRescheduleSeatedBooking(bookingUid, body) | ||
| : await this.transformInputRescheduleBooking(bookingUid, body); | ||
| const bodyTransformed = | ||
| !isIndividualSeatReschedule && "seatUid" in body | ||
| ? await this.transformInputRescheduleSeatedBooking(bookingUid, body) | ||
| : await this.transformInputRescheduleBooking(bookingUid, body, isIndividualSeatReschedule); | ||
|
Comment on lines
+513
to
+516
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 3. Seat reschedule routing broken For seated reschedules where seatUid is present, createRescheduleBookingRequest routes the request to transformInputRescheduleBooking (non-seat path) because of a negated condition, causing individual-seat reschedules to fail with the “seatUid required” error. Agent Prompt
|
||
|
|
||
| const oAuthClientParams = await this.platformBookingsService.getOAuthClientParams( | ||
| bodyTransformed.eventTypeId | ||
|
|
@@ -606,7 +608,11 @@ export class InputBookingsService_2024_08_13 { | |
| }; | ||
| } | ||
|
|
||
| async transformInputRescheduleBooking(bookingUid: string, inputBooking: RescheduleBookingInput_2024_08_13) { | ||
| async transformInputRescheduleBooking( | ||
| bookingUid: string, | ||
| inputBooking: RescheduleBookingInput_2024_08_13, | ||
| isIndividualSeatReschedule: boolean | ||
| ) { | ||
| const booking = await this.bookingsRepository.getByUidWithAttendeesAndUserAndEvent(bookingUid); | ||
| if (!booking) { | ||
| throw new NotFoundException(`Booking with uid=${bookingUid} not found`); | ||
|
|
@@ -618,7 +624,7 @@ export class InputBookingsService_2024_08_13 { | |
| if (!eventType) { | ||
| throw new NotFoundException(`Event type with id=${booking.eventTypeId} not found`); | ||
| } | ||
| if (eventType.seatsPerTimeSlot) { | ||
| if (eventType.seatsPerTimeSlot && isIndividualSeatReschedule) { | ||
| throw new BadRequestException( | ||
| `Booking with uid=${bookingUid} is a seated booking which means you have to provide seatUid in the request body to specify which seat specifically you want to reschedule. First, fetch the booking using https://cal.com/docs/api-reference/v2/bookings/get-a-booking and then within the attendees array you will find the seatUid of the booking you want to reschedule. Second, provide the seatUid in the request body to specify which seat specifically you want to reschedule using the reschedule endpoint https://cal.com/docs/api-reference/v2/bookings/reschedule-a-booking#option-2` | ||
| ); | ||
|
|
@@ -652,7 +658,6 @@ export class InputBookingsService_2024_08_13 { | |
|
|
||
| const startTime = DateTime.fromISO(inputBooking.start, { zone: "utc" }).setZone(attendee.timeZone); | ||
| const endTime = startTime.plus({ minutes: eventType.length }); | ||
|
|
||
| return { | ||
| start: startTime.toISO(), | ||
| end: endTime.toISO(), | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -28,6 +28,7 @@ import { parseRecurringEvent } from "@calcom/lib/isRecurringEvent"; | |
| import logger from "@calcom/lib/logger"; | ||
| import { safeStringify } from "@calcom/lib/safeStringify"; | ||
| import { getTranslation } from "@calcom/lib/server/i18n"; | ||
| import { PrismaOrgMembershipRepository } from "@calcom/lib/server/repository/PrismaOrgMembershipRepository"; | ||
| import { getTimeFormatStringFromUserTimeFormat } from "@calcom/lib/timeFormat"; | ||
| // TODO: Prisma import would be used from DI in a followup PR when we remove `handler` export | ||
| import prisma from "@calcom/prisma"; | ||
|
|
@@ -144,8 +145,19 @@ async function handler(input: CancelBookingInput) { | |
|
|
||
| const userIsOwnerOfEventType = bookingToDelete.eventType.owner?.id === userId; | ||
|
|
||
| if (!userIsHost && !userIsOwnerOfEventType) { | ||
| throw new HttpError({ statusCode: 401, message: "User not a host of this event" }); | ||
| const userIsOrgAdminOfBookingUser = | ||
| userId && | ||
| bookingToDelete.userId && | ||
| (await PrismaOrgMembershipRepository.isLoggedInUserOrgAdminOfBookingHost( | ||
| userId, | ||
| bookingToDelete.userId | ||
| )); | ||
|
|
||
| if (!userIsHost && !userIsOwnerOfEventType && userIsOrgAdminOfBookingUser) { | ||
| throw new HttpError({ | ||
| statusCode: 401, | ||
| message: "User not a host of this event or an admin of the booking user", | ||
| }); | ||
|
Comment on lines
+156
to
+160
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [breaking-issue] [Critical Logic Error: Missing NOT Operator in Cancel Booking Authorization] >� Agent PromptCopy this prompt and use it to remediate the issue with your preferred AI generation tools
Comment on lines
+156
to
+160
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [action-required] [Inverted Authorization Logic in Cancel Booking] >� Agent PromptCopy this prompt and use it to remediate the issue with your preferred AI generation tools
Comment on lines
+156
to
+160
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [action-required] [Incorrect Authorization Logic in Cancel Booking] >Agent PromptCopy this prompt and use it to remediate the issue with your preferred AI generation tools
Comment on lines
+148
to
+160
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [action-required] [Incorrect Boolean Logic in Cancel Authorization] >Agent PromptCopy this prompt and use it to remediate the issue with your preferred AI generation tools
Comment on lines
+148
to
+160
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [action-required] [Incorrect Authorization Logic in Cancel Booking] >Agent PromptCopy this prompt and use it to remediate the issue with your preferred AI generation tools
Comment on lines
+156
to
+160
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [action-required] [Incorrect Authorization Logic in Cancel Booking] >Agent PromptCopy this prompt and use it to remediate the issue with your preferred AI generation tools
Comment on lines
+148
to
+160
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 4. Cancel auth condition inverted handleCancelBooking throws 401 when userIsOrgAdminOfBookingUser is true, which blocks org admins from cancelling seated bookings (all seats) once the org-admin check is corrected. Agent Prompt
|
||
| } | ||
| } | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
5. Org admin reschedule get denied
🐞 Bug≡ CorrectnessAgent Prompt
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools