# Role Permission Review

วันที่ตรวจสอบ: 2026-06-30

## Scope

เอกสารนี้เป็นการตรวจสอบระบบสิทธิ์การเข้าถึงของ 3 กลุ่มหลัก:

- Employee
- HRD Admin
- IT Admin

ขอบเขตที่ตรวจ:

- การมองเห็นเมนูใน sidebar
- การกัน direct URL ที่ระดับ page
- การกันสิทธิ์ที่ระดับ API / server-side
- การจำกัดขอบเขตข้อมูล
- ความสอดคล้องระหว่าง requirement กับ implementation ปัจจุบัน

ไฟล์หลักที่ใช้ตรวจ:

- `src/config/nav-config.ts`
- `src/hooks/use-nav.ts`
- `src/lib/auth/roles.ts`
- `src/lib/auth/session.ts`
- `src/lib/auth/page-guards.ts`
- `src/proxy.ts`
- `src/app/dashboard/**`
- `src/app/api/**`

## Requirement Summary

จากแผนงานที่ให้ตรวจ ระบบนี้ควรแยกสิทธิ์อย่างน้อยเป็น 3 ระดับ:

- Employee เห็นเฉพาะส่วนที่ใช้กับตัวเอง และข้อมูลต้องถูก scope เป็นของตัวเอง
- HRD Admin จัดการข้อมูลฝั่งงานอบรมและข้อมูลบุคลากรในองค์กรได้
- IT Admin จัดการสิทธิ์ระดับสูงกว่า HRD และเข้าถึงส่วน technical / audit ได้

ประเด็นสำคัญที่แผนเน้นเป็นพิเศษ:

- เมนูและหน้าจอต้องสอดคล้องกับสิทธิ์จริง
- ห้ามพึ่งแค่ซ่อนเมนู ต้องกันที่ server ด้วย
- ข้อมูลของ Employee ต้องไม่ทะลุไปเห็นของคนอื่น
- ฟังก์ชัน `Import พนักงาน` ควรถูกทบทวน/ถอดออกจาก flow ถ้าไม่ได้ต้องการใช้งานแล้ว

## Findings

### 1. High: หน้าและ API `Import พนักงาน` ยังเปิดใช้งานอยู่

สถานะ: `Fail`

หลักฐาน:

- [src/app/dashboard/employee-directory/page.tsx](/d:/WY-2569/HRD/training-system-minimal-refactor/src/app/dashboard/employee-directory/page.tsx:54) ยังมีปุ่มลิงก์ไป `/dashboard/import-employees`
- [src/app/dashboard/import-employees/page.tsx](/d:/WY-2569/HRD/training-system-minimal-refactor/src/app/dashboard/import-employees/page.tsx:5) ยังเปิดหน้า import
- [src/app/api/import-employees/route.ts](/d:/WY-2569/HRD/training-system-minimal-refactor/src/app/api/import-employees/route.ts:401) ยังเปิด API import โดยใช้ `requireHRD()`

ผลกระทบ:

- ถ้า requirement ล่าสุดต้องการยกเลิก flow นี้ ปัจจุบันถือว่ายังไม่ปิดจริง
- ผู้ใช้ HRD / IT ยังเข้าใช้งานได้ผ่านปุ่ม, direct URL และ API

คำแนะนำ:

- ถ้าต้องการถอดฟังก์ชันนี้จริง ควรลบปุ่ม, ปิดหน้า route, ปิด API และตรวจ flow ที่เกี่ยวข้องกับ `master-review` ต่อด้วย

### 2. Medium: สิทธิ์ฝั่ง Employee สำหรับเมนูบางหน้าไม่สอดคล้องกับสิทธิ์จริงของหน้า

สถานะ: `Need Review`

หลักฐาน:

