Files
alla-tms/docs/permission-display-audit.md
2026-07-16 09:53:14 +07:00

37 KiB

Permission and Display Audit Report

Audit date: 2026-07-14

Scope: Review-only audit of UI display, navigation, route guards, API authorization, role helpers, permission definitions, and current database permission templates. No runtime source code or database data was changed.

1. Executive Summary

Overall assessment: the system is partially aligned with the permission model. Content workflow modules such as announcements and online lessons use effectivePermissions and shared workflow helpers consistently for most row actions. However, several HRD operational modules still authorize by businessRole, membership.role, or broad role helper functions instead of the explicit permission keys that already exist.

Finding count by severity:

Severity Count
Critical 0
High 6
Medium 5
Low 3

Highest risk modules:

Module Risk
Employee import and master review Explicit permissions exist but route/page/API guards use HRD role only.
Training records review and reports Organization-wide access and review are derived from membership role instead of granular permissions.
Training matrix and courses Some routes use role guards while navigation suggests permission-aware access.
Navigation Sidebar uses role/businessRole for many items, while pages and APIs sometimes use different checks.

Data exposure observed:

  • Reports can expose organization-wide data when membership.role is admin, even if the active permission template only grants reports:self_read.
  • Employee directory and master-data review areas are visible to HRD-role users even when the template does not include employee_import:run or employee_master_review:approve.

Backend authorization summary:

  • Announcements, online lessons, training policies, training record import, and permission management have meaningful permission checks.
  • Courses, employee directory, employee import, master review, training matrix, training record review, reports, audit logs, and users still contain role-based or membership-role authorization seams.

2. Roles and Permissions Discovered

Role sources:

Source Evidence Behavior
systemRole src/lib/auth/roles.ts super_admin is treated as IT-like and HRD-capable in some helpers.
membership.role src/db/schema.ts, src/lib/auth/roles.ts owner/admin imply HRD; member implies EMPLOYEE.
businessRole src/lib/auth/roles.ts Derived from system and membership role, not from permission template.
effectivePermissions src/auth.ts, src/lib/auth/authorization.server.ts Resolved from active permission template, overrides, or fallback membership/default permissions.

Permission definition source:

  • src/lib/auth/permissions.ts defines all permission keys and defaults.
  • src/features/permission-management/server/permission-management-catalog.ts defines catalog metadata.
  • permission_templates, permission_template_permissions, user_permission_template_assignments, and user_permission_overrides provide database-configured permissions.

Important discovered permission groups:

Group Examples
Employee directory/import employee_directory:read, employee_directory:write, employee_import:run, employee_master_review:approve
Courses and policy courses:read, courses:write, training_policy:read, training_policy:write
Training matrix training_matrix:read, training_matrix:write
Training records training_record:self_read, training_record:read_all, training_record:write_all, training_record:review, training_record:approve, training_record:reject
Announcements announcement:create, announcement:edit_draft, announcement:approve, announcement:revision, announcement:publish
Online lessons online_lesson:create, online_lesson:edit_draft, online_lesson:approve, online_lesson:revision, online_lesson:publish
Reports reports:self_read, reports:organization_read, reports:export, reports:export_excel, reports:export_pdf
Permission management permission_template:*, user_permission:*

Database template observations:

Template Count Notable permissions
employee 8 Self training record, public announcements, public online lessons, self reports, notifications.
hrd-staff id 2 42 Includes training_record:approve, training_record:review, reports:organization_read, content publishing but excludes employee_import:run and employee_master_review:approve.
hrd-staff id 5/id 8 30 Includes HRD write/read basics but excludes review/approve/report-org and employee import/master-review permissions.
manager 56 Includes training review, reports export, online lesson approve/revision/reject, announcement approve/revision/reject.
IT 78 Broad system, permission management, audit, import, master review, and all content workflow permissions.

3. Permission Matrix

