From fe190f20f360e843fc4c971271781a9fa1eeeaba Mon Sep 17 00:00:00 2001 From: Eunjae Lee Date: Wed, 24 Sep 2025 15:46:24 +0200 Subject: [PATCH] refactor: replace isTeamAdminOrOwner with PBAC permissions (#24037) * refactor: replace isTeamAdminOrOwner with PBAC permissions - Remove isTeamAdminOrOwner from team-members-view.tsx, rely on server-side permissions - Replace role checks in addMembersToEventTypes.handler.ts with eventType.update permission - Follow PBAC refactoring guide patterns for consistent permission checking - Fix TypeScript any type usage and unused variable warnings Co-Authored-By: eunjae@cal.com * use enum --------- Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- apps/web/modules/teams/team-members-view.tsx | 16 ++++++++++------ .../teams/addMembersToEventTypes.handler.ts | 18 ++++++++++++------ 2 files changed, 22 insertions(+), 12 deletions(-) diff --git a/apps/web/modules/teams/team-members-view.tsx b/apps/web/modules/teams/team-members-view.tsx index 061cbe4b49..74e437e0bc 100644 --- a/apps/web/modules/teams/team-members-view.tsx +++ b/apps/web/modules/teams/team-members-view.tsx @@ -2,7 +2,6 @@ import { useState } from "react"; -import { checkAdminOrOwner } from "@calcom/features/auth/lib/checkAdminOrOwner"; import LicenseRequired from "@calcom/features/ee/common/components/LicenseRequired"; import { MemberInvitationModalWithoutMembers } from "@calcom/features/ee/teams/components/MemberInvitationModal"; import MemberList from "@calcom/features/ee/teams/components/MemberList"; @@ -23,18 +22,23 @@ interface TeamMembersViewProps { }[]; }[]; }; - attributes?: any[]; + attributes?: { + id: string; + name: string; + options: { + value: string; + }[]; + }[]; permissions: MemberPermissions; } export const TeamMembersView = ({ team, facetedTeamValues, permissions }: TeamMembersViewProps) => { const { t } = useLocale(); const [showMemberInvitationModal, setShowMemberInvitationModal] = useState(false); - const [showInviteLinkSettingsModal, setShowInviteLinkSettingsModal] = useState(false); + const [_showInviteLinkSettingsModal, setShowInviteLinkSettingsModal] = useState(false); - // Use PBAC permissions if available, otherwise fall back to role-based check - const isTeamAdminOrOwner = checkAdminOrOwner(team.membership.role); - const canLoggedInUserSeeMembers = permissions?.canListMembers ?? (!team.isPrivate || isTeamAdminOrOwner); + // Use PBAC permissions - server-side permission check should be done in parent component + const canLoggedInUserSeeMembers = permissions?.canListMembers ?? false; return ( diff --git a/packages/trpc/server/routers/viewer/teams/addMembersToEventTypes.handler.ts b/packages/trpc/server/routers/viewer/teams/addMembersToEventTypes.handler.ts index 501fd0d7ba..8eb0287bc1 100644 --- a/packages/trpc/server/routers/viewer/teams/addMembersToEventTypes.handler.ts +++ b/packages/trpc/server/routers/viewer/teams/addMembersToEventTypes.handler.ts @@ -1,6 +1,7 @@ -import { isTeamAdmin, isTeamOwner } from "@calcom/features/ee/teams/lib/queries"; -import prisma from "@calcom/prisma"; +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/enums"; import { TRPCError } from "@trpc/server"; @@ -17,11 +18,16 @@ type AddBulkToEventTypeHandler = { export async function addMembersToEventTypesHandler({ ctx, input }: AddBulkToEventTypeHandler) { const { eventTypeIds, userIds, teamId } = input; - const isTeamAdminOrOwner = - (await isTeamAdmin(ctx.user.id, teamId)) || (await isTeamOwner(ctx.user.id, teamId)); + const permissionCheckService = new PermissionCheckService(); + const hasPermission = await permissionCheckService.checkPermission({ + userId: ctx.user.id, + teamId, + permission: "eventType.update", + fallbackRoles: [MembershipRole.ADMIN, MembershipRole.OWNER], + }); - // check if user is admin or owner of team - if (!isTeamAdminOrOwner) throw new TRPCError({ code: "UNAUTHORIZED" }); + // check if user has eventType.update permission + if (!hasPermission) throw new TRPCError({ code: "UNAUTHORIZED" }); const data: Prisma.HostCreateManyInput[] = eventTypeIds.flatMap((eventId) => userIds.map((userId) => ({