- เมนู `Reports` ใน [src/config/nav-config.ts](/d:/WY-2569/HRD/training-system-minimal-refactor/src/config/nav-config.ts:103) จำกัดเป็น `businessRole: 'HRD'`
- แต่หน้า reports ใช้ [src/app/dashboard/reports/page.tsx](/d:/WY-2569/HRD/training-system-minimal-refactor/src/app/dashboard/reports/page.tsx:20) ผ่าน `requireEmployeeDashboardAccess()`
- data scope ของ reports ก็รองรับ employee แบบ self-scope ใน [src/features/reports/server/report-data.ts](/d:/WY-2569/HRD/training-system-minimal-refactor/src/features/reports/server/report-data.ts:126)

ข้อสังเกตเพิ่ม:

- หน้า `Announcements` ใช้ `requireEmployeeDashboardAccess()` ใน [src/app/dashboard/announcements/page.tsx](/d:/WY-2569/HRD/training-system-minimal-refactor/src/app/dashboard/announcements/page.tsx:18)
- หน้า `Notifications` ใช้ `requireEmployeeDashboardAccess()` ใน [src/app/dashboard/notifications/page.tsx](/d:/WY-2569/HRD/training-system-minimal-refactor/src/app/dashboard/notifications/page.tsx:16)
- แต่ใน `nav-config.ts` เมนู `Announcements` ถูกวางไว้ในกลุ่ม HRD/IT เท่านั้น และ `Notifications` ไม่มีใน sidebar

ผลกระทบ:

- สิทธิ์จริงของระบบกับสิ่งที่ผู้ใช้เห็นในเมนูไม่ตรงกัน
- ผู้ใช้ employee อาจ “เข้าได้แต่ไม่เห็นเมนู”

คำแนะนำ:

- ตกลงให้ชัดว่าหน้าใดควรเป็น employee-facing
- จากนั้น sync ระหว่าง `nav-config.ts` กับ page/API permission ให้ตรงกัน

### 3. Medium: โมเดลสิทธิ์ IT กับ HRD ยังพึ่งการตีความทางอ้อมมากกว่าตารางสิทธิ์ที่ชัด

สถานะ: `Need Review`

หลักฐาน:

- `super_admin` ถูก map เป็น `IT` ใน [src/lib/auth/roles.ts](/d:/WY-2569/HRD/training-system-minimal-refactor/src/lib/auth/roles.ts:48)
- `isHRD()` คืนค่า `true` ให้ทั้ง HRD และ super admin ใน [src/lib/auth/roles.ts](/d:/WY-2569/HRD/training-system-minimal-refactor/src/lib/auth/roles.ts:63)
- `requireHRD()` จึงเปิดให้ IT ผ่านได้ด้วยใน [src/lib/auth/session.ts](/d:/WY-2569/HRD/training-system-minimal-refactor/src/lib/auth/session.ts:143)

ผลกระทบ:

- ตอนนี้ระบบทำงานแบบ “IT เข้าส่วน HRD ได้ทั้งหมด” ซึ่งอาจถูกต้องก็ได้
- แต่ถ้าอนาคตอยากแยกสิทธิ์ HRD กับ IT ให้ต่างกันมากขึ้น จะตามแก้ยาก เพราะ logic ผูกกันอยู่หลายชั้น

คำแนะนำ:

- ถ้าต้องการให้ IT = สิทธิ์มากกว่า HRD แบบตั้งใจ ถือว่าใช้งานได้
- แต่ถ้าต้องการ matrix สิทธิ์ที่ตรวจสอบง่าย ควรมี role-permission matrix กลางที่อ่านแล้วตอบได้ทันทีว่าแต่ละ role ทำอะไรได้บ้าง

### 4. Low: เอกสาร RBAC ปัจจุบันไม่ตรงกับ implementation บางจุด

สถานะ: `Need Review`

หลักฐาน:

- [docs/nav-rbac.md](/d:/WY-2569/HRD/training-system-minimal-refactor/docs/nav-rbac.md:85) ระบุว่า HRD เข้าถึง `Audit Logs` ได้
- แต่เมนู `Audit Logs` ใน [src/config/nav-config.ts](/d:/WY-2569/HRD/training-system-minimal-refactor/src/config/nav-config.ts:94) จำกัด `systemRole: 'super_admin'`
- และหน้า audit log ใช้ [src/app/dashboard/audit-logs/page.tsx](/d:/WY-2569/HRD/training-system-minimal-refactor/src/app/dashboard/audit-logs/page.tsx:16) ผ่าน `requireITDashboardAccess()`

ผลกระทบ:

- คนดูเอกสารอาจเข้าใจสิทธิ์ผิด
- เสี่ยงตัดสินใจต่อยอด feature จากเอกสารที่ไม่ตรงโค้ดจริง

คำแนะนำ:

- อัปเดตเอกสารให้ตรงกับ implementation ปัจจุบัน

## Menu Visibility Review

### Employee

ผลที่พบ:

- เห็น `Overview`, `Training Records`, `Online Lessons`
- ไม่เห็น `Reports` จาก sidebar แม้ว่าหน้า reports จะเข้าได้จริง
- ไม่เห็น `Announcements` และ `Notifications` จาก sidebar แม้ว่าหน้าเหล่านี้เปิดสิทธิ์ employee

สรุป: `Partial`

### HRD Admin

ผลที่พบ:

- เห็น `Pending Review`
- เห็นกลุ่ม `ข้อมูลหลัก` และเข้าถึง `Employee Directory`, `Courses`, `Training Policy`, `Announcements`
- เข้า `Import Employees` ได้ผ่าน direct URL และจากปุ่มในหน้า employee directory
- ไม่เห็น `Users` เพราะเมนูนั้นเปิดเฉพาะ IT

สรุป: `Mostly Pass`

### IT Admin

ผลที่พบ:

- เห็นเมนู `Users`
- เห็นส่วนของ HRD ได้ทั้งหมดผ่าน role mapping
- เห็น `Audit Logs` เฉพาะ `super_admin`

สรุป: `Pass`

## Route Protection Review

### ผ่าน

- หน้า employee-facing ใช้ `requireEmployeeDashboardAccess()`
- หน้า HRD ใช้ `requireHRDDashboardAccess()`
- หน้า IT ใช้ `requireITDashboardAccess()` หรือ `requireUserManagementDashboardAccess()`

หลักฐานสำคัญ:

- [src/lib/auth/page-guards.ts](/d:/WY-2569/HRD/training-system-minimal-refactor/src/lib/auth/page-guards.ts:23)
- [src/app/dashboard/users/page.tsx](/d:/WY-2569/HRD/training-system-minimal-refactor/src/app/dashboard/users/page.tsx:18)
- [src/app/dashboard/employee-directory/page.tsx](/d:/WY-2569/HRD/training-system-minimal-refactor/src/app/dashboard/employee-directory/page.tsx:20)
- [src/app/dashboard/pending-review/page.tsx](/d:/WY-2569/HRD/training-system-minimal-refactor/src/app/dashboard/pending-review/page.tsx:21)
- [src/app/dashboard/audit-logs/page.tsx](/d:/WY-2569/HRD/training-system-minimal-refactor/src/app/dashboard/audit-logs/page.tsx:16)

### ข้อสังเกต

- [src/proxy.ts](/d:/WY-2569/HRD/training-system-minimal-refactor/src/proxy.ts:27) กันได้แค่ “ล็อกอินหรือยัง”
- การกัน “สิทธิ์ role ไหนเข้าได้” ยังต้องพึ่ง page guard และ API handler ต่อ ซึ่งตอนนี้ถือว่าทำถูกทิศทางแล้ว

สรุป: `Pass`

## API / Server Action Permission Review

### ผ่าน

- `Users API` เปิดเฉพาะ IT ผ่าน `requireUserManagementAccess()`
  - [src/app/api/users/route.ts](/d:/WY-2569/HRD/training-system-minimal-refactor/src/app/api/users/route.ts:23)
  - [src/app/api/users/[id]/route.ts](/d:/WY-2569/HRD/training-system-minimal-refactor/src/app/api/users/[id]/route.ts:25)
