refactor: use permission check service in /organizations endpoints (#24099)

* getUserConnectedApps

* create teams

* use permission check in team deletion

* team read

* revert

* publish handler

* wip
This commit is contained in:
Benny Joo
2025-09-29 11:07:19 +00:00
committed by GitHub
parent f4d09ece43
commit 72dd970b6e
4 changed files with 88 additions and 24 deletions
@@ -1,6 +1,7 @@
import { isOrganisationAdmin } from "@calcom/lib/server/queries/organisations";
import { PermissionCheckService } from "@calcom/features/pbac/services/permission-check.service";
import prisma from "@calcom/prisma";
import type { Prisma } from "@calcom/prisma/client";
import { MembershipRole } from "@calcom/prisma/client";
import { TRPCError } from "@trpc/server";
@@ -18,9 +19,21 @@ type AddBulkToEventTypeHandler = {
export async function addMembersToEventTypesHandler({ ctx, input }: AddBulkToEventTypeHandler) {
if (!ctx.user.organizationId) throw new TRPCError({ code: "UNAUTHORIZED" });
// check if user is admin of organization
if (!(await isOrganisationAdmin(ctx.user?.id, ctx.user.organizationId)))
throw new TRPCError({ code: "UNAUTHORIZED" });
// Check if user has permission to manage event types in the organization
const permissionCheckService = new PermissionCheckService();
const hasPermission = await permissionCheckService.checkPermission({
userId: ctx.user.id,
teamId: ctx.user.organizationId,
permission: "eventType.update",
fallbackRoles: [MembershipRole.OWNER, MembershipRole.ADMIN],
});
if (!hasPermission) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to manage event types in this organization",
});
}
const { eventTypeIds, teamIds, userIds } = input;
@@ -1,5 +1,6 @@
import { getOrgFullOrigin } from "@calcom/ee/organizations/lib/orgDomains";
import stripe from "@calcom/features/ee/payments/server/stripe";
import { PermissionCheckService } from "@calcom/features/pbac/services/permission-check.service";
import logger from "@calcom/lib/logger";
import { safeStringify } from "@calcom/lib/safeStringify";
import { UserRepository } from "@calcom/lib/server/repository/user";
@@ -41,24 +42,20 @@ export const createTeamsHandler = async ({ ctx, input }: CreateTeamsOptions) =>
throw new NotAuthorizedError();
}
// Validate user membership role
const userMembershipRole = await prisma.membership.findFirst({
where: {
userId: organizationOwner.id,
teamId: orgId,
role: {
in: ["OWNER", "ADMIN"],
},
// @TODO: not sure if this already setup earlier
// accepted: true,
},
select: {
role: true,
},
// Validate user has permission to create teams in the organization
const permissionCheckService = new PermissionCheckService();
const hasPermission = await permissionCheckService.checkPermission({
userId: organizationOwner.id,
teamId: orgId,
permission: "team.create",
fallbackRoles: [MembershipRole.OWNER, MembershipRole.ADMIN],
});
if (!userMembershipRole) {
log.error("User is not a member of the organization", safeStringify({ orgId, organizationOwner }));
if (!hasPermission) {
log.error(
"User is not authorized to create teams in the organization",
safeStringify({ orgId, organizationOwner })
);
throw new NotAuthorizedError();
}
@@ -1,12 +1,52 @@
import { PermissionCheckService } from "@calcom/features/pbac/services/permission-check.service";
import { prisma } from "@calcom/prisma";
import { MembershipRole } from "@calcom/prisma/client";
import { TRPCError } from "@trpc/server";
import type { TrpcSessionUser } from "../../../types";
import type { TDeleteTeamInputSchema } from "./deleteTeam.schema";
type DeleteOptions = {
ctx: {
user: NonNullable<TrpcSessionUser>;
};
input: TDeleteTeamInputSchema;
};
export const deleteTeamHandler = async ({ input }: DeleteOptions) => {
export const deleteTeamHandler = async ({ ctx, input }: DeleteOptions) => {
const team = await prisma.team.findUnique({
where: { id: input.teamId },
select: {
id: true,
parentId: true,
},
});
if (!team) {
throw new TRPCError({ code: "NOT_FOUND", message: "Team not found" });
}
if (!team.parentId) {
throw new TRPCError({ code: "BAD_REQUEST", message: "Can only delete teams within organizations" });
}
// Check if user has permission to delete teams in this organization
const permissionCheckService = new PermissionCheckService();
const hasPermission = await permissionCheckService.checkPermission({
userId: ctx.user.id,
teamId: team.parentId,
permission: "team.delete",
fallbackRoles: [MembershipRole.OWNER, MembershipRole.ADMIN],
});
if (!hasPermission) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to delete teams in this organization",
});
}
// delete all memberships
await prisma.membership.deleteMany({
where: {
@@ -1,8 +1,9 @@
import { getRequestedSlugError } from "@calcom/app-store/stripepayment/lib/team-billing";
import { purchaseTeamOrOrgSubscription } from "@calcom/features/ee/teams/lib/payments";
import { PermissionCheckService } from "@calcom/features/pbac/services/permission-check.service";
import { IS_TEAM_BILLING_ENABLED, WEBAPP_URL } from "@calcom/lib/constants";
import { isOrganisationAdmin } from "@calcom/lib/server/queries/organisations";
import { prisma } from "@calcom/prisma";
import { MembershipRole } from "@calcom/prisma/client";
import { teamMetadataStrictSchema } from "@calcom/prisma/zod-utils";
import { TRPCError } from "@trpc/server";
@@ -20,7 +21,21 @@ export const publishHandler = async ({ ctx }: PublishOptions) => {
if (!orgId)
throw new TRPCError({ code: "UNAUTHORIZED", message: "You do not have an organization to upgrade" });
if (!(await isOrganisationAdmin(ctx.user.id, orgId))) throw new TRPCError({ code: "UNAUTHORIZED" });
// Check if user has permission to publish the organization
const permissionCheckService = new PermissionCheckService();
const hasPermission = await permissionCheckService.checkPermission({
userId: ctx.user.id,
teamId: orgId,
permission: "organization.update",
fallbackRoles: [MembershipRole.OWNER, MembershipRole.ADMIN],
});
if (!hasPermission) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to publish this organization",
});
}
const prevTeam = await prisma.team.findUnique({
where: {
@@ -66,7 +81,6 @@ export const publishHandler = async ({ ctx }: PublishOptions) => {
const { requestedSlug, ...newMetadata } = metadata.data;
let updatedTeam: Awaited<ReturnType<typeof prisma.team.update>>;
try {
updatedTeam = await prisma.team.update({
where: { id: orgId },