fix: Rechteausweitung ueber Rollen-Rechtezuweisung geschlossen
RolesService.create/update uebernahm eine explizit uebergebene permissionKeys-Liste ungeprueft. Ein Nutzer mit nur roles:update konnte damit JEDER Rolle - auch der eigenen, ohne users:assign-role - beliebige weitere Rechte (bis hin zu allen 92 Katalog-Eintraegen) hinzufuegen, da das Bearbeiten der eigenen Rollen-Rechte nicht ueber User.roleId laeuft und daher vom bereits gefixten users:assign-role-Check nicht erfasst wurde. Neue Regel (Standard-RBAC-Prinzip "keine Rechte ueber das eigene Rechte-Set hinaus vergeben"): create/update pruefen jetzt, dass der aufrufende Nutzer jedes zu vergebende Recht selbst bereits besitzt, bevor die Rolle angelegt/geaendert wird. roles.service.ts hatte bislang keine Unit-Tests; neue Spec-Datei deckt den Fix sowie die uebrigen Methoden (create/update/deactivate/ reactivate) ab. Co-Authored-By: Claude Sonnet 5 <[email protected]>
This commit is contained in:
1 parent
8b8616ce03
commit
f57cffe6a8
2 files changed
+317
No files matched your search
@@ -81,6 +81,31 @@ export class RolesService {
|
||||
return this.toSummary(await this.findById(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* Nobody can grant a role a permission they do not hold themselves - a
|
||||
* caller with only roles:update could otherwise turn any role (including
|
||||
* one they belong to) into a shadow-admin by adding every catalog key, no
|
||||
* users:assign-role needed at all since editing a role's own grants
|
||||
* doesn't touch User.roleId. This is the standard "can't escalate beyond
|
||||
* what you have" RBAC rule.
|
||||
*/
|
||||
private async assertCanGrantPermissions(
|
||||
permissionKeys: string[],
|
||||
actingUserId?: string,
|
||||
): Promise<void> {
|
||||
if (!actingUserId) {
|
||||
return;
|
||||
}
|
||||
const callerPermissions =
|
||||
await this.permissionResolver.getPermissions(actingUserId);
|
||||
const missing = permissionKeys.filter((key) => !callerPermissions.has(key));
|
||||
if (missing.length > 0) {
|
||||
throw new ForbiddenException(
|
||||
`Cannot grant permissions you do not hold yourself: ${missing.join(', ')}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async create(
|
||||
dto: CreateRoleDto,
|
||||
actingUserId?: string,
|
||||
@@ -91,6 +116,7 @@ export class RolesService {
|
||||
if (existing) {
|
||||
throw new ConflictException('Role key already in use');
|
||||
}
|
||||
await this.assertCanGrantPermissions(dto.permissionKeys, actingUserId);
|
||||
const role = await this.prisma.$transaction(async (tx) => {
|
||||
const created = await tx.role.create({
|
||||
data: {
|
||||
@@ -126,6 +152,9 @@ export class RolesService {
|
||||
if (before.isSystem) {
|
||||
throw new ForbiddenException('System roles cannot be edited');
|
||||
}
|
||||
if (dto.permissionKeys) {
|
||||
await this.assertCanGrantPermissions(dto.permissionKeys, actingUserId);
|
||||
}
|
||||
const role = await this.prisma.$transaction(async (tx) => {
|
||||
await tx.role.update({
|
||||
where: { id },
|
||||
|
||||
Reference in new issue
Block a user