← Blog

Inside Zulivio: How Role-Based Access Control and Guarded Work Assignments Actually Work

Most software that claims “role-based access control” means a checkbox in a settings page that hides a button in the interface. Hide the button, and the underlying action is usually still one API call away for anyone who opens their browser’s network tab. Real access control has to be enforced where the request actually lands — the server — not where it’s merely inconvenient to reach in the UI. Here’s how Zulivio does it, and how that claim is actually tested rather than just asserted.

A strict, five-level hierarchy

Zulivio’s roles form a total order, not a flat set of permissions to mix and match:

  • Master Owner — the company owner; can do everything, including create Company Admins.
  • Company Admin — everything except creating other admins or owners.
  • Sales Head — manages managers and employees, assignments, and reports.
  • Manager — adds and removes employees below them, assigns work, sees their team’s reports.
  • Employee — their own attendance, their own assignments, the knowledge base, and tips.

In code this is EMPLOYEE < MANAGER < SALES_HEAD < COMPANY_ADMIN < MASTER_OWNER, and every privileged action checks the actor’s rank against the target’s rank before it runs — not after, and not only in the interface.

Where the enforcement actually lives

Every request that touches employee data, assignments, or reports passes through a guard on the backend before it reaches business logic — a NestJS guard plus per-service checks, sitting in front of the API, not inside a React component. Concretely, this means:

  • A manager cannot construct a request — through the UI, a modified request, or a raw API call — that promotes a peer or a higher role to their own rank or above. The check runs against the database record of both actor and target on every single call, so there’s no client state to spoof.
  • An employee cannot fetch another employee’s attendance or performance report by guessing or changing an ID in the request — the query is scoped to what that actor is authorized to see before it ever runs.
  • Every business record carries an organization ID taken from the authenticated session, never from anything the client sends — so even a multi-tenant deployment can’t leak one organization’s data into another’s view by accident.

The other half: a pipeline that rejects bad states

Access control answers “who can act.” A second, related problem is “what actions are even valid” — and that’s where Zulivio’s work-assignment pipeline comes in. An assignment moves through an explicit sequence:

ASSIGNED → IN_PROGRESS → FOLLOW_UP / BLOCKED → COMPLETED / CANCELED

The backend validates every transition against this state machine. Trying to jump straight from ASSIGNED to COMPLETED, or trying to mutate an assignment that’s already in a terminal state, is rejected — not silently accepted and not merely discouraged by the UI. Every transition that does succeed is recorded with who made it, when, and an optional outcome note, so the full history of how a piece of work moved from assigned to done is queryable later, not reconstructed from memory. See the complete pipeline and API surface in the assignments documentation.

Tested, not just asserted

Claims about authorization are cheap to make and easy to get subtly wrong — the interesting question is whether they’re verified. Zulivio’s backend ships an end-to-end test suite (apps/backend/test/app.e2e-spec.ts) that runs against a real PostgreSQL database, not a mock, and specifically targets the failure modes that matter for access control:

  • Privilege escalation attempts, blocked on both employee creation and employee editing.
  • Cross-employee report access, blocked.
  • The full attendance state machine, including rejecting a second concurrent shift or break.
  • The full assignment lifecycle, including rejecting invalid transitions and edits to a terminal assignment.

At the last run, all 26 tests pass. That number is small enough to be meaningful — it’s a targeted suite against the exact places where “role-based access control” commonly turns out to be theater, not a vanity metric. The full security model, including session handling and password hashing, is documented in Security Model, and anyone can clone the repository and run the same suite themselves — see Local Development.

Read more about why this matters once a team grows past a handful of people, or go straight to the full documentation.