Skip to main content

πŸ‘¨πŸ»β€πŸ’» User Management & Access Control β€” End-to-End Flow Guide

This document describes the full process in ABAS API V2 for:

  1. Adding a new submodule under an existing module (e.g. Accounting)
  2. Attaching permissions (create, read, update, delete) to that submodule
  3. Defining a role with those permissions
  4. Creating a user account that inherits the role and can access the feature

It is based on the current implementation in prisma/schema.prisma, administrator services, role services, and user management services.


Table of Contents​

  1. Concept Overview
  2. Entity Relationship Diagram (ERD)
  3. Data Model Layers
  4. End-to-End Process Flow
  5. Worked Example: Accounting Submodule
  6. Runtime Authorization (How API Access Is Enforced)
  7. Prerequisites Checklist
  8. Common Pitfalls
  9. Related API Endpoints

Concept Overview​

Access control in ABAS is built in layers. Each layer must exist before the next one works.

Module
└── SubModule (feature / screen)
└── SubModulePermission (which actions this feature supports)
└── RolePermission (which role gets which actions, per department)
└── UserPermission (copied to user when account is created)
LayerPurposeExample
ModuleTop-level application areaAccounting
SubModuleA screen or feature inside a moduleAccounts Payable
SubModuleActionGlobal catalog of possible actionscreate, read, update, delete
SubModulePermissionActions enabled for one submoduleAccounts Payable + read
RoleNamed job functionAccounting Clerk
RolePermissionRole + submodule + action (+ department scope)Accounting Clerk can read Accounts Payable in Accounting Dept
UserLogin account linked to an employeejane.doe@company.com
UserRoleRole assigned to a userJane β†’ Accounting Clerk
UserPermissionUser's effective permissions (snapshot from role)Jane β†’ read on Accounts Payable

Important: Creating a submodule alone does not grant anyone access. You must also create RolePermission records and assign that role when creating the user account.


Entity Relationship Diagram (ERD)​


Data Model Layers​

Layer 1 β€” Module & Submodule (Navigation Structure)​

  • Module groups related features (HR, Accounting, Administrator, etc.).
  • SubModule is the unit used for authorization. API routes use the submodule name as the CASL subject.

The Accounting module is seeded in prisma/seed.js, but it currently has no submodules. New accounting features must be added explicitly.

Layer 2 β€” Permission Inventory (What Actions Exist)​

Two tables work together:

TableRole
SubModuleActionMaster list of action strings (create, read, update, delete, …)
SubModulePermissionLinks specific actions to a specific submodule

When you assign permissions to a submodule, the system looks up actions in SubModuleAction and creates rows in SubModulePermission.

Layer 3 β€” Role & Role Permissions (Who Can Do What)​

  • A Role is a reusable template (e.g. Accounting Clerk, HR Manager).
  • RolePermission connects:
    • a role
    • a submodule
    • one or more actions
    • a department (required)
    • an optional position

Validation in RoleService.createRolePermissions() ensures every action you assign already exists in SubModulePermission for that submodule.

Layer 4 β€” User Account (Assigning Access to a Person)​

When a manager creates a user account (UserManagementService.createUserAccount()):

  1. A User record is created and linked to an existing Employee.
  2. If role_name is provided:
    • A UserRole row is created.
    • All active RolePermission rows for that role are copied into UserPermission.
  3. A password reset token and session token are created.

The user does not get permissions directly from the submodule. They get them through the role.


End-to-End Process Flow​

Phase 1 β€” Administrator: Structure the Module​

Who: Administrator / Super Administrator
Guard: @Can({ action, subject: 'system management' })

StepActionAPIService
1Create module (if missing)POST /v2/administrator/modulesModuleService.createModule()
2Create submodulePOST /v2/administrator/sub-modulesSubModuleService.createSubModule()
3Register action strings (global inventory)POST /v2/administrator/sub-modules/permissionsSubModuleService.addSubModuleAction()
4Assign actions to submodulePUT /v2/administrator/sub-modules/permissionsSubModuleService.assignSubModulePermissions()

Note: Step 3 is only needed if the action strings (e.g. create, read) are not already in SubModuleAction. The seed script pre-populates common actions.

