- Auth (apps/api/src/auth/): Argon2id com parâmetros versionados
(ADR-0005), access token JWT de 15min, refresh token opaco de 256 bits
com rotação e detecção de replay (reuso de token já rotacionado revoga
toda a cadeia de sessões do usuário). Endpoints /api/v1/auth/
{login,refresh,logout,me}.
- Permissões resource+action+scope (ADR-0004) via PermissionGuard,
resolvidas do banco a cada request (nunca cacheadas no JWT) — mudança
de papel tem efeito imediato. super_admin com bypass hardcoded,
espelhando a trava já existente no banco.
- Companies (migration + CRUD): leitura aberta a qualquer autenticado,
escrita restrita a companies:manage; validação de CNPJ via
packages/domain-shared (dígito verificador).
- SeedService: cria o primeiro super_admin a partir de
EDEN_SUPERADMIN_EMAIL/PASSWORD só quando `users` está vazia.
- OpenAPI/Swagger em /api/docs; ValidationPipe global com
whitelist+forbidNonWhitelisted (proteção contra mass-assignment).
- packages/database: repositórios (roles/users/sessions/audit/companies)
reutilizáveis por api e worker.
Validado manualmente contra a stack real (não só typecheck): login com
senha errada → 401 genérico; refresh rotaciona e invalida o token
anterior; reuso do token antigo → replay detectado, cadeia revogada;
logout idempotente; audit log com a sequência completa de eventos;
CNPJ inválido rejeitado; mass-assignment rejeitado.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
51 lines
1.9 KiB
TypeScript
51 lines
1.9 KiB
TypeScript
import { Inject, Injectable, Logger, OnModuleInit } from "@nestjs/common";
|
|
import { usersRepo } from "@eden/database";
|
|
import type { EdenEnv } from "@eden/config";
|
|
import { EDEN_ENV } from "../config/config.constants.js";
|
|
import { PasswordService } from "../auth/password.service.js";
|
|
import { SUPER_ADMIN_ROLE } from "../auth/permissions.js";
|
|
|
|
/**
|
|
* Mirrors the legacy's seedAdmin() (eden.md §5.7): runs once at boot,
|
|
* only acts if `users` is empty, creates the first super_admin from env
|
|
* vars — never hardcoded, never auto-generated-and-logged. If the env
|
|
* vars aren't set, boot proceeds without seeding (useful for CI/tests
|
|
* that don't need a real login) rather than crashing.
|
|
*/
|
|
@Injectable()
|
|
export class SeedService implements OnModuleInit {
|
|
private readonly logger = new Logger(SeedService.name);
|
|
|
|
constructor(
|
|
@Inject(EDEN_ENV) private readonly env: EdenEnv,
|
|
private readonly passwords: PasswordService,
|
|
) {}
|
|
|
|
async onModuleInit() {
|
|
const existing = await usersRepo.countUsers();
|
|
if (existing > 0) return;
|
|
|
|
if (!this.env.EDEN_SUPERADMIN_EMAIL || !this.env.EDEN_SUPERADMIN_PASSWORD) {
|
|
this.logger.warn(
|
|
"users está vazia e EDEN_SUPERADMIN_EMAIL/EDEN_SUPERADMIN_PASSWORD não estão definidos — pulando seed do super_admin.",
|
|
);
|
|
return;
|
|
}
|
|
|
|
const { hash, algoVersion } = await this.passwords.hash(this.env.EDEN_SUPERADMIN_PASSWORD);
|
|
const user = await usersRepo.createUser({
|
|
email: this.env.EDEN_SUPERADMIN_EMAIL,
|
|
passwordHash: hash,
|
|
passwordAlgoVersion: algoVersion,
|
|
roleKey: SUPER_ADMIN_ROLE,
|
|
fullName: "Super Administrador",
|
|
});
|
|
|
|
await usersRepo.grantUserApplication(user.id, "core");
|
|
await usersRepo.grantUserApplication(user.id, "reseller");
|
|
await usersRepo.grantUserApplication(user.id, "subscriber");
|
|
|
|
this.logger.log(`super_admin inicial criado (${user.email}) a partir de EDEN_SUPERADMIN_EMAIL.`);
|
|
}
|
|
}
|