Enforce team role hierarchy on user updates and removals

This commit is contained in:
Mike Cao
2026-06-02 21:32:34 -07:00
parent 0f36990040
commit 46819a4443
2 changed files with 30 additions and 0 deletions
@@ -1,4 +1,5 @@
import { z } from 'zod';
import { TEAM_ROLE_RANK } from '@/lib/constants';
import { parseRequest } from '@/lib/request';
import { badRequest, json, ok, unauthorized } from '@/lib/response';
import { teamRoleParam } from '@/lib/schema';
@@ -52,6 +53,17 @@ export async function POST(
return badRequest({ message: 'The User does not exists on this team.' });
}
// Server-side rank check: actor must outrank target to modify their role.
if (!auth.user.isAdmin) {
const actorTeamUser = await getTeamUser(teamId, auth.user.id);
const actorRank = TEAM_ROLE_RANK[actorTeamUser?.role] ?? -1;
const targetRank = TEAM_ROLE_RANK[teamUser.role] ?? -1;
if (actorRank <= targetRank) {
return unauthorized({ message: 'You do not have permission to modify this user.' });
}
}
const user = await updateTeamUser(teamUser.id, body);
return json(user);
@@ -79,6 +91,17 @@ export async function DELETE(
return badRequest({ message: 'The User does not exists on this team.' });
}
// Server-side rank check: actor must outrank target to remove them.
if (!auth.user.isAdmin && userId !== auth.user.id) {
const actorTeamUser = await getTeamUser(teamId, auth.user.id);
const actorRank = TEAM_ROLE_RANK[actorTeamUser?.role] ?? -1;
const targetRank = TEAM_ROLE_RANK[teamUser.role] ?? -1;
if (actorRank <= targetRank) {
return unauthorized({ message: 'You do not have permission to remove this user.' });
}
}
await deleteTeamUser(teamId, userId);
return ok();
+7
View File
@@ -180,6 +180,13 @@ export const ROLES = {
teamViewOnly: 'team-view-only',
} as const;
export const TEAM_ROLE_RANK: Record<string, number> = {
[ROLES.teamViewOnly]: 0,
[ROLES.teamMember]: 1,
[ROLES.teamManager]: 2,
[ROLES.teamOwner]: 3,
} as const;
export const PERMISSIONS = {
all: 'all',
websiteCreate: 'website:create',