Phase 2 β€” Administrator: Configure the Role​

StepActionAPIService
5Create role (if missing)POST /v2/administrator/rolesRoleService.createRole()
6Assign submodule permissions to rolePUT /v2/administrator/roles/role_permissionRoleService.createRolePermissions()

Each RolePermission row requires:

  • role_id
  • sub_module_id
  • action[] β€” e.g. ["create", "read", "update", "delete"]
  • department_id β€” scopes permission to a department
  • position_id (optional)

Phase 3 β€” Manager: Create the User Account​

Who: Administrator, Super Administrator, or Manager
Guard: @Can({ action: 'create', subject: 'user account' }) + security clearance level 5

StepActionAPIService
7Create user linked to employeePOST /v2/usersUserManagementService.createUserAccount()

Request body (UserDetailsDto):

{
"employee_id": "ABISC-250710-001",
"username": "jane_accounting",
"email": "jane.doe@company.com",
"password": "temporaryPassword123",
"role_name": "Accounting Clerk"
}

What happens internally:

Role (Accounting Clerk)
β†’ RolePermission rows (create/read/update/delete on Accounts Payable)
β†’ UserRole (user ↔ role)
β†’ UserPermission rows (one per RolePermission)

If the role has no RolePermission rows, user creation fails with:
"No permissions found for this role".

Phase 4 β€” Developer: Protect the Feature API​

When you build the Accounting feature controller, each route must declare the submodule subject:

@Can({ action: ACTION_READ, subject: 'accounts payable' })
@Get('accounts-payable')
getAccountsPayable() { ... }

The subject must match the SubModule name (case-insensitive). Add a constant in src/utils/constants/ability.constant.ts for consistency.


Worked Example: Accounting Submodule​

Goal: Add Accounts Payable under Accounting, give an Accounting Clerk CRUD access, and create a user for a new accounting employee.

Step 0 β€” Prerequisites​

  • Logged-in user with System Management permissions (Administrator).
  • An employee in the accounting department (no user account yet).
  • Accounting module already exists (seeded).

Step 1 β€” Get the Accounting Module ID​

GET /v2/administrator/modules?search=Accounting

Save module.id from the response.

Step 2 β€” Create the Submodule​

POST /v2/administrator/sub-modules
{
"name": "Accounts Payable",
"module_id": "<accounting-module-uuid>"
}

Save subModule_id from the response.

Step 3 β€” Assign CRUD Permissions to the Submodule​

First confirm actions exist:

GET /v2/administrator/sub-modules/permissions

If create, read, update, delete are listed, proceed:

PUT /v2/administrator/sub-modules/permissions
{
"sub_module_id": "<accounts-payable-submodule-uuid>",
"action": ["create", "read", "update", "delete"]
}

This creates rows in SubModulePermission.

Step 4 β€” Create the Role (if it does not exist)​

POST /v2/administrator/roles
{
"name": "Accounting Clerk",
"description": "Handles accounts payable entries"
}

Save role.id.

Step 5 β€” Assign Role Permissions​

PUT /v2/administrator/roles/role_permission
{
"role_id": "<accounting-clerk-role-uuid>",
"sub_module_id": "<accounts-payable-submodule-uuid>",
"department_id": "<accounting-department-uuid>",
"action": ["create", "read", "update", "delete"]
}

This creates four RolePermission rows (one per action).

Step 6 β€” Create the User Account​

POST /v2/users
{
"employee_id": "ABISC-250710-001",
"username": "jane_accounting",
"email": "jane.doe@company.com",
"password": "ChangeMe123!",
"role_name": "Accounting Clerk"
}

Result in the database:

TableRecords created
User1 (linked to employee)
UserRole1 (Accounting Clerk)
UserPermission4 (create, read, update, delete on Accounts Payable)
PasswordResetToken1
UserToken1

Step 7 β€” Verify Access​

After login, the JWT strategy loads:

User β†’ UserRole β†’ Role β†’ RolePermission β†’ SubModule

The user's JWT payload includes roles grouped as:

{
"roles": [
{
"name": "Accounting Clerk",
"sub_modules": [
{
"name": "Accounts Payable",
"actions": ["create", "read", "update", "delete"]
}
]
}
]
}

