Compare commits
31 Commits
main
...
fix/allow-
Author | SHA1 | Date | |
---|---|---|---|
7c091b3181 | |||
17e9a739c2 | |||
88500a2b80 | |||
7eacc34ed9 | |||
030833c28c | |||
5acf986770 | |||
03feb12488 | |||
a62bd2eb47 | |||
5dbb2799b7 | |||
a94b968303 | |||
4b7016d3d3 | |||
b6faa9a57e | |||
fd007efefc | |||
fd4a5f783c | |||
6413acc4f4 | |||
d6510516db | |||
b1ed0aeb25 | |||
492aa81905 | |||
9c565d3ec9 | |||
c825275d52 | |||
30e65c4e0e | |||
8d7a7c8b1f | |||
33be1a89ec | |||
77f3c0cb50 | |||
04293a25ca | |||
9464e4cbe5 | |||
a229dea41c | |||
f10f3c05ae | |||
37aa750f04 | |||
13f682961b | |||
5115965239 |
|
@ -1,3 +1,4 @@
|
|||
import { EventType, Membership, MembershipRole, User } from "@prisma/client";
|
||||
import { TFunction } from "next-i18next";
|
||||
|
||||
type EventNameObjectType = {
|
||||
|
@ -17,3 +18,40 @@ export function getEventName(eventNameObj: EventNameObjectType) {
|
|||
attendeeName: eventNameObj.attendeeName,
|
||||
});
|
||||
}
|
||||
|
||||
export function canEventBeEdited({
|
||||
user,
|
||||
eventType,
|
||||
}: {
|
||||
user: {
|
||||
id: User["id"];
|
||||
};
|
||||
eventType: {
|
||||
users: {
|
||||
id: User["id"];
|
||||
}[];
|
||||
userId: User["id"] | null;
|
||||
team: {
|
||||
members: {
|
||||
userId: Membership["userId"];
|
||||
role: MembershipRole;
|
||||
}[];
|
||||
} | null;
|
||||
};
|
||||
}) {
|
||||
// I am the creator of the event.
|
||||
if (eventType.userId === user.id) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check if he is the owner of the team to which event belongs
|
||||
if (eventType.team) {
|
||||
return eventType.team.members
|
||||
.filter((member) => {
|
||||
return member.role === MembershipRole.OWNER || member.role === MembershipRole.ADMIN;
|
||||
})
|
||||
.map((member) => member.userId)
|
||||
.includes(user.id);
|
||||
}
|
||||
return eventType.users.find((user) => user.id === user.id);
|
||||
}
|
||||
|
|
|
@ -63,7 +63,6 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
|||
};
|
||||
|
||||
await sendPasswordResetEmail(passwordEmail);
|
||||
|
||||
/** So we can test the password reset flow on CI */
|
||||
if (process.env.NEXT_PUBLIC_IS_E2E) {
|
||||
return res.status(201).json({ message: "Reset Requested", resetLink });
|
||||
|
|
|
@ -59,13 +59,13 @@ interface EventTypeListHeadingProps {
|
|||
type EventTypeGroup = inferQueryOutput<"viewer.eventTypes">["eventTypeGroups"][number];
|
||||
type EventType = EventTypeGroup["eventTypes"][number];
|
||||
interface EventTypeListProps {
|
||||
group: EventTypeGroup;
|
||||
groupIndex: number;
|
||||
readOnly: boolean;
|
||||
group: EventTypeGroup;
|
||||
types: EventType[];
|
||||
profile: { slug: string | null };
|
||||
}
|
||||
|
||||
const Item = ({ type, group, readOnly }: { type: EventType; group: EventTypeGroup; readOnly: boolean }) => {
|
||||
const Item = ({ type, group }: { type: EventType; group: EventTypeGroup }) => {
|
||||
const { t } = useLocale();
|
||||
|
||||
return (
|
||||
|
@ -90,7 +90,7 @@ const Item = ({ type, group, readOnly }: { type: EventType; group: EventTypeGrou
|
|||
{t("hidden")}
|
||||
</span>
|
||||
)}
|
||||
{readOnly && (
|
||||
{type.readOnly && (
|
||||
<span className="rtl:mr-2inline items-center rounded-sm bg-gray-100 px-1.5 py-0.5 text-xs font-medium text-gray-800 ltr:ml-2">
|
||||
{t("readonly")}
|
||||
</span>
|
||||
|
@ -104,7 +104,7 @@ const Item = ({ type, group, readOnly }: { type: EventType; group: EventTypeGrou
|
|||
|
||||
const MemoizedItem = React.memo(Item);
|
||||
|
||||
export const EventTypeList = ({ group, groupIndex, readOnly, types }: EventTypeListProps): JSX.Element => {
|
||||
export const EventTypeList = ({ group, groupIndex, types, profile }: EventTypeListProps): JSX.Element => {
|
||||
const { t } = useLocale();
|
||||
const router = useRouter();
|
||||
|
||||
|
@ -184,10 +184,17 @@ export const EventTypeList = ({ group, groupIndex, readOnly, types }: EventTypeL
|
|||
showToast(t("event_type_deleted_successfully"), "success");
|
||||
},
|
||||
onError: (err) => {
|
||||
let message: string;
|
||||
if (err instanceof HttpError) {
|
||||
const message = `${err.statusCode}: ${err.message}`;
|
||||
showToast(message, "error");
|
||||
message = `${err.statusCode}: ${err.message}`;
|
||||
}
|
||||
|
||||
if (err.data?.code === "UNAUTHORIZED") {
|
||||
message = `${err.data.code}: You are not able to delete this event`;
|
||||
} else {
|
||||
message = "Some Error Occurred";
|
||||
}
|
||||
showToast(message, "error");
|
||||
},
|
||||
});
|
||||
|
||||
|
@ -232,7 +239,7 @@ export const EventTypeList = ({ group, groupIndex, readOnly, types }: EventTypeL
|
|||
</button>
|
||||
</>
|
||||
)}
|
||||
<MemoizedItem type={type} group={group} readOnly={readOnly} />
|
||||
<MemoizedItem type={type} group={group} />
|
||||
<div className="mt-4 hidden flex-shrink-0 sm:mt-0 sm:ml-5 sm:flex">
|
||||
<div className="flex justify-between space-x-2 rtl:space-x-reverse">
|
||||
{type.users?.length > 1 && (
|
||||
|
@ -590,10 +597,10 @@ const EventTypesPage = () => {
|
|||
/>
|
||||
)}
|
||||
<EventTypeList
|
||||
profile={group.profile}
|
||||
types={group.eventTypes}
|
||||
group={group}
|
||||
groupIndex={index}
|
||||
readOnly={group.metadata.readOnly}
|
||||
/>
|
||||
</Fragment>
|
||||
))}
|
||||
|
|
|
@ -10,6 +10,7 @@ import { bookingMinimalSelect } from "@calcom/prisma";
|
|||
import { RecurringEvent } from "@calcom/types/Calendar";
|
||||
|
||||
import { checkRegularUsername } from "@lib/core/checkRegularUsername";
|
||||
import { canEventBeEdited } from "@lib/event";
|
||||
import jackson from "@lib/jackson";
|
||||
import {
|
||||
isSAMLLoginEnabled,
|
||||
|
@ -135,6 +136,7 @@ const loggedInViewerRouter = createProtectedRouter()
|
|||
price: true,
|
||||
currency: true,
|
||||
position: true,
|
||||
userId: true,
|
||||
successRedirectUrl: true,
|
||||
hashedLink: true,
|
||||
users: {
|
||||
|
@ -144,6 +146,11 @@ const loggedInViewerRouter = createProtectedRouter()
|
|||
name: true,
|
||||
},
|
||||
},
|
||||
team: {
|
||||
select: {
|
||||
members: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const user = await prisma.user.findUnique({
|
||||
|
@ -206,7 +213,6 @@ const loggedInViewerRouter = createProtectedRouter()
|
|||
},
|
||||
},
|
||||
});
|
||||
|
||||
if (!user) {
|
||||
throw new TRPCError({ code: "INTERNAL_SERVER_ERROR" });
|
||||
}
|
||||
|
@ -235,9 +241,8 @@ const loggedInViewerRouter = createProtectedRouter()
|
|||
};
|
||||
metadata: {
|
||||
membershipCount: number;
|
||||
readOnly: boolean;
|
||||
};
|
||||
eventTypes: (typeof user.eventTypes[number] & { $disabled?: boolean })[];
|
||||
eventTypes: (typeof user.eventTypes[number] & { $disabled?: boolean; readOnly?: boolean })[];
|
||||
};
|
||||
|
||||
let eventTypeGroups: EventTypeGroup[] = [];
|
||||
|
@ -260,12 +265,10 @@ const loggedInViewerRouter = createProtectedRouter()
|
|||
eventTypes: _.orderBy(mergedEventTypes, ["position", "id"], ["desc", "asc"]),
|
||||
metadata: {
|
||||
membershipCount: 1,
|
||||
readOnly: false,
|
||||
},
|
||||
});
|
||||
|
||||
eventTypeGroups = ([] as EventTypeGroup[]).concat(
|
||||
eventTypeGroups,
|
||||
eventTypeGroups = eventTypeGroups.concat(
|
||||
user.teams.map((membership) => ({
|
||||
teamId: membership.team.id,
|
||||
profile: {
|
||||
|
@ -275,9 +278,11 @@ const loggedInViewerRouter = createProtectedRouter()
|
|||
},
|
||||
metadata: {
|
||||
membershipCount: membership.team.members.length,
|
||||
readOnly: membership.role === MembershipRole.MEMBER,
|
||||
},
|
||||
eventTypes: membership.team.eventTypes,
|
||||
eventTypes: membership.team.eventTypes.map((eventType) => ({
|
||||
...eventType,
|
||||
readOnly: !canEventBeEdited({ user, eventType }),
|
||||
})),
|
||||
}))
|
||||
);
|
||||
|
||||
|
|
|
@ -7,6 +7,8 @@ import { _DestinationCalendarModel, _EventTypeCustomInputModel, _EventTypeModel
|
|||
import { stringOrNumber } from "@calcom/prisma/zod-utils";
|
||||
import { createEventTypeInput } from "@calcom/prisma/zod/custom/eventtype";
|
||||
|
||||
import { canEventBeEdited } from "@lib/event";
|
||||
|
||||
import { createProtectedRouter } from "@server/createRouter";
|
||||
import { viewerRouter } from "@server/routers/viewer";
|
||||
import { TRPCError } from "@trpc/server";
|
||||
|
@ -115,7 +117,7 @@ export const eventTypesRouter = createProtectedRouter()
|
|||
|
||||
const data: Prisma.EventTypeCreateInput = {
|
||||
...rest,
|
||||
userId: teamId ? undefined : userId,
|
||||
userId: userId,
|
||||
users: {
|
||||
connect: {
|
||||
id: userId,
|
||||
|
@ -187,13 +189,7 @@ export const eventTypesRouter = createProtectedRouter()
|
|||
}
|
||||
|
||||
const isAuthorized = (function () {
|
||||
if (event.team) {
|
||||
return event.team.members
|
||||
.filter((member) => member.role === MembershipRole.OWNER || member.role === MembershipRole.ADMIN)
|
||||
.map((member) => member.userId)
|
||||
.includes(ctx.user.id);
|
||||
}
|
||||
return event.userId === ctx.user.id || event.users.find((user) => user.id === ctx.user.id);
|
||||
return canEventBeEdited({ user: ctx.user, eventType: event });
|
||||
})();
|
||||
|
||||
if (!isAuthorized) {
|
||||
|
|
Loading…
Reference in New Issue
Block a user