Module UI/Page Entry Primary Page Guard API Guard Permission Alignment
Overview requireOrg nav Employee dashboard access Server report/overview scope Medium: mixed role/permission usage.
Users IT nav User management access user management helper Medium: helper is IT/business-role based, not users:*.
Employee directory HRD/IT nav HRD dashboard access requireHRD() High mismatch: ignores employee_directory:*.
Employee import hidden/direct route HRD dashboard access requireHRD() High mismatch: ignores employee_import:run.
Master review hidden/direct route HRD dashboard access requireHRD() High mismatch: ignores employee_master_review:approve.
Courses HRD/IT nav HRD dashboard access requireHRD() High mismatch: ignores courses:read/write.
Training policy HRD/IT nav training_policy:read training_policy:read/write Good alignment.
Training matrix HRD/IT nav training_matrix:read membership.role via canManageTrainingMatrix() High mismatch between page guard and API.
Training records list all org users Employee dashboard access membership role and data helper Medium: permissions exist but access logic is role based.
Training records import conditional button write_all + approve write_all + approve Good alignment.
Pending review HRD nav HRD dashboard access training record list query Medium mismatch: page/nav role based, review permission not required.
Reports HRD nav, direct employee page Employee dashboard access report scope by role High mismatch: ignores reports:organization_read/export.
Announcements HRD/IT nav Employee dashboard access plus action permissions content workflow permissions Mostly aligned.
Online lessons all org users Employee dashboard access plus action permissions content workflow permissions Mostly aligned.
Audit logs super admin nav IT dashboard access requireIT() Medium mismatch: permission keys exist but not used.
Permission management super admin nav super admin dashboard access assumed super admin route guards Low: intentionally super admin-only, not permission-template based.

4. Route and Navigation Audit

ID Route Menu Permission Route Guard Direct URL Result Status
NAV-001 /dashboard/pending-review businessRole: HRD requireHRDDashboardAccess() HRD-role users can enter without checking training_record:review Mismatch
NAV-002 /dashboard/reports businessRole: HRD requireEmployeeDashboardAccess() Employees can enter directly; dataset scopes by membership role Mismatch
NAV-003 /dashboard/employee-directory businessRoles: HRD, IT requireHRDDashboardAccess() HRD-role users can enter without employee_directory:read Mismatch
NAV-004 /dashboard/courses businessRoles: HRD, IT requireHRDDashboardAccess() HRD-role users can enter without courses:read Mismatch
NAV-005 /dashboard/training-policy businessRoles: HRD, IT requirePermissionDashboardAccess('training_policy:read') Users with nav role but missing permission would see menu but be redirected UI/route mismatch possible
NAV-006 /dashboard/training-matrix businessRoles: HRD, IT requirePermissionDashboardAccess('training_matrix:read') Menu is role-based; route is permission-based UI/route mismatch possible
NAV-007 /dashboard/import-employees No sidebar item found requireHRDDashboardAccess() HRD-role users can enter directly without employee_import:run Hidden but accessible
NAV-008 /dashboard/master-review No sidebar item found requireHRDDashboardAccess() HRD-role users can enter directly without employee_master_review:approve Hidden but accessible
NAV-009 /dashboard/audit-logs systemRole: super_admin requireITDashboardAccess() IT-like users may satisfy page guard depending business role Mismatch
NAV-010 /dashboard/online-lessons requireOrg Employee dashboard access plus action permissions Direct URL aligns for read; actions are permission checked Acceptable
NAV-011 /dashboard/announcements businessRoles: HRD, IT Employee dashboard access plus action permissions Read access can be broad; create/action buttons use permissions Mostly aligned

5. UI Display Audit

