From eeb6977e36c0a0efa780b910a0074e5a07016350 Mon Sep 17 00:00:00 2001 From: "Matheus (Handix)" Date: Thu, 3 Sep 2026 09:52:07 -0300 Subject: [PATCH] =?UTF-8?q?fix:=20assertCanAssignRole/assertCanActOnUser?= =?UTF-8?q?=20validam=20exist=C3=AAncia=20do=20papel=20mesmo=20com=20bypas?= =?UTF-8?q?s=20do=20super=5Fadmin?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bug encontrado em teste manual: convidar usuário com role_key inexistente, sendo o ator super_admin, pulava a checagem de peso inteira (incluindo a validação de existência) e caía direto na constraint FK do banco, virando um 500 não tratado em vez de um 400 claro. Agora a existência do papel é sempre validada antes do bypass de peso. Co-Authored-By: Claude Sonnet 5 --- apps/api/src/auth/role-weight.ts | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/apps/api/src/auth/role-weight.ts b/apps/api/src/auth/role-weight.ts index 90ff841..839312c 100644 --- a/apps/api/src/auth/role-weight.ts +++ b/apps/api/src/auth/role-weight.ts @@ -1,4 +1,4 @@ -import { ForbiddenException } from "@nestjs/common"; +import { BadRequestException, ForbiddenException } from "@nestjs/common"; import { rolesRepo } from "@eden/database"; import { SUPER_ADMIN_ROLE } from "./permissions.js"; @@ -7,20 +7,23 @@ import { SUPER_ADMIN_ROLE } from "./permissions.js"; * heavier than their own, nor act on a user whose *current* role is * heavier than the actor's — orthogonal to the resource/action/scope * permission check. super_admin (weight 100, hardcoded ceiling) always - * passes both checks. + * passes the *weight comparison* — but role existence is still validated + * even for super_admin, otherwise an invalid role key would sail past + * this check and only fail later as an unhandled FK-constraint 500 from + * the database (an earlier bug caught by manual testing: this function + * must be the single place that turns "role doesn't exist" into a clean + * 400, not the DB constraint). */ async function getWeight(roleKey: string): Promise { const role = await rolesRepo.getRoleByKey(roleKey); - if (!role) throw new ForbiddenException("Papel inválido"); + if (!role) throw new BadRequestException(`Papel '${roleKey}' não existe`); return role.weight; } export async function assertCanAssignRole(actorRoleKey: string, targetRoleKey: string): Promise { + const targetWeight = await getWeight(targetRoleKey); if (actorRoleKey === SUPER_ADMIN_ROLE) return; - const [actorWeight, targetWeight] = await Promise.all([ - getWeight(actorRoleKey), - getWeight(targetRoleKey), - ]); + const actorWeight = await getWeight(actorRoleKey); if (targetWeight > actorWeight) { throw new ForbiddenException("Você não pode atribuir um papel com peso maior que o seu."); } @@ -30,11 +33,9 @@ export async function assertCanActOnUser( actorRoleKey: string, targetUserRoleKey: string, ): Promise { + const targetWeight = await getWeight(targetUserRoleKey); if (actorRoleKey === SUPER_ADMIN_ROLE) return; - const [actorWeight, targetWeight] = await Promise.all([ - getWeight(actorRoleKey), - getWeight(targetUserRoleKey), - ]); + const actorWeight = await getWeight(actorRoleKey); if (targetWeight > actorWeight) { throw new ForbiddenException("Você não pode alterar um usuário com papel de peso maior que o seu."); }