# 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` ให้ตรงกับโค้ดปัจจุบัน