Roles & Permissions

A complete reference for CareNova's four user roles, the full permissions matrix, how permissions are enforced, and how to customize them.

Written By Dev010

Last updated 19 days ago

CareNova uses a Role-Based Access Control (RBAC) system with four roles and 50+ granular permission keys. Permissions are enforced at two levels β€” the dashboard navigation and every server action β€” so access restrictions cannot be bypassed by manipulating the UI.

The Four Roles

Role

Typical User

System Access

admin

Clinic owner, IT manager

Full access to all modules and settings

doctor

Physician, dentist, specialist

Clinical modules β€” patients, records, prescriptions

receptionist

Front desk staff

Scheduling, patient registration, billing

nurse

Nurse, clinical assistant

Clinical support β€” records, vitals, inventory

Every user account has exactly one role stored in users.role. The role is assigned when an account is approved by an admin.

What Each Role Can Access

Admin

Full unrestricted access to every module and every action in the system. Admin bypasses all permission checks β€” hasPermission() always returns true for admin regardless of what is configured in the permissions matrix.

Admin-exclusive modules:

  • Staff Management

  • Permissions configuration

  • Landing Settings

  • Pending Approval

  • Full financial management (invoices, payments, expenses)

  • All system settings

Doctor

Focused on clinical workflows. Doctors see patients, manage their own appointments, write prescriptions, create medical records, and review test reports.

Default doctor access:

  • Analytics dashboard (view only)

  • Patients β€” view, create, edit

  • Appointments β€” view, create, edit, reschedule, cancel

  • Medical Records β€” view, create, edit

  • Prescriptions β€” view, create, edit

  • Test Reports β€” view, create, edit

  • Odontograms β€” view, create, edit (dental clinics only)

  • Services β€” view only

  • Departments β€” view only

  • Blog β€” view and manage posts

Receptionist

Focused on front desk operations. Receptionists book appointments, manage patient profiles, and handle billing.

Default receptionist access:

  • Analytics dashboard (view only)

  • Patients β€” view, create, edit

  • Appointments β€” view, create, edit, reschedule, cancel, assign

  • Invoices β€” view, create, edit

  • Payments β€” view, create, edit

  • Services β€” view only

  • Departments β€” view only

  • Staff β€” view only

Nurse

Focused on clinical support. Nurses record vitals, assist with medical records, manage inventory, and support clinical workflows.

Default nurse access:

  • Patients β€” view, edit

  • Appointments β€” view, edit, reschedule

  • Medical Records β€” view, create

  • Prescriptions β€” view only

  • Test Reports β€” view only

  • Inventory β€” view, create, edit, delete

  • Services β€” view only

  • Departments β€” view only

Permission Keys

Permissions are controlled by individual keys in the format module.action. There are 50+ keys across all modules:

Module

Available Actions

analytics

dashboard, export, reports

appointments

view, create, edit, delete, assign, reschedule, cancel, export

patients

view, create, edit, delete, export

billing

view, create, edit, delete, export

medical_records

view, create, edit, delete

prescriptions

view, create, edit, delete

test_reports

view, create, edit, delete

inventory

view, create, edit, delete

staff

view, create, edit, delete

services

view, create, edit, delete

departments

view, create, edit, delete

settings

view, edit

odontogram

view, create, edit

Full key examples:

appointments.view
appointments.create
appointments.delete
patients.export
billing.view
medical_records.create
odontogram.edit
settings.edit

Permissions Matrix

The table below shows the default permissions for each role across all modules:

Permission

Admin

Doctor

Receptionist

Nurse

analytics.dashboard

βœ…

βœ…

βœ…

❌

analytics.export

βœ…

❌

❌

❌

appointments.view

βœ…

βœ…

βœ…

βœ…

appointments.create

βœ…

βœ…

βœ…

❌

appointments.edit

βœ…

βœ…

βœ…

βœ…

appointments.delete

βœ…

❌

❌

❌

appointments.reschedule

βœ…

βœ…

βœ…

βœ…

appointments.cancel

βœ…

βœ…

βœ…

❌

appointments.assign

βœ…

❌

βœ…

❌

appointments.export

βœ…

❌

❌

❌

patients.view

βœ…

βœ…

βœ…

βœ…

patients.create

βœ…

βœ…

βœ…

❌

patients.edit

βœ…

βœ…

βœ…

βœ…

patients.delete

βœ…

❌

❌

❌

patients.export

βœ…

❌

❌

❌

billing.view

βœ…

❌

βœ…

❌

billing.create

βœ…

❌

βœ…

❌

billing.edit

βœ…

❌

βœ…

❌

billing.delete

βœ…

❌

❌

❌

medical_records.view

βœ…

βœ…

❌

βœ…

medical_records.create

βœ…

βœ…

❌

βœ…

medical_records.edit

