fix: Invalid 403 forbidden for event-types GET (#14528)

This commit is contained in:
Keith Williams
2024-04-11 19:15:21 +00:00
committed by GitHub
parent 711aceba0e
commit ac77fe360d
5 changed files with 165 additions and 22 deletions
@@ -1,18 +0,0 @@
import type { NextApiRequest } from "next";
import { HttpError } from "@calcom/lib/http-error";
import prisma from "@calcom/prisma";
import { schemaQueryIdParseInt } from "~/lib/validations/shared/queryIdTransformParseInt";
async function authMiddleware(req: NextApiRequest) {
const { userId, isAdmin } = req;
const { id } = schemaQueryIdParseInt.parse(req.query);
if (isAdmin) return;
const eventType = await prisma.eventType.findFirst({
where: { id, users: { some: { id: userId } } },
});
if (!eventType) throw new HttpError({ statusCode: 403, message: "Forbidden" });
}
export default authMiddleware;
@@ -4,11 +4,8 @@ import { defaultHandler, defaultResponder } from "@calcom/lib/server";
import { withMiddleware } from "~/lib/helpers/withMiddleware";
import authMiddleware from "./_auth-middleware";
export default withMiddleware()(
defaultResponder(async (req: NextApiRequest, res: NextApiResponse) => {
await authMiddleware(req);
return defaultHandler({
GET: import("./_get"),
PATCH: import("./_patch"),
@@ -26,7 +26,7 @@ export default async function checkTeamEventEditPermission(
if (!membership?.role || !["ADMIN", "OWNER"].includes(membership.role)) {
throw new HttpError({
statusCode: 401,
statusCode: 403,
message: "No permission to operate on event-type for this team",
});
}
@@ -0,0 +1,143 @@
import prismaMock from "../../../../../../../tests/libs/__mocks__/prismaMock";
import type { Request, Response } from "express";
import type { NextApiRequest, NextApiResponse } from "next";
import { createMocks } from "node-mocks-http";
import { describe, expect, test } from "vitest";
import { buildEventType } from "@calcom/lib/test/builder";
import { MembershipRole } from "@calcom/prisma/enums";
import handler from "../../../../pages/api/event-types/[id]/_get";
type CustomNextApiRequest = NextApiRequest & Request;
type CustomNextApiResponse = NextApiResponse & Response;
describe("GET /api/event-types/[id]", () => {
describe("Errors", () => {
test("Returns 403 if user not admin/team member/event owner", async () => {
const { req, res } = createMocks<CustomNextApiRequest, CustomNextApiResponse>({
method: "GET",
body: {},
query: {
id: 123456,
},
});
prismaMock.eventType.findUnique.mockResolvedValue(
buildEventType({
id: 123456,
userId: 444444,
})
);
req.userId = 333333;
await handler(req, res);
expect(res.statusCode).toBe(403);
});
});
describe("Success", async () => {
test("Returns event type if user is admin", async () => {
const eventTypeId = 123456;
const { req, res } = createMocks<CustomNextApiRequest, CustomNextApiResponse>({
method: "GET",
body: {},
query: {
id: eventTypeId,
},
});
prismaMock.eventType.findUnique.mockResolvedValue(
buildEventType({
id: eventTypeId,
})
);
req.isAdmin = true;
req.userId = 333333;
await handler(req, res);
expect(res.statusCode).toBe(200);
expect(JSON.parse(res._getData()).event_type.id).toEqual(eventTypeId);
});
test("Returns event type if user is in team associated with event type", async () => {
const eventTypeId = 123456;
const teamId = 9999;
const userId = 333333;
const { req, res } = createMocks<CustomNextApiRequest, CustomNextApiResponse>({
method: "GET",
body: {},
query: {
id: eventTypeId,
},
});
prismaMock.eventType.findUnique.mockResolvedValue(
buildEventType({
id: eventTypeId,
teamId,
})
);
prismaMock.team.findFirst.mockResolvedValue({
id: teamId,
members: [
{
userId,
},
],
});
req.isAdmin = false;
req.userId = userId;
await handler(req, res);
expect(res.statusCode).toBe(200);
expect(JSON.parse(res._getData()).event_type.id).toEqual(eventTypeId);
expect(prismaMock.team.findFirst).toHaveBeenCalledWith({
where: {
id: teamId,
members: {
some: {
userId: req.userId,
role: {
in: [MembershipRole.OWNER, MembershipRole.ADMIN, MembershipRole.MEMBER],
},
},
},
},
});
});
test("Returns event type if user is the event type owner", async () => {
const eventTypeId = 123456;
const userId = 333333;
const { req, res } = createMocks<CustomNextApiRequest, CustomNextApiResponse>({
method: "GET",
body: {},
query: {
id: eventTypeId,
},
});
prismaMock.eventType.findUnique.mockResolvedValue(
buildEventType({
id: eventTypeId,
userId,
scheduleId: 1111,
})
);
req.isAdmin = false;
req.userId = userId;
await handler(req, res);
expect(res.statusCode).toBe(200);
expect(JSON.parse(res._getData()).event_type.id).toEqual(eventTypeId);
expect(prismaMock.team.findFirst).not.toHaveBeenCalled();
});
});
});
+21
View File
@@ -41,12 +41,33 @@ const workspaces = packagedEmbedTestsOnly
"packages/embeds/**/*",
"packages/lib/hooks/**/*",
"packages/platform/**/*",
"apps/api/v1/**/*",
"apps/api/v2/**/*",
],
name: "@calcom/core",
setupFiles: ["setupVitest.ts"],
},
},
{
test: {
include: ["apps/api/v1/**/*.{test,spec}.{ts,js}"],
exclude: [
"**/node_modules/**/*",
"**/.next/**/*",
"packages/embeds/**/*",
"packages/lib/hooks/**/*",
"packages/platform/**/*",
"apps/api/v2/**/*",
],
name: "@calcom/api",
setupFiles: ["setupVitest.ts"],
},
resolve: {
alias: {
"~": new URL("./apps/api/v1", import.meta.url).pathname,
},
},
},
{
test: {
globals: true,