ID Module Page / Component Element Expected Actual Severity Evidence
UI-001 Navigation Sidebar HRD module links Show by explicit permission where permission exists Many HRD links show by businessRoles Medium src/config/nav-config.ts
UI-002 Training records Listing page Review queue button Show only with review permission Uses isHRD() Medium src/app/dashboard/training-records/page.tsx
UI-003 Training records Pending review page Page visibility Require training_record:review or approve/reject permissions Requires HRD role only Medium src/app/dashboard/pending-review/page.tsx
UI-004 Employee import Direct page Page visibility Require employee_import:run Requires HRD role only High src/app/dashboard/import-employees/page.tsx
UI-005 Master review Direct page Page visibility Require employee_master_review:approve Requires HRD role only High src/app/dashboard/master-review/page.tsx
UI-006 Courses Listing and create link Create/write access Require courses:write for write UI HRD page role gate; action visibility not permission-scoped at page level Medium src/app/dashboard/courses/page.tsx
UI-007 Reports Reports page Organization filters and organization-wide data Require reports:organization_read Uses membership role for org-wide access High src/features/reports/server/report-data.ts
UI-008 Training policy Page and actions Read/write Uses permission guard and write permission in component Aligned Low src/app/dashboard/training-policy/page.tsx, src/features/training-policy/components/training-policy-page.tsx
UI-009 Online lessons Action menu edit/submit/approve/return/reject/publish Use content workflow permissions and status Aligned Low src/features/online-lessons/components/online-lesson-action-menu.tsx
UI-010 Announcements Action menu edit/submit/approve/return/reject/publish Use content workflow permissions and status Aligned Low src/features/announcements/components/announcement-action-menu.tsx

6. Backend Authorization Audit

ID Endpoint / Action Authentication Permission Ownership Status Guard Result
API-001 POST /api/import-employees requireHRD() None for employee_import:run Organization scoped File validation only High mismatch
API-002 GET /api/import-employees/template requireHRD() None for employee_import:run N/A N/A High mismatch
API-003 GET/PATCH /api/master-review requireHRD() None for employee_master_review:approve Organization scoped Entity action check High mismatch
API-004 GET/POST/PATCH/DELETE /api/courses requireHRD() or isHRD() None for courses:read/write Organization scoped N/A High mismatch
API-005 /api/employee-directory and targets requireHRD() None for employee_directory:read/write Organization scoped N/A High mismatch
API-006 /api/training-matrix* requireOrganizationAccess() Uses membership.role, not training_matrix:* Organization scoped N/A High mismatch
API-007 /api/training-records/[id]/review requireOrganizationAccess() Uses canReviewTrainingRecords(membership.role) Organization scoped Review status guard in data layer High mismatch
API-008 /api/training-records/import requireAllPermissions(['training_record:write_all','training_record:approve']) Explicit Organization scoped Upload validation Aligned
API-009 /api/training-policies* requirePermission() Explicit read/write Organization scoped Active/clone/deactivate guards Aligned
API-010 /api/announcements* Organization access plus workflow helpers Explicit content permissions Owner and status checked Workflow status checked Aligned
API-011 /api/online-lessons* Organization access plus workflow helpers Explicit content permissions Owner and status checked Workflow status checked Aligned
API-012 /api/reports/export Not fully reviewed in detail Permission keys exist Requires confirmation Requires confirmation Needs follow-up
API-013 /api/audit-logs* requireIT() Does not use audit_logs:* Organization scoped N/A Medium mismatch
API-014 /api/users* User management helper Helper is IT/businessRole based Organization scoped N/A Medium mismatch

7. Findings

PERM-001: HRD role grants employee import without employee_import:run

  • Severity: High
  • Module: Employee import
  • Affected roles: HRD users without employee_import:run, including some hrd-staff templates.
  • Expected behavior: page and API should require employee_import:run.
  • Actual behavior: page uses requireHRDDashboardAccess() and API uses requireHRD().
  • Security or business impact: HRD-role users can import employee/training data by direct URL/API even if the configured template does not grant import.
  • Root cause: role guard is used instead of permission guard.
  • Evidence: employee_import:run exists in src/lib/auth/permissions.ts; guards are in src/app/dashboard/import-employees/page.tsx and src/app/api/import-employees/route.ts.
  • File path: src/app/dashboard/import-employees/page.tsx, src/app/api/import-employees/route.ts, src/app/api/import-employees/template/route.ts
  • Line or code reference: page line 6, route line 454, template route line 12.
  • Reproduction steps: sign in as HRD-role user with template lacking employee_import:run, open /dashboard/import-employees, submit Excel to /api/import-employees.
  • Recommended remediation: replace page/API guards with requirePermissionDashboardAccess('employee_import:run') and requirePermission('employee_import:run').
  • Related findings: PERM-002, PERM-003.

