fix: use domain errors instead of trpc (#22341)
This commit is contained in:
1 parent
3285b252c2
commit
9892d521fb
4 files changed
+77
-17
No files matched your search
@@ -0,0 +1,12 @@
|
||||
export class RoleManagementError extends Error {
|
||||
constructor(message: string, public readonly code: RoleManagementErrorCode) {
|
||||
super(message);
|
||||
this.name = "RoleManagementError";
|
||||
}
|
||||
}
|
||||
|
||||
export enum RoleManagementErrorCode {
|
||||
UNAUTHORIZED = "UNAUTHORIZED",
|
||||
ROLE_NOT_FOUND = "ROLE_NOT_FOUND",
|
||||
INVALID_ROLE = "INVALID_ROLE",
|
||||
}
|
||||
@@ -5,8 +5,7 @@ import { isOrganisationAdmin } from "@calcom/lib/server/queries/organisations";
|
||||
import { prisma } from "@calcom/prisma";
|
||||
import { MembershipRole } from "@calcom/prisma/enums";
|
||||
|
||||
import { TRPCError } from "@trpc/server";
|
||||
|
||||
import { RoleManagementError, RoleManagementErrorCode } from "../../domain/errors/role-management.error";
|
||||
import { DEFAULT_ROLE_IDS } from "../../lib/constants";
|
||||
import { PermissionCheckService } from "../permission-check.service";
|
||||
import { RoleManagementFactory } from "../role-management.factory";
|
||||
@@ -117,7 +116,12 @@ describe("RoleManagementFactory", () => {
|
||||
it("should throw UNAUTHORIZED when user lacks permission", async () => {
|
||||
mockPermissionCheckService.checkPermission.mockResolvedValue(false);
|
||||
const manager = await factory.createRoleManager(organizationId);
|
||||
await expect(manager.checkPermissionToChangeRole(userId, organizationId)).rejects.toThrow(TRPCError);
|
||||
await expect(manager.checkPermissionToChangeRole(userId, organizationId)).rejects.toThrow(
|
||||
new RoleManagementError(
|
||||
"You do not have permission to change roles",
|
||||
RoleManagementErrorCode.UNAUTHORIZED
|
||||
)
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -144,14 +148,16 @@ describe("RoleManagementFactory", () => {
|
||||
expect(mockRoleService.assignRoleToMember).toHaveBeenCalledWith(customRoleId, membershipId);
|
||||
});
|
||||
|
||||
it("should throw UNAUTHORIZED for invalid custom role", async () => {
|
||||
it("should throw INVALID_ROLE for invalid custom role", async () => {
|
||||
const customRoleId = "invalid-role";
|
||||
mockRoleService.roleBelongsToTeam.mockResolvedValue(false);
|
||||
|
||||
const manager = await factory.createRoleManager(organizationId);
|
||||
await expect(
|
||||
manager.assignRole(userId, organizationId, customRoleId as MembershipRole, membershipId)
|
||||
).rejects.toThrow(TRPCError);
|
||||
).rejects.toThrow(
|
||||
new RoleManagementError("You do not have access to this role", RoleManagementErrorCode.INVALID_ROLE)
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -159,13 +165,33 @@ describe("RoleManagementFactory", () => {
|
||||
describe("LegacyRoleManager", () => {
|
||||
beforeEach(() => {
|
||||
mockFeaturesRepository.checkIfTeamHasFeature.mockResolvedValue(false);
|
||||
vi.mocked(prisma.membership.update).mockResolvedValue({});
|
||||
vi.mocked(prisma.membership.update).mockResolvedValue({
|
||||
id: 1,
|
||||
teamId: organizationId,
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
role: MembershipRole.MEMBER,
|
||||
userId: userId,
|
||||
disableImpersonation: false,
|
||||
accepted: true,
|
||||
customRoleId: null,
|
||||
});
|
||||
vi.mocked(isOrganisationAdmin).mockResolvedValue(false);
|
||||
});
|
||||
|
||||
describe("checkPermissionToChangeRole", () => {
|
||||
it("should allow role change when user is owner", async () => {
|
||||
vi.mocked(isOrganisationAdmin).mockResolvedValue(true);
|
||||
vi.mocked(isOrganisationAdmin).mockResolvedValue({
|
||||
id: 1,
|
||||
teamId: organizationId,
|
||||
userId: userId,
|
||||
role: MembershipRole.OWNER,
|
||||
accepted: true,
|
||||
disableImpersonation: false,
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
customRoleId: null,
|
||||
});
|
||||
const manager = await factory.createRoleManager(organizationId);
|
||||
await expect(manager.checkPermissionToChangeRole(userId, organizationId)).resolves.not.toThrow();
|
||||
});
|
||||
@@ -173,7 +199,12 @@ describe("RoleManagementFactory", () => {
|
||||
it("should throw UNAUTHORIZED when user is not owner", async () => {
|
||||
vi.mocked(isOrganisationAdmin).mockResolvedValue(false);
|
||||
const manager = await factory.createRoleManager(organizationId);
|
||||
await expect(manager.checkPermissionToChangeRole(userId, organizationId)).rejects.toThrow(TRPCError);
|
||||
await expect(manager.checkPermissionToChangeRole(userId, organizationId)).rejects.toThrow(
|
||||
new RoleManagementError(
|
||||
"Only owners or admin can update roles",
|
||||
RoleManagementErrorCode.UNAUTHORIZED
|
||||
)
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -3,8 +3,7 @@ import { isOrganisationAdmin } from "@calcom/lib/server/queries/organisations";
|
||||
import { prisma } from "@calcom/prisma";
|
||||
import { MembershipRole } from "@calcom/prisma/enums";
|
||||
|
||||
import { TRPCError } from "@trpc/server";
|
||||
|
||||
import { RoleManagementError, RoleManagementErrorCode } from "../domain/errors/role-management.error";
|
||||
import { DEFAULT_ROLE_IDS } from "../lib/constants";
|
||||
import { PermissionCheckService } from "./permission-check.service";
|
||||
import { RoleService } from "./role.service";
|
||||
@@ -38,7 +37,10 @@ class PBACRoleManager implements IRoleManager {
|
||||
});
|
||||
|
||||
if (!hasPermission) {
|
||||
throw new TRPCError({ code: "UNAUTHORIZED" });
|
||||
throw new RoleManagementError(
|
||||
"You do not have permission to change roles",
|
||||
RoleManagementErrorCode.UNAUTHORIZED
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,7 +57,10 @@ class PBACRoleManager implements IRoleManager {
|
||||
} else {
|
||||
const roleExists = await this.roleService.roleBelongsToTeam(role, organizationId);
|
||||
if (!roleExists) {
|
||||
throw new TRPCError({ code: "UNAUTHORIZED", message: "You do not have access to this role" });
|
||||
throw new RoleManagementError(
|
||||
"You do not have access to this role",
|
||||
RoleManagementErrorCode.INVALID_ROLE
|
||||
);
|
||||
}
|
||||
await this.roleService.assignRoleToMember(role, membershipId);
|
||||
}
|
||||
@@ -73,11 +78,14 @@ class PBACRoleManager implements IRoleManager {
|
||||
class LegacyRoleManager implements IRoleManager {
|
||||
public isPBACEnabled = false;
|
||||
async checkPermissionToChangeRole(userId: number, organizationId: number): Promise<void> {
|
||||
const isUpdaterAnOwner = await isOrganisationAdmin(userId, organizationId);
|
||||
const membership = await isOrganisationAdmin(userId, organizationId);
|
||||
|
||||
// Only OWNER can update role to OWNER
|
||||
if (!isUpdaterAnOwner) {
|
||||
throw new TRPCError({ code: "UNAUTHORIZED" });
|
||||
// Only OWNER/ADMIN can update role
|
||||
if (!membership) {
|
||||
throw new RoleManagementError(
|
||||
"Only owners or admin can update roles",
|
||||
RoleManagementErrorCode.UNAUTHORIZED
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ import type { Prisma, PrismaPromise, User, Membership, Profile } from "@prisma/c
|
||||
|
||||
import { ensureOrganizationIsReviewed } from "@calcom/ee/organizations/lib/ensureOrganizationIsReviewed";
|
||||
import { checkAdminOrOwner } from "@calcom/features/auth/lib/checkAdminOrOwner";
|
||||
import { RoleManagementError } from "@calcom/features/pbac/domain/errors/role-management.error";
|
||||
import { RoleManagementFactory } from "@calcom/features/pbac/services/role-management.factory";
|
||||
import { uploadAvatar } from "@calcom/lib/server/avatar";
|
||||
import { checkRegularUsername } from "@calcom/lib/server/checkRegularUsername";
|
||||
@@ -44,7 +45,15 @@ export const updateUserHandler = async ({ ctx, input }: UpdateUserOptions) => {
|
||||
throw new TRPCError({ code: "UNAUTHORIZED", message: "You must be a member of an organizaiton" });
|
||||
|
||||
const roleManager = await RoleManagementFactory.getInstance().createRoleManager(organizationId);
|
||||
await roleManager.checkPermissionToChangeRole(userId, organizationId);
|
||||
|
||||
try {
|
||||
await roleManager.checkPermissionToChangeRole(userId, organizationId);
|
||||
} catch (error) {
|
||||
if (error instanceof RoleManagementError) {
|
||||
throw new TRPCError({ code: "UNAUTHORIZED", message: error.message });
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
|
||||
await ensureOrganizationIsReviewed(organizationId);
|
||||
|
||||
|
||||
Reference in new issue
Block a user