717a26f223
* fix: prevent bulk update of locked locations in child managed event types - Filter out child managed event types with locked locations in getBulkUserEventTypes - Add validation in bulkUpdateEventsToDefaultLocation to prevent updating locked fields - Implements defense in depth with validation at multiple layers Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com> * Abstract filtering logic * test: add comprehensive tests for bulk location update filtering - Add unit tests for filterEventTypesWhereLocationUpdateIsAllowed - Add unit tests for bulkUpdateEventsToDefaultLocation - Add integration tests for getBulkUserEventTypes - Fix bug: change unlockedFields?.locations check from !== undefined to === true This ensures that locations: false is properly treated as locked, addressing the security issue identified in PR review comments Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com> * fix: filter locked managed event types on app installation page - Add parentId to eventTypeSelect in getEventTypes function - Apply filterEventTypesWhereLocationUpdateIsAllowed to both team and user event types - Only filter when isConferencing is true to avoid affecting other app types - Fixes issue where locked managed event types were showing in the event type selection list on /apps/installation/event-types page Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com> * fix(embed-react): remove obsolete availabilityLoaded event listener The availabilityLoaded event does not exist in the EventDataMap type system in embed-core. This code was causing 5 TypeScript errors in CI: - Type 'availabilityLoaded' does not satisfy constraint 'keyof EventDataMap' - 'data' is of type 'unknown' (2 occurrences) - Type 'availabilityLoaded' is not assignable to action union (2 occurrences) Since this is an example file and the event is not defined in the type system, removing this obsolete code resolves the type errors. Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com> * fix: correct Prisma type for metadata in test helper function Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com> * fix: use flexible PrismaLike type for better test compatibility Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com> * fix: properly type mock Prisma objects in test files Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com> * fix: properly mock Prisma methods in test file Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com> * Filter out metadata * Undo change in embed file * Address feedback --------- Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
78 lines
1.9 KiB
TypeScript
78 lines
1.9 KiB
TypeScript
import type { PrismaClient } from "@calcom/prisma";
|
|
import type { User } from "@calcom/prisma/client";
|
|
import { userMetadata as userMetadataSchema } from "@calcom/prisma/zod-utils";
|
|
|
|
import type { LocationObject } from "../locations";
|
|
import { getAppFromSlug } from "../utils";
|
|
import { filterEventTypesWhereLocationUpdateIsAllowed } from "./getBulkEventTypes";
|
|
|
|
type PrismaLike = Pick<PrismaClient, "credential" | "eventType">;
|
|
|
|
export const bulkUpdateEventsToDefaultLocation = async ({
|
|
eventTypeIds,
|
|
user,
|
|
prisma,
|
|
}: {
|
|
eventTypeIds: number[];
|
|
user: Pick<User, "id" | "metadata">;
|
|
prisma: PrismaLike;
|
|
}) => {
|
|
const defaultApp = userMetadataSchema.parse(user.metadata)?.defaultConferencingApp;
|
|
|
|
if (!defaultApp) {
|
|
throw new Error("Default conferencing app not set");
|
|
}
|
|
|
|
const foundApp = getAppFromSlug(defaultApp.appSlug);
|
|
const appType = foundApp?.appData?.location?.type;
|
|
if (!appType) {
|
|
throw new Error(`Default conferencing app '${defaultApp.appSlug}' doesnt exist.`);
|
|
}
|
|
|
|
const credential = await prisma.credential.findFirst({
|
|
where: {
|
|
userId: user.id,
|
|
appId: foundApp.slug,
|
|
},
|
|
select: {
|
|
id: true,
|
|
},
|
|
});
|
|
|
|
const eventTypesToUpdate = await prisma.eventType.findMany({
|
|
where: {
|
|
id: {
|
|
in: eventTypeIds,
|
|
},
|
|
userId: user.id,
|
|
},
|
|
select: {
|
|
id: true,
|
|
metadata: true,
|
|
parentId: true,
|
|
},
|
|
});
|
|
|
|
const validEventTypeIds = filterEventTypesWhereLocationUpdateIsAllowed(eventTypesToUpdate).map(
|
|
(eventType) => eventType.id
|
|
);
|
|
|
|
if (validEventTypeIds.length === 0) {
|
|
return { count: 0 };
|
|
}
|
|
|
|
return await prisma.eventType.updateMany({
|
|
where: {
|
|
id: {
|
|
in: validEventTypeIds,
|
|
},
|
|
userId: user.id,
|
|
},
|
|
data: {
|
|
locations: [
|
|
{ type: appType, link: defaultApp.appLink, credentialId: credential?.id },
|
|
] as LocationObject[],
|
|
},
|
|
});
|
|
};
|