PERM-002: Master review does not require employee_master_review:approve

  • Severity: High
  • Module: Master review
  • Affected roles: HRD users without employee_master_review:approve.
  • Expected behavior: viewing and approving/rejecting imported master data should require employee_master_review:approve.
  • Actual behavior: page and API use HRD role only.
  • Security or business impact: users can approve/reject company, department, and position changes beyond configured template permissions.
  • Root cause: permission key exists but is not enforced.
  • Evidence: permission key in src/lib/auth/permissions.ts; route guards in src/app/dashboard/master-review/page.tsx and src/app/api/master-review/route.ts.
  • File path: src/app/dashboard/master-review/page.tsx, src/app/api/master-review/route.ts
  • Line or code reference: page line 9, API lines 42 and 137.
  • Reproduction steps: HRD user without employee_master_review:approve opens /dashboard/master-review, calls PATCH /api/master-review.
  • Recommended remediation: require employee_master_review:approve on page and API.
  • Related findings: PERM-001.

PERM-003: Employee directory read/write ignores employee_directory:*

  • Severity: High
  • Module: Employee directory
  • Affected roles: HRD users whose effective template excludes directory read/write.
  • Expected behavior: list/detail requires employee_directory:read; create/edit/targets require employee_directory:write.
  • Actual behavior: dashboard pages and API use HRD role only.
  • Security or business impact: broad employee profile and target management access can exceed template configuration.
  • Root cause: legacy HRD guard remains after permission keys were introduced.
  • Evidence: employee_directory:read/write exist in src/lib/auth/permissions.ts; route uses requireHRD() in src/app/api/employee-directory/route.ts and target route.
  • File path: src/app/dashboard/employee-directory/page.tsx, src/app/api/employee-directory/route.ts, src/app/api/employee-directory/[id]/target/route.ts
  • Line or code reference: dashboard line 17, API line 7, target API lines 27 and 62.
  • Reproduction steps: HRD user without employee directory permissions opens /dashboard/employee-directory or calls /api/employee-directory.
  • Recommended remediation: map read/write operations to employee_directory:read and employee_directory:write.
  • Related findings: PERM-001.

PERM-004: Courses use HRD role instead of courses:read/write

  • Severity: High
  • Module: Courses
  • Affected roles: HRD users without course permissions; users with courses:* but not HRD.
  • Expected behavior: list/detail require courses:read; create/update/delete require courses:write.
  • Actual behavior: dashboard and API use HRD role checks.
  • Security or business impact: permission templates cannot precisely control course access.
  • Root cause: role-based feature guard predates explicit permission keys.
  • Evidence: courses:read/write in src/lib/auth/permissions.ts; page uses requireHRDDashboardAccess(); API uses requireHRD() and isHRD().
  • File path: src/app/dashboard/courses/page.tsx, src/app/api/courses/route.ts, src/app/api/courses/[id]/route.ts
  • Line or code reference: page line 20, API route lines 22 and 64.
  • Reproduction steps: assign user template with/without courses:* and compare UI/API with HRD membership role.
  • Recommended remediation: use requirePermissionDashboardAccess('courses:read') for read pages and requirePermission('courses:write') for mutations.
  • Related findings: PERM-006.

