# Permission Template Authorization Implementation วันที่อัปเดต: 2026-07-07 ## Scope ของเฟสแรก เฟสแรกของงาน `permission-authorization` เน้นวาง foundation ให้ระบบย้ายจาก role-based authorization ไปสู่ permission-first model แบบค่อยเป็นค่อยไป โดยยังไม่บังคับเปลี่ยนทุก feature พร้อมกันในครั้งเดียว สิ่งที่ทำในเฟสนี้: - เพิ่ม permission catalog กลาง - เพิ่ม helper สำหรับเช็ก permission แบบ type-safe - ผูก default permission ของ membership เข้ากับ catalog กลาง - เพิ่ม session helper สำหรับ `requirePermission()` / `requireAnyPermission()` / `requireAllPermissions()` - เชื่อม `src/auth.ts` ให้ resolve effective permissions จาก template assignment และ override - ขยาย session payload ให้มี `effectivePermissions` และ `permissionTemplateIds` - เพิ่ม schema และ migration สำหรับ permission template, assignment, override, และ audit log ## โครงสร้าง Permission-first Model โครงสร้างเป้าหมายของระบบ: ```text Organization -> Permission Template -> Template Permissions -> User Template Assignments -> User Permission Overrides -> Effective Permissions ``` หลักการในเฟสนี้: - `Permission` คือ source of truth ใหม่ - `membership.role` ยังอยู่เพื่อ compatibility ระหว่าง migration - `businessRole`, `isHRD()`, `isIT()` ยังไม่ถูกลบทิ้ง แต่ไม่ควรเป็นฐานของ feature ใหม่ - `super_admin` ยังเป็น system-level bypass ได้ตาม helper ปัจจุบัน ## Permission Catalog ไฟล์หลัก: - [src/lib/auth/permissions.ts](/D:/WY-2569/HRD/training-system-minimal-refactor/src/lib/auth/permissions.ts) - [src/lib/auth/authorization.ts](/D:/WY-2569/HRD/training-system-minimal-refactor/src/lib/auth/authorization.ts) สิ่งที่เพิ่ม: - `Permission` type แบบรวมค่า permission ทั้งระบบ - permission catalog แยกตาม module - default permission set สำหรับ `owner`, `admin`, `member` - helper: - `hasPermission()` - `hasAnyPermission()` - `hasAllPermissions()` - `normalizePermissions()` - `isPermission()` ## Default Permission Mapping ไฟล์ที่ผูก default mapping: - [src/lib/auth/roles.ts](/D:/WY-2569/HRD/training-system-minimal-refactor/src/lib/auth/roles.ts) สถานะปัจจุบัน: - `getDefaultPermissionsForRole()` ถูกเปลี่ยนให้ดึงจาก catalog กลางแล้ว - ระบบเดิมยังสามารถสร้าง default permissions จาก membership role ได้เหมือนเดิม - ยังไม่ได้ย้ายไปใช้ seeded permission templates จริงในฐานข้อมูล ## Session และ Authorization Helper ไฟล์หลัก: - [src/auth.ts](/D:/WY-2569/HRD/training-system-minimal-refactor/src/auth.ts) - [src/lib/auth/session.ts](/D:/WY-2569/HRD/training-system-minimal-refactor/src/lib/auth/session.ts) - [src/types/next-auth.d.ts](/D:/WY-2569/HRD/training-system-minimal-refactor/src/types/next-auth.d.ts) - [src/types/index.ts](/D:/WY-2569/HRD/training-system-minimal-refactor/src/types/index.ts) สิ่งที่เพิ่ม: - `resolveEffectivePermissions()` ถูกใช้ใน session callback แล้ว - `requirePermission(permission)` - `requireAnyPermission(permissions)` - `requireAllPermissions(permissions)` - nav access metadata รองรับ `Permission` type มากขึ้น ข้อสำคัญ: - session จะพยายามอ่านสิทธิ์จาก: - active permission template assignments - active user permission overrides - fallback membership permissions หรือ default role permissions - `session.user.permissions` และ `session.user.effectivePermissions` ตอนนี้ชี้ไปที่ผลลัพธ์ effective permissions ชุดเดียวกัน - `requireOrganizationAccess()` และ `requirePermission()` เปลี่ยนมาอ้าง `session.user.effectivePermissions` เป็นหลักแล้ว ## Database / Schema ที่เพิ่ม ไฟล์หลัก: - [src/db/schema.ts](/D:/WY-2569/HRD/training-system-minimal-refactor/src/db/schema.ts) - [drizzle/0015_permission_template_authorization.sql](/D:/WY-2569/HRD/training-system-minimal-refactor/drizzle/0015_permission_template_authorization.sql) ตารางและ enum ที่เพิ่ม: 1. `permission_override_effect` ใช้เก็บค่า `allow` และ `deny` 2. `permission_templates` เก็บ template รายองค์กร คอลัมน์สำคัญ: - `organization_id` - `code` - `name` - `description` - `is_system` - `is_default` - `is_active` - `deleted_at` 3. `permission_template_permissions` เก็บรายการ permission ของแต่ละ template 4. `user_permission_template_assignments` เก็บการ assign template ให้ user แบบผูกกับ organization หมายเหตุ: - รองรับหลาย template ต่อ user ต่อ organization ได้ - มี `is_active` สำหรับรองรับการปิด assignment โดยไม่ต้องลบทิ้ง 5. `user_permission_overrides` เก็บ override รายบุคคล ความสามารถที่รองรับแล้วใน schema: - `allow` override - `deny` override - `expires_at` - `revoked_at` - `revoked_by` 6. `permission_audit_logs` เก็บ audit trail สำหรับการเปลี่ยน template, assignment, override ## วิธีคำนวณ Effective Permissions logic ที่ถูกต่อเข้ากับ session แล้วในรอบนี้: ```text effective permissions = permissions จาก template assignment ที่ active + allow overrides ที่ยังไม่หมดอายุ - deny overrides ที่ active และยังไม่หมดอายุ ``` กติกาที่ต้องใช้ในเฟสถัดไป: - `deny` มี priority สูงกว่า `allow` - override ที่หมดอายุแล้วต้องไม่ถูกใช้ - ทุกการคำนวณต้องผูกกับ `active organization` - ถ้า user ยังไม่มี active template assignment จะ fallback ไปที่ `memberships.permissions` - ถ้า membership ไม่มี permissions เลย จะ fallback ไปที่ default permissions ตาม role เดิม ## ไฟล์ที่แก้ในเฟสนี้ - [src/lib/auth/permissions.ts](/D:/WY-2569/HRD/training-system-minimal-refactor/src/lib/auth/permissions.ts) - [src/lib/auth/authorization.ts](/D:/WY-2569/HRD/training-system-minimal-refactor/src/lib/auth/authorization.ts) - [src/lib/auth/roles.ts](/D:/WY-2569/HRD/training-system-minimal-refactor/src/lib/auth/roles.ts) - [src/auth.ts](/D:/WY-2569/HRD/training-system-minimal-refactor/src/auth.ts) - [src/lib/auth/session.ts](/D:/WY-2569/HRD/training-system-minimal-refactor/src/lib/auth/session.ts) - [src/types/next-auth.d.ts](/D:/WY-2569/HRD/training-system-minimal-refactor/src/types/next-auth.d.ts) - [src/types/index.ts](/D:/WY-2569/HRD/training-system-minimal-refactor/src/types/index.ts) - [src/db/schema.ts](/D:/WY-2569/HRD/training-system-minimal-refactor/src/db/schema.ts) - [drizzle/0015_permission_template_authorization.sql](/D:/WY-2569/HRD/training-system-minimal-refactor/drizzle/0015_permission_template_authorization.sql) ## สิ่งที่ยังไม่ได้ทำ - seed default permission templates ลงฐานข้อมูล - helper `getEffectivePermissions(userId, organizationId)` - admin UI สำหรับจัดการ template - UI สำหรับ assign template และตั้ง override รายบุคคล - migration ของ feature guards จาก role-based เป็น permission-based แบบครบทุกหน้า - audit write logic ตอน create/update/assign/override ## ลำดับงานถัดไปที่แนะนำ 1. เพิ่ม permission template service และ effective permission resolver 2. seed default permission templates และ assignment strategy สำหรับข้อมูลเดิม 3. ทำ seed default templates ต่อ organization 4. ย้าย page guards และ navigation ไปใช้ permission helper 5. ย้าย route handlers สำคัญไปใช้ `requirePermission()` 6. ค่อยทำ admin UI สำหรับ template และ user assignment ## Verification ตรวจสอบแล้วในรอบนี้: - `oxlint` ผ่านสำหรับไฟล์ auth/schema ที่แก้ - `tsc --noEmit` ผ่านหลังเพิ่ม foundation ของ permission helper - `session.user.permissions` ถูกย้ายให้ใช้ผลจาก effective permission resolver แล้ว หมายเหตุ: - หลังเพิ่ม migration `0015` ควรรัน `npm run migrate` ในเครื่องก่อนเริ่มทำ service/UI เฟสถัดไป - ยังไม่ได้เพิ่ม snapshot ใหม่ใน `drizzle/meta` เพราะรอบนี้เพิ่ม migration SQL แบบ manual เพื่อคุมผลกระทบให้แคบที่สุด