Files
B2BCall-dialer/packages/auth/src/permissions.ts
Matheus 70c5586595 feat: implement authentication and RBAC
- packages/auth: Argon2id password hashing, JWT access tokens (jose),
  opaque refresh tokens with rotation, generic error messages (no
  user-enumeration via timing or message differences)
- roles/permissions/role_permissions/user_roles/sessions/audit_logs schema
  (agente.md secoes 142-150); RBAC scope PLATFORM vs TENANT
- withUserContext(): narrow RLS exception so a user can discover their own
  tenant_memberships before a tenant is chosen (login flow)
- userHasPermission()/isPlatformUser(): explicit service-layer RBAC checks
  (roles/permissions tables are not RLS-protected — documented why in
  docs/AUTHENTICATION.md)
- seed: permission catalog, 4 system roles, initial Platform Super Admin
  (password written once to FIRST_LOGIN.txt, 600, outside Git)
- automated end-to-end test: login, RBAC check, refresh rotation, logout
2026-08-28 05:58:49 -03:00

41 lines
1.4 KiB
TypeScript

import { getPrismaClient } from "@b2bcall/database";
/**
* Verifica se `userId` tem a permissão `permissionKey`, considerando roles
* PLATFORM (tenantId nulo, valem em qualquer contexto) e roles TENANT
* atribuídas especificamente em `tenantId` (agente.md secao 144: role scope
* PLATFORM ou TENANT — nunca escopo global acidental).
*
* Não usa RLS (ver comentário na migration auth_and_rbac): a checagem é
* feita explicitamente aqui, na camada de serviço confiável.
*/
export async function userHasPermission(
userId: string,
permissionKey: string,
tenantId?: string,
): Promise<boolean> {
const prisma = getPrismaClient();
const where = tenantId
? { userId, OR: [{ tenantId }, { tenantId: null }] }
: { userId, tenantId: null };
const userRoles = await prisma.userRole.findMany({
where,
include: { role: { include: { rolePermissions: { include: { permission: true } } } } },
});
return userRoles.some((userRole) =>
userRole.role.rolePermissions.some((rp) => rp.permission.key === permissionKey),
);
}
/** Atalho: usuário tem QUALQUER role com scope PLATFORM (ex.: platform_super_admin). */
export async function isPlatformUser(userId: string): Promise<boolean> {
const prisma = getPrismaClient();
const count = await prisma.userRole.count({
where: { userId, tenantId: null, role: { scope: "PLATFORM" } },
});
return count > 0;
}