From 081b511e1e399f07f737728e719d29663f573e45 Mon Sep 17 00:00:00 2001 From: Agusti Fernandez Pardo Date: Mon, 11 Apr 2022 15:20:38 +0200 Subject: [PATCH 1/5] Make attendees id endpoint return only user owned resources --- pages/api/attendees/[id].ts | 104 ++++++++++++++++++++++-------------- 1 file changed, 63 insertions(+), 41 deletions(-) diff --git a/pages/api/attendees/[id].ts b/pages/api/attendees/[id].ts index 093579a799..50b6b10cec 100644 --- a/pages/api/attendees/[id].ts +++ b/pages/api/attendees/[id].ts @@ -4,6 +4,7 @@ import prisma from "@calcom/prisma"; import { withMiddleware } from "@lib/helpers/withMiddleware"; import type { AttendeeResponse } from "@lib/types"; +import { getCalcomUserId } from "@lib/utils/getCalcomUserId"; import { schemaAttendeeBodyParams, schemaAttendeePublic } from "@lib/validations/attendee"; import { schemaQueryIdParseInt, @@ -83,48 +84,69 @@ export async function attendeeById(req: NextApiRequest, res: NextApiResponse schemaAttendeePublic.parse(data)) - .then((attendee) => res.status(200).json({ attendee })) - .catch((error: Error) => - res.status(404).json({ message: `Attendee with id: ${safeQuery.data.id} not found`, error }) - ); - break; - - case "PATCH": - if (!safeBody.success) throw new Error("Invalid request body"); - await prisma.attendee - .update({ - where: { id: safeQuery.data.id }, - data: safeBody.data, - }) - .then((data) => schemaAttendeePublic.parse(data)) - .then((attendee) => res.status(200).json({ attendee })) - .catch((error: Error) => - res.status(404).json({ message: `Attendee with id: ${safeQuery.data.id} not found`, error }) - ); - break; - - case "DELETE": - await prisma.attendee - .delete({ where: { id: safeQuery.data.id } }) - .then(() => - res.status(200).json({ message: `Attendee with id: ${safeQuery.data.id} deleted successfully` }) - ) - .catch((error: Error) => - res.status(404).json({ message: `Attendee with id: ${safeQuery.data.id} not found`, error }) - ); - break; - - default: - res.status(405).json({ message: "Method not allowed" }); - break; + if (!safeQuery.success) { + throw new Error("Invalid request query", safeQuery.error); } + const userId = getCalcomUserId(res); + const userBookings = await prisma.booking.findMany({ + where: { userId }, + include: { attendees: true }, + }); + const attendees = userBookings.map((booking) => booking.attendees).flat(); + const attendeeIds = attendees.map((attendee) => attendee.id); + // Here we make sure to only return attendee's of the user's own bookings. + if (attendeeIds.includes(safeQuery.data.id)) { + switch (method) { + case "GET": + await prisma.attendee + .findUnique({ where: { id: safeQuery.data.id } }) + .then((data) => schemaAttendeePublic.parse(data)) + .then((attendee) => res.status(200).json({ attendee })) + .catch((error: Error) => + res.status(404).json({ + message: `Attendee with id: ${safeQuery.data.id} not found`, + error, + }) + ); + break; + + case "PATCH": + if (!safeBody.success) { + throw new Error("Invalid request body"); + } + await prisma.attendee + .update({ where: { id: safeQuery.data.id }, data: safeBody.data }) + .then((data) => schemaAttendeePublic.parse(data)) + .then((attendee) => res.status(200).json({ attendee })) + .catch((error: Error) => + res.status(404).json({ + message: `Attendee with id: ${safeQuery.data.id} not found`, + error, + }) + ); + break; + + case "DELETE": + await prisma.attendee + .delete({ where: { id: safeQuery.data.id } }) + .then(() => + res.status(200).json({ + message: `Attendee with id: ${safeQuery.data.id} deleted successfully`, + }) + ) + .catch((error: Error) => + res.status(404).json({ + message: `Attendee with id: ${safeQuery.data.id} not found`, + error, + }) + ); + break; + + default: + res.status(405).json({ message: "Method not allowed" }); + break; + } + } else res.status(401).json({ message: "Unauthorized" }); } export default withMiddleware("HTTP_GET_DELETE_PATCH")(withValidQueryIdTransformParseInt(attendeeById)); From fca49a23c59b772186d92721402abb1f9110820c Mon Sep 17 00:00:00 2001 From: Agusti Fernandez Pardo Date: Mon, 11 Apr 2022 15:26:06 +0200 Subject: [PATCH 2/5] feat: make availabilities safe and return only userId related resources --- pages/api/availabilities/[id].ts | 83 +++++++++++++++++-------------- pages/api/availabilities/index.ts | 5 +- 2 files changed, 50 insertions(+), 38 deletions(-) diff --git a/pages/api/availabilities/[id].ts b/pages/api/availabilities/[id].ts index 34bab03008..db660b1074 100644 --- a/pages/api/availabilities/[id].ts +++ b/pages/api/availabilities/[id].ts @@ -4,6 +4,7 @@ import prisma from "@calcom/prisma"; import { withMiddleware } from "@lib/helpers/withMiddleware"; import type { AvailabilityResponse } from "@lib/types"; +import { getCalcomUserId } from "@lib/utils/getCalcomUserId"; import { schemaAvailabilityBodyParams, schemaAvailabilityPublic } from "@lib/validations/availability"; import { schemaQueryIdParseInt, @@ -84,46 +85,54 @@ export async function availabilityById(req: NextApiRequest, res: NextApiResponse const safeQuery = schemaQueryIdParseInt.safeParse(query); const safeBody = schemaAvailabilityBodyParams.safeParse(body); if (!safeQuery.success) throw new Error("Invalid request query", safeQuery.error); + const userId = await getCalcomUserId(res); + const data = await prisma.availability.findMany({ where: { userId } }); + const availabiltiesIds = data.map((availability) => availability.id); + if (availabiltiesIds.includes(safeQuery.data.id)) { + switch (method) { + case "GET": + await prisma.availability + .findUnique({ where: { id: safeQuery.data.id } }) + .then((data) => schemaAvailabilityPublic.parse(data)) + .then((availability) => res.status(200).json({ availability })) + .catch((error: Error) => + res.status(404).json({ message: `Availability with id: ${safeQuery.data.id} not found`, error }) + ); + break; - switch (method) { - case "GET": - await prisma.availability - .findUnique({ where: { id: safeQuery.data.id } }) - .then((data) => schemaAvailabilityPublic.parse(data)) - .then((availability) => res.status(200).json({ availability })) - .catch((error: Error) => - res.status(404).json({ message: `Availability with id: ${safeQuery.data.id} not found`, error }) - ); - break; + case "PATCH": + if (!safeBody.success) throw new Error("Invalid request body"); + await prisma.availability + .update({ + where: { id: safeQuery.data.id }, + data: safeBody.data, + }) + .then((data) => schemaAvailabilityPublic.parse(data)) + .then((availability) => res.status(200).json({ availability })) + .catch((error: Error) => + res.status(404).json({ message: `Availability with id: ${safeQuery.data.id} not found`, error }) + ); + break; - case "PATCH": - if (!safeBody.success) throw new Error("Invalid request body"); - await prisma.availability - .update({ - where: { id: safeQuery.data.id }, - data: safeBody.data, - }) - .then((data) => schemaAvailabilityPublic.parse(data)) - .then((availability) => res.status(200).json({ availability })) - .catch((error: Error) => - res.status(404).json({ message: `Availability with id: ${safeQuery.data.id} not found`, error }) - ); - break; + case "DELETE": + await prisma.availability + .delete({ where: { id: safeQuery.data.id } }) + .then(() => + res + .status(200) + .json({ message: `Availability with id: ${safeQuery.data.id} deleted successfully` }) + ) + .catch((error: Error) => + res.status(404).json({ message: `Availability with id: ${safeQuery.data.id} not found`, error }) + ); + break; - case "DELETE": - await prisma.availability - .delete({ where: { id: safeQuery.data.id } }) - .then(() => - res.status(200).json({ message: `Availability with id: ${safeQuery.data.id} deleted successfully` }) - ) - .catch((error: Error) => - res.status(404).json({ message: `Availability with id: ${safeQuery.data.id} not found`, error }) - ); - break; - - default: - res.status(405).json({ message: "Method not allowed" }); - break; + default: + res.status(405).json({ message: "Method not allowed" }); + break; + } + } else { + res.status(401).json({ message: "Unauthorized" }); } } diff --git a/pages/api/availabilities/index.ts b/pages/api/availabilities/index.ts index 9778bfdbfa..425436bb14 100644 --- a/pages/api/availabilities/index.ts +++ b/pages/api/availabilities/index.ts @@ -4,6 +4,7 @@ import prisma from "@calcom/prisma"; import { withMiddleware } from "@lib/helpers/withMiddleware"; import { AvailabilityResponse, AvailabilitiesResponse } from "@lib/types"; +import { getCalcomUserId } from "@lib/utils/getCalcomUserId"; import { schemaAvailabilityBodyParams, schemaAvailabilityPublic } from "@lib/validations/availability"; /** @@ -38,8 +39,10 @@ async function createOrlistAllAvailabilities( res: NextApiResponse ) { const { method } = req; + const userId = await getCalcomUserId(res); + if (method === "GET") { - const data = await prisma.availability.findMany(); + const data = await prisma.availability.findMany({ where: { userId } }); const availabilities = data.map((availability) => schemaAvailabilityPublic.parse(availability)); if (availabilities) res.status(200).json({ availabilities }); else From 9cefd119ee306f5a8eb81b055d02a7005eb13cca Mon Sep 17 00:00:00 2001 From: Agusti Fernandez Pardo Date: Mon, 11 Apr 2022 15:42:50 +0200 Subject: [PATCH 3/5] feat: make booking-references only return user booking related resources --- pages/api/booking-references/[id].ts | 101 ++++++++++++++++---------- pages/api/booking-references/index.ts | 11 ++- 2 files changed, 73 insertions(+), 39 deletions(-) diff --git a/pages/api/booking-references/[id].ts b/pages/api/booking-references/[id].ts index 2d48aa7413..2fac6f79db 100644 --- a/pages/api/booking-references/[id].ts +++ b/pages/api/booking-references/[id].ts @@ -1,9 +1,11 @@ +import { BookingModel } from "@/../../packages/prisma/zod"; import type { NextApiRequest, NextApiResponse } from "next"; import prisma from "@calcom/prisma"; import { withMiddleware } from "@lib/helpers/withMiddleware"; import type { BookingReferenceResponse } from "@lib/types"; +import { getCalcomUserId } from "@lib/utils/getCalcomUserId"; import { schemaBookingReferenceBodyParams, schemaBookingReferencePublic, @@ -91,47 +93,70 @@ export async function bookingReferenceById( const safeBody = schemaBookingReferenceBodyParams.safeParse(body); if (!safeQuery.success) throw new Error("Invalid request query", safeQuery.error); // FIXME: Allow only userId owner of booking ref to edit it + const userId = await getCalcomUserId(res); + const userWithBookings = await prisma.user.findUnique({ + where: { id: userId }, + include: { bookings: true }, + }); + if (!userWithBookings) throw new Error("User not found"); + const userBookingIds = userWithBookings.bookings.map((booking: any) => booking.id).flat(); + console.log(userBookingIds); + const bookingReference = await prisma.bookingReference.findUnique({ where: { id: safeQuery.data.id } }); + if (!bookingReference) throw new Error("BookingReference not found"); + if (userBookingIds.includes(bookingReference.bookingId)) { + switch (method) { + case "GET": + await prisma.bookingReference + .findUnique({ where: { id: safeQuery.data.id } }) + .then((data) => schemaBookingReferencePublic.parse(data)) + .then((booking_reference) => res.status(200).json({ booking_reference })) + .catch((error: Error) => + res.status(404).json({ + message: `BookingReference with id: ${safeQuery.data.id} not found`, + error, + }) + ); + break; - switch (method) { - case "GET": - await prisma.bookingReference - .findUnique({ where: { id: safeQuery.data.id } }) - .then((data) => schemaBookingReferencePublic.parse(data)) - .then((booking_reference) => res.status(200).json({ booking_reference })) - .catch((error: Error) => - res.status(404).json({ message: `BookingReference with id: ${safeQuery.data.id} not found`, error }) - ); - break; + case "PATCH": + if (!safeBody.success) { + throw new Error("Invalid request body"); + } + await prisma.bookingReference + .update({ where: { id: safeQuery.data.id }, data: safeBody.data }) + .then((data) => schemaBookingReferencePublic.parse(data)) + .then((booking_reference) => res.status(200).json({ booking_reference })) + .catch((error: Error) => + res.status(404).json({ + message: `BookingReference with id: ${safeQuery.data.id} not found`, + error, + }) + ); + break; - case "PATCH": - if (!safeBody.success) throw new Error("Invalid request body"); - await prisma.bookingReference - .update({ - where: { id: safeQuery.data.id }, - data: safeBody.data, - }) - .then((data) => schemaBookingReferencePublic.parse(data)) - .then((booking_reference) => res.status(200).json({ booking_reference })) - .catch((error: Error) => - res.status(404).json({ message: `BookingReference with id: ${safeQuery.data.id} not found`, error }) - ); - break; + case "DELETE": + await prisma.bookingReference + .delete({ + where: { id: safeQuery.data.id }, + }) + .then(() => + res.status(200).json({ + message: `BookingReference with id: ${safeQuery.data.id} deleted`, + }) + ) + .catch((error: Error) => + res.status(404).json({ + message: `BookingReference with id: ${safeQuery.data.id} not found`, + error, + }) + ); + break; - case "DELETE": - await prisma.bookingReference - .delete({ where: { id: safeQuery.data.id } }) - .then(() => - res.status(200).json({ message: `BookingReference with id: ${safeQuery.data.id} deleted` }) - ) - .catch((error: Error) => - res.status(404).json({ message: `BookingReference with id: ${safeQuery.data.id} not found`, error }) - ); - break; - - default: - res.status(405).json({ message: "Method not allowed" }); - break; - } + default: + res.status(405).json({ message: "Method not allowed" }); + break; + } + } else res.status(401).json({ message: "Unauthorized" }); } export default withMiddleware("HTTP_GET_DELETE_PATCH")( diff --git a/pages/api/booking-references/index.ts b/pages/api/booking-references/index.ts index 0ca58c2f8a..13519a67ec 100644 --- a/pages/api/booking-references/index.ts +++ b/pages/api/booking-references/index.ts @@ -4,6 +4,7 @@ import prisma from "@calcom/prisma"; import { withMiddleware } from "@lib/helpers/withMiddleware"; import { BookingReferenceResponse, BookingReferencesResponse } from "@lib/types"; +import { getCalcomUserId } from "@lib/utils/getCalcomUserId"; import { schemaBookingReferenceBodyParams, schemaBookingReferencePublic, @@ -41,8 +42,16 @@ async function createOrlistAllBookingReferences( res: NextApiResponse ) { const { method } = req; + const userId = await getCalcomUserId(res); + const userWithBookings = await prisma.user.findUnique({ + where: { id: userId }, + include: { bookings: true }, + }); + if (!userWithBookings) throw new Error("User not found"); + const userBookingIds = userWithBookings.bookings.map((booking: any) => booking.id).flat(); + console.log(userBookingIds); if (method === "GET") { - const data = await prisma.bookingReference.findMany(); + const data = await prisma.bookingReference.findMany({ where: { id: { in: userBookingIds } } }); const booking_references = data.map((bookingReference) => schemaBookingReferencePublic.parse(bookingReference) ); From 9447bd859de40b735afc366caefa171abc1d7120 Mon Sep 17 00:00:00 2001 From: Agusti Fernandez Pardo Date: Mon, 11 Apr 2022 16:47:01 +0200 Subject: [PATCH 4/5] feat hardened attendees, availabilites, booking-references, and bookings --- pages/api/attendees/index.ts | 41 ++++++++---- pages/api/availabilities/index.ts | 2 +- pages/api/booking-references/[id].ts | 2 - pages/api/bookings/[id].ts | 98 +++++++++++++++++----------- pages/api/bookings/index.ts | 7 +- 5 files changed, 95 insertions(+), 55 deletions(-) diff --git a/pages/api/attendees/index.ts b/pages/api/attendees/index.ts index 509907bb8d..59561c736e 100644 --- a/pages/api/attendees/index.ts +++ b/pages/api/attendees/index.ts @@ -65,20 +65,39 @@ async function createOrlistAllAttendees( throw new Error("Invalid request body", safe.error); } const bookingId = safe.data.bookingId; - delete safe.data.bookingId; - const noBookingId = safe.data; - const data = await prisma.attendee.create({ - data: { ...noBookingId, booking: { connect: { id: parseInt(bookingId as string) } } }, + const userId = await getCalcomUserId(res); + const userWithBookings = await prisma.user.findUnique({ + where: { id: userId }, + include: { bookings: true }, }); - const attendee = schemaAttendeePublic.parse(data); + if (!userWithBookings) { + throw new Error("User not found"); + } + const userBookingIds = userWithBookings.bookings.map((booking: any) => booking.id).flat(); + if (userBookingIds.includes(bookingId)) { + delete safe.data.bookingId; + const noBookingId = safe.data; + const data = await prisma.attendee.create({ + data: { + ...noBookingId, + booking: { connect: { id: bookingId } }, + }, + }); + const attendee = schemaAttendeePublic.parse(data); - if (attendee) res.status(201).json({ attendee, message: "Attendee created successfully" }); - else - (error: Error) => - res.status(400).json({ - message: "Could not create new attendee", - error, + if (attendee) { + res.status(201).json({ + attendee, + message: "Attendee created successfully", }); + } else { + (error: Error) => + res.status(400).json({ + message: "Could not create new attendee", + error, + }); + } + } else res.status(401).json({ message: "Unauthorized" }); } else res.status(405).json({ message: `Method ${method} not allowed` }); } diff --git a/pages/api/availabilities/index.ts b/pages/api/availabilities/index.ts index 425436bb14..45349b6b3f 100644 --- a/pages/api/availabilities/index.ts +++ b/pages/api/availabilities/index.ts @@ -55,7 +55,7 @@ async function createOrlistAllAvailabilities( const safe = schemaAvailabilityBodyParams.safeParse(req.body); if (!safe.success) throw new Error("Invalid request body"); - const data = await prisma.availability.create({ data: safe.data }); + const data = await prisma.availability.create({ data: { ...safe.data, userId } }); const availability = schemaAvailabilityPublic.parse(data); if (availability) res.status(201).json({ availability, message: "Availability created successfully" }); diff --git a/pages/api/booking-references/[id].ts b/pages/api/booking-references/[id].ts index 2fac6f79db..c9527e3f99 100644 --- a/pages/api/booking-references/[id].ts +++ b/pages/api/booking-references/[id].ts @@ -92,7 +92,6 @@ export async function bookingReferenceById( const safeQuery = schemaQueryIdParseInt.safeParse(query); const safeBody = schemaBookingReferenceBodyParams.safeParse(body); if (!safeQuery.success) throw new Error("Invalid request query", safeQuery.error); - // FIXME: Allow only userId owner of booking ref to edit it const userId = await getCalcomUserId(res); const userWithBookings = await prisma.user.findUnique({ where: { id: userId }, @@ -100,7 +99,6 @@ export async function bookingReferenceById( }); if (!userWithBookings) throw new Error("User not found"); const userBookingIds = userWithBookings.bookings.map((booking: any) => booking.id).flat(); - console.log(userBookingIds); const bookingReference = await prisma.bookingReference.findUnique({ where: { id: safeQuery.data.id } }); if (!bookingReference) throw new Error("BookingReference not found"); if (userBookingIds.includes(bookingReference.bookingId)) { diff --git a/pages/api/bookings/[id].ts b/pages/api/bookings/[id].ts index 45f24774e6..54e21b5109 100644 --- a/pages/api/bookings/[id].ts +++ b/pages/api/bookings/[id].ts @@ -4,6 +4,7 @@ import prisma from "@calcom/prisma"; import { withMiddleware } from "@lib/helpers/withMiddleware"; import type { BookingResponse } from "@lib/types"; +import { getCalcomUserId } from "@lib/utils/getCalcomUserId"; import { schemaBookingBodyParams, schemaBookingPublic } from "@lib/validations/booking"; import { schemaQueryIdParseInt, @@ -84,47 +85,68 @@ export async function bookingById(req: NextApiRequest, res: NextApiResponse schemaBookingPublic.parse(data)) - .then((booking) => res.status(200).json({ booking })) - .catch((error: Error) => - res.status(404).json({ message: `Booking with id: ${safeQuery.data.id} not found`, error }) - ); - break; + const userId = await getCalcomUserId(res); + const userWithBookings = await prisma.user.findUnique({ + where: { id: userId }, + include: { bookings: true }, + }); + if (!userWithBookings) throw new Error("User not found"); + const userBookingIds = userWithBookings.bookings.map((booking: any) => booking.id).flat(); + if (userBookingIds.includes(safeQuery.data.id)) { + switch (method) { + case "GET": + await prisma.booking + .findUnique({ where: { id: safeQuery.data.id } }) + .then((data) => schemaBookingPublic.parse(data)) + .then((booking) => res.status(200).json({ booking })) + .catch((error: Error) => + res.status(404).json({ + message: `Booking with id: ${safeQuery.data.id} not found`, + error, + }) + ); + break; - case "PATCH": - if (!safeBody.success) throw new Error("Invalid request body"); - await prisma.booking - .update({ - where: { id: safeQuery.data.id }, - data: safeBody.data, - }) - .then((data) => schemaBookingPublic.parse(data)) - .then((booking) => res.status(200).json({ booking })) - .catch((error: Error) => - res.status(404).json({ message: `Booking with id: ${safeQuery.data.id} not found`, error }) - ); - break; + case "PATCH": + if (!safeBody.success) { + throw new Error("Invalid request body"); + } + await prisma.booking + .update({ + where: { id: safeQuery.data.id }, + data: safeBody.data, + }) + .then((data) => schemaBookingPublic.parse(data)) + .then((booking) => res.status(200).json({ booking })) + .catch((error: Error) => + res.status(404).json({ + message: `Booking with id: ${safeQuery.data.id} not found`, + error, + }) + ); + break; - case "DELETE": - await prisma.booking - .delete({ where: { id: safeQuery.data.id } }) - .then(() => - res.status(200).json({ message: `Booking with id: ${safeQuery.data.id} deleted successfully` }) - ) - .catch((error: Error) => - res.status(404).json({ message: `Booking with id: ${safeQuery.data.id} not found`, error }) - ); - break; + case "DELETE": + await prisma.booking + .delete({ where: { id: safeQuery.data.id } }) + .then(() => + res.status(200).json({ + message: `Booking with id: ${safeQuery.data.id} deleted successfully`, + }) + ) + .catch((error: Error) => + res.status(404).json({ + message: `Booking with id: ${safeQuery.data.id} not found`, + error, + }) + ); + break; - default: - res.status(405).json({ message: "Method not allowed" }); - break; - } + default: + res.status(405).json({ message: "Method not allowed" }); + break; + } + } else res.status(401).json({ message: "Unauthorized" }); } export default withMiddleware("HTTP_GET_DELETE_PATCH")(withValidQueryIdTransformParseInt(bookingById)); diff --git a/pages/api/bookings/index.ts b/pages/api/bookings/index.ts index fd5f8b19af..2b2b55bf21 100644 --- a/pages/api/bookings/index.ts +++ b/pages/api/bookings/index.ts @@ -4,6 +4,7 @@ import prisma from "@calcom/prisma"; import { withMiddleware } from "@lib/helpers/withMiddleware"; import { BookingResponse, BookingsResponse } from "@lib/types"; +import { getCalcomUserId } from "@lib/utils/getCalcomUserId"; import { schemaBookingBodyParams, schemaBookingPublic, withValidBooking } from "@lib/validations/booking"; /** @@ -38,10 +39,10 @@ async function createOrlistAllBookings( res: NextApiResponse ) { const { method } = req; - // FIXME: List only bookings owner by userId + const userId = await getCalcomUserId(res); if (method === "GET") { - const data = await prisma.booking.findMany(); + const data = await prisma.booking.findMany({ where: { userId } }); const bookings = data.map((booking) => schemaBookingPublic.parse(booking)); if (bookings) res.status(200).json({ bookings }); else @@ -54,7 +55,7 @@ async function createOrlistAllBookings( const safe = schemaBookingBodyParams.safeParse(req.body); if (!safe.success) throw new Error("Invalid request body"); - const data = await prisma.booking.create({ data: safe.data }); + const data = await prisma.booking.create({ data: { ...safe.data, userId } }); const booking = schemaBookingPublic.parse(data); if (booking) res.status(201).json({ booking, message: "Booking created successfully" }); From d987d52dbbacfa4c62e01ac77953b1d11b123033 Mon Sep 17 00:00:00 2001 From: Agusti Fernandez Pardo Date: Wed, 13 Apr 2022 02:12:16 +0200 Subject: [PATCH 5/5] feat: credentials, teams --- .env.example | 2 +- lib/helpers/verifyApiKey.ts | 4 +- pages/api/attendees/index.ts | 1 - pages/api/booking-references/index.ts | 38 +++++++++---- pages/api/credentials/[id].ts | 82 ++++++++++++++------------- pages/api/credentials/index.ts | 10 +++- pages/api/teams/[id].ts | 1 + pages/api/teams/index.ts | 2 +- 8 files changed, 85 insertions(+), 55 deletions(-) diff --git a/.env.example b/.env.example index f3271e560c..038c1ebd26 100644 --- a/.env.example +++ b/.env.example @@ -1 +1 @@ -API_KEY_PREFIX=cal_ \ No newline at end of file +API_KEY_PREFIX=pt_secret_ \ No newline at end of file diff --git a/lib/helpers/verifyApiKey.ts b/lib/helpers/verifyApiKey.ts index 694de73593..9986707884 100644 --- a/lib/helpers/verifyApiKey.ts +++ b/lib/helpers/verifyApiKey.ts @@ -15,7 +15,7 @@ const today = new Date(); export const verifyApiKey: NextMiddleware = async (req, res, next) => { if (!req.query.apiKey) res.status(401).json({ message: "No API key provided" }); - const strippedApiKey = `${req.query.apiKey}`.replace(process.env.API_KEY_PREFIX || "cal_", ""); + const strippedApiKey = `${req.query.apiKey}`.replace(process.env.API_KEY_PREFIX || "pt_secret_", ""); const hashedKey = hashAPIKey(strippedApiKey); await prisma.apiKey @@ -25,7 +25,7 @@ export const verifyApiKey: NextMiddleware = async (req, res, next) => { res.status(401).json({ error: "You did not provide an api key" }); throw new Error("No api key found"); } - if (apiKey.userId) res.setHeader("X-Calcom-User-ID", apiKey?.userId); + if (apiKey.userId) res.setHeader("X-Calcom-User-ID", apiKey.userId); if (apiKey.expiresAt && apiKey.userId && dateInPast(today, apiKey.expiresAt)) await next(); }) .catch((error) => { diff --git a/pages/api/attendees/index.ts b/pages/api/attendees/index.ts index 59561c736e..5185acda2c 100644 --- a/pages/api/attendees/index.ts +++ b/pages/api/attendees/index.ts @@ -61,7 +61,6 @@ async function createOrlistAllAttendees( } else if (method === "POST") { const safe = schemaAttendeeBodyParams.safeParse(req.body); if (!safe.success) { - console.log(safe.error); throw new Error("Invalid request body", safe.error); } const bookingId = safe.data.bookingId; diff --git a/pages/api/booking-references/index.ts b/pages/api/booking-references/index.ts index 13519a67ec..bef6acdca1 100644 --- a/pages/api/booking-references/index.ts +++ b/pages/api/booking-references/index.ts @@ -49,7 +49,6 @@ async function createOrlistAllBookingReferences( }); if (!userWithBookings) throw new Error("User not found"); const userBookingIds = userWithBookings.bookings.map((booking: any) => booking.id).flat(); - console.log(userBookingIds); if (method === "GET") { const data = await prisma.bookingReference.findMany({ where: { id: { in: userBookingIds } } }); const booking_references = data.map((bookingReference) => @@ -64,18 +63,37 @@ async function createOrlistAllBookingReferences( }); } else if (method === "POST") { const safe = schemaBookingReferenceBodyParams.safeParse(req.body); - if (!safe.success) throw new Error("Invalid request body"); + if (!safe.success) { + throw new Error("Invalid request body"); + } - const data = await prisma.bookingReference.create({ data: safe.data }); + const data = await prisma.bookingReference.create({ + data: { ...safe.data }, + }); const booking_reference = schemaBookingReferencePublic.parse(data); - - if (data) res.status(201).json({ booking_reference, message: "BookingReference created successfully" }); - else - (error: Error) => - res.status(400).json({ - message: "Could not create new booking reference", - error, + const userId = await getCalcomUserId(res); + const userWithBookings = await prisma.user.findUnique({ + where: { id: userId }, + include: { bookings: true }, + }); + if (!userWithBookings) { + throw new Error("User not found"); + } + const userBookingIds = userWithBookings.bookings.map((booking: any) => booking.id).flat(); + if (userBookingIds.includes(safe.data.bookingId)) { + if (data) { + res.status(201).json({ + booking_reference, + message: "BookingReference created successfully", }); + } else { + (error: Error) => + res.status(400).json({ + message: "Could not create new booking reference", + error, + }); + } + } else res.status(401).json({ message: "Unauthorized" }); } else res.status(405).json({ message: `Method ${method} not allowed` }); } diff --git a/pages/api/credentials/[id].ts b/pages/api/credentials/[id].ts index bb82d0f019..52c95fd023 100644 --- a/pages/api/credentials/[id].ts +++ b/pages/api/credentials/[id].ts @@ -4,6 +4,7 @@ import prisma from "@calcom/prisma"; import { withMiddleware } from "@lib/helpers/withMiddleware"; import type { CredentialResponse } from "@lib/types"; +import { getCalcomUserId } from "@lib/utils/getCalcomUserId"; import { schemaCredentialBodyParams, schemaCredentialPublic } from "@lib/validations/credential"; import { schemaQueryIdParseInt, @@ -84,47 +85,52 @@ export async function credentialById(req: NextApiRequest, res: NextApiResponse credential.id); + // res.status(200).json({ data }); + if (credentialIds.includes(safeQuery.data.id)) { + switch (method) { + case "GET": + await prisma.credential + .findUnique({ where: { id: safeQuery.data.id } }) + .then((data) => schemaCredentialPublic.parse(data)) + .then((credential) => res.status(200).json({ credential })) + .catch((error: Error) => + res.status(404).json({ message: `Credential with id: ${safeQuery.data.id} not found`, error }) + ); + break; - switch (method) { - case "GET": - await prisma.credential - .findUnique({ where: { id: safeQuery.data.id } }) - .then((data) => schemaCredentialPublic.parse(data)) - .then((credential) => res.status(200).json({ credential })) - .catch((error: Error) => - res.status(404).json({ message: `Credential with id: ${safeQuery.data.id} not found`, error }) - ); - break; + case "PATCH": + if (!safeBody.success) throw new Error("Invalid request body"); + await prisma.credential + .update({ + where: { id: safeQuery.data.id }, + data: safeBody.data, + }) + .then((data) => schemaCredentialPublic.parse(data)) + .then((credential) => res.status(200).json({ credential })) + .catch((error: Error) => + res.status(404).json({ message: `Credential with id: ${safeQuery.data.id} not found`, error }) + ); + break; - case "PATCH": - if (!safeBody.success) throw new Error("Invalid request body"); - await prisma.credential - .update({ - where: { id: safeQuery.data.id }, - data: safeBody.data, - }) - .then((data) => schemaCredentialPublic.parse(data)) - .then((credential) => res.status(200).json({ credential })) - .catch((error: Error) => - res.status(404).json({ message: `Credential with id: ${safeQuery.data.id} not found`, error }) - ); - break; + case "DELETE": + await prisma.credential + .delete({ where: { id: safeQuery.data.id } }) + .then(() => + res.status(200).json({ message: `Credential with id: ${safeQuery.data.id} deleted successfully` }) + ) + .catch((error: Error) => + res.status(404).json({ message: `Credential with id: ${safeQuery.data.id} not found`, error }) + ); + break; - case "DELETE": - await prisma.credential - .delete({ where: { id: safeQuery.data.id } }) - .then(() => - res.status(200).json({ message: `Credential with id: ${safeQuery.data.id} deleted successfully` }) - ) - .catch((error: Error) => - res.status(404).json({ message: `Credential with id: ${safeQuery.data.id} not found`, error }) - ); - break; - - default: - res.status(405).json({ message: "Method not allowed" }); - break; - } + default: + res.status(405).json({ message: "Method not allowed" }); + break; + } + } else res.status(401).json({ message: "Unauthorized" }); } export default withMiddleware("HTTP_GET_DELETE_PATCH")(withValidQueryIdTransformParseInt(credentialById)); diff --git a/pages/api/credentials/index.ts b/pages/api/credentials/index.ts index 8ba9a5aeb5..d7b9ed651f 100644 --- a/pages/api/credentials/index.ts +++ b/pages/api/credentials/index.ts @@ -4,6 +4,7 @@ import prisma from "@calcom/prisma"; import { withMiddleware } from "@lib/helpers/withMiddleware"; import { CredentialResponse, CredentialsResponse } from "@lib/types"; +import { getCalcomUserId } from "@lib/utils/getCalcomUserId"; import { schemaCredentialBodyParams, schemaCredentialPublic } from "@lib/validations/credential"; /** @@ -38,8 +39,10 @@ async function createOrlistAllCredentials( res: NextApiResponse ) { const { method } = req; + const userId = getCalcomUserId(res); + if (method === "GET") { - const data = await prisma.credential.findMany(); + const data = await prisma.credential.findMany({ where: { userId } }); const credentials = data.map((credential) => schemaCredentialPublic.parse(credential)); if (credentials) res.status(200).json({ credentials }); else @@ -55,7 +58,10 @@ async function createOrlistAllCredentials( const data = await prisma.credential.create({ data: safe.data }); const credential = schemaCredentialPublic.parse(data); - if (credential) res.status(201).json({ credential, message: "Credential created successfully" }); + if (credential) + res + .status(201) + .json({ credential: { ...credential, userId }, message: "Credential created successfully" }); else (error: Error) => res.status(400).json({ diff --git a/pages/api/teams/[id].ts b/pages/api/teams/[id].ts index 5435b18dc5..70f88c1b35 100644 --- a/pages/api/teams/[id].ts +++ b/pages/api/teams/[id].ts @@ -89,6 +89,7 @@ export async function teamById(req: NextApiRequest, res: NextApiResponse