RBAC

Controle de acesso baseado em roles com CASL.

Visao geral

O sistema de RBAC (Role-Based Access Control) usa a biblioteca CASL para definir permissoes granulares por role. O PlazerCLI gera 3 roles padrao e toda a infraestrutura de guards e abilities.

Roles padrao

RoleValorPermissoes
Admin admin Gerencia tudo (manage all). Acesso total ao sistema.
User user Gerencia proprio perfil. Cria/edita/deleta seus proprios recursos. Leitura da organizacao.
Owner owner Gerencia a organizacao e todos os recursos dentro dela. Nao pode deletar outros owners/admins.
export enum Role {
  ADMIN = 'admin',
  USER = 'user',
  OWNER = 'owner',
}

CASL Abilities

A AbilitiesFactory (NestJS) ou defineAbilitiesFor() (Express/Fastify) centraliza toda a logica de permissoes:

// Exemplo de abilities para o role OWNER
case Role.OWNER:
  can('manage', 'Organization', { id: user.organizationId });
  can('read', 'Organization');
  can('manage', 'Post', { organizationId: user.organizationId });
  can('manage', 'User', { organizationId: user.organizationId });
  cannot('delete', 'User', { role: { in: [Role.ADMIN, Role.OWNER] } });
  can('manage', 'User', { id: user.id });
  break;

// Exemplo para USER
case Role.USER:
  can('read', 'Organization', { id: user.organizationId });
  can('read', 'User', { id: user.id });
  can('update', 'User', { id: user.id });
  can('create', 'Post');
  can('read', 'Post', { organizationId: user.organizationId });
  can('update', 'Post', { authorId: user.id });
  can('delete', 'Post', { authorId: user.id });
  break;

Se Prisma estiver habilitado, o CASL usa @casl/prisma para queries type-safe. Caso contrario, usa @casl/ability com MongoDB-style.

Guards por framework

NestJS — RolesGuard global + decorators

// O guard e registrado globalmente via APP_GUARD
@Global()
@Module({
  providers: [
    AbilitiesFactory,
    { provide: APP_GUARD, useClass: RolesGuard },
  ],
})
export class RbacModule {}

// Uso em controllers:
@Post('admin-only')
@Roles(Role.ADMIN)
createAdminResource() { ... }

@Delete(':id')
@Roles(Role.ADMIN, Role.OWNER)
deleteResource() { ... }

Express — Middleware requireRole()

import { requireRole } from './rbac/rbac.middleware.js';
import { Role } from './rbac/roles.js';

router.delete('/users/:id', requireRole(Role.ADMIN, Role.OWNER), handler);
router.post('/admin-only', requireRole(Role.ADMIN), handler);

Fastify — preHandler hook requireRole()

import { requireRole } from './rbac/rbac.hook.js';
import { Role } from './rbac/roles.js';

app.delete('/users/:id', {
  preHandler: [authHook, requireRole(Role.ADMIN, Role.OWNER)]
}, handler);

Arquivos gerados

apps/api/src/rbac/
├── roles.enum.ts / roles.ts       # Enum Role (ADMIN, USER, OWNER)
├── roles.decorator.ts             # @Roles() decorator (NestJS)
├── roles.guard.ts                 # Guard global (NestJS)
├── rbac.middleware.ts             # requireRole() (Express)
├── rbac.hook.ts                   # requireRole() (Fastify)
├── abilities.factory.ts          # AbilitiesFactory (NestJS)
├── abilities.ts                   # defineAbilitiesFor() (Express/Fastify)
└── rbac.module.ts                 # Modulo global (NestJS)