- `Audit Logs API` เปิดเฉพาะ IT ผ่าน `requireIT()`
  - [src/app/api/audit-logs/route.ts](/d:/WY-2569/HRD/training-system-minimal-refactor/src/app/api/audit-logs/route.ts:8)
- `Employee Directory`, `Courses`, `Training Policies`, `Import Employees`, `Master Review` เปิดเฉพาะ HRD/IT ผ่าน `requireHRD()`

### ผ่านแบบมีเงื่อนไข

- `Training Records` ใช้ `requireOrganizationAccess()` แล้วคุมต่อด้วย scope และ review permission ภายใน service
- `Reports Export` กัน employee ไม่ให้ export รายงานรวม โดยอนุญาตเฉพาะ `employeeTranscript`
  - [src/app/api/reports/export/route.ts](/d:/WY-2569/HRD/training-system-minimal-refactor/src/app/api/reports/export/route.ts:42)

สรุป: `Pass`

## Data Scope Review

### Employee

ผ่าน:

- training records ถูก scope ตาม employee mapping ของ user
  - [src/features/training-records/server/training-record-data.ts](/d:/WY-2569/HRD/training-system-minimal-refactor/src/features/training-records/server/training-record-data.ts:224)
- reports ถูก scope ตาม employee ของตัวเอง ถ้า role ไม่ใช่ owner/admin
  - [src/features/reports/server/report-data.ts](/d:/WY-2569/HRD/training-system-minimal-refactor/src/features/reports/server/report-data.ts:126)

### HRD / IT

ผ่าน:

- reports ระดับองค์กรเปิดให้ membership role `owner/admin`
- training review เปิดเฉพาะ role ที่ review ได้
  - [src/app/api/training-records/[id]/review/route.ts](/d:/WY-2569/HRD/training-system-minimal-refactor/src/app/api/training-records/[id]/review/route.ts:31)

สรุป: `Pass`

## Hardcoded Roles Review

สิ่งที่พบ:

- role string หลักกระจุกอยู่ใน `src/lib/auth/roles.ts`
- แต่ยังมีการอ้าง role ตรง ๆ ซ้ำใน `nav-config.ts`, `session.ts`, เอกสาร, และบาง feature

ประเมิน:

- ยังไม่ถึงขั้นกระจายมั่ว
- แต่ยังไม่มี permission matrix กลางที่เป็น single source of truth

สรุป: `Acceptable but should improve later`

## Summary

ภาพรวมปัจจุบัน:

- ระบบกันสิทธิ์ที่ระดับ page และ API ถือว่าใช้ได้
- data scope ของ employee ฝั่ง training records และ reports ถือว่าปลอดภัยในระดับหนึ่ง
- จุดที่ไม่ตรง requirement ชัดที่สุดคือ `Import พนักงาน` ยังไม่ถูกถอดออกจริง
- อีกจุดที่ควรรีบจัดระเบียบคือความไม่ตรงกันระหว่าง “เมนูที่เห็น” กับ “สิทธิ์จริงของหน้า” โดยเฉพาะ `Reports`, `Announcements`, `Notifications`

## Recommended Next Actions

1. ตัดสินใจให้ชัดว่า `Import พนักงาน` จะเก็บหรือจะถอด แล้วปิดทั้ง UI, route, API ให้ครบ
2. ทำ matrix สิทธิ์ของ `Employee / HRD / IT` แบบสั้น ๆ แล้วใช้เป็นตัวเทียบกับ `nav-config.ts`
3. sync `nav-config.ts` กับ page/API permission ให้ผู้ใช้เห็นเมนูตรงกับสิทธิ์จริง
4. อัปเดต `docs/nav-rbac.md` ให้ตรงกับโค้ดปัจจุบัน