PERM-005: Training matrix page guard uses permissions but API uses membership role

  • Severity: High
  • Module: Training matrix
  • Affected roles: users with training_matrix:read/write but non-admin membership, and admin users without template permission.
  • Expected behavior: API should enforce training_matrix:read or training_matrix:write.
  • Actual behavior: API calls canManageTrainingMatrix(membership.role).
  • Security or business impact: permission template changes may not affect API access; UI and backend can disagree.
  • Root cause: split guard model between page and API.
  • Evidence: page uses requirePermissionDashboardAccess('training_matrix:read'); API uses canManageTrainingMatrix(membership.role).
  • File path: src/app/dashboard/training-matrix/page.tsx, src/features/training-matrix/server/training-matrix-data.ts, src/app/api/training-matrix/route.ts
  • Line or code reference: page line 20, helper line 62, API lines 17 and 54.
  • Reproduction steps: grant training_matrix:* to a member-role user or deny it to an admin-role user, then call API directly.
  • Recommended remediation: replace role helper at API boundary with requirePermission() checks.
  • Related findings: PERM-004.

PERM-006: Training record review uses membership role instead of review permissions

  • Severity: High
  • Module: Training records
  • Affected roles: admin-role users without training_record:review/approve/reject, and non-admin users with explicit review permissions.
  • Expected behavior: review queue and review API should require explicit review/approve/reject permissions.
  • Actual behavior: review eligibility uses isHRD() or canReviewTrainingRecords(membership.role).
  • Security or business impact: review authority is tied to membership role and can exceed template configuration.
  • Root cause: old membership-role model remains in canonical training-record data helpers.
  • Evidence: canReviewTrainingRecords(role) delegates to owner/admin role; pending-review page uses HRD guard.
  • File path: src/features/training-records/server/training-record-data.ts, src/app/dashboard/pending-review/page.tsx, src/app/api/training-records/[id]/review/route.ts
  • Line or code reference: helper line 132, pending page line 20, review API line 34.
  • Reproduction steps: HRD/admin user lacking training_record:review opens pending review and calls review API.
  • Recommended remediation: introduce permission-aware review checks using training_record:review, training_record:approve, and training_record:reject.
  • Related findings: PERM-007.

PERM-007: Reports organization scope ignores reports:organization_read

  • Severity: High
  • Module: Reports
  • Affected roles: admin-role users with only reports:self_read, and non-admin users with reports:organization_read.
  • Expected behavior: organization-wide reports require reports:organization_read; export requires export permissions.
  • Actual behavior: organization scope is based on membership role through canViewAllReports(role).
  • Security or business impact: admin-role users can see organization-wide reports even when the template grants only self reports.
  • Root cause: reports scope uses membership role instead of permission keys.
  • Evidence: reports:organization_read exists in permissions; canViewAllReports(role) uses owner/admin.
  • File path: src/features/reports/server/report-data.ts, src/app/dashboard/reports/page.tsx
  • Line or code reference: report helper line 61, dashboard page line 20.
  • Reproduction steps: HRD staff template id 5/id 8 has only reports:self_read but membership role admin can still be scoped as all by role.
  • Recommended remediation: calculate report scope from effectivePermissions.
  • Related findings: PERM-006.

PERM-008: Sidebar uses business roles where explicit permissions exist

  • Severity: Medium
  • Module: Navigation
  • Affected roles: users whose templates diverge from membership roles.
  • Expected behavior: sidebar should hide/show entries based on the same permission used by page/API where possible.
  • Actual behavior: many items use businessRole or businessRoles.
  • Security or business impact: users may see menu items they cannot use, or not see items they can use by direct URL.
  • Root cause: nav configuration predates fine-grained permission templates.
  • Evidence: src/config/nav-config.ts uses businessRole: "HRD" and businessRoles: ["HRD","IT"].
  • File path: src/config/nav-config.ts, src/hooks/use-nav.ts
  • Line or code reference: nav lines 23, 64, 72, 80, 88, 96, 99, 159.
  • Reproduction steps: assign a template with read permission but non-HRD membership role, or remove a permission from HRD template.
  • Recommended remediation: prefer access.permission for permission-backed modules; keep role access only for role-only concepts.
  • Related findings: PERM-004, PERM-005.

