Skip to content

Comments

fix: Allow seat UID access for private seated events#23859

Closed
Anshumancanrock wants to merge 7 commits intocalcom:mainfrom
Anshumancanrock:hidden-access
Closed

fix: Allow seat UID access for private seated events#23859
Anshumancanrock wants to merge 7 commits intocalcom:mainfrom
Anshumancanrock:hidden-access

Conversation

@Anshumancanrock
Copy link
Contributor

What does this PR do?

Fixes a critical issue where users cannot cancel their individual seats in seated events when attendees are hidden. The GET booking endpoint now supports optional authentication to allow legitimate users to access seat UIDs while maintaining privacy controls.

Visual Demo (For contributors especially)

Mandatory Tasks (DO NOT REMOVE)

  • I have self-reviewed the code (A decent size PR without self-review might be rejected).
  • I have updated the developer docs in /docs if this PR makes changes that would require a documentation change. If N/A, write N/A here and check the checkbox.
  • I confirm automated tests are in place that prove my fix is effective or that my feature works.

How should this be tested?

Cal.com development environment

Checklist

@Anshumancanrock Anshumancanrock requested a review from a team September 15, 2025 23:39
@Anshumancanrock Anshumancanrock requested a review from a team as a code owner September 15, 2025 23:39
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Sep 15, 2025

Walkthrough

The GET "/:bookingUid" controller route now uses OptionalApiAuthGuard alongside BookingUidGuard and accepts an optional AuthOptionalUser passed into bookingsService.getBooking. BookingsService.getBooking(uid, authUser?) was added and a private shouldShowAttendees helper computes showAttendees from eventType.seatsShowAttendees and the auth context (viewer membership, event owner, team admin). That showAttendees value is propagated into output generation and service flows for seated and recurring bookings (create, createRecurring, reschedule, cancel, confirm, decline, markAbsent, list/get). OutputBookingsService methods gained showAttendees parameters with fallback to eventType.seatsShowAttendees.

Possibly related PRs

Pre-merge checks and finishing touches

❌ Failed checks (1 inconclusive)
Check name Status Explanation Resolution
Linked Issues Check ❓ Inconclusive The changes implement the core coding objectives from [#23848] and [CAL-6412]: a shouldShowAttendees helper was added, BookingsService now accepts an optional authUser and propagates a showAttendees flag into output generation, and output.service was updated to respect that flag so authorized requests can receive attendee data (including seatUid) while unauthenticated requests fall back to eventType.seatsShowAttendees. Service-level updates cover seated and recurring-seated flows and getBookings now considers showAttendees for formatting authenticated listings. However, the provided summary only shows the controller-level OptionalApiAuthGuard added for GET /:bookingUid and does not confirm optional-auth wiring on create/reschedule/cancel endpoints nor the presence of the described e2e tests, so those items remain unverified. Please confirm that all public booking controller endpoints (create, reschedule, cancel, and fetch) accept optional API auth or otherwise forward request auth to the service layer and that controller callsites pass the new authUser parameter, and ensure the e2e tests described are included and exercise both unauthenticated and authorized flows; once those controller updates and tests are present the implementation will fully meet the linked-issue requirements.
✅ Passed checks (4 passed)
Check name Status Explanation
Title Check ✅ Passed The title succinctly and accurately describes the primary change — enabling authorized access to seat UIDs for private/seated events — and aligns with the diff’s core updates (optional auth and auth-aware attendee visibility). It is concise, specific, and suitable for changelog/history scanning. No misleading or extraneous terms are present.
Out of Scope Changes Check ✅ Passed The changes are narrowly focused on attendee-visibility logic and its propagation (service helpers, method signatures, and output formatting) with a limited controller guard adjustment; I do not see unrelated feature or unrelated-module changes in the provided summary. Method-signature updates and callsite changes appear consistent with the stated objective to make visibility auth-aware.
Description Check ✅ Passed The PR description clearly states the problem, the implemented solution (optional authentication to allow authorized users to view hidden attendees), and references the related issues (#23848 and CAL-6412), so it is on-topic and related to the changeset. It also notes testing and checklist completion, which matches the claimed scope of the change.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Tip

👮 Agentic pre-merge checks are now available in preview!

Pro plan users can now enable pre-merge checks in their settings to enforce checklists before merging PRs.

  • Built-in checks – Quickly apply ready-made checks to enforce title conventions, require pull request descriptions that follow templates, validate linked issues for compliance, and more.
  • Custom agentic checks – Define your own rules using CodeRabbit’s advanced agentic capabilities to enforce organization-specific policies and workflows. For example, you can instruct CodeRabbit’s agent to verify that API documentation is updated whenever API schema files are modified in a PR. Note: Upto 5 custom checks are currently allowed during the preview period. Pricing for this feature will be announced in a few weeks.

Please see the documentation for more information.

Example:

reviews:
  pre_merge_checks:
    custom_checks:
      - name: "Undocumented Breaking Changes"
        mode: "warning"
        instructions: |
          Pass/fail criteria: All breaking changes to public APIs, CLI flags, environment variables, configuration keys, database schemas, or HTTP/GraphQL endpoints must be documented in the "Breaking Change" section of the PR description and in CHANGELOG.md. Exclude purely internal or private changes (e.g., code not exported from package entry points or explicitly marked as internal).

Please share your feedback with us on this Discord post.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@graphite-app graphite-app bot added the community Created by Linear-GitHub Sync label Sep 15, 2025
@graphite-app graphite-app bot requested a review from a team September 15, 2025 23:39
@github-actions github-actions bot added the api area: API, enterprise API, access token, OAuth label Sep 15, 2025
@vercel
Copy link

vercel bot commented Sep 15, 2025

@Anshumancanrock is attempting to deploy a commit to the cal Team on Vercel.

A member of the Team first needs to authorize it.

@github-actions github-actions bot added bookings area: bookings, availability, timezones, double booking High priority Created by Linear-GitHub Sync platform Anything related to our platform plan seats area: seats, guest meetings, multiple people ✨ feature New feature or request labels Sep 15, 2025
@dosubot dosubot bot added the 🐛 bug Something isn't working label Sep 15, 2025
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/api/v2/src/ee/bookings/2024-08-13/services/bookings.service.ts (1)

726-737: getBookings should always include attendees (endpoint requires auth).

Per PR objectives, “When fetching all bookings (already requires authentication), always include attendees.” Replace eventType gating with a constant true so seat UIDs are always present for seated bookings.

Apply this diff:

-          this.outputService.getOutputRecurringSeatedBooking(
-            formatted,
-            !!formatted.eventType?.seatsShowAttendees
-          )
+          this.outputService.getOutputRecurringSeatedBooking(
+            formatted,
+            true
+          )
...
-          await this.outputService.getOutputSeatedBooking(
-            formatted,
-            !!formatted.eventType?.seatsShowAttendees
-          )
+          await this.outputService.getOutputSeatedBooking(
+            formatted,
+            true
+          )
🧹 Nitpick comments (2)
apps/api/v2/src/ee/bookings/2024-08-13/controllers/bookings.controller.ts (1)

175-180: Propagate optional user to service — good; consider clarifying docs that Authorization is optional.

Handler correctly forwards AuthOptionalUser. Suggest noting in the route description that Authorization is optional to surface seat UIDs for authorized viewers.

apps/api/v2/src/ee/bookings/2024-08-13/services/bookings.service.ts (1)

606-666: Make attendee email comparison case-insensitive to avoid false negatives.

Some systems store mixed‑case emails; normalize before comparing.

Apply this diff:

-    const isAttendee = booking.attendees.some(attendee => attendee.email === authUser.email);
+    const isAttendee = booking.attendees.some(
+      (attendee) => attendee.email?.toLowerCase() === authUser.email?.toLowerCase()
+    );

If your platform uses managed/aliased attendee emails (OAuth client suffixes), consider normalizing the auth user email to the managed form before comparison.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 7e415b4 and 03e4339.

📒 Files selected for processing (3)
  • apps/api/v2/src/ee/bookings/2024-08-13/controllers/bookings.controller.ts (2 hunks)
  • apps/api/v2/src/ee/bookings/2024-08-13/services/bookings.service.ts (6 hunks)
  • apps/api/v2/src/ee/bookings/2024-08-13/services/output.service.ts (2 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
**/*.ts

📄 CodeRabbit inference engine (.cursor/rules/review.mdc)

**/*.ts: For Prisma queries, only select data you need; never use include, always use select
Ensure the credential.key field is never returned from tRPC endpoints or APIs

Files:

  • apps/api/v2/src/ee/bookings/2024-08-13/controllers/bookings.controller.ts
  • apps/api/v2/src/ee/bookings/2024-08-13/services/output.service.ts
  • apps/api/v2/src/ee/bookings/2024-08-13/services/bookings.service.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/review.mdc)

Flag excessive Day.js use in performance-critical code; prefer native Date or Day.js .utc() in hot paths like loops

Files:

  • apps/api/v2/src/ee/bookings/2024-08-13/controllers/bookings.controller.ts
  • apps/api/v2/src/ee/bookings/2024-08-13/services/output.service.ts
  • apps/api/v2/src/ee/bookings/2024-08-13/services/bookings.service.ts
**/*.{ts,tsx,js,jsx}

⚙️ CodeRabbit configuration file

Flag default exports and encourage named exports. Named exports provide better tree-shaking, easier refactoring, and clearer imports. Exempt main components like pages, layouts, and components that serve as the primary export of a module.

Files:

  • apps/api/v2/src/ee/bookings/2024-08-13/controllers/bookings.controller.ts
  • apps/api/v2/src/ee/bookings/2024-08-13/services/output.service.ts
  • apps/api/v2/src/ee/bookings/2024-08-13/services/bookings.service.ts
**/*.{service,repository}.ts

📄 CodeRabbit inference engine (.cursor/rules/review.mdc)

Avoid dot-suffixes like .service.ts or .repository.ts for new files; reserve .test.ts, .spec.ts, .types.ts for their specific purposes

Files:

  • apps/api/v2/src/ee/bookings/2024-08-13/services/output.service.ts
  • apps/api/v2/src/ee/bookings/2024-08-13/services/bookings.service.ts
🧠 Learnings (5)
📓 Common learnings
Learnt from: ShashwatPS
PR: calcom/cal.com#23638
File: packages/trpc/server/routers/viewer/calendars/setDestinationReminder.handler.test.ts:198-199
Timestamp: 2025-09-06T11:00:34.372Z
Learning: In calcom/cal.com PR #23638, the maintainer ShashwatPS determined that authorization checks in the setDestinationReminder handler are "not applicable" when CodeRabbit suggested adding user-scoped WHERE clauses to prevent users from modifying other users' destination calendar reminder settings.
Learnt from: supalarry
PR: calcom/cal.com#23217
File: apps/api/v2/src/ee/event-types/event-types_2024_06_14/services/output-event-types.service.ts:93-94
Timestamp: 2025-08-21T13:44:06.805Z
Learning: In apps/api/v2/src/ee/event-types/event-types_2024_06_14/event-types.repository.ts, repository functions that use explicit Prisma select clauses (like getEventTypeWithSeats) are used for specific purposes and don't need to include all EventType fields like bookingRequiresAuthentication. These methods don't feed into the general OutputEventTypesService_2024_06_14 flow.
📚 Learning: 2025-08-21T13:44:06.805Z
Learnt from: supalarry
PR: calcom/cal.com#23217
File: apps/api/v2/src/ee/event-types/event-types_2024_06_14/services/output-event-types.service.ts:93-94
Timestamp: 2025-08-21T13:44:06.805Z
Learning: In apps/api/v2/src/ee/event-types/event-types_2024_06_14/event-types.repository.ts, repository functions that use explicit Prisma select clauses (like getEventTypeWithSeats) are used for specific purposes and don't need to include all EventType fields like bookingRequiresAuthentication. These methods don't feed into the general OutputEventTypesService_2024_06_14 flow.

Applied to files:

  • apps/api/v2/src/ee/bookings/2024-08-13/services/output.service.ts
  • apps/api/v2/src/ee/bookings/2024-08-13/services/bookings.service.ts
📚 Learning: 2025-09-03T11:54:05.409Z
Learnt from: supalarry
PR: calcom/cal.com#23514
File: apps/api/v2/src/ee/bookings/2024-08-13/services/bookings.service.ts:579-582
Timestamp: 2025-09-03T11:54:05.409Z
Learning: In calcom/cal.com bookings repository methods, when Prisma select uses `eventType: true`, all eventType fields including seatsShowAttendees are automatically included in the selection. Explicit field selection is not required when using `true` for nested relations.

Applied to files:

  • apps/api/v2/src/ee/bookings/2024-08-13/services/output.service.ts
📚 Learning: 2025-09-03T11:54:05.409Z
Learnt from: supalarry
PR: calcom/cal.com#23514
File: apps/api/v2/src/ee/bookings/2024-08-13/services/bookings.service.ts:579-582
Timestamp: 2025-09-03T11:54:05.409Z
Learning: In calcom/cal.com bookings repository methods, when Prisma include uses `eventType: true`, all eventType fields including seatsShowAttendees are automatically included in the selection. Explicit field selection is not required when using `true` for nested relations.

Applied to files:

  • apps/api/v2/src/ee/bookings/2024-08-13/services/output.service.ts
📚 Learning: 2025-08-19T09:47:49.478Z
Learnt from: eunjae-lee
PR: calcom/cal.com#23166
File: packages/prisma/migrations/20250818151914_routing_form_response_denormalized_backfill2/migration.sql:65-66
Timestamp: 2025-08-19T09:47:49.478Z
Learning: The Booking table has a unique constraint and index on the uid column (defined as `uid String unique` in schema.prisma), so JOINs on Booking.uid have optimal performance and don't require additional indexing.

Applied to files:

  • apps/api/v2/src/ee/bookings/2024-08-13/services/bookings.service.ts
🧬 Code graph analysis (3)
apps/api/v2/src/ee/bookings/2024-08-13/controllers/bookings.controller.ts (2)
apps/api/v2/src/modules/auth/guards/optional-api-auth/optional-api-auth.guard.ts (1)
  • OptionalApiAuthGuard (7-23)
apps/api/v2/src/modules/auth/decorators/get-optional-user/get-optional-user.decorator.ts (2)
  • GetOptionalUser (7-32)
  • AuthOptionalUser (5-5)
apps/api/v2/src/ee/bookings/2024-08-13/services/output.service.ts (1)
packages/platform/types/bookings/2024-08-13/outputs/booking.output.ts (2)
  • CreateSeatedBookingOutput_2024_08_13 (349-360)
  • CreateRecurringSeatedBookingOutput_2024_08_13 (365-381)
apps/api/v2/src/ee/bookings/2024-08-13/services/bookings.service.ts (1)
apps/api/v2/src/modules/auth/decorators/get-optional-user/get-optional-user.decorator.ts (1)
  • AuthOptionalUser (5-5)
⏰ Context from checks skipped due to timeout of 180000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Install dependencies / Yarn install & cache
🔇 Additional comments (7)
apps/api/v2/src/ee/bookings/2024-08-13/controllers/bookings.controller.ts (1)

164-164: Guard order LGTM; optional auth enables attendee visibility without breaking UID checks.

Using OptionalApiAuthGuard before BookingUidGuard is appropriate so invalid creds still fail while no-creds requests proceed as anonymous.

Please confirm BookingUidGuard does not rely on request.user; if it does, keep this guard order, but ensure it gracefully handles null users.

apps/api/v2/src/ee/bookings/2024-08-13/services/output.service.ts (3)

270-279: showAttendees override is correctly plumbed.

Using showAttendees ?? !!databaseBooking.eventType?.seatsShowAttendees preserves legacy behavior while enabling auth-aware overrides.


329-360: Conditional attendee rendering preserves privacy; seatUid surfaced when allowed.

Good use of safeParse and explicit email redaction from bookingFieldsResponses.


390-394: Recurring seated create: defaulting to show attendees is consistent with creator expectations.

Signature and call-site updates look correct.

Also applies to: 396-406

apps/api/v2/src/ee/bookings/2024-08-13/services/bookings.service.ts (3)

557-557: Always showing attendees on seated creation paths makes sense.

Passing true ensures the creator immediately gets seat UIDs.


700-717: Order preservation via Map lookup is fine.

This restores input order without N+1 queries — good compromise.


816-827: Reschedule flows: passing true ensures seat UIDs are returned immediately.

Correctly aligns with the cancellation/reschedule UX needs.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/api/v2/src/ee/bookings/2024-08-13/services/bookings.service.ts (1)

700-717: Respect auth-aware attendee visibility in getBookings — use shouldShowAttendees

getBookings currently passes eventType.seatsShowAttendees directly; compute showAttendees via this.shouldShowAttendees(formatted, user) and pass that to the outputService for seated/recurring‑seated bookings.

@@
-  for (const booking of orderedBookings) {
+  for (const booking of orderedBookings) {
     const bookingCasted = booking as any;
     const formatted = {
       ...bookingCasted,
       eventType: bookingCasted.eventType,
       eventTypeId: bookingCasted.eventTypeId,
       startTime: new Date(bookingCasted.startTime),
       endTime: new Date(bookingCasted.endTime),
       absentHost: !!bookingCasted.noShowHost,
     };
 
     const isRecurring = !!formatted.recurringEventId;
     const isSeated = !!formatted.eventType?.seatsPerTimeSlot;
     if (isRecurring && !isSeated) {
       formattedBookings.push(this.outputService.getOutputRecurringBooking(formatted));
     } else if (isRecurring && isSeated) {
-      formattedBookings.push(
-        this.outputService.getOutputRecurringSeatedBooking(
-          formatted,
-          !!formatted.eventType?.seatsShowAttendees
-        )
-      );
+      const showAttendees = await this.shouldShowAttendees(formatted, user as any);
+      formattedBookings.push(
+        this.outputService.getOutputRecurringSeatedBooking(formatted, showAttendees)
+      );
     } else if (isSeated) {
-      formattedBookings.push(
-        await this.outputService.getOutputSeatedBooking(
-          formatted,
-          !!formatted.eventType?.seatsShowAttendees
-        )
-      );
+      const showAttendees = await this.shouldShowAttendees(formatted, user as any);
+      formattedBookings.push(await this.outputService.getOutputSeatedBooking(formatted, showAttendees));
     } else {
       formattedBookings.push(await this.outputService.getOutputBooking(formatted));
     }
   }

Optional: type the user param as ApiAuthGuardUser in getBookings to avoid the as any cast.
Also applies to the similar block around lines ~724–737.

♻️ Duplicate comments (2)
apps/api/v2/src/ee/bookings/2024-08-13/services/bookings.service.ts (2)

598-601: Recurring-UID branch now honors auth-aware attendee visibility.

This addresses the earlier review feedback for recurring seated bookings.


885-891: Forwarding request.user into getBooking is correct.

This ensures auth-aware attendee visibility post-cancel.

🧹 Nitpick comments (3)
apps/api/v2/src/ee/bookings/2024-08-13/services/bookings.service.ts (3)

558-558: Avoid hardcoding showAttendees=true on create.

If the creator isn’t always entitled to view other attendees (e.g., managed/embedded flows), this could over‑expose. Consider deriving via shouldShowAttendees or clearly document the intentional exposure.


606-666: Make attendee email comparison case-insensitive to avoid false negatives.

Email addresses should be compared case-insensitively.

Apply this diff:

-    const isAttendee = booking.attendees.some(attendee => attendee.email === authUser.email);
+    const isAttendee = booking.attendees.some(
+      (attendee) => attendee.email?.toLowerCase() === authUser.email?.toLowerCase()
+    );

818-827: Confirm intent of showAttendees=true during reschedule outputs.

Same concern as create: if the acting user isn’t guaranteed to be authorized to view all attendees, consider deriving via shouldShowAttendees.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 03e4339 and 67001a9.

📒 Files selected for processing (1)
  • apps/api/v2/src/ee/bookings/2024-08-13/services/bookings.service.ts (12 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{service,repository}.ts

📄 CodeRabbit inference engine (.cursor/rules/review.mdc)

Avoid dot-suffixes like .service.ts or .repository.ts for new files; reserve .test.ts, .spec.ts, .types.ts for their specific purposes

Files:

  • apps/api/v2/src/ee/bookings/2024-08-13/services/bookings.service.ts
**/*.ts

📄 CodeRabbit inference engine (.cursor/rules/review.mdc)

**/*.ts: For Prisma queries, only select data you need; never use include, always use select
Ensure the credential.key field is never returned from tRPC endpoints or APIs

Files:

  • apps/api/v2/src/ee/bookings/2024-08-13/services/bookings.service.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/review.mdc)

Flag excessive Day.js use in performance-critical code; prefer native Date or Day.js .utc() in hot paths like loops

Files:

  • apps/api/v2/src/ee/bookings/2024-08-13/services/bookings.service.ts
**/*.{ts,tsx,js,jsx}

⚙️ CodeRabbit configuration file

Flag default exports and encourage named exports. Named exports provide better tree-shaking, easier refactoring, and clearer imports. Exempt main components like pages, layouts, and components that serve as the primary export of a module.

Files:

  • apps/api/v2/src/ee/bookings/2024-08-13/services/bookings.service.ts
🧠 Learnings (2)
📚 Learning: 2025-08-21T13:44:06.805Z
Learnt from: supalarry
PR: calcom/cal.com#23217
File: apps/api/v2/src/ee/event-types/event-types_2024_06_14/services/output-event-types.service.ts:93-94
Timestamp: 2025-08-21T13:44:06.805Z
Learning: In apps/api/v2/src/ee/event-types/event-types_2024_06_14/event-types.repository.ts, repository functions that use explicit Prisma select clauses (like getEventTypeWithSeats) are used for specific purposes and don't need to include all EventType fields like bookingRequiresAuthentication. These methods don't feed into the general OutputEventTypesService_2024_06_14 flow.

Applied to files:

  • apps/api/v2/src/ee/bookings/2024-08-13/services/bookings.service.ts
📚 Learning: 2025-08-19T09:47:49.478Z
Learnt from: eunjae-lee
PR: calcom/cal.com#23166
File: packages/prisma/migrations/20250818151914_routing_form_response_denormalized_backfill2/migration.sql:65-66
Timestamp: 2025-08-19T09:47:49.478Z
Learning: The Booking table has a unique constraint and index on the uid column (defined as `uid String unique` in schema.prisma), so JOINs on Booking.uid have optimal performance and don't require additional indexing.

Applied to files:

  • apps/api/v2/src/ee/bookings/2024-08-13/services/bookings.service.ts
🧬 Code graph analysis (1)
apps/api/v2/src/ee/bookings/2024-08-13/services/bookings.service.ts (2)
apps/api/v2/src/modules/auth/decorators/get-optional-user/get-optional-user.decorator.ts (1)
  • AuthOptionalUser (5-5)
apps/api/v2/src/modules/auth/strategies/api-auth/api-auth.strategy.ts (1)
  • ApiAuthGuardUser (25-25)
⏰ Context from checks skipped due to timeout of 180000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Tests / Unit
  • GitHub Check: Type check / check-types
  • GitHub Check: Linters / lint
  • GitHub Check: Codacy Static Code Analysis
🔇 Additional comments (3)
apps/api/v2/src/ee/bookings/2024-08-13/services/bookings.service.ts (3)

10-10: Import of ApiAuthGuardUser is appropriate and scoped to typing.

No issues. Keeps types explicit where needed.


569-587: Optional-auth GET path correctly computes auth-aware attendee visibility.

Good use of shouldShowAttendees for both seated and recurring+seated branches.


893-903: LGTM: recurring-by-individual UID now threads auth to the unified getter.

Matches the new optional-auth flow.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
apps/api/v2/src/ee/bookings/2024-08-13/services/bookings.service.ts (2)

735-747: getBookings should include attendees for seated bookings on this authenticated endpoint.
Per PR objectives, authenticated listing should show attendees; currently it still obeys seatsShowAttendees.

Apply this diff:

-        formattedBookings.push(
-          this.outputService.getOutputRecurringSeatedBooking(
-            formatted,
-            !!formatted.eventType?.seatsShowAttendees
-          )
-        );
+        formattedBookings.push(
+          this.outputService.getOutputRecurringSeatedBooking(formatted, true)
+        );
@@
-        formattedBookings.push(
-          await this.outputService.getOutputSeatedBooking(
-            formatted,
-            !!formatted.eventType?.seatsShowAttendees
-          )
-        );
+        formattedBookings.push(
+          await this.outputService.getOutputSeatedBooking(formatted, true)
+        );

If stricter auth-awareness is preferred, fetch the full user once and call shouldShowAttendees per booking instead of hardcoding true.


1166-1169: Replace Prisma include with explicit select in event-types repository

getEventTypeByIdIncludeUsersAndTeam uses include: { users: true, team: true } (apps/api/v2/src/ee/event-types/event-types_2024_06_14/event-types.repository.ts:96–99). The same file has multiple other include usages (hosts, users, schedule, destinationCalendar, calVideoSettings, owner, team) at ~lines 56, 66, 75, 85, 92, 99, 115, 127, 139. Call site: apps/api/v2/src/ee/bookings/2024-08-13/services/bookings.service.ts:1166–1169. Refactor these Prisma queries to use explicit select and request only the nested fields actually needed.

♻️ Duplicate comments (2)
apps/api/v2/src/ee/bookings/2024-08-13/services/bookings.service.ts (2)

598-601: Recurring‑UID branch now auth‑aware — nice fix.
This addresses earlier feedback to respect auth for recurring seated bookings.


950-964: Type/contract mismatch: use findByIdWithProfile before toApiAuthGuardUser.
toApiAuthGuardUser expects UserWithProfile; findById returns plain User. Fetch the profile variant.

Apply this diff:

-    const bookingOwner = await this.usersRepository.findById(bookingOwnerId);
+    const bookingOwner = await this.usersRepository.findByIdWithProfile(bookingOwnerId);
     if (!bookingOwner) {
       throw new NotFoundException(`Booking owner with id=${bookingOwnerId} not found.`);
     }
@@
-    const authUser = this.toApiAuthGuardUser(bookingOwner);
+    const authUser = this.toApiAuthGuardUser(bookingOwner);

Also ensure isSystemAdmin derives correctly from role. Add a unit test covering this path.

🧹 Nitpick comments (2)
apps/api/v2/src/ee/bookings/2024-08-13/services/bookings.service.ts (2)

606-666: Normalize emails when checking attendee membership.
Email comparison should be case‑insensitive to avoid false negatives.

Apply this diff:

-    // Check if user is an attendee of this booking
-    const isAttendee = booking.attendees.some(attendee => attendee.email === authUser.email);
+    // Check if user is an attendee of this booking (case-insensitive)
+    const authEmail = (authUser.email || "").toLowerCase();
+    const isAttendee = booking.attendees.some(
+      (attendee) => (attendee.email || "").toLowerCase() === authEmail
+    );

710-712: Avoid any in bookingMap.
Tighten typing to reduce accidental misuse.

Apply this diff:

-    const bookingMap = new Map(bookings.map((booking: any) => [booking.id, booking]));
+    const bookingMap = new Map<number, (typeof bookings)[number]>(
+      bookings.map((b) => [b.id, b])
+    );
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 67001a9 and f0b445b.

📒 Files selected for processing (1)
  • apps/api/v2/src/ee/bookings/2024-08-13/services/bookings.service.ts (12 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{service,repository}.ts

📄 CodeRabbit inference engine (.cursor/rules/review.mdc)

Avoid dot-suffixes like .service.ts or .repository.ts for new files; reserve .test.ts, .spec.ts, .types.ts for their specific purposes

Files:

  • apps/api/v2/src/ee/bookings/2024-08-13/services/bookings.service.ts
**/*.ts

📄 CodeRabbit inference engine (.cursor/rules/review.mdc)

**/*.ts: For Prisma queries, only select data you need; never use include, always use select
Ensure the credential.key field is never returned from tRPC endpoints or APIs

Files:

  • apps/api/v2/src/ee/bookings/2024-08-13/services/bookings.service.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/review.mdc)

Flag excessive Day.js use in performance-critical code; prefer native Date or Day.js .utc() in hot paths like loops

Files:

  • apps/api/v2/src/ee/bookings/2024-08-13/services/bookings.service.ts
**/*.{ts,tsx,js,jsx}

⚙️ CodeRabbit configuration file

Flag default exports and encourage named exports. Named exports provide better tree-shaking, easier refactoring, and clearer imports. Exempt main components like pages, layouts, and components that serve as the primary export of a module.

Files:

  • apps/api/v2/src/ee/bookings/2024-08-13/services/bookings.service.ts
🧠 Learnings (4)
📚 Learning: 2025-08-21T13:44:06.805Z
Learnt from: supalarry
PR: calcom/cal.com#23217
File: apps/api/v2/src/ee/event-types/event-types_2024_06_14/services/output-event-types.service.ts:93-94
Timestamp: 2025-08-21T13:44:06.805Z
Learning: In apps/api/v2/src/ee/event-types/event-types_2024_06_14/event-types.repository.ts, repository functions that use explicit Prisma select clauses (like getEventTypeWithSeats) are used for specific purposes and don't need to include all EventType fields like bookingRequiresAuthentication. These methods don't feed into the general OutputEventTypesService_2024_06_14 flow.

Applied to files:

  • apps/api/v2/src/ee/bookings/2024-08-13/services/bookings.service.ts
📚 Learning: 2025-08-27T12:15:43.830Z
Learnt from: Udit-takkar
PR: calcom/cal.com#22995
File: packages/trpc/server/routers/viewer/aiVoiceAgent/testCall.handler.ts:41-44
Timestamp: 2025-08-27T12:15:43.830Z
Learning: In calcom/cal.com, the AgentService.getAgent() method in packages/features/calAIPhone/providers/retellAI/services/AgentService.ts does NOT include authorization checks - it only validates the agentId parameter and directly calls the repository without verifying user/team access. This contrasts with other methods like getAgentWithDetails() which properly use findByIdWithUserAccessAndDetails() for authorization. When reviewing updateToolsFromAgentId() calls, always verify both agent ownership and eventType ownership are checked.

Applied to files:

  • apps/api/v2/src/ee/bookings/2024-08-13/services/bookings.service.ts
📚 Learning: 2025-08-08T09:29:11.681Z
Learnt from: Udit-takkar
PR: calcom/cal.com#22919
File: packages/features/calAIPhone/interfaces/AIPhoneService.interface.ts:118-143
Timestamp: 2025-08-08T09:29:11.681Z
Learning: In calcom/cal.com PR #22919, packages/features/calAIPhone/interfaces/AIPhoneService.interface.ts (TypeScript), the AIPhoneServiceAgentListItem is required to include user.email in listAgents responses (per maintainer Udit-takkar). Future reviews should not flag this as unnecessary PII unless requirements change.

Applied to files:

  • apps/api/v2/src/ee/bookings/2024-08-13/services/bookings.service.ts
📚 Learning: 2025-08-19T09:47:49.478Z
Learnt from: eunjae-lee
PR: calcom/cal.com#23166
File: packages/prisma/migrations/20250818151914_routing_form_response_denormalized_backfill2/migration.sql:65-66
Timestamp: 2025-08-19T09:47:49.478Z
Learning: The Booking table has a unique constraint and index on the uid column (defined as `uid String unique` in schema.prisma), so JOINs on Booking.uid have optimal performance and don't require additional indexing.

Applied to files:

  • apps/api/v2/src/ee/bookings/2024-08-13/services/bookings.service.ts
🧬 Code graph analysis (1)
apps/api/v2/src/ee/bookings/2024-08-13/services/bookings.service.ts (3)
apps/api/v2/src/modules/auth/decorators/get-optional-user/get-optional-user.decorator.ts (1)
  • AuthOptionalUser (5-5)
apps/api/v2/src/modules/users/users.repository.ts (1)
  • UserWithProfile (10-13)
apps/api/v2/src/modules/auth/strategies/api-auth/api-auth.strategy.ts (1)
  • ApiAuthGuardUser (25-25)
⏰ Context from checks skipped due to timeout of 180000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Install dependencies / Yarn install & cache
  • GitHub Check: Codacy Static Code Analysis
🔇 Additional comments (7)
apps/api/v2/src/ee/bookings/2024-08-13/services/bookings.service.ts (7)

10-10: Import is appropriate.
No action needed.


569-587: Auth‑aware attendee visibility in getBooking — LGTM.
Good split across seated/recurring and forwarding showAttendees.


668-676: toApiAuthGuardUser helper — LGTM.
Simple, correct mapping of isSystemAdmin.


898-901: Forwarding auth to getBooking — LGTM.
Type‑safe cast and pass‑through looks fine.


903-914: Helper getAllRecurringBookingsByIndividualUid passes auth — LGTM.
Good reuse of getBooking to centralize attendee visibility.


1117-1119: Confirm flow returns auth‑aware booking — LGTM.
Good use of toApiAuthGuardUser.


1151-1153: Decline flow returns auth‑aware booking — LGTM.
Consistent with confirm.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
apps/api/v2/src/ee/bookings/2024-08-13/services/bookings.service.ts (2)

477-504: Compute showAttendees using auth, not only event setting (breaks objective).

Current code derives showAttendees solely from eventType.seatsShowAttendees, ignoring authorized viewers (creator/host/admin). This fails the objective to reveal attendees to authorized users on creation.

Apply this diff to compute auth-aware visibility once using the first created booking (minimal extra query):

   const bookings = await handleNewRecurringBooking({
     bookingData: bookingRequest.body,
     userId: bookingRequest.userId,
     hostname: bookingRequest.headers?.host || "",
     platformClientId: bookingRequest.platformClientId,
     platformRescheduleUrl: bookingRequest.platformRescheduleUrl,
     platformCancelUrl: bookingRequest.platformCancelUrl,
     platformBookingUrl: bookingRequest.platformBookingUrl,
     platformBookingLocation: bookingRequest.platformBookingLocation,
     areCalendarEventsEnabled: bookingRequest.areCalendarEventsEnabled,
   });
-  
-  // Compute proper attendee visibility based on event type and authentication
-  // For recurring bookings, use the event type settings to determine authorization
-  const showAttendees = eventType.seatsShowAttendees || false; // Default to secure for recurring bookings
-  
+  // Compute attendee visibility based on event settings and viewer authorization
+  let showAttendees = !!eventType.seatsShowAttendees;
+  if (!showAttendees && bookings[0]?.uid) {
+    const firstDbBooking =
+      await this.bookingsRepository.getByUidWithAttendeesWithBookingSeatAndUserAndEvent(bookings[0]!.uid!);
+    if (firstDbBooking) {
+      showAttendees = await this.shouldShowAttendees(firstDbBooking, authUser);
+    }
+  }
 
   return this.outputService.getOutputCreateRecurringSeatedBookings(
     bookings.map((booking) => ({ uid: booking.uid || "", seatUid: booking.seatReferenceUid || "" })),
     showAttendees
   );

720-761: Type/logic issue: building ApiAuthGuardUser from a partial user in getBookings.

this.toApiAuthGuardUser(user) expects UserWithProfile, but user here is { id; email; orgId? }. This will fail typing and may mis-evaluate admin checks.

Fetch the full user once and reuse:

-    const bookingMap = new Map(bookings.map((booking: any) => [booking.id, booking]));
-    const orderedBookings = ids.map((id) => bookingMap.get(id)).filter(Boolean);
+    const bookingMap = new Map(bookings.map((booking: any) => [booking.id, booking]));
+    const orderedBookings = ids.map((id) => bookingMap.get(id)).filter(Boolean);
+    const fullUser = await this.usersRepository.findByIdWithProfile(user.id);
+    const authUser = fullUser ? this.toApiAuthGuardUser(fullUser) : undefined;
@@
-      // Compute proper attendee visibility instead of just using event setting
-      const showAttendees = isSeated ? await this.shouldShowAttendees(formatted, this.toApiAuthGuardUser(user)) : true;
+      // Compute proper attendee visibility instead of just using event setting
+      const showAttendees = isSeated ? await this.shouldShowAttendees(formatted, authUser) : true;
♻️ Duplicate comments (2)
apps/api/v2/src/ee/bookings/2024-08-13/services/bookings.service.ts (2)

608-611: Recurring-UID branch fixed to be auth‑aware (thank you).

This addresses the earlier review note to apply the same logic here.


968-982: Build guard user from full profile in markAbsent (typing/consistency).

findById returns a plain User, but toApiAuthGuardUser expects UserWithProfile. Fetch the profile variant.

-    const bookingOwner = await this.usersRepository.findById(bookingOwnerId);
+    const bookingOwner = await this.usersRepository.findByIdWithProfile(bookingOwnerId);
@@
-    const authUser = this.toApiAuthGuardUser(bookingOwner);
+    const authUser = this.toApiAuthGuardUser(bookingOwner);
🧹 Nitpick comments (2)
apps/api/v2/src/ee/bookings/2024-08-13/services/output.service.ts (1)

328-361: DRY: factor attendee-mapping into a helper used by both seated paths.

The attendee transformation logic is duplicated in seated and recurring seated outputs. Extract to a private helper to reduce divergence risk.

Apply this minimal change at the call sites:

-    parsed.attendees = showAttendees
-      ? databaseBooking.attendees.map((attendee) => {
-          const { responses } = safeParse(
-            seatedBookingDataSchema,
-            attendee.bookingSeat?.data,
-            defaultSeatedBookingData,
-            false
-          );
-          const attendeeData = { ... };
-          const attendeeParsed = plainToClass(SeatedAttendee, attendeeData, { strategy: "excludeAll" });
-          attendeeParsed.bookingFieldsResponses = responses || {};
-          attendeeParsed.metadata = safeParse(
-            seatedBookingMetadataSchema,
-            attendee.bookingSeat?.metadata,
-            defaultSeatedBookingMetadata,
-            false
-          );
-          delete attendeeParsed.bookingFieldsResponses.email;
-          return attendeeParsed;
-        })
-      : [];
+    parsed.attendees = showAttendees ? this.buildSeatedAttendees(databaseBooking.attendees) : [];

Suggested helper (outside the shown ranges):

private buildSeatedAttendees(attendees: DatabaseBooking["attendees"]): SeatedAttendee[] {
  return attendees.map((attendee) => {
    const { responses } = safeParse(
      seatedBookingDataSchema,
      attendee.bookingSeat?.data,
      defaultSeatedBookingData,
      false
    );
    const attendeeData = {
      name: attendee.name,
      email: attendee.email,
      timeZone: attendee.timeZone,
      language: attendee.locale,
      absent: !!attendee.noShow,
      seatUid: attendee.bookingSeat?.referenceUid,
      bookingFieldsResponses: {},
    };
    const attendeeParsed = plainToClass(SeatedAttendee, attendeeData, { strategy: "excludeAll" });
    attendeeParsed.bookingFieldsResponses = responses || {};
    attendeeParsed.metadata = safeParse(seatedBookingMetadataSchema, attendee.bookingSeat?.metadata, defaultSeatedBookingMetadata, false);
    delete attendeeParsed.bookingFieldsResponses.email;
    return attendeeParsed;
  });
}

Also applies to: 447-477

apps/api/v2/src/ee/bookings/2024-08-13/services/bookings.service.ts (1)

616-676: Normalize emails for attendee check (reduce false negatives).

Compare attendee emails case‑insensitively to handle mixed‑case inputs.

-    const isAttendee = booking.attendees.some(attendee => attendee.email === authUser.email);
+    const toLower = (s?: string | null) => (s ?? "").toLowerCase();
+    const isAttendee = booking.attendees.some(
+      (attendee) => toLower(attendee.email) === toLower(authUser.email)
+    );
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between f0b445b and ce3831b.

📒 Files selected for processing (2)
  • apps/api/v2/src/ee/bookings/2024-08-13/services/bookings.service.ts (16 hunks)
  • apps/api/v2/src/ee/bookings/2024-08-13/services/output.service.ts (3 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{service,repository}.ts

📄 CodeRabbit inference engine (.cursor/rules/review.mdc)

Avoid dot-suffixes like .service.ts or .repository.ts for new files; reserve .test.ts, .spec.ts, .types.ts for their specific purposes

Files:

  • apps/api/v2/src/ee/bookings/2024-08-13/services/bookings.service.ts
  • apps/api/v2/src/ee/bookings/2024-08-13/services/output.service.ts
**/*.ts

📄 CodeRabbit inference engine (.cursor/rules/review.mdc)

**/*.ts: For Prisma queries, only select data you need; never use include, always use select
Ensure the credential.key field is never returned from tRPC endpoints or APIs

Files:

  • apps/api/v2/src/ee/bookings/2024-08-13/services/bookings.service.ts
  • apps/api/v2/src/ee/bookings/2024-08-13/services/output.service.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/review.mdc)

Flag excessive Day.js use in performance-critical code; prefer native Date or Day.js .utc() in hot paths like loops

Files:

  • apps/api/v2/src/ee/bookings/2024-08-13/services/bookings.service.ts
  • apps/api/v2/src/ee/bookings/2024-08-13/services/output.service.ts
**/*.{ts,tsx,js,jsx}

⚙️ CodeRabbit configuration file

Flag default exports and encourage named exports. Named exports provide better tree-shaking, easier refactoring, and clearer imports. Exempt main components like pages, layouts, and components that serve as the primary export of a module.

Files:

  • apps/api/v2/src/ee/bookings/2024-08-13/services/bookings.service.ts
  • apps/api/v2/src/ee/bookings/2024-08-13/services/output.service.ts
🧠 Learnings (6)
📚 Learning: 2025-08-21T13:44:06.805Z
Learnt from: supalarry
PR: calcom/cal.com#23217
File: apps/api/v2/src/ee/event-types/event-types_2024_06_14/services/output-event-types.service.ts:93-94
Timestamp: 2025-08-21T13:44:06.805Z
Learning: In apps/api/v2/src/ee/event-types/event-types_2024_06_14/event-types.repository.ts, repository functions that use explicit Prisma select clauses (like getEventTypeWithSeats) are used for specific purposes and don't need to include all EventType fields like bookingRequiresAuthentication. These methods don't feed into the general OutputEventTypesService_2024_06_14 flow.

Applied to files:

  • apps/api/v2/src/ee/bookings/2024-08-13/services/bookings.service.ts
  • apps/api/v2/src/ee/bookings/2024-08-13/services/output.service.ts
📚 Learning: 2025-08-27T12:15:43.830Z
Learnt from: Udit-takkar
PR: calcom/cal.com#22995
File: packages/trpc/server/routers/viewer/aiVoiceAgent/testCall.handler.ts:41-44
Timestamp: 2025-08-27T12:15:43.830Z
Learning: In calcom/cal.com, the AgentService.getAgent() method in packages/features/calAIPhone/providers/retellAI/services/AgentService.ts does NOT include authorization checks - it only validates the agentId parameter and directly calls the repository without verifying user/team access. This contrasts with other methods like getAgentWithDetails() which properly use findByIdWithUserAccessAndDetails() for authorization. When reviewing updateToolsFromAgentId() calls, always verify both agent ownership and eventType ownership are checked.

Applied to files:

  • apps/api/v2/src/ee/bookings/2024-08-13/services/bookings.service.ts
📚 Learning: 2025-08-08T09:29:11.681Z
Learnt from: Udit-takkar
PR: calcom/cal.com#22919
File: packages/features/calAIPhone/interfaces/AIPhoneService.interface.ts:118-143
Timestamp: 2025-08-08T09:29:11.681Z
Learning: In calcom/cal.com PR #22919, packages/features/calAIPhone/interfaces/AIPhoneService.interface.ts (TypeScript), the AIPhoneServiceAgentListItem is required to include user.email in listAgents responses (per maintainer Udit-takkar). Future reviews should not flag this as unnecessary PII unless requirements change.

Applied to files:

  • apps/api/v2/src/ee/bookings/2024-08-13/services/bookings.service.ts
📚 Learning: 2025-09-03T11:54:05.409Z
Learnt from: supalarry
PR: calcom/cal.com#23514
File: apps/api/v2/src/ee/bookings/2024-08-13/services/bookings.service.ts:579-582
Timestamp: 2025-09-03T11:54:05.409Z
Learning: In calcom/cal.com bookings repository methods, when Prisma select uses `eventType: true`, all eventType fields including seatsShowAttendees are automatically included in the selection. Explicit field selection is not required when using `true` for nested relations.

Applied to files:

  • apps/api/v2/src/ee/bookings/2024-08-13/services/bookings.service.ts
  • apps/api/v2/src/ee/bookings/2024-08-13/services/output.service.ts
📚 Learning: 2025-09-03T11:54:05.409Z
Learnt from: supalarry
PR: calcom/cal.com#23514
File: apps/api/v2/src/ee/bookings/2024-08-13/services/bookings.service.ts:579-582
Timestamp: 2025-09-03T11:54:05.409Z
Learning: In calcom/cal.com bookings repository methods, when Prisma include uses `eventType: true`, all eventType fields including seatsShowAttendees are automatically included in the selection. Explicit field selection is not required when using `true` for nested relations.

Applied to files:

  • apps/api/v2/src/ee/bookings/2024-08-13/services/bookings.service.ts
  • apps/api/v2/src/ee/bookings/2024-08-13/services/output.service.ts
📚 Learning: 2025-08-19T09:47:49.478Z
Learnt from: eunjae-lee
PR: calcom/cal.com#23166
File: packages/prisma/migrations/20250818151914_routing_form_response_denormalized_backfill2/migration.sql:65-66
Timestamp: 2025-08-19T09:47:49.478Z
Learning: The Booking table has a unique constraint and index on the uid column (defined as `uid String unique` in schema.prisma), so JOINs on Booking.uid have optimal performance and don't require additional indexing.

Applied to files:

  • apps/api/v2/src/ee/bookings/2024-08-13/services/bookings.service.ts
🧬 Code graph analysis (2)
apps/api/v2/src/ee/bookings/2024-08-13/services/bookings.service.ts (3)
apps/api/v2/src/modules/auth/decorators/get-optional-user/get-optional-user.decorator.ts (1)
  • AuthOptionalUser (5-5)
apps/api/v2/src/modules/users/users.repository.ts (1)
  • UserWithProfile (10-13)
apps/api/v2/src/modules/auth/strategies/api-auth/api-auth.strategy.ts (1)
  • ApiAuthGuardUser (25-25)
apps/api/v2/src/ee/bookings/2024-08-13/services/output.service.ts (1)
packages/platform/types/bookings/2024-08-13/outputs/booking.output.ts (2)
  • CreateSeatedBookingOutput_2024_08_13 (349-360)
  • CreateRecurringSeatedBookingOutput_2024_08_13 (365-381)
⏰ Context from checks skipped due to timeout of 180000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Codacy Static Code Analysis
🔇 Additional comments (13)
apps/api/v2/src/ee/bookings/2024-08-13/services/output.service.ts (4)

270-279: Auth-aware attendee gating plumbed correctly (LGTM).

Optional showAttendees param with a safe fallback to !!databaseBooking.eventType?.seatsShowAttendees looks good.


282-363: Seated output respects visibility flag (LGTM).

Conditional population of parsed.attendees based on showAttendees aligns with privacy requirements. The email redaction from bookingFieldsResponses is also proper.


381-394: Batch create recurring seated output now controlled via flag (LGTM).

Signature and forwarding of showAttendees look good; sorting preserved.


396-405: Create recurring seated output respects visibility flag (LGTM).

Fallback behavior to event setting when flag is undefined is appropriate here.

apps/api/v2/src/ee/bookings/2024-08-13/services/bookings.service.ts (9)

144-151: Auth propagated to seated creation paths (LGTM).

Passing authUser into the seated creation flows matches the PR objective.


539-541: Auth propagated to single seated creation (LGTM).

Parameter threading is correct.


566-569: Auth-aware visibility on seated creation (LGTM).

Using shouldShowAttendees(databaseBooking, authUser) prevents leaks.


579-599: Auth-aware getBooking for seated and recurring seated (LGTM).

Visibility computed once and passed to output service correctly.


681-686: Helper for guard user construction (LGTM).

Keeps call sites tidy. Consider sourcing isSystemAdmin from profile/org roles if available.


912-919: Forwarding request user on cancel response (LGTM).

Ensures consistent visibility after cancel operations.


921-931: Recurring fetch helper now auth‑aware (LGTM).

Pass-through to getBooking with authUser is correct.


835-855: Reschedule response uses auth-aware visibility (LGTM).

Using request.user to compute showAttendees prevents overexposure.


1135-1137: Confirm/decline responses routed through auth-aware getBooking (LGTM).

Good consistency across post‑mutation responses.

Also applies to: 1169-1171

@Anshumancanrock
Copy link
Contributor Author

@coderabbitai review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Sep 16, 2025

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (5)
apps/api/v2/src/ee/bookings/2024-08-13/services/output.service.ts (1)

382-395: Fix redeclaration and undefined symbol in CreateRecurringSeated output.

  • showAttendees is both a parameter and a const inside the function.
  • userIsEventTypeAdminOrOwner is not defined in scope.
    This currently fails lint/build and always shadows the input flag.

Apply this diff:

   getOutputCreateRecurringSeatedBooking(
     databaseBooking: DatabaseBooking,
     seatUid: string,
     showAttendees?: boolean
   ): CreateRecurringSeatedBookingOutput_2024_08_13 {
-    const showAttendees = userIsEventTypeAdminOrOwner || !!databaseBooking.eventType?.seatsShowAttendees;
-    const getRecurringSeatedBookingOutput = this.getOutputRecurringSeatedBooking(
-      databaseBooking,
-      showAttendees ?? !!databaseBooking.eventType?.seatsShowAttendees
-    );
+    const show = showAttendees ?? !!databaseBooking.eventType?.seatsShowAttendees;
+    const getRecurringSeatedBookingOutput = this.getOutputRecurringSeatedBooking(databaseBooking, show);
     return { ...getRecurringSeatedBookingOutput, seatUid };
   }

Also applies to: 397-406

apps/api/v2/src/ee/bookings/2024-08-13/services/bookings.service.ts (4)

487-514: Compute showAttendees using auth, not only event setting (recurring + seated create).

Using only eventType.seatsShowAttendees hides attendees even for authorized creators (owner/admin). Derive it from auth or you’ll miss the PR objective.

Apply this diff:

   // Compute proper attendee visibility based on event type and authentication
-  // For recurring bookings, use the event type settings to determine authorization
-  const showAttendees = eventType.seatsShowAttendees || false; // Default to secure for recurring bookings
+  // Authorized users (owner/admin/host) or explicit event setting can see attendees
+  const isAdminOrOwner = authUser ? await this.userIsEventTypeAdminOrOwner(authUser, eventType) : false;
+  const showAttendees = isAdminOrOwner || !!eventType.seatsShowAttendees;

589-626: Fix mis-nested branching and wrong return for recurring seated in getBooking.

Current structure opens if (isRecurring && isSeated) then immediately checks if (isSeated) and returns non-recurring seated output. This is a functional bug and breaks the type/shape.

Apply this diff:

   if (isRecurring && !isSeated) {
     return this.outputService.getOutputRecurringBooking(booking);
   }
-  if (isRecurring && isSeated) {
-  if (isSeated) {
-    return this.outputService.getOutputSeatedBooking(booking, showAttendees);
-  }
+  if (isRecurring && isSeated) {
+    return this.outputService.getOutputRecurringSeatedBooking(booking, showAttendees);
+  }
+  if (isSeated) {
+    return this.outputService.getOutputSeatedBooking(booking, showAttendees);
+  }
   return this.outputService.getOutputBooking(booking);

732-774: Don’t call toApiAuthGuardUser with a minimal user; fetch profile once and reuse.

getBookings receives a minimal {id,email}; converting it to ApiAuthGuardUser without profile/role will mis‑type and can mis-evaluate authorization.

Apply this diff:

   const bookings = await this.bookingsRepository.getByIdsWithAttendeesWithBookingSeatAndUserAndEvent(ids);
+  // Build proper auth context once for visibility checks
+  const requestUserWithProfile = await this.usersRepository.findByIdWithProfile(user.id);
+  const authCtx: ApiAuthGuardUser | null = requestUserWithProfile
+    ? this.toApiAuthGuardUser(requestUserWithProfile)
+    : null;
@@
-      // Compute proper attendee visibility instead of just using event setting
-      const showAttendees = isSeated ? await this.shouldShowAttendees(formatted, this.toApiAuthGuardUser(user)) : true;
+      // Compute proper attendee visibility using a valid ApiAuthGuardUser
+      const showAttendees = isSeated ? await this.shouldShowAttendees(formatted, authCtx) : true;

857-876: Avoid redeclaring authUser inside rescheduleBooking.

Shadowing the parameter with const authUser = request.user... is confusing and flagged by linters.

Apply this diff:

-      const authUser = request.user as ApiAuthGuardUser | undefined;
-      const showAttendees = isSeated ? await this.shouldShowAttendees(databaseBooking, authUser) : true;
+      const requestAuthUser = request.user as ApiAuthGuardUser | undefined;
+      const showAttendees = isSeated ? await this.shouldShowAttendees(databaseBooking, requestAuthUser) : true;
♻️ Duplicate comments (1)
apps/api/v2/src/ee/bookings/2024-08-13/services/bookings.service.ts (1)

994-1008: Use findByIdWithProfile before toApiAuthGuardUser (type/logic).

toApiAuthGuardUser expects UserWithProfile; findById returns a plain User. Fetch the profile to avoid type errors and ensure correct admin flag computation.

Apply this diff:

-    const bookingOwner = await this.usersRepository.findById(bookingOwnerId);
+    const bookingOwner = await this.usersRepository.findByIdWithProfile(bookingOwnerId);
     if (!bookingOwner) {
       throw new NotFoundException(`Booking owner with id=${bookingOwnerId} not found.`);
     }
@@
-    const authUser = this.toApiAuthGuardUser(bookingOwner);
+    const authUser = this.toApiAuthGuardUser(bookingOwner);
     return this.getBooking(bookingUid, authUser);
🧹 Nitpick comments (2)
apps/api/v2/src/ee/bookings/2024-08-13/services/output.service.ts (1)

330-361: Confirm intended exposure of attendee email for seated bookings.

You delete bookingFieldsResponses.email but still populate attendeeParsed.email. If seated-attendee emails are meant to be hidden unless authorized, ensure email exposure is tied to showAttendees and product policy.

apps/api/v2/src/ee/bookings/2024-08-13/services/bookings.service.ts (1)

942-945: Avoid redeclaring authUser inside cancelBooking.

Rename the local to avoid clobbering the function parameter.

Apply this diff:

-    const authUser = request.user as ApiAuthGuardUser | undefined;
-    return this.getBooking(bookingUid, authUser);
+    const requestAuthUser = request.user as ApiAuthGuardUser | undefined;
+    return this.getBooking(bookingUid, requestAuthUser);
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between ce3831b and 2d182a0.

📒 Files selected for processing (3)
  • apps/api/v2/src/ee/bookings/2024-08-13/controllers/bookings.controller.ts (1 hunks)
  • apps/api/v2/src/ee/bookings/2024-08-13/services/bookings.service.ts (16 hunks)
  • apps/api/v2/src/ee/bookings/2024-08-13/services/output.service.ts (4 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/api/v2/src/ee/bookings/2024-08-13/controllers/bookings.controller.ts
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{service,repository}.ts

📄 CodeRabbit inference engine (.cursor/rules/review.mdc)

Avoid dot-suffixes like .service.ts or .repository.ts for new files; reserve .test.ts, .spec.ts, .types.ts for their specific purposes

Files:

  • apps/api/v2/src/ee/bookings/2024-08-13/services/output.service.ts
  • apps/api/v2/src/ee/bookings/2024-08-13/services/bookings.service.ts
**/*.ts

📄 CodeRabbit inference engine (.cursor/rules/review.mdc)

**/*.ts: For Prisma queries, only select data you need; never use include, always use select
Ensure the credential.key field is never returned from tRPC endpoints or APIs

Files:

  • apps/api/v2/src/ee/bookings/2024-08-13/services/output.service.ts
  • apps/api/v2/src/ee/bookings/2024-08-13/services/bookings.service.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/review.mdc)

Flag excessive Day.js use in performance-critical code; prefer native Date or Day.js .utc() in hot paths like loops

Files:

  • apps/api/v2/src/ee/bookings/2024-08-13/services/output.service.ts
  • apps/api/v2/src/ee/bookings/2024-08-13/services/bookings.service.ts
**/*.{ts,tsx,js,jsx}

⚙️ CodeRabbit configuration file

Flag default exports and encourage named exports. Named exports provide better tree-shaking, easier refactoring, and clearer imports. Exempt main components like pages, layouts, and components that serve as the primary export of a module.

Files:

  • apps/api/v2/src/ee/bookings/2024-08-13/services/output.service.ts
  • apps/api/v2/src/ee/bookings/2024-08-13/services/bookings.service.ts
🧠 Learnings (7)
📓 Common learnings
Learnt from: ShashwatPS
PR: calcom/cal.com#23638
File: packages/trpc/server/routers/viewer/calendars/setDestinationReminder.handler.test.ts:198-199
Timestamp: 2025-09-06T11:00:34.372Z
Learning: In calcom/cal.com PR #23638, the maintainer ShashwatPS determined that authorization checks in the setDestinationReminder handler are "not applicable" when CodeRabbit suggested adding user-scoped WHERE clauses to prevent users from modifying other users' destination calendar reminder settings.
📚 Learning: 2025-08-21T13:44:06.805Z
Learnt from: supalarry
PR: calcom/cal.com#23217
File: apps/api/v2/src/ee/event-types/event-types_2024_06_14/services/output-event-types.service.ts:93-94
Timestamp: 2025-08-21T13:44:06.805Z
Learning: In apps/api/v2/src/ee/event-types/event-types_2024_06_14/event-types.repository.ts, repository functions that use explicit Prisma select clauses (like getEventTypeWithSeats) are used for specific purposes and don't need to include all EventType fields like bookingRequiresAuthentication. These methods don't feed into the general OutputEventTypesService_2024_06_14 flow.

Applied to files:

  • apps/api/v2/src/ee/bookings/2024-08-13/services/output.service.ts
  • apps/api/v2/src/ee/bookings/2024-08-13/services/bookings.service.ts
📚 Learning: 2025-09-03T11:54:05.409Z
Learnt from: supalarry
PR: calcom/cal.com#23514
File: apps/api/v2/src/ee/bookings/2024-08-13/services/bookings.service.ts:579-582
Timestamp: 2025-09-03T11:54:05.409Z
Learning: In calcom/cal.com bookings repository methods, when Prisma include uses `eventType: true`, all eventType fields including seatsShowAttendees are automatically included in the selection. Explicit field selection is not required when using `true` for nested relations.

Applied to files:

  • apps/api/v2/src/ee/bookings/2024-08-13/services/output.service.ts
  • apps/api/v2/src/ee/bookings/2024-08-13/services/bookings.service.ts
📚 Learning: 2025-09-03T11:54:05.409Z
Learnt from: supalarry
PR: calcom/cal.com#23514
File: apps/api/v2/src/ee/bookings/2024-08-13/services/bookings.service.ts:579-582
Timestamp: 2025-09-03T11:54:05.409Z
Learning: In calcom/cal.com bookings repository methods, when Prisma select uses `eventType: true`, all eventType fields including seatsShowAttendees are automatically included in the selection. Explicit field selection is not required when using `true` for nested relations.

Applied to files:

  • apps/api/v2/src/ee/bookings/2024-08-13/services/output.service.ts
  • apps/api/v2/src/ee/bookings/2024-08-13/services/bookings.service.ts
📚 Learning: 2025-08-27T12:15:43.830Z
Learnt from: Udit-takkar
PR: calcom/cal.com#22995
File: packages/trpc/server/routers/viewer/aiVoiceAgent/testCall.handler.ts:41-44
Timestamp: 2025-08-27T12:15:43.830Z
Learning: In calcom/cal.com, the AgentService.getAgent() method in packages/features/calAIPhone/providers/retellAI/services/AgentService.ts does NOT include authorization checks - it only validates the agentId parameter and directly calls the repository without verifying user/team access. This contrasts with other methods like getAgentWithDetails() which properly use findByIdWithUserAccessAndDetails() for authorization. When reviewing updateToolsFromAgentId() calls, always verify both agent ownership and eventType ownership are checked.

Applied to files:

  • apps/api/v2/src/ee/bookings/2024-08-13/services/bookings.service.ts
📚 Learning: 2025-08-08T09:29:11.681Z
Learnt from: Udit-takkar
PR: calcom/cal.com#22919
File: packages/features/calAIPhone/interfaces/AIPhoneService.interface.ts:118-143
Timestamp: 2025-08-08T09:29:11.681Z
Learning: In calcom/cal.com PR #22919, packages/features/calAIPhone/interfaces/AIPhoneService.interface.ts (TypeScript), the AIPhoneServiceAgentListItem is required to include user.email in listAgents responses (per maintainer Udit-takkar). Future reviews should not flag this as unnecessary PII unless requirements change.

Applied to files:

  • apps/api/v2/src/ee/bookings/2024-08-13/services/bookings.service.ts
📚 Learning: 2025-08-19T09:47:49.478Z
Learnt from: eunjae-lee
PR: calcom/cal.com#23166
File: packages/prisma/migrations/20250818151914_routing_form_response_denormalized_backfill2/migration.sql:65-66
Timestamp: 2025-08-19T09:47:49.478Z
Learning: The Booking table has a unique constraint and index on the uid column (defined as `uid String unique` in schema.prisma), so JOINs on Booking.uid have optimal performance and don't require additional indexing.

Applied to files:

  • apps/api/v2/src/ee/bookings/2024-08-13/services/bookings.service.ts
🧬 Code graph analysis (2)
apps/api/v2/src/ee/bookings/2024-08-13/services/output.service.ts (2)
packages/platform/types/bookings/2024-08-13/outputs/booking.output.ts (2)
  • CreateSeatedBookingOutput_2024_08_13 (349-360)
  • CreateRecurringSeatedBookingOutput_2024_08_13 (365-381)
apps/api/v2/src/ee/bookings/2024-08-13/services/bookings.service.ts (1)
  • userIsEventTypeAdminOrOwner (181-217)
apps/api/v2/src/ee/bookings/2024-08-13/services/bookings.service.ts (3)
apps/api/v2/src/modules/auth/decorators/get-optional-user/get-optional-user.decorator.ts (1)
  • AuthOptionalUser (5-5)
apps/api/v2/src/modules/users/users.repository.ts (1)
  • UserWithProfile (10-13)
apps/api/v2/src/modules/auth/strategies/api-auth/api-auth.strategy.ts (1)
  • ApiAuthGuardUser (25-25)
🪛 Biome (2.1.2)
apps/api/v2/src/ee/bookings/2024-08-13/services/output.service.ts

[error] 402-402: Shouldn't redeclare 'showAttendees'. Consider to delete it or rename it.

'showAttendees' is defined here:

(lint/suspicious/noRedeclare)

apps/api/v2/src/ee/bookings/2024-08-13/services/bookings.service.ts

[error] 637-637: Illegal use of reserved keyword private as an identifier in strict mode

(parse)


[error] 637-637: Expected a semicolon or an implicit semicolon after a statement, but found none

An explicit or implicit semicolon is expected here...

...Which is required to end this statement

(parse)


[error] 637-637: Expected a semicolon or an implicit semicolon after a statement, but found none

An explicit or implicit semicolon is expected here...

...Which is required to end this statement

(parse)


[error] 638-638: expected , but instead found :

Remove :

(parse)


[error] 639-639: expected , but instead found attendees

Remove attendees

(parse)


[error] 639-639: expected , but instead found :

Remove :

(parse)


[error] 639-639: expected , but instead found [

Remove [

(parse)


[error] 639-639: expected , but instead found ;

Remove ;

(parse)


[error] 641-641: Expected an expression, or an assignment but instead found ':'.

Expected an expression, or an assignment here.

(parse)


[error] 642-642: Expected an expression, or an assignment but instead found ':'.

Expected an expression, or an assignment here.

(parse)


[error] 643-643: Expected an expression, or an assignment but instead found ':'.

Expected an expression, or an assignment here.

(parse)


[error] 644-644: Expected an expression, or an assignment but instead found ':'.

Expected an expression, or an assignment here.

(parse)


[error] 645-645: Expected a statement but instead found '| null'.

Expected a statement here.

(parse)


[error] 646-646: Expected an identifier, a string literal, a number literal, a private field name, or a computed name but instead found ','.

Expected an identifier, a string literal, a number literal, a private field name, or a computed name here.

(parse)


[error] 648-648: Expected an identifier, a string literal, a number literal, a private field name, or a computed name but instead found ')'.

Expected an identifier, a string literal, a number literal, a private field name, or a computed name here.

(parse)


[error] 648-648: Expected a class parameters but instead found '{'.

Expected a class parameters here.

(parse)


[error] 680-680: await is only allowed within async functions and at the top levels of modules.

(parse)


[error] 943-943: Shouldn't redeclare 'authUser'. Consider to delete it or rename it.

'authUser' is defined here:

(lint/suspicious/noRedeclare)

⏰ Context from checks skipped due to timeout of 180000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Install dependencies / Yarn install & cache
🔇 Additional comments (2)
apps/api/v2/src/ee/bookings/2024-08-13/services/output.service.ts (1)

271-280: Good: auth-aware fallback for attendee visibility is correctly plumbed.

Passing an optional showAttendees with a sane fallback to eventType?.seatsShowAttendees is aligned with the PR objectives.

apps/api/v2/src/ee/bookings/2024-08-13/services/bookings.service.ts (1)

1161-1163: LGTM: returning via getBooking ensures visibility is consistently computed.

Good reuse; once the helper issues above are fixed, these paths will honor auth-aware attendee visibility.

Also applies to: 1195-1197

Comment on lines +628 to +699
/**
* Determines whether attendees should be shown based on privacy settings and user authorization.
* Returns true if:
* 1. Event type allows showing attendees (seatsShowAttendees), OR
* 2. User is authenticated and is either:
* - An attendee of the booking, OR
* - The event organizer/host, OR
* - Has admin privileges for the event
*/
private async shouldShowAttendees(
booking: {
attendees: { email: string }[];
eventType: {
seatsShowAttendees?: boolean | null;
id?: number;
teamId?: number | null;
owner?: { id: number } | null
} | null
},
authUser?: AuthOptionalUser
): Promise<boolean> {
// If event type allows showing attendees, always show them
if (booking.eventType?.seatsShowAttendees) {
return true;
}

// If no authentication provided, respect the privacy setting
if (!authUser || !booking.eventType) {
return false;
}

// Check if user is an attendee of this booking
const isAttendee = booking.attendees.some(attendee => attendee.email === authUser.email);
if (isAttendee) {
return true;
}

// Check if user is the event organizer/host
if (booking.eventType.owner?.id === authUser.id) {
return true;
}

// For team events, check if user has admin access
if (booking.eventType.teamId) {
try {
const eventType = {
id: booking.eventType.id || 0,
teamId: booking.eventType.teamId,
owner: booking.eventType.owner,
bookingRequiresAuthentication: true, // Force authentication check for team access validation
} as EventTypeWithOwnerAndTeam;

await this.checkBookingRequiresAuthenticationSetting(eventType, authUser);
return true; // If no exception thrown, user has team admin access
} catch {
// User doesn't have team admin access
}
}

return false;
}

/**
* Convert UserWithProfile to ApiAuthGuardUser format for proper authorization
*/
private toApiAuthGuardUser(user: UserWithProfile): ApiAuthGuardUser {
return {
...user,
isSystemAdmin: user.role === "ADMIN",
};
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Rewrite shouldShowAttendees: incorrect eventType fields and throwing helper usage.

  • Uses eventType.owner?.id which is not guaranteed; the model exposes userId.
  • Calls checkBookingRequiresAuthenticationSetting(...) with a mismatched signature and a throwing contract; inappropriate for a boolean helper.
  • Logic can be simplified to: event setting OR attendee OR admin/owner/host.

Apply this diff:

-  private async shouldShowAttendees(
-    booking: { 
-      attendees: { email: string }[];
-      eventType: { 
-        seatsShowAttendees?: boolean | null; 
-        id?: number; 
-        teamId?: number | null; 
-        owner?: { id: number } | null 
-      } | null 
-    },
-    authUser?: AuthOptionalUser
-  ): Promise<boolean> {
-    // If event type allows showing attendees, always show them
-    if (booking.eventType?.seatsShowAttendees) {
-      return true;
-    }
-
-    // If no authentication provided, respect the privacy setting
-    if (!authUser || !booking.eventType) {
-      return false;
-    }
-
-    // Check if user is an attendee of this booking
-    const isAttendee = booking.attendees.some(attendee => attendee.email === authUser.email);
-    if (isAttendee) {
-      return true;
-    }
-
-    // Check if user is the event organizer/host
-    if (booking.eventType.owner?.id === authUser.id) {
-      return true;
-    }
-
-    // For team events, check if user has admin access
-    if (booking.eventType.teamId) {
-      try {
-        const eventType = {
-          id: booking.eventType.id || 0,
-          teamId: booking.eventType.teamId,
-          owner: booking.eventType.owner,
-          bookingRequiresAuthentication: true, // Force authentication check for team access validation
-        } as EventTypeWithOwnerAndTeam;
-
-        await this.checkBookingRequiresAuthenticationSetting(eventType, authUser);
-        return true; // If no exception thrown, user has team admin access
-      } catch {
-        // User doesn't have team admin access
-      }
-    }
-
-    return false;
-  }
+  private async shouldShowAttendees(
+    booking: {
+      attendees: { email: string }[];
+      eventType: Pick<EventType, "id" | "userId" | "teamId" | "seatsShowAttendees"> | null;
+    },
+    authUser?: AuthOptionalUser
+  ): Promise<boolean> {
+    // 1) Event setting
+    if (booking.eventType?.seatsShowAttendees) return true;
+    // 2) No auth -> hide
+    if (!authUser || !booking.eventType) return false;
+    // 3) Attendee of the booking
+    if (booking.attendees.some((a) => a.email === authUser.email)) return true;
+    // 4) Owner/host/admin via existing helper (covers ownerId, host/assigned, team/org admin, system admin)
+    return this.userIsEventTypeAdminOrOwner(authUser, booking.eventType as EventType);
+  }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
/**
* Determines whether attendees should be shown based on privacy settings and user authorization.
* Returns true if:
* 1. Event type allows showing attendees (seatsShowAttendees), OR
* 2. User is authenticated and is either:
* - An attendee of the booking, OR
* - The event organizer/host, OR
* - Has admin privileges for the event
*/
private async shouldShowAttendees(
booking: {
attendees: { email: string }[];
eventType: {
seatsShowAttendees?: boolean | null;
id?: number;
teamId?: number | null;
owner?: { id: number } | null
} | null
},
authUser?: AuthOptionalUser
): Promise<boolean> {
// If event type allows showing attendees, always show them
if (booking.eventType?.seatsShowAttendees) {
return true;
}
// If no authentication provided, respect the privacy setting
if (!authUser || !booking.eventType) {
return false;
}
// Check if user is an attendee of this booking
const isAttendee = booking.attendees.some(attendee => attendee.email === authUser.email);
if (isAttendee) {
return true;
}
// Check if user is the event organizer/host
if (booking.eventType.owner?.id === authUser.id) {
return true;
}
// For team events, check if user has admin access
if (booking.eventType.teamId) {
try {
const eventType = {
id: booking.eventType.id || 0,
teamId: booking.eventType.teamId,
owner: booking.eventType.owner,
bookingRequiresAuthentication: true, // Force authentication check for team access validation
} as EventTypeWithOwnerAndTeam;
await this.checkBookingRequiresAuthenticationSetting(eventType, authUser);
return true; // If no exception thrown, user has team admin access
} catch {
// User doesn't have team admin access
}
}
return false;
}
/**
* Convert UserWithProfile to ApiAuthGuardUser format for proper authorization
*/
private toApiAuthGuardUser(user: UserWithProfile): ApiAuthGuardUser {
return {
...user,
isSystemAdmin: user.role === "ADMIN",
};
}
private async shouldShowAttendees(
booking: {
attendees: { email: string }[];
eventType: Pick<EventType, "id" | "userId" | "teamId" | "seatsShowAttendees"> | null;
},
authUser?: AuthOptionalUser
): Promise<boolean> {
// 1) Event setting
if (booking.eventType?.seatsShowAttendees) return true;
// 2) No auth -> hide
if (!authUser || !booking.eventType) return false;
// 3) Attendee of the booking
if (booking.attendees.some((a) => a.email === authUser.email)) return true;
// 4) Owner/host/admin via existing helper (covers ownerId, host/assigned, team/org admin, system admin)
return this.userIsEventTypeAdminOrOwner(authUser, booking.eventType as EventType);
}
/**
* Convert UserWithProfile to ApiAuthGuardUser format for proper authorization
*/
private toApiAuthGuardUser(user: UserWithProfile): ApiAuthGuardUser {
return {
...user,
isSystemAdmin: user.role === "ADMIN",
};
}
🧰 Tools
🪛 Biome (2.1.2)

[error] 637-637: Illegal use of reserved keyword private as an identifier in strict mode

(parse)


[error] 637-637: Expected a semicolon or an implicit semicolon after a statement, but found none

An explicit or implicit semicolon is expected here...

...Which is required to end this statement

(parse)


[error] 637-637: Expected a semicolon or an implicit semicolon after a statement, but found none

An explicit or implicit semicolon is expected here...

...Which is required to end this statement

(parse)


[error] 638-638: expected , but instead found :

Remove :

(parse)


[error] 639-639: expected , but instead found attendees

Remove attendees

(parse)


[error] 639-639: expected , but instead found :

Remove :

(parse)


[error] 639-639: expected , but instead found [

Remove [

(parse)


[error] 639-639: expected , but instead found ;

Remove ;

(parse)


[error] 641-641: Expected an expression, or an assignment but instead found ':'.

Expected an expression, or an assignment here.

(parse)


[error] 642-642: Expected an expression, or an assignment but instead found ':'.

Expected an expression, or an assignment here.

(parse)


[error] 643-643: Expected an expression, or an assignment but instead found ':'.

Expected an expression, or an assignment here.

(parse)


[error] 644-644: Expected an expression, or an assignment but instead found ':'.

Expected an expression, or an assignment here.

(parse)


[error] 645-645: Expected a statement but instead found '| null'.

Expected a statement here.

(parse)


[error] 646-646: Expected an identifier, a string literal, a number literal, a private field name, or a computed name but instead found ','.

Expected an identifier, a string literal, a number literal, a private field name, or a computed name here.

(parse)


[error] 648-648: Expected an identifier, a string literal, a number literal, a private field name, or a computed name but instead found ')'.

Expected an identifier, a string literal, a number literal, a private field name, or a computed name here.

(parse)


[error] 648-648: Expected a class parameters but instead found '{'.

Expected a class parameters here.

(parse)


[error] 680-680: await is only allowed within async functions and at the top levels of modules.

(parse)

🤖 Prompt for AI Agents
In apps/api/v2/src/ee/bookings/2024-08-13/services/bookings.service.ts around
lines 628-699, shouldShowAttendees is using the wrong event owner field and an
inappropriate throwing helper: replace any use of booking.eventType.owner?.id
with booking.eventType.userId (or the correct userId field from the eventType
model), remove the try/catch around checkBookingRequiresAuthenticationSetting
and instead perform a non-throwing boolean team-admin check (either call an
existing userHasTeamAdminAccess/teamPermission helper that returns boolean or
implement a simple check that returns true only if authUser has admin access for
booking.eventType.teamId), and simplify the logic to return true when
seatsShowAttendees is true OR authUser is an attendee OR authUser.id ===
eventType.userId OR authUser is system admin OR the non-throwing team-admin
check passes; ensure proper null/undefined guards for eventType and types match
the signature.

@supalarry
Copy link
Contributor

Thank you for your contribution - we really appreciate it! I will close this issue though because a fix was merged already in #23868

@supalarry supalarry closed this Sep 17, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

api area: API, enterprise API, access token, OAuth bookings area: bookings, availability, timezones, double booking 🐛 bug Something isn't working community Created by Linear-GitHub Sync ✨ feature New feature or request High priority Created by Linear-GitHub Sync platform Anything related to our platform plan seats area: seats, guest meetings, multiple people size/L

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: authorized request can view hidden attendees

2 participants