A route protected with @Can({ action: 'read', subject: 'accounts payable' }) will succeed.


Runtime Authorization (How API Access Is Enforced)​

Matching rules:

  1. action in @Can() is lowercased and must be in VALID_ACTIONS.
  2. subject is the submodule name lowercased (e.g. "Accounts Payable" β†’ "accounts payable").
  3. The user must have a RolePermission (via UserPermission) with that exact action on that submodule.

Prerequisites Checklist​

Before creating a user for a new submodule, confirm:

  • Module exists (Accounting)
  • SubModule created under that module
  • SubModulePermission rows exist for required actions (create, read, update, delete)
  • Role exists
  • RolePermission rows link role + submodule + actions + department
  • Employee exists in the correct department
  • Creator has create on user account submodule
  • Feature controller uses @Can() with matching submodule subject name
  • Constant added to ability.constant.ts for the new submodule (recommended)

Common Pitfalls​

IssueCauseFix
"Invalid action(s) for this sub module" when adding role permissionsActions not assigned to submodule in SubModulePermissionRun Step 3 (PUT sub-modules/permissions) first
"No permissions found for this role" when creating userRole has no RolePermission rowsComplete Step 5
403 You do not have permission to read accounts payableSubmodule name mismatch in @Can() subjectUse exact submodule name (case-insensitive)
User created but cannot access admin routesUser role lacks System Management permissionsExpected β€” accounting users should not get admin access unless intended
Submodule created with no permissionscreateSubModule() only creates the submodule recordPermissions must be assigned separately
Seed vs production behavior differsSeed assigns all actions to all submodules automaticallyIn production, use explicit assignSubModulePermissions()

All paths use API version v2.

Administrator β€” Modules​

MethodPathDescription
GET/administrator/modulesList modules
GET/administrator/modules/:moduleIdModule with submodules
POST/administrator/modulesCreate module
PUT/administrator/modules/:idUpdate module

Administrator β€” Submodules & Permissions​

MethodPathDescription
GET/administrator/sub-modulesList submodules
GET/administrator/sub-modules/:subModuleIdSubmodule detail
POST/administrator/sub-modulesCreate submodule
GET/administrator/sub-modules/permissionsList global action inventory
POST/administrator/sub-modules/permissionsAdd new action strings to inventory
PUT/administrator/sub-modules/permissionsAssign actions to a submodule
PUT/administrator/sub-module/permissions/:idUpdate an action in inventory

Administrator β€” Roles​

MethodPathDescription
GET/administrator/rolesList roles
GET/administrator/roles/:idRole with permissions
POST/administrator/rolesCreate role
PUT/administrator/roles/:roleIdUpdate role
PUT/administrator/roles/role_permissionAssign permissions to role
PUT/administrator/roles/role_permission/:idUpdate role permissions

User Management​

MethodPathDescription
GET/usersList user accounts
GET/users/new_employeesEmployees without accounts
GET/users/:userIdUser detail
POST/usersCreate user account (with optional role_name)
PUT/users/deactivate/:userIdDeactivate account
PUT/users/reactivate/:userIdReactivate account

Alternative Path: Permission Templates​

The codebase also supports Permission Templates (PermissionTemplateService.assignTemplateToUser()), which bundle role permissions by department. User account creation was refactored to use role_name directly, but templates remain available for bulk or standardized assignments.

For the standard flow described above, use role_name at user creation time.


Source References​

AreaFile
Database schemaprisma/schema.prisma
Seed data (modules, actions, permissions)prisma/seed.js
Create submodulesrc/modules/administrator/sub_module/sub_module.service.ts
Assign submodule permissionssrc/modules/administrator/sub_module/sub_module.service.ts β†’ assignSubModulePermissions()
Create role permissionssrc/modules/administrator/role/role.service.ts β†’ createRolePermissions()
Create user accountsrc/modules/manager/user_management/user_management.service.ts β†’ createUserAccount()
JWT / role loadingsrc/middleware/jwt/jwt.strategy.ts
Permission guard (CASL)src/middleware/guards/permission.guard.ts
Action / submodule constantssrc/utils/constants/ability.constant.ts