Skip to content

Commit

Permalink
Add unauthorization check for delete function and test
Browse files Browse the repository at this point in the history
  • Loading branch information
hp77-creator committed Feb 12, 2025
1 parent 2f5f508 commit 0d8458b
Show file tree
Hide file tree
Showing 3 changed files with 133 additions and 0 deletions.
118 changes: 118 additions & 0 deletions apps/web/test/lib/deleteBooking.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
import prismaMock from "../../../../tests/libs/__mocks__/prismaMock";

import { describe, expect, it, beforeEach, vi } from "vitest";

import { deleteHandler } from "@calcom/trpc/server/routers/viewer/bookings/delete.handler";
import { deletePastBookingsHandler } from "@calcom/trpc/server/routers/viewer/bookings/deletePastBookings.handler";

describe("Booking deletion", () => {
beforeEach(() => {
vi.clearAllMocks();
});

it("should delete a single booking successfully", async () => {
const mockBooking = {
id: 1,
userId: 123,
};

prismaMock.booking.findFirst.mockResolvedValue(mockBooking);
prismaMock.booking.delete.mockResolvedValue(mockBooking);

const ctx = {
user: {
id: 123,
},
};

await deleteHandler({
ctx,
input: { id: 1 },
});

expect(prismaMock.booking.delete).toHaveBeenCalledTimes(1);
expect(prismaMock.booking.delete).toHaveBeenCalledWith({
where: { id: 1 },
});
});

it("should delete multiple past bookings correctly", async () => {
const mockBookings = [
{ id: 1, userId: 123 },
{ id: 2, userId: 123 },
];

prismaMock.booking.findMany.mockResolvedValue(mockBookings);
prismaMock.booking.deleteMany.mockResolvedValue({ count: 2 });

const ctx = {
user: {
id: 123,
},
};

await deletePastBookingsHandler({
ctx,
input: { ids: [1, 2] },
});

expect(prismaMock.booking.deleteMany).toHaveBeenCalledTimes(1);
expect(prismaMock.booking.deleteMany).toHaveBeenCalledWith({
where: {
OR: [{ userId: 123 }, { attendees: { some: { email: undefined } } }],
endTime: { lt: expect.any(Date) },
id: { in: undefined },
status: { notIn: ["CANCELLED", "REJECTED"] },
},
});
});

it("should prevent deletion of unauthorized bookings", async () => {
const mockBooking = {
id: 1,
userId: 456,
};

prismaMock.booking.findFirst.mockResolvedValue(mockBooking);

const ctx = {
user: {
id: 123,
},
};

await expect(
deleteHandler({
ctx,
input: { id: 1 },
})
).rejects.toThrow(/unauthorized/i);

expect(prismaMock.booking.delete).not.toHaveBeenCalled();
});

it("should prevent deletion of unauthorized multiple bookings", async () => {
const mockBooking = [
{ id: 1, userId: 456 },
{ id: 2, userId: 456 },
];

prismaMock.booking.findMany.mockResolvedValue(mockBooking);
prismaMock.booking.deleteMany.mockResolvedValue({ count: 0 });

const ctx = {
user: {
id: 123,
},
};

await expect(
deletePastBookingsHandler({
ctx,
input: { id: [1, 2] },
})
).rejects.toThrow(/unauthorized/i);

expect(prismaMock.booking.deleteMany).not.toHaveBeenCalledTimes(1);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ export const deleteHandler = async ({ ctx, input }: DeleteOptions) => {
throw new Error("Booking not found");
}

if (booking.userId !== user.id) {
throw new Error("Unauthorized: You don't have permission to delete this booking");
}

await prisma.booking.delete({
where: {
id: booking.id,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,17 @@ export const deletePastBookingsHandler = async ({ ctx, input }: DeletePastBookin
const { user } = ctx;
const { bookingIds } = input;

const bookings = await prisma.booking.findMany({
where: {
id: { in: bookingIds },
},
});

const unauthorized = bookings.some((booking) => booking.userId !== user.id);
if (unauthorized) {
throw new Error("Unauthorized: Cannot delete bookings that don't belong to you");
}

const result = await prisma.booking.deleteMany({
where: {
id: { in: bookingIds },
Expand Down

0 comments on commit 0d8458b

Please sign in to comment.