βœ…

βœ…

❌

❌

medical_records.delete

βœ…

❌

❌

❌

prescriptions.view

βœ…

βœ…

❌

βœ…

prescriptions.create

βœ…

βœ…

❌

❌

prescriptions.edit

βœ…

βœ…

❌

❌

prescriptions.delete

βœ…

❌

❌

❌

test_reports.view

βœ…

βœ…

❌

βœ…

test_reports.create

βœ…

βœ…

❌

❌

test_reports.edit

βœ…

βœ…

❌

❌

test_reports.delete

βœ…

❌

❌

❌

inventory.view

βœ…

❌

❌

βœ…

inventory.create

βœ…

❌

❌

βœ…

inventory.edit

βœ…

❌

❌

βœ…

inventory.delete

βœ…

❌

❌

βœ…

staff.view

βœ…

❌

βœ…

❌

staff.create

βœ…

❌

❌

❌

staff.edit

βœ…

❌

❌

❌

staff.delete

βœ…

❌

❌

❌

services.view

βœ…

βœ…

βœ…

βœ…

services.create

βœ…

❌

❌

❌

services.edit

βœ…

❌

❌

❌

departments.view

βœ…

βœ…

βœ…

βœ…

departments.create

βœ…

❌

❌

❌

departments.edit

βœ…

❌

❌

❌

settings.view

βœ…

❌

❌

❌

settings.edit

βœ…

❌

❌

❌

odontogram.view

βœ…

βœ…

❌

❌

odontogram.create

βœ…

βœ…

❌

❌

odontogram.edit

βœ…

βœ…

❌

❌

These are the default permissions. All of these can be customized from the Permissions page in the dashboard.

Customizing Permissions

The permissions matrix is fully editable from the dashboard β€” no code changes required.

Navigate to Dashboard β†’ Permissions (Admin only).

The Permissions page shows a toggle matrix for every permission key across Doctor, Receptionist, and Nurse roles. Toggle any permission on or off and click Save Permissions.

Changes take effect immediately β€” the next request from affected users will reflect the updated permissions.

Admin permissions cannot be modified from the UI. Admin always has full access to everything β€” this is hardcoded and cannot be changed.

Seeding Default Permissions

If the role_permissions table is empty, run the permissions seed script to populate it with the defaults shown in the matrix above:

npm run seed:permissions

This is safe to run multiple times β€” it uses upsert logic and will not duplicate existing entries.

How Permissions Are Enforced

Permissions are enforced at two independent levels. Both must pass for an action to succeed.

Level 1 β€” Navigation (UI)

The sidebar navigation filters items based on the user's role and permissions. If a user does not have module.view permission, the module does not appear in the sidebar and the route is effectively hidden.

This is handled in nav-main.tsx β€” each nav item has a permissionKey property that is checked against the user's loaded permissions before rendering.

Level 2 β€” Server Actions (Backend)

Every mutation β€” create, update, delete β€” calls requirePermission() or requireRole() at the start of the server action before any database operation runs:

// Example from appointment-actions.ts
export async function createAppointment(data) {
  const user = await getCachedCurrentUser();
  await requirePermission("appointments.create");
  // ... rest of action
}

If the permission check fails, a PermissionDeniedError is thrown and the action returns an error β€” no data is written.

Hiding a nav item in the UI is not sufficient security on its own. CareNova always enforces permissions server-side as well. A user cannot bypass access control by navigating directly to a URL or calling a server action directly.

Where Permissions Are Stored

Permissions are stored in the role_permissions table:

Column

Description

role

Role name (doctor, receptionist, nurse)

permission_key

Permission key (appointments.create)

granted

true or false

Each (role, permission_key) pair is unique. When hasPermission(role, key) is called, it queries this table for the matching row and returns the granted value.

Admin is never stored in this table β€” the hasPermission function returns true for admin unconditionally.

Troubleshooting

A module is not visible in the sidebar for a role that should have access:

  • Go to Dashboard β†’ Permissions and confirm the module.view permission is toggled on for that role

  • Check that npm run seed:permissions has been run β€” an empty role_permissions table means no permissions are granted

  • Hard refresh the browser after making permission changes

A user can see a module but cannot create or edit records:

  • The module.view permission is granted but module.create or module.edit is not

  • Go to Dashboard β†’ Permissions and enable the specific action permissions needed

Permission changes not taking effect:

  • Permissions are loaded per-request β€” the user does not need to log out

  • Hard refresh the browser (Cmd+Shift+R)

  • Confirm the save was successful β€” check for the success toast notification

role_permissions table is empty:

  • Run npm run seed:permissions to populate with default permissions

  • Or go to Dashboard β†’ Permissions and configure manually

Next Step

Continue to Audit Logs to learn how CareNova records authentication events and how admins can review system activity.