Merge pull request #3240 from bluewave-labs/fix/invite-permisisons

fix/invite permisisons
This commit is contained in:
Alexander Holliday
2026-02-04 10:13:25 -08:00
committed by GitHub
4 changed files with 61 additions and 6 deletions
@@ -112,6 +112,13 @@ const requireUserEmail = (userEmail?: string): string => {
return userEmail;
};
export const requireUserRoles = (userRoles?: string[]): string[] => {
if (!userRoles || userRoles.length === 0) {
throw new AppError({ message: "User roles are required", status: 400 });
}
return userRoles;
};
export {
fetchMonitorCertificate,
requireString,
+10 -3
View File
@@ -1,5 +1,6 @@
import { Request, Response, NextFunction } from "express";
import { inviteBodyValidation, inviteVerificationBodyValidation } from "@/validation/joi.js";
import { requireTeamId, requireUserRoles } from "@/controllers/controllerUtils.js";
const SERVICE_NAME = "inviteController";
class InviteController {
@@ -14,12 +15,14 @@ class InviteController {
}
getInviteToken = async (req: Request, res: Response, next: NextFunction) => {
console.log(req.body);
try {
const teamId = requireTeamId(req.user?.teamId);
const userRoles = requireUserRoles(req.user?.role);
const invite = req.body;
const teamId = req?.user?.teamId;
invite.teamId = teamId;
await inviteBodyValidation.validateAsync(invite);
const inviteToken = await this.inviteService.getInviteToken({ invite, teamId });
const inviteToken = await this.inviteService.getInviteToken({ invite, teamId, userRoles });
return res.status(200).json({
success: true,
msg: "Invite token generated successfully",
@@ -32,13 +35,17 @@ class InviteController {
sendInviteEmail = async (req: Request, res: Response, next: NextFunction) => {
try {
const teamId = requireTeamId(req?.user?.teamId);
const userRoles = requireUserRoles(req?.user?.role);
const inviteRequest = req.body;
inviteRequest.teamId = req?.user?.teamId;
inviteRequest.teamId = teamId;
await inviteBodyValidation.validateAsync(inviteRequest);
const inviteToken = await this.inviteService.sendInviteEmail({
invite: inviteRequest,
firstName: req?.user?.firstName,
userRoles,
});
return res.status(200).json({
success: true,
+33 -3
View File
@@ -1,4 +1,5 @@
import type { Invite } from "@/types/index.js";
import type { Invite, UserRole } from "@/types/index.js";
import { canManageRole } from "@/types/user.js";
import type { IInvitesRepository } from "@/repositories/index.js";
import { AppError } from "@/utils/AppError.js";
@@ -29,13 +30,42 @@ class InviteService {
return InviteService.SERVICE_NAME;
}
getInviteToken = async ({ invite, teamId }: { invite: Partial<Invite>; teamId: string }) => {
getInviteToken = async ({ invite, teamId, userRoles }: { invite: Partial<Invite>; teamId: string; userRoles: UserRole[] }) => {
invite.teamId = teamId;
const inviteRoles = invite.role ?? [];
for (const targetRole of inviteRoles) {
const canManage = userRoles.some((actorRole) => canManageRole(actorRole, targetRole));
if (!canManage) {
throw new AppError({
message: "You do not have permission to create this invite",
service: SERVICE_NAME,
method: "getInviteToken",
status: 403,
});
}
}
const inviteToken = await this.invitesRepository.create(invite);
return inviteToken;
};
sendInviteEmail = async ({ invite, firstName }: { invite: Partial<Invite>; firstName: any }) => {
sendInviteEmail = async ({ invite, firstName, userRoles }: { invite: Partial<Invite>; firstName: any; userRoles: UserRole[] }) => {
const inviteRoles = invite.role ?? [];
for (const targetRole of inviteRoles) {
const canManage = userRoles.some((actorRole) => canManageRole(actorRole, targetRole));
if (!canManage) {
throw new AppError({
message: "You do not have permission to create this invite",
service: SERVICE_NAME,
method: "getInviteToken",
status: 403,
});
}
}
const inviteToken = await this.invitesRepository.create(invite);
const { clientHost } = this.settingsService.getSettings();
+11
View File
@@ -1,6 +1,17 @@
export const UserRoles = ["user", "admin", "superadmin", "demo"] as const;
export type UserRole = (typeof UserRoles)[number];
export const RoleHierarchy: Record<UserRole, number> = {
demo: 0,
user: 1,
admin: 2,
superadmin: 3,
};
export const canManageRole = (actorRole: UserRole, targetRole: UserRole): boolean => {
return RoleHierarchy[actorRole] > RoleHierarchy[targetRole];
};
export interface UserProfileImage {
data?: Buffer;
contentType?: string;