PERM-009: use-nav reads session.user.permissions instead of explicitly using effectivePermissions

  • Severity: Medium
  • Module: Navigation
  • Affected roles: users with allow/deny overrides if permissions ever diverges from effectivePermissions.
  • Expected behavior: permission-based nav should use effective permissions.
  • Actual behavior: nav access context uses session.user.permissions.
  • Security or business impact: low current risk because Auth.js currently assigns both fields to effective permissions, but maintainability risk if they diverge later.
  • Root cause: naming ambiguity between permissions and effectivePermissions.
  • Evidence: src/auth.ts currently assigns both; src/hooks/use-nav.ts uses session.user.permissions.
  • File path: src/auth.ts, src/hooks/use-nav.ts
  • Line or code reference: auth lines 265-298, hook line 106.
  • Reproduction steps: future change separates raw permissions from effective permissions.
  • Recommended remediation: use session.user.effectivePermissions in navigation.
  • Related findings: PERM-008.

PERM-010: Audit logs use IT role instead of audit_logs:*

  • Severity: Medium
  • Module: Audit logs
  • Affected roles: users with audit permissions but not IT role; IT users without explicit audit permissions.
  • Expected behavior: read/export require audit_logs:read and audit_logs:export.
  • Actual behavior: page and APIs use IT role helpers.
  • Security or business impact: permission templates cannot independently control audit access.
  • Root cause: audit module is role-gated.
  • Evidence: audit_logs:* keys exist; audit page/API use IT guard.
  • File path: src/app/dashboard/audit-logs/page.tsx, src/app/api/audit-logs/route.ts, src/app/api/audit-logs/export/route.ts
  • Line or code reference: dashboard line 16, API line 8, export API line 16.
  • Reproduction steps: grant audit_logs:read to non-IT or remove audit permissions from IT template.
  • Recommended remediation: use permission guards while preserving super admin fallback if required.
  • Related findings: PERM-008.

PERM-011: User management helper is role-based despite users:* permissions

  • Severity: Medium
  • Module: Users
  • Affected roles: users with users:* but not IT role, and IT-role users without users:*.
  • Expected behavior: users list and mutations should enforce users:read/create/update/delete/manage.
  • Actual behavior: requireUserManagementAccess() delegates to canManageUsers(), which checks IT role.
  • Security or business impact: permission templates cannot precisely delegate user-management tasks.
  • Root cause: business-role helper is used as the source of truth.
  • Evidence: users:* keys exist; canManageUsers() returns isIT().
  • File path: src/lib/auth/session.ts, src/lib/auth/roles.ts, src/app/dashboard/users/page.tsx
  • Line or code reference: session line 199, roles line 94, users page line 18.
  • Reproduction steps: assign a non-IT user users:read; user still cannot access. Remove users:* from IT template; IT role may still pass helper.
  • Recommended remediation: introduce permission-specific user management guards.
  • Related findings: PERM-010.

PERM-012: Permission templates with same code differ across organizations

  • Severity: Low
  • Module: Permission management / operations
  • Affected roles: users assigned hrd-staff in different organizations.
  • Expected behavior: same template code may vary by organization, but reports should make this explicit.
  • Actual behavior: DB has hrd-staff with 42 permissions in one org and 30 in others.
  • Security or business impact: operators may assume "HRD Staff" means the same capability everywhere.
  • Root cause: organization-scoped templates share code/name but differ in permission sets.
  • Evidence: database query returned hrd-staff id 2 with 42 permissions, id 5/id 8 with 30 permissions.
  • File path: database tables permission_templates, permission_template_permissions.
  • Line or code reference: schema tables in src/db/schema.ts.
  • Reproduction steps: compare permission templates by code = 'hrd-staff' across organizations.
  • Recommended remediation: document organization-scoped template differences and add UI comparison/audit affordance.
  • Related findings: PERM-008.

