Fix/user creds (#8393)

* fix-user-creds

* Type fixes

* Type fixes

* Type fixes

---------

Co-authored-by: Alex van Andel <me@alexvanandel.com>
This commit is contained in:
alannnc
2023-04-20 15:55:19 +00:00
committed by GitHub
co-authored by Alex van Andel
parent c1c95f711b
commit fe996b4e41
13 changed files with 92 additions and 39 deletions
@@ -18,7 +18,7 @@ export const getStaticProps: GetStaticProps<
{ user: string }
> = async (context) => {
const { user: username, month } = paramsSchema.parse(context.params);
const user = await prisma.user.findUnique({
const userWithCredentials = await prisma.user.findUnique({
where: {
username,
},
@@ -34,14 +34,15 @@ export const getStaticProps: GetStaticProps<
).startOf("day");
const endDate = startDate.endOf("month");
try {
const results = user?.credentials
const results = userWithCredentials?.credentials
? await getCachedResults(
user?.credentials,
userWithCredentials?.credentials,
startDate.format(),
endDate.format(),
user?.selectedCalendars
userWithCredentials?.selectedCalendars
)
: [];
return {
props: { results, date: new Date().toISOString() },
revalidate: 1,
+4 -4
View File
@@ -14,7 +14,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
return;
}
const user = await prisma.user.findUnique({
const userWithCredentials = await prisma.user.findUnique({
where: {
id: session.user.id,
},
@@ -25,11 +25,11 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
selectedCalendars: true,
},
});
if (!user) {
if (!userWithCredentials) {
res.status(401).json({ message: "Not authenticated" });
return;
}
const { credentials, ...user } = userWithCredentials;
if (req.method === "POST") {
await prisma.selectedCalendar.upsert({
@@ -80,7 +80,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
});
// get user's credentials + their connected integrations
const calendarCredentials = getCalendarCredentials(user.credentials);
const calendarCredentials = getCalendarCredentials(credentials);
// get all the connected integrations' calendars (from third party)
const { connectedCalendars } = await getConnectedCalendars(calendarCredentials, user.selectedCalendars);
const calendars = connectedCalendars.flatMap((c) => c.calendars).filter(notEmpty);
@@ -54,7 +54,6 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
user: {
select: {
id: true,
credentials: true,
timeZone: true,
email: true,
name: true,
-1
View File
@@ -282,7 +282,6 @@ export async function getServerSideProps(context: GetServerSidePropsContext) {
user: {
select: {
id: true,
credentials: true,
timeZone: true,
name: true,
email: true,
+4 -1
View File
@@ -70,7 +70,10 @@ type EventType = Awaited<ReturnType<typeof getEventType>>;
const getUser = (where: Prisma.UserWhereUniqueInput) =>
prisma.user.findUnique({
where,
select: availabilityUserSelect,
select: {
...availabilityUserSelect,
credentials: true,
},
});
type User = Awaited<ReturnType<typeof getUser>>;
@@ -38,7 +38,7 @@ async function getBookingToDelete(id: number | undefined, uid: string | undefine
user: {
select: {
id: true,
credentials: true,
credentials: true, // Not leaking at the moment, be careful with
email: true,
timeZone: true,
name: true,
@@ -197,7 +197,12 @@ const getEventTypesFromDB = async (eventTypeId: number) => {
id: true,
customInputs: true,
disableGuests: true,
users: userSelect,
users: {
select: {
credentials: true,
...userSelect.select,
},
},
slug: true,
team: {
select: {
@@ -253,7 +258,12 @@ const getEventTypesFromDB = async (eventTypeId: number) => {
hosts: {
select: {
isFixed: true,
user: userSelect,
user: {
select: {
credentials: true,
...userSelect.select,
},
},
},
},
availability: {
@@ -277,7 +287,7 @@ const getEventTypesFromDB = async (eventTypeId: number) => {
};
};
type IsFixedAwareUser = User & { isFixed: boolean };
type IsFixedAwareUser = User & { isFixed: boolean; credentials: Credential[] };
async function ensureAvailableUsers(
eventType: Awaited<ReturnType<typeof getEventTypesFromDB>> & {
@@ -623,6 +633,7 @@ async function handler(
},
select: {
...userSelect.select,
credentials: true, // Don't leak to client
metadata: true,
},
})
@@ -654,7 +665,10 @@ async function handler(
where: {
id: eventType.userId,
},
...userSelect,
select: {
credentials: true, // Don't leak to client
...userSelect.select,
},
});
if (!eventTypeUser) {
log.warn({ message: "NewBooking: eventTypeUser.notFound" });
+33 -7
View File
@@ -55,7 +55,6 @@ async function getBooking(bookingId: number) {
select: {
id: true,
username: true,
credentials: true,
timeZone: true,
email: true,
name: true,
@@ -176,9 +175,11 @@ async function handlePaymentSuccess(event: Stripe.Event) {
eventTypeRaw = await getEventType(booking.eventTypeId);
}
const { user } = booking;
const { user: userWithCredentials } = booking;
if (!user) throw new HttpCode({ statusCode: 204, message: "No user found" });
if (!userWithCredentials) throw new HttpCode({ statusCode: 204, message: "No user found" });
const { credentials, ...user } = userWithCredentials;
const t = await getTranslation(user.locale ?? "en", "common");
const attendeesListPromises = booking.attendees.map(async (attendee) => {
@@ -227,7 +228,7 @@ async function handlePaymentSuccess(event: Stripe.Event) {
const isConfirmed = booking.status === BookingStatus.ACCEPTED;
if (isConfirmed) {
const eventManager = new EventManager(user);
const eventManager = new EventManager(userWithCredentials);
const scheduleResult = await eventManager.create(evt);
bookingData.references = { create: scheduleResult.referencesToCreate };
}
@@ -255,7 +256,14 @@ async function handlePaymentSuccess(event: Stripe.Event) {
await prisma.$transaction([paymentUpdate, bookingUpdate]);
if (!isConfirmed && !eventTypeRaw?.requiresConfirmation) {
await handleConfirmation({ user, evt, prisma, bookingId: booking.id, booking, paid: true });
await handleConfirmation({
user: userWithCredentials,
evt,
prisma,
bookingId: booking.id,
booking,
paid: true,
});
} else {
await sendScheduledEmails({ ...evt });
}
@@ -276,14 +284,32 @@ const handleSetupSuccess = async (event: Stripe.Event) => {
if (!payment?.data || !payment?.id) throw new HttpCode({ statusCode: 204, message: "Payment not found" });
const { booking, user, evt, eventTypeRaw } = await getBooking(payment.bookingId);
const { user, evt, eventTypeRaw } = await getBooking(payment.bookingId);
const bookingData: Prisma.BookingUpdateInput = {
paid: true,
};
const userWithCredentials = await prisma.user.findUnique({
where: {
id: user.id,
},
select: {
id: true,
username: true,
timeZone: true,
email: true,
name: true,
locale: true,
destinationCalendar: true,
credentials: true,
},
});
if (!userWithCredentials) throw new HttpCode({ statusCode: 204, message: "No user found" });
if (!eventTypeRaw?.requiresConfirmation) {
const eventManager = new EventManager(user);
const eventManager = new EventManager(userWithCredentials);
const scheduleResult = await eventManager.create(evt);
bookingData.references = { create: scheduleResult.referencesToCreate };
bookingData.status = BookingStatus.ACCEPTED;
+2 -2
View File
@@ -1,4 +1,4 @@
import type { Prisma } from "@prisma/client";
import type { Prisma, Credential } from "@prisma/client";
import { PeriodType, SchedulingType } from "@prisma/client";
import { DailyLocationType } from "@calcom/app-store/locations";
@@ -25,7 +25,7 @@ type UsernameSlugLinkProps = {
slug: string;
};
const user: User = {
const user: User & { credentials: Credential[] } = {
metadata: null,
theme: null,
credentials: [],
+5 -5
View File
@@ -1,16 +1,15 @@
import { Prisma } from "@prisma/client";
export const availabilityUserSelect = Prisma.validator<Prisma.UserSelect>()({
credentials: true,
id: true,
timeZone: true,
bufferTime: true,
availability: true,
id: true,
startTime: true,
username: true,
endTime: true,
selectedCalendars: true,
timeFormat: true,
defaultScheduleId: true,
// Relationships
schedules: {
select: {
availability: true,
@@ -18,7 +17,8 @@ export const availabilityUserSelect = Prisma.validator<Prisma.UserSelect>()({
id: true,
},
},
defaultScheduleId: true,
availability: true,
selectedCalendars: true,
});
export const baseUserSelect = Prisma.validator<Prisma.UserSelect>()({
-1
View File
@@ -1041,7 +1041,6 @@ const loggedInViewerRouter = router({
user: {
select: {
id: true,
credentials: true,
email: true,
timeZone: true,
name: true,
+19 -8
View File
@@ -218,12 +218,16 @@ async function getEventType(ctx: { prisma: typeof prisma }, input: z.infer<typeo
select: {
isFixed: true,
user: {
select: availabilityUserSelect,
select: {
credentials: true, // Don't leak credentials to the client
...availabilityUserSelect,
},
},
},
},
users: {
select: {
credentials: true, // Don't leak credentials to the client
...availabilityUserSelect,
},
},
@@ -250,6 +254,7 @@ async function getDynamicEventType(ctx: { prisma: typeof prisma }, input: z.infe
},
select: {
allowDynamicBooking: true,
credentials: true, // Don't leak credentials to the client
...availabilityUserSelect,
},
});
@@ -305,17 +310,17 @@ export async function getSchedule(input: z.infer<typeof getScheduleSchema>, ctx:
}
let currentSeats: CurrentSeats | undefined;
let users = eventType.users.map((user) => ({
let usersWithCredentials = eventType.users.map((user) => ({
isFixed: !eventType.schedulingType || eventType.schedulingType === SchedulingType.COLLECTIVE,
...user,
}));
// overwrite if it is a team event & hosts is set, otherwise keep using users.
if (eventType.schedulingType && !!eventType.hosts?.length) {
users = eventType.hosts.map(({ isFixed, user }) => ({ isFixed, ...user }));
usersWithCredentials = eventType.hosts.map(({ isFixed, user }) => ({ isFixed, ...user }));
}
/* We get all users working hours and busy slots */
const userAvailability = await Promise.all(
users.map(async (currentUser) => {
usersWithCredentials.map(async (currentUser) => {
const {
busy,
workingHours,
@@ -398,7 +403,7 @@ export async function getSchedule(input: z.infer<typeof getScheduleSchema>, ctx:
/* FIXME: For some reason this returns undefined while testing in Jest */
(await ctx.prisma.selectedSlots.findMany({
where: {
userId: { in: users.map((user) => user.id) },
userId: { in: usersWithCredentials.map((user) => user.id) },
releaseAt: { gt: dayjs.utc().format() },
},
select: {
@@ -527,13 +532,19 @@ export async function getSchedule(input: z.infer<typeof getScheduleSchema>, ctx:
) => {
// TODO: Adds unit tests to prevent regressions in getSchedule (try multiple timezones)
const time = _time.tz(input.timeZone);
r[time.format("YYYY-MM-DD")] = r[time.format("YYYY-MM-DD")] || [];
r[time.format("YYYY-MM-DD")].push({
...passThroughProps,
time: time.toISOString(),
users: (eventType.hosts ? eventType.hosts.map((host) => host.user) : eventType.users).map(
(user) => user.username || ""
),
users: (eventType.hosts
? eventType.hosts.map((hostUserWithCredentials) => {
const { user } = hostUserWithCredentials;
const { credentials: _credentials, ...hostUser } = user;
return hostUser;
})
: eventType.users
).map((user) => user.username || ""),
// Conditionally add the attendees and booking id to slots object if there is already a booking during that time
...(currentSeats?.some((booking) => booking.startTime.toISOString() === time.toISOString()) && {
attendees:
@@ -520,6 +520,7 @@ export const viewerTeamsRouter = router({
include: {
user: {
select: {
credentials: true, // needed for getUserAvailability
...availabilityUserSelect,
},
},