Files
eden/apps/api/src/auth/role-weight.ts
Matheus (Handix) eeb6977e36 fix: assertCanAssignRole/assertCanActOnUser validam existência do papel mesmo com bypass do super_admin
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 <noreply@anthropic.com>
2026-09-03 09:52:07 -03:00

43 lines
1.9 KiB
TypeScript

import { BadRequestException, ForbiddenException } from "@nestjs/common";
import { rolesRepo } from "@eden/database";
import { SUPER_ADMIN_ROLE } from "./permissions.js";
/**
* Role weight (eden.md §1.6, ADR-0004): an actor can never assign a role
* 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 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<number> {
const role = await rolesRepo.getRoleByKey(roleKey);
if (!role) throw new BadRequestException(`Papel '${roleKey}' não existe`);
return role.weight;
}
export async function assertCanAssignRole(actorRoleKey: string, targetRoleKey: string): Promise<void> {
const targetWeight = await getWeight(targetRoleKey);
if (actorRoleKey === SUPER_ADMIN_ROLE) return;
const actorWeight = await getWeight(actorRoleKey);
if (targetWeight > actorWeight) {
throw new ForbiddenException("Você não pode atribuir um papel com peso maior que o seu.");
}
}
export async function assertCanActOnUser(
actorRoleKey: string,
targetUserRoleKey: string,
): Promise<void> {
const targetWeight = await getWeight(targetUserRoleKey);
if (actorRoleKey === SUPER_ADMIN_ROLE) return;
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.");
}
}