PERM-013: Direct hidden routes exist for HRD-only workflows

  • Severity: Medium
  • Module: Navigation / routes
  • Affected roles: HRD users without explicit hidden-route permissions.
  • Expected behavior: hidden routes should still enforce exact permissions.
  • Actual behavior: hidden import/master-review routes are not sidebar-visible but are accessible by direct URL to HRD role.
  • Security or business impact: hiding the menu does not prevent access.
  • Root cause: route guards are less strict than intended permission keys.
  • Evidence: /dashboard/import-employees and /dashboard/master-review are not in sidebar but have HRD-only guards.
  • File path: src/config/nav-config.ts, src/app/dashboard/import-employees/page.tsx, src/app/dashboard/master-review/page.tsx
  • Line or code reference: nav does not include these routes; page lines 6 and 9.
  • Reproduction steps: manually type direct route URL as HRD staff.
  • Recommended remediation: direct routes must require explicit permission, regardless of sidebar visibility.
  • Related findings: PERM-001, PERM-002.

PERM-014: Content workflow modules are comparatively well aligned

  • Severity: Low
  • Module: Announcements and online lessons
  • Affected roles: content creators, reviewers, publishers.
  • Expected behavior: status transitions and row actions use explicit permissions.
  • Actual behavior: shared workflow helpers map actions to module-specific permissions and are used in UI/API.
  • Security or business impact: low; this is a positive control area and should be used as a reference.
  • Root cause: centralized content workflow helper.
  • Evidence: canEditContent, canDeleteContent, and canTransitionContent are used by action menus and APIs.
  • File path: src/features/content-approval/lib/workflow.ts, online lesson/announcement components and APIs.
  • Line or code reference: workflow lines 109-168.
  • Reproduction steps: compare action menu and API guards for status transitions.
  • Recommended remediation: reuse this pattern for other modules.
  • Related findings: PERM-004 to PERM-011.

8. Role-based Test Scenarios

Test ID Role / Template Page Action Expected Result
T-001 Employee template /dashboard/online-lessons View public lesson Allowed if published/public.
T-002 Employee template /dashboard/online-lessons/manage/new Create lesson Denied without online_lesson:create.
T-003 HRD Staff id 5/id 8 /dashboard/import-employees Direct URL Should be denied because template lacks employee_import:run. Current likely allowed by HRD role.
T-004 HRD Staff id 5/id 8 /dashboard/master-review Approve/reject master data Should be denied because template lacks employee_master_review:approve. Current likely allowed by HRD role.
T-005 HRD Staff id 5/id 8 /dashboard/reports Organization-wide report Should be denied because template lacks reports:organization_read. Current may be allowed by admin membership role.
T-006 HRD Staff id 5/id 8 /dashboard/pending-review Open review queue Should require training_record:review; current uses HRD role.
T-007 HRD Staff id 2 /dashboard/training-records/import Import records Allowed because template includes training_record:write_all and training_record:approve.
T-008 HRD Manager Online lesson pending item Return for revision Allowed with online_lesson:revision.
T-009 HRD Staff Returned own online lesson Edit and resubmit Allowed with online_lesson:edit_draft and online_lesson:submit.
T-010 Non-owner HRD Staff Other user's returned online lesson Edit Denied unless a future manage permission is introduced.
T-011 Member with training_matrix:read override /dashboard/training-matrix Open page and call API Page should allow; current API may deny by membership role.
T-012 IT template /dashboard/audit-logs Read/export audit logs Allowed currently by IT role; should also be verified against audit_logs:*.
T-013 Super admin Permission management Manage templates/users Allowed by super admin.

9. Prioritized Remediation Plan

Priority 0 - Critical Security

No confirmed Critical findings in this audit. The most serious confirmed issues are High permission mismatches where broad HRD/admin roles override finer-grained permission templates.

