Vivek Jalondhara. All writing

Security

RBAC the Right Way: Module, Entity, and Field-Level Access

By Vivek Jalondhara · 1 min read
Published Oct 2025 · Updated

Real access control isn't a single role check — it's layered, and it belongs in one place.

Coarse role checks fail the moment a product grows. On the CRM I built access control across three levels: modules (can you see this area), entities (can you touch this record), and fields (can you read or edit this column).

The key is centralizing the policy. Scattering permission checks across controllers guarantees gaps; a single authorization layer that every request flows through is auditable and hard to bypass.

Visibility rules then power collaboration features — notes, documents, tagging, and search — so users only ever see what they're entitled to, without each feature reinventing the logic.

Check the record boundary as well as the role

A CRM role may grant edit permission without granting access to every tenant's records. The policy must combine those facts before returning or updating data.

Illustrative example · TypeScript · Simplified for explanation

function canEditRecord(user: User, record: RecordData) {
  return user.tenantId === record.tenantId
    && user.permissions.includes("records:edit");
}

// Enforce on the server before reading protected fields or writing changes.
How the example fits together
  1. Authenticated user
  2. Tenant boundary
  3. Record & field policy
  4. Allowed operation

Conceptual flow for the example above.

Tradeoff to keep in mind

This example shows only the tenant and permission checks. Real policies may also depend on ownership, record sharing, and field restrictions.

Related project: SmackDab CRM →

Related posts