feat: add authentication and RBAC

- packages/database: schema Prisma (users/sessions/roles/permissions/
  user_roles/role_permissions/audit_logs/password_reset_tokens), migration
  inicial e seed (permissoes+perfis+bootstrap super_admin com senha
  aleatoria em FIRST_LOGIN.txt). Decisao de ORM (Prisma) documentada em
  docs/ARCHITECTURE.md
- packages/shared: catalogo de permissoes (fonte unica usada por seed e API)
- apps/api: NestJS 11 + Fastify
  - autenticacao: Argon2id, access JWT + refresh token opaco com rotacao,
    cookies HttpOnly/SameSite=Lax, change/forgot/reset password
  - rate limiting progressivo de login via Redis (bloqueio crescente por IP)
  - RBAC reforcado no backend (PermissionsGuard), protecao contra
    auto-elevacao de privilegio
  - auditoria (audit_logs) nas acoes sensiveis, com redacao de segredos
  - health checks reais (postgres+redis), swagger desabilitavel, logs
    estruturados JSON com request_id de correlacao, filtro global de
    excecoes sem vazar erro cru
- infrastructure/docker/api.Dockerfile: build multi-stage do monorepo pnpm
- docker-compose.yml: servico api na rede interna, sem porta publicada

Testado via containers reais: login, /me, refresh, change-password,
rate limit (7 tentativas -> 429), RBAC (nega/permite), bloqueio de
auto-elevacao (403), audit log populado, health checks, lint e testes
unitarios passando.
This commit is contained in:
2026-08-27 12:23:02 -03:00
parent eee6d7aece
commit a2898fa566
69 changed files with 10161 additions and 7 deletions

View File

@@ -0,0 +1,65 @@
import type { IncomingMessage } from 'node:http';
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { APP_FILTER, APP_GUARD } from '@nestjs/core';
import { ThrottlerModule, ThrottlerGuard } from '@nestjs/throttler';
import { LoggerModule } from 'nestjs-pino';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { PrismaModule } from './prisma/prisma.module';
import { RedisModule } from './redis/redis.module';
import { AuditModule } from './audit/audit.module';
import { AuthModule } from './auth/auth.module';
import { UsersModule } from './users/users.module';
import { RolesModule } from './roles/roles.module';
import { HealthModule } from './health/health.module';
import { AuthGuard } from './common/guards/auth.guard';
import { PermissionsGuard } from './common/guards/permissions.guard';
import { GlobalExceptionFilter } from './common/filters/global-exception.filter';
@Module({
imports: [
ConfigModule.forRoot({ isGlobal: true }),
// Logs estruturados JSON com request_id de correlação (agente.md seções
// 60/61). Nunca loga segredos: senha, tokens, credenciais AMI/ARI/SIP.
LoggerModule.forRoot({
pinoHttp: {
genReqId: (req) => req.id,
redact: {
paths: [
'req.headers.authorization',
'req.headers.cookie',
'req.body.password',
'req.body.currentPassword',
'req.body.newPassword',
'res.headers["set-cookie"]',
],
censor: '[REDACTED]',
},
customProps: (req: IncomingMessage & { id: string }) => ({
service: 'b2bcall-api',
requestId: req.id,
}),
},
}),
// Rate limit global genérico (proteção geral contra abuso). O login tem
// sua própria proteção progressiva mais rígida (LoginThrottleService).
ThrottlerModule.forRoot([{ ttl: 60_000, limit: 120 }]),
PrismaModule,
RedisModule,
AuditModule,
AuthModule,
UsersModule,
RolesModule,
HealthModule,
],
controllers: [AppController],
providers: [
AppService,
{ provide: APP_GUARD, useClass: ThrottlerGuard },
{ provide: APP_GUARD, useClass: AuthGuard },
{ provide: APP_GUARD, useClass: PermissionsGuard },
{ provide: APP_FILTER, useClass: GlobalExceptionFilter },
],
})
export class AppModule {}