Priority 1 - Permission Mismatch

  1. Replace employee import page/API guards with employee_import:run.
  2. Replace master review page/API guards with employee_master_review:approve.
  3. Replace courses page/API guards with courses:read and courses:write.
  4. Replace employee directory read/write guards with employee_directory:read and employee_directory:write.
  5. Replace training matrix API role helper with training_matrix:read/write.
  6. Replace training record review role helper with training_record:review/approve/reject.
  7. Replace report organization-wide scope with reports:organization_read and export checks with export permissions.

Priority 2 - Data Exposure

  1. Review reports data scope first because it can expose organization-wide data through membership role.
  2. Review employee-directory detail and target routes for employee profile/target exposure.
  3. Review hidden HRD routes and enforce exact permissions.

Priority 3 - Maintainability

  1. Update sidebar config to use access.permission where explicit permissions exist.
  2. Use effectivePermissions consistently in navigation.
  3. Extract module-level permission helpers for courses, employee directory, reports, training matrix, and training records.
  4. Use content workflow helper pattern as a reference for other workflow modules.

10. Files Reviewed

File Reason
plans/system-ui-permission-review-plan.md Audit requirements and report structure.
.agents/skills/kiranism-shadcn-dashboard/SKILL.md Project dashboard/RBAC guidance.
docs/AI_DEVELOPMENT_GUIDE.md Project implementation conventions.
docs/PROJECT_ARCHITECTURE.md Architecture and auth/RBAC context.
src/lib/auth/permissions.ts Permission definitions and defaults.
src/lib/auth/roles.ts Business role and membership role mapping.
src/lib/auth/session.ts Server auth guard behavior.
src/lib/auth/authorization.server.ts Effective permission resolution.
src/auth.ts Auth.js session enrichment and effective permission assignment.
src/config/nav-config.ts Sidebar visibility.
src/hooks/use-nav.ts Client navigation filtering.
src/features/content-approval/lib/workflow.ts Content workflow permission helper.
src/features/online-lessons/components/online-lesson-action-menu.tsx Online lesson UI action checks.
src/features/announcements/components/announcement-action-menu.tsx Announcement UI action checks.
src/features/training-records/server/training-record-data.ts Training record access/review logic.
src/features/training-matrix/server/training-matrix-data.ts Training matrix role helper.
src/features/reports/server/report-data.ts Report scope logic.
src/app/dashboard/**/page.tsx Route/page guards.
src/app/api/**/route.ts API authorization patterns.
Database permission templates Current configured role/template reality.

11. Files Not Reviewed or Limitations

  • Manual browser verification was not performed in this pass.
  • API calls were not executed as each role; findings are based on static code inspection and database configuration.
  • Report export route was not fully expanded; it should be reviewed in a follow-up because export permission keys exist.
  • Legacy/template modules such as products, forms, kanban, chat, and billing were not deeply audited because they are outside the current training-system permission core.
  • Some Thai text appears mojibake in terminal output because of encoding, but file paths and authorization logic were readable.

12. Final Assessment

The system is not yet fully permission-first. It is safe in many content workflow areas because online lessons and announcements consistently use effectivePermissions, ownership, and status transition helpers. The main readiness gap is older HRD operational modules that still trust businessRole or membership.role.

Before production hardening, fix the High findings around employee import, master review, employee directory, courses, training matrix API, training record review, and reports scope. The most urgent business risk is that users with HRD/admin membership can access or act on data beyond what their assigned permission template appears to grant.

Confirmed deliverables:

  • Found 14 findings: 0 Critical, 6 High, 5 Medium, 3 Low.
  • Highest risk modules: employee import, master review, reports, training records review, training matrix API, employee directory.
  • Data exposure risk exists for reports and employee directory/master-data review.
  • No API route was found with no authentication at all in the reviewed core modules, but several APIs enforce broad role guards instead of explicit permissions.
  • This report was created at permission-display-audit.md.
  • No source code behavior or database data was changed during the audit.