chore: add permission checks to invite member and list team members handlers (#25679)
* chore: update invite team member handler * chore: update invite team member handler * fix: add mock for PermissionCheckService in inviteMember handler tests Co-Authored-By: morgan@cal.com <morgan@cal.com> * fix: add mocks for PBAC dependencies in inviteMember handler tests Co-Authored-By: morgan@cal.com <morgan@cal.com> --------- Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
co-authored by
morgan@cal.com
Devin AI
parent
aedc0f5a03
commit
1182460d5c
@@ -1,9 +1,10 @@
|
||||
import z from "zod";
|
||||
|
||||
import { getBookerBaseUrlSync } from "@calcom/features/ee/organizations/lib/getBookerBaseUrlSync";
|
||||
import { PermissionCheckService } from "@calcom/features/pbac/services/permission-check.service";
|
||||
import { UserRepository } from "@calcom/features/users/repositories/UserRepository";
|
||||
import { prisma } from "@calcom/prisma";
|
||||
import type { Prisma } from "@calcom/prisma/client";
|
||||
import { MembershipRole, type Prisma } from "@calcom/prisma/client";
|
||||
|
||||
import { TRPCError } from "@trpc/server";
|
||||
|
||||
@@ -26,7 +27,7 @@ type ListOptions = {
|
||||
input: TListOtherTeamMembersSchema;
|
||||
};
|
||||
|
||||
export const listOtherTeamMembers = async ({ input }: ListOptions) => {
|
||||
export const listOtherTeamMembers = async ({ ctx, input }: ListOptions) => {
|
||||
const whereConditional: Prisma.MembershipWhereInput = {
|
||||
teamId: input.teamId,
|
||||
};
|
||||
@@ -76,6 +77,28 @@ export const listOtherTeamMembers = async ({ input }: ListOptions) => {
|
||||
});
|
||||
}
|
||||
|
||||
if (!team.parentId) {
|
||||
throw new TRPCError({
|
||||
code: "BAD_REQUEST",
|
||||
message: "Team does not belong to an organization",
|
||||
});
|
||||
}
|
||||
|
||||
const permissionCheckService = new PermissionCheckService();
|
||||
const hasPermission = await permissionCheckService.checkPermission({
|
||||
userId: ctx.user.id,
|
||||
teamId: team.parentId,
|
||||
permission: "team.listMembers",
|
||||
fallbackRoles: [MembershipRole.OWNER, MembershipRole.ADMIN],
|
||||
});
|
||||
|
||||
if (!hasPermission) {
|
||||
throw new TRPCError({
|
||||
code: "FORBIDDEN",
|
||||
message: "You are not authorized to view team members in this organization",
|
||||
});
|
||||
}
|
||||
|
||||
const members = await prisma.membership.findMany({
|
||||
where: whereConditional,
|
||||
select: {
|
||||
|
||||
@@ -41,7 +41,34 @@ vi.mock("@calcom/prisma", () => {
|
||||
};
|
||||
});
|
||||
|
||||
// Mock PBAC dependencies - these need to be mocked before PermissionCheckService
|
||||
vi.mock("@calcom/features/pbac/infrastructure/repositories/PermissionRepository");
|
||||
vi.mock("@calcom/features/flags/features.repository");
|
||||
vi.mock("@calcom/features/membership/repositories/MembershipRepository");
|
||||
vi.mock("@calcom/features/pbac/services/permission.service", () => {
|
||||
return {
|
||||
PermissionService: vi.fn().mockImplementation(() => ({
|
||||
validatePermission: vi.fn().mockReturnValue({ isValid: true }),
|
||||
validatePermissions: vi.fn().mockReturnValue({ isValid: true }),
|
||||
})),
|
||||
};
|
||||
});
|
||||
|
||||
vi.mock("@calcom/features/pbac/services/permission-check.service", () => {
|
||||
return {
|
||||
PermissionCheckService: vi.fn().mockImplementation(() => ({
|
||||
checkPermission: vi.fn().mockResolvedValue(true),
|
||||
checkPermissions: vi.fn().mockResolvedValue(true),
|
||||
getUserPermissions: vi.fn().mockResolvedValue([]),
|
||||
getResourcePermissions: vi.fn().mockResolvedValue([]),
|
||||
getTeamIdsWithPermission: vi.fn().mockResolvedValue([]),
|
||||
getTeamIdsWithPermissions: vi.fn().mockResolvedValue([]),
|
||||
})),
|
||||
};
|
||||
});
|
||||
|
||||
function fakeNoUsersFoundMatchingInvitations(args: {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
team: any;
|
||||
invitations: {
|
||||
role: MembershipRole;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { type TFunction } from "i18next";
|
||||
|
||||
import { getTeamBillingServiceFactory } from "@calcom/ee/billing/di/containers/Billing";
|
||||
import { PermissionCheckService } from "@calcom/features/pbac/services/permission-check.service";
|
||||
import { UserRepository } from "@calcom/features/users/repositories/UserRepository";
|
||||
import { checkRateLimitAndThrowError } from "@calcom/lib/checkRateLimitAndThrowError";
|
||||
import logger from "@calcom/lib/logger";
|
||||
@@ -247,6 +248,22 @@ const inviteMembers = async ({ ctx, input }: InviteMemberOptions) => {
|
||||
const { usernameOrEmail, role, isPlatform, creationSource } = input;
|
||||
|
||||
const team = await getTeamOrThrow(input.teamId);
|
||||
|
||||
const permissionCheckService = new PermissionCheckService();
|
||||
const hasPermission = await permissionCheckService.checkPermission({
|
||||
userId: ctx.user.id,
|
||||
teamId: team.id,
|
||||
permission: "team.invite",
|
||||
fallbackRoles: [MembershipRole.OWNER, MembershipRole.ADMIN],
|
||||
});
|
||||
|
||||
if (!hasPermission) {
|
||||
throw new TRPCError({
|
||||
code: "FORBIDDEN",
|
||||
message: "You are not authorized to invite team members in this organization's team",
|
||||
});
|
||||
}
|
||||
|
||||
const requestedSlugForTeam = team?.metadata?.requestedSlug ?? null;
|
||||
const isTeamAnOrg = team.isOrganization;
|
||||
const organization = inviter.profile.organization;
|
||||
|
||||
Reference in New Issue
Block a user