fix: Org/team admin or owner should be marked as ‘no show’ in a member’s booking (#26009)
* fix: update tempOrgRediret on updating s lug * Update packages/trpc/server/routers/viewer/organizations/adminUpdate.handler.ts Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com> * refactor * fix * fix * revert * fix handle no show * revert * Remove metadata from organization query selection Removed metadata selection from organization query. * use repo * update * Update packages/features/bookings/lib/checkIfUserIsAuthorizedToManageBooking.ts Co-authored-by: Syed Ali Shahbaz <52925846+alishaz-polymath@users.noreply.github.com> * update --------- Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com> Co-authored-by: Syed Ali Shahbaz <52925846+alishaz-polymath@users.noreply.github.com>
This commit is contained in:
co-authored by
cubic-dev-ai[bot]
Syed Ali Shahbaz
parent
33eee8dfc3
commit
23920fe9ce
+17
-7
@@ -16,9 +16,9 @@ import { BookingsRepositoryFixture } from "test/fixtures/repository/bookings.rep
|
||||
import { EventTypesRepositoryFixture } from "test/fixtures/repository/event-types.repository.fixture";
|
||||
import { OAuthClientRepositoryFixture } from "test/fixtures/repository/oauth-client.repository.fixture";
|
||||
import { TeamRepositoryFixture } from "test/fixtures/repository/team.repository.fixture";
|
||||
import { TokensRepositoryFixture } from "test/fixtures/repository/tokens.repository.fixture";
|
||||
import { UserRepositoryFixture } from "test/fixtures/repository/users.repository.fixture";
|
||||
import { randomString } from "test/utils/randomString";
|
||||
import { withApiAuth } from "test/utils/withApiAuth";
|
||||
|
||||
import { CAL_API_VERSION_HEADER, SUCCESS_STATUS, VERSION_2024_08_13 } from "@calcom/platform-constants";
|
||||
import {
|
||||
@@ -73,6 +73,7 @@ type EmailSetup = {
|
||||
eventTypeId: number;
|
||||
createdBookingUid: string;
|
||||
rescheduledBookingUid: string;
|
||||
accessToken: string;
|
||||
};
|
||||
|
||||
describe("Bookings Endpoints 2024-08-13 confirm emails", () => {
|
||||
@@ -85,6 +86,7 @@ describe("Bookings Endpoints 2024-08-13 confirm emails", () => {
|
||||
let eventTypesRepositoryFixture: EventTypesRepositoryFixture;
|
||||
let oauthClientRepositoryFixture: OAuthClientRepositoryFixture;
|
||||
let teamRepositoryFixture: TeamRepositoryFixture;
|
||||
let tokensRepositoryFixture: TokensRepositoryFixture;
|
||||
|
||||
let emailsEnabledSetup: EmailSetup;
|
||||
let emailsDisabledSetup: EmailSetup;
|
||||
@@ -94,12 +96,9 @@ describe("Bookings Endpoints 2024-08-13 confirm emails", () => {
|
||||
const userEmailsDisabled = `confirm-emails-2024-08-13-user-${randomString()}@api.com`;
|
||||
|
||||
beforeAll(async () => {
|
||||
const moduleRef = await withApiAuth(
|
||||
authEmail,
|
||||
Test.createTestingModule({
|
||||
imports: [AppModule, PrismaModule, UsersModule, SchedulesModule_2024_04_15],
|
||||
})
|
||||
)
|
||||
const moduleRef = await Test.createTestingModule({
|
||||
imports: [AppModule, PrismaModule, UsersModule, SchedulesModule_2024_04_15],
|
||||
})
|
||||
.overrideGuard(PermissionsGuard)
|
||||
.useValue({
|
||||
canActivate: () => true,
|
||||
@@ -112,6 +111,7 @@ describe("Bookings Endpoints 2024-08-13 confirm emails", () => {
|
||||
oauthClientRepositoryFixture = new OAuthClientRepositoryFixture(moduleRef);
|
||||
teamRepositoryFixture = new TeamRepositoryFixture(moduleRef);
|
||||
schedulesService = moduleRef.get<SchedulesService_2024_04_15>(SchedulesService_2024_04_15);
|
||||
tokensRepositoryFixture = new TokensRepositoryFixture(moduleRef);
|
||||
|
||||
organization = await teamRepositoryFixture.create({
|
||||
name: `confirm-emails-2024-08-13-organization-${randomString()}`,
|
||||
@@ -152,6 +152,8 @@ describe("Bookings Endpoints 2024-08-13 confirm emails", () => {
|
||||
},
|
||||
});
|
||||
|
||||
const tokens = await tokensRepositoryFixture.createTokens(user.id, oAuthClientEmailsEnabled.id);
|
||||
|
||||
const userSchedule: CreateScheduleInput_2024_04_15 = {
|
||||
name: `confirm-emails-2024-08-13-schedule-${randomString()}`,
|
||||
timeZone: "Europe/Rome",
|
||||
@@ -174,6 +176,7 @@ describe("Bookings Endpoints 2024-08-13 confirm emails", () => {
|
||||
eventTypeId: event.id,
|
||||
createdBookingUid: "",
|
||||
rescheduledBookingUid: "",
|
||||
accessToken: tokens.accessToken,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -188,6 +191,8 @@ describe("Bookings Endpoints 2024-08-13 confirm emails", () => {
|
||||
},
|
||||
},
|
||||
});
|
||||
const tokens = await tokensRepositoryFixture.createTokens(user.id, oAuthClientEmailsDisabled.id);
|
||||
|
||||
const userSchedule: CreateScheduleInput_2024_04_15 = {
|
||||
name: `confirm-emails-2024-08-13-schedule-${randomString()}`,
|
||||
timeZone: "Europe/Rome",
|
||||
@@ -209,6 +214,7 @@ describe("Bookings Endpoints 2024-08-13 confirm emails", () => {
|
||||
eventTypeId: event.id,
|
||||
createdBookingUid: "",
|
||||
rescheduledBookingUid: "",
|
||||
accessToken: tokens.accessToken,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -277,6 +283,7 @@ describe("Bookings Endpoints 2024-08-13 confirm emails", () => {
|
||||
return request(app.getHttpServer())
|
||||
.post(`/v2/bookings/${emailsDisabledSetup.createdBookingUid}/confirm`)
|
||||
.set(CAL_API_VERSION_HEADER, VERSION_2024_08_13)
|
||||
.set("Authorization", `Bearer ${emailsDisabledSetup.accessToken}`)
|
||||
.expect(200)
|
||||
.then(async (response) => {
|
||||
const responseBody: GetBookingOutput_2024_08_13 = response.body;
|
||||
@@ -331,6 +338,7 @@ describe("Bookings Endpoints 2024-08-13 confirm emails", () => {
|
||||
return request(app.getHttpServer())
|
||||
.post(`/v2/bookings/${emailsDisabledSetup.createdBookingUid}/decline`)
|
||||
.set(CAL_API_VERSION_HEADER, VERSION_2024_08_13)
|
||||
.set("Authorization", `Bearer ${emailsDisabledSetup.accessToken}`)
|
||||
.expect(200)
|
||||
.then(async (response) => {
|
||||
const responseBody: GetBookingOutput_2024_08_13 = response.body;
|
||||
@@ -391,6 +399,7 @@ describe("Bookings Endpoints 2024-08-13 confirm emails", () => {
|
||||
return request(app.getHttpServer())
|
||||
.post(`/v2/bookings/${emailsEnabledSetup.createdBookingUid}/confirm`)
|
||||
.set(CAL_API_VERSION_HEADER, VERSION_2024_08_13)
|
||||
.set("Authorization", `Bearer ${emailsEnabledSetup.accessToken}`)
|
||||
.expect(200)
|
||||
.then(async (response) => {
|
||||
const responseBody: GetBookingOutput_2024_08_13 = response.body;
|
||||
@@ -508,6 +517,7 @@ describe("Bookings Endpoints 2024-08-13 confirm emails", () => {
|
||||
return request(app.getHttpServer())
|
||||
.post(`/v2/bookings/${emailsEnabledSetup.createdBookingUid}/decline`)
|
||||
.set(CAL_API_VERSION_HEADER, VERSION_2024_08_13)
|
||||
.set("Authorization", `Bearer ${emailsEnabledSetup.accessToken}`)
|
||||
.expect(200)
|
||||
.then(async (response) => {
|
||||
const responseBody: GetBookingOutput_2024_08_13 = response.body;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { type TFunction } from "i18next";
|
||||
|
||||
import { BookingRepository } from "@calcom/features/bookings/repositories/BookingRepository";
|
||||
import { BookingAccessService } from "@calcom/features/bookings/services/BookingAccessService";
|
||||
import { CreditService } from "@calcom/features/ee/billing/credit-service";
|
||||
import { getBookerBaseUrl } from "@calcom/features/ee/organizations/lib/getBookerUrlServer";
|
||||
import { workflowSelect } from "@calcom/features/ee/workflows/lib/getAllWorkflows";
|
||||
@@ -423,9 +424,14 @@ const assertCanAccessBooking = async (bookingUid: string, userId?: number) => {
|
||||
if (!userId) throw new HttpError({ statusCode: 401 });
|
||||
|
||||
const bookingRepo = new BookingRepository(prisma);
|
||||
const booking = await bookingRepo.findBookingByUidAndUserId({ bookingUid, userId });
|
||||
const booking = await bookingRepo.findByUidIncludeEventTypeAndReferences({ bookingUid });
|
||||
const bookingAccessService = new BookingAccessService(prisma);
|
||||
const isAuthorized = await bookingAccessService.doesUserIdHaveAccessToBooking({
|
||||
userId,
|
||||
bookingUid,
|
||||
});
|
||||
|
||||
if (!booking)
|
||||
if (!isAuthorized)
|
||||
throw new HttpError({ statusCode: 403, message: "You are not allowed to access this booking" });
|
||||
|
||||
const isUpcoming = new Date(booking.endTime) >= new Date();
|
||||
|
||||
@@ -7,12 +7,12 @@ import { getCalEventResponses } from "@calcom/features/bookings/lib/getCalEventR
|
||||
import { handleConfirmation } from "@calcom/features/bookings/lib/handleConfirmation";
|
||||
import { handleWebhookTrigger } from "@calcom/features/bookings/lib/handleWebhookTrigger";
|
||||
import { processPaymentRefund } from "@calcom/features/bookings/lib/payment/processPaymentRefund";
|
||||
import { BookingAccessService } from "@calcom/features/bookings/services/BookingAccessService";
|
||||
import { CreditService } from "@calcom/features/ee/billing/credit-service";
|
||||
import { getBookerBaseUrl } from "@calcom/features/ee/organizations/lib/getBookerUrlServer";
|
||||
import { workflowSelect } from "@calcom/features/ee/workflows/lib/getAllWorkflows";
|
||||
import { getAllWorkflowsFromEventType } from "@calcom/features/ee/workflows/lib/getAllWorkflowsFromEventType";
|
||||
import { WorkflowService } from "@calcom/features/ee/workflows/lib/service/WorkflowService";
|
||||
import { PrismaOrgMembershipRepository } from "@calcom/features/membership/repositories/PrismaOrgMembershipRepository";
|
||||
import type { GetSubscriberOptions } from "@calcom/features/webhooks/lib/getWebhooks";
|
||||
import type { EventPayloadType, EventTypeInfo } from "@calcom/features/webhooks/lib/sendPayload";
|
||||
import getOrgIdFromMemberOrTeamId from "@calcom/lib/getOrgIdFromMemberOrTeamId";
|
||||
@@ -24,13 +24,7 @@ import { getTimeFormatStringFromUserTimeFormat } from "@calcom/lib/timeFormat";
|
||||
import type { TraceContext } from "@calcom/lib/tracing";
|
||||
import { prisma } from "@calcom/prisma";
|
||||
import { Prisma } from "@calcom/prisma/client";
|
||||
import {
|
||||
BookingStatus,
|
||||
MembershipRole,
|
||||
WebhookTriggerEvents,
|
||||
WorkflowTriggerEvents,
|
||||
UserPermissionRole,
|
||||
} from "@calcom/prisma/enums";
|
||||
import { BookingStatus, WebhookTriggerEvents, WorkflowTriggerEvents } from "@calcom/prisma/enums";
|
||||
import type { EventTypeMetadata } from "@calcom/prisma/zod-utils";
|
||||
import type { CalendarEvent } from "@calcom/types/Calendar";
|
||||
|
||||
@@ -149,14 +143,19 @@ export const confirmHandler = async ({ ctx, input }: ConfirmOptions) => {
|
||||
throw new TRPCError({ code: "BAD_REQUEST", message: "Booking must have an organizer" });
|
||||
}
|
||||
|
||||
await checkIfUserIsAuthorizedToConfirmBooking({
|
||||
eventTypeId: booking.eventTypeId,
|
||||
loggedInUserId: ctx.user.id,
|
||||
teamId: booking.eventType?.teamId || booking.eventType?.parent?.teamId,
|
||||
bookingUserId: booking.userId,
|
||||
userRole: ctx.user.role,
|
||||
const bookingAccessService = new BookingAccessService(prisma);
|
||||
const isUserAuthorizedToConfirmBooking = await bookingAccessService.doesUserIdHaveAccessToBooking({
|
||||
userId: ctx.user.id,
|
||||
bookingId: bookingId,
|
||||
});
|
||||
|
||||
if (!isUserAuthorizedToConfirmBooking) {
|
||||
throw new TRPCError({
|
||||
code: "UNAUTHORIZED",
|
||||
message: "User is not authorized to confirm this booking",
|
||||
});
|
||||
}
|
||||
|
||||
// Do not move this before authorization check.
|
||||
// This is done to avoid exposing extra information to the requester.
|
||||
if (booking.status === BookingStatus.ACCEPTED) {
|
||||
@@ -443,68 +442,3 @@ export const confirmHandler = async ({ ctx, input }: ConfirmOptions) => {
|
||||
|
||||
return { message, status };
|
||||
};
|
||||
|
||||
const checkIfUserIsAuthorizedToConfirmBooking = async ({
|
||||
eventTypeId,
|
||||
loggedInUserId,
|
||||
teamId,
|
||||
bookingUserId,
|
||||
userRole,
|
||||
}: {
|
||||
eventTypeId: number | null;
|
||||
loggedInUserId: number;
|
||||
teamId?: number | null;
|
||||
bookingUserId: number | null;
|
||||
userRole: string;
|
||||
}): Promise<void> => {
|
||||
// check system wide admin
|
||||
if (userRole === UserPermissionRole.ADMIN) return;
|
||||
|
||||
// Check if the user is the owner of the event type
|
||||
if (bookingUserId === loggedInUserId) return;
|
||||
|
||||
// Check if user is associated with the event type
|
||||
if (eventTypeId) {
|
||||
const [loggedInUserAsHostOfEventType, loggedInUserAsUserOfEventType] = await Promise.all([
|
||||
prisma.eventType.findUnique({
|
||||
where: {
|
||||
id: eventTypeId,
|
||||
hosts: { some: { userId: loggedInUserId } },
|
||||
},
|
||||
select: { id: true },
|
||||
}),
|
||||
prisma.eventType.findUnique({
|
||||
where: {
|
||||
id: eventTypeId,
|
||||
users: { some: { id: loggedInUserId } },
|
||||
},
|
||||
select: { id: true },
|
||||
}),
|
||||
]);
|
||||
|
||||
if (loggedInUserAsHostOfEventType || loggedInUserAsUserOfEventType) return;
|
||||
}
|
||||
|
||||
// Check if the user is an admin/owner of the team the booking belongs to
|
||||
if (teamId) {
|
||||
const membership = await prisma.membership.findFirst({
|
||||
where: {
|
||||
userId: loggedInUserId,
|
||||
teamId: teamId,
|
||||
role: {
|
||||
in: [MembershipRole.OWNER, MembershipRole.ADMIN],
|
||||
},
|
||||
},
|
||||
});
|
||||
if (membership) return;
|
||||
}
|
||||
|
||||
if (
|
||||
bookingUserId &&
|
||||
(await PrismaOrgMembershipRepository.isLoggedInUserOrgAdminOfBookingHost(loggedInUserId, bookingUserId))
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
throw new TRPCError({ code: "UNAUTHORIZED", message: "User is not authorized to confirm this booking" });
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user