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
This commit is contained in:
@@ -41,3 +41,22 @@ export async function withTenantContext<T>(
|
||||
return fn(tx);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Like withTenantContext, but sets `app.current_user_id` instead of a
|
||||
* tenant. Used only during login/tenant-resolution, when the caller needs to
|
||||
* see a user's OWN tenant_memberships before any tenant has been chosen. The
|
||||
* RLS policy on tenant_memberships allows this narrow self-lookup without
|
||||
* exposing other users' memberships or other tenants' data (see
|
||||
* docs/AUTHENTICATION.md).
|
||||
*/
|
||||
export async function withUserContext<T>(
|
||||
client: PrismaClient,
|
||||
userId: string,
|
||||
fn: (tx: Prisma.TransactionClient) => Promise<T>,
|
||||
): Promise<T> {
|
||||
return client.$transaction(async (tx) => {
|
||||
await tx.$executeRaw`SELECT set_config('app.current_user_id', ${userId}, true)`;
|
||||
return fn(tx);
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user