- 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
41 lines
1.4 KiB
TypeScript
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;
|
|
}
|