diff --git a/apps/web/lib/org/[orgSlug]/[user]/[type]/getServerSideProps.tsx b/apps/web/lib/org/[orgSlug]/[user]/[type]/getServerSideProps.tsx index 0fe7c10c0c..7ea44ca57c 100644 --- a/apps/web/lib/org/[orgSlug]/[user]/[type]/getServerSideProps.tsx +++ b/apps/web/lib/org/[orgSlug]/[user]/[type]/getServerSideProps.tsx @@ -2,6 +2,9 @@ import type { GetServerSidePropsContext } from "next"; import z from "zod"; import { getSlugOrRequestedSlug } from "@calcom/features/ee/organizations/lib/orgDomains"; +import { checkRateLimitAndThrowError } from "@calcom/lib/checkRateLimitAndThrowError"; +import getIP from "@calcom/lib/getIP"; +import { piiHasher } from "@calcom/lib/server/PiiHasher"; import slugify from "@calcom/lib/slugify"; import prisma from "@calcom/prisma"; @@ -16,6 +19,12 @@ const paramsSchema = z.object({ }); export const getServerSideProps = async (ctx: GetServerSidePropsContext) => { + const requestorIp = getIP(ctx.req as unknown as Request); + await checkRateLimitAndThrowError({ + rateLimitingType: "core", + identifier: `[orgSlug]/[user]/[type]-${piiHasher.hash(requestorIp)}`, + }); + const { user: teamOrUserSlugOrDynamicGroup, orgSlug, type } = paramsSchema.parse(ctx.params); const team = await prisma.team.findFirst({ where: { diff --git a/apps/web/lib/team/[slug]/[type]/getServerSideProps.tsx b/apps/web/lib/team/[slug]/[type]/getServerSideProps.tsx index fcd939cd87..decc0347f0 100644 --- a/apps/web/lib/team/[slug]/[type]/getServerSideProps.tsx +++ b/apps/web/lib/team/[slug]/[type]/getServerSideProps.tsx @@ -9,7 +9,10 @@ import { getOrganizationSEOSettings } from "@calcom/features/ee/organizations/li import { FeaturesRepository } from "@calcom/features/flags/features.repository"; import { getBrandingForEventType } from "@calcom/features/profile/lib/getBranding"; import { shouldHideBrandingForTeamEvent } from "@calcom/features/profile/lib/hideBranding"; +import { checkRateLimitAndThrowError } from "@calcom/lib/checkRateLimitAndThrowError"; import { getPlaceholderAvatar } from "@calcom/lib/defaultAvatarImage"; +import getIP from "@calcom/lib/getIP"; +import { piiHasher } from "@calcom/lib/server/PiiHasher"; import slugify from "@calcom/lib/slugify"; import { prisma } from "@calcom/prisma"; import type { User } from "@calcom/prisma/client"; @@ -28,6 +31,11 @@ function hasApiV2RouteInEnv() { } export const getServerSideProps = async (context: GetServerSidePropsContext) => { + const requestorIp = getIP(context.req as unknown as Request); + await checkRateLimitAndThrowError({ + rateLimitingType: "core", + identifier: `team/[slug]/[type]-${piiHasher.hash(requestorIp)}`, + }); const { req, params, query } = context; const session = await getServerSession({ req }); const { slug: teamSlug, type: meetingSlug } = paramsSchema.parse(params); diff --git a/apps/web/server/lib/[user]/[type]/getServerSideProps.ts b/apps/web/server/lib/[user]/[type]/getServerSideProps.ts index 2923b8674a..329a759c16 100644 --- a/apps/web/server/lib/[user]/[type]/getServerSideProps.ts +++ b/apps/web/server/lib/[user]/[type]/getServerSideProps.ts @@ -11,6 +11,9 @@ import type { getPublicEvent } from "@calcom/features/eventtypes/lib/getPublicEv import { EventRepository } from "@calcom/features/eventtypes/repositories/EventRepository"; import { shouldHideBrandingForUserEvent } from "@calcom/features/profile/lib/hideBranding"; import { UserRepository } from "@calcom/features/users/repositories/UserRepository"; +import { checkRateLimitAndThrowError } from "@calcom/lib/checkRateLimitAndThrowError"; +import getIP from "@calcom/lib/getIP"; +import { piiHasher } from "@calcom/lib/server/PiiHasher"; import slugify from "@calcom/lib/slugify"; import { prisma } from "@calcom/prisma"; import { BookingStatus, RedirectType } from "@calcom/prisma/enums"; @@ -63,6 +66,7 @@ async function processReschedule({ (booking?.eventTypeId === props.eventData?.id && (booking.status !== BookingStatus.CANCELLED || allowRescheduleForCancelledBooking || + // eslint-disable-next-line @typescript-eslint/no-explicit-any !!(props.eventData as any)?.allowReschedulingCancelledBookings)) ) { props.booking = booking; @@ -314,6 +318,12 @@ const paramsSchema = z.object({ // Booker page fetches a tiny bit of data server side, to determine early // whether the page should show an away state or dynamic booking not allowed. export const getServerSideProps = async (context: GetServerSidePropsContext) => { + const requestorIp = getIP(context.req as unknown as Request); + await checkRateLimitAndThrowError({ + rateLimitingType: "core", + identifier: `[user]/[type]-${piiHasher.hash(requestorIp)}`, + }); + const { user } = paramsSchema.parse(context.params); const isDynamicGroup = user.length > 1; diff --git a/apps/web/server/lib/[user]/getServerSideProps.ts b/apps/web/server/lib/[user]/getServerSideProps.ts index 9122cbaadc..173e930300 100644 --- a/apps/web/server/lib/[user]/getServerSideProps.ts +++ b/apps/web/server/lib/[user]/getServerSideProps.ts @@ -8,11 +8,14 @@ import { getUsernameList } from "@calcom/features/eventtypes/lib/defaultEvents"; import { getEventTypesPublic } from "@calcom/features/eventtypes/lib/getEventTypesPublic"; import { getBrandingForUser } from "@calcom/features/profile/lib/getBranding"; import { UserRepository } from "@calcom/features/users/repositories/UserRepository"; +import { checkRateLimitAndThrowError } from "@calcom/lib/checkRateLimitAndThrowError"; import { DEFAULT_DARK_BRAND_COLOR, DEFAULT_LIGHT_BRAND_COLOR } from "@calcom/lib/constants"; import { getUserAvatarUrl } from "@calcom/lib/getAvatarUrl"; +import getIP from "@calcom/lib/getIP"; import logger from "@calcom/lib/logger"; import { markdownToSafeHTML } from "@calcom/lib/markdownToSafeHTML"; import { safeStringify } from "@calcom/lib/safeStringify"; +import { piiHasher } from "@calcom/lib/server/PiiHasher"; import { stripMarkdown } from "@calcom/lib/stripMarkdown"; import { prisma } from "@calcom/prisma"; import type { EventType, User } from "@calcom/prisma/client"; @@ -79,6 +82,12 @@ type UserPageProps = { } & EmbedProps; export const getServerSideProps: GetServerSideProps = async (context) => { + // handle IP based rate limiting + const requestorIp = getIP(context.req as unknown as Request); + await checkRateLimitAndThrowError({ + rateLimitingType: "core", + identifier: `[user]-${piiHasher.hash(requestorIp)}`, + }); const { currentOrgDomain, isValidOrgDomain } = orgDomainConfig(context.req, context.params?.orgSlug); const usernameList = getUsernameList(context.query.user as string); const isARedirectFromNonOrgLink = context.query.orgRedirection === "true"; @@ -144,8 +153,7 @@ export const getServerSideProps: GetServerSideProps = async (cont theme: branding.theme, brandColor: branding.brandColor ?? DEFAULT_LIGHT_BRAND_COLOR, avatarUrl: user.avatarUrl, - darkBrandColor: - branding.darkBrandColor ?? DEFAULT_DARK_BRAND_COLOR, + darkBrandColor: branding.darkBrandColor ?? DEFAULT_DARK_BRAND_COLOR, allowSEOIndexing: user.allowSEOIndexing ?? true, username: user.username, organization: user.profile.organization,