Skip to main content

สิทธิ์ผู้ใช้ Permission และ Module Gate

ภาพรวม

ระบบสิทธิ์ของ cms-api-go ประกอบด้วย สี่ชั้นที่ทำงานต่างกัน และเป็นจุดที่เข้าใจสับสนได้ง่ายมาก จึงต้องแยกให้ชัดเจน

  1. CASL policy (@CheckPolicies) — มี metadata กำกับครบทุก route แต่ ปิดการบังคับใช้อยู่ เพราะ CheckPolicies เป็น no-op ที่ปล่อยผ่านทุก request ตรงกับ NestJS เดิมที่ PoliciesGuard.canActivate คืนค่า true เสมอ ผลคือ permission ในตาราง system_module และ system_role_module ถูกใช้เพียงเพื่อ ซ่อนหรือแสดงเมนูใน cms-web ผ่าน GET /api/user/:id/permission เท่านั้น
  2. SuperAdminGuard — บังคับใช้จริง โดยกำหนดว่า roleId ต้องเท่ากับ 1
  3. ModuleGate — บังคับใช้จริง เมื่อ platform admin ปิดโมดูลของ organization ใด ลูกค้าที่เรียก API ตรงก็จะได้ 403
  4. AppEnabledGuard — บังคับใช้จริง โดย app อย่าง appointment, loyalty หรือ bulletin ต้องถูกเปิดให้ organization นั้นก่อนจึงจะใช้งานได้

Business Flow

การอ่าน permission ของตัวเอง (สำหรับแสดงเมนู)

  1. cms-web เรียก GET /api/user/:id/permission โดยพารามิเตอร์ :id ต้องเป็น id ของผู้เรียกเอง หากไม่ใช่จะได้ 400 พร้อมข้อความ Cannot access other userId
  2. service โหลดข้อมูล user พร้อม SystemRoleModule ซึ่งเป็น baseline ตาม role และดึงรายชื่อโมดูลทั้งหมดจาก system_module
  3. หาก user มี type = customer และ organization นั้นมีแถวอยู่ใน organization_module ระบบจะใช้ override ของ organization นั้นแทน baseline โดย platform admin เป็นผู้กำหนด
  4. หาก user มี type = onemoby ซึ่งรวมถึง role 1 ที่เป็น platform operator ระบบจะใช้ baseline ตาม role เสมอ ไม่ถูก override
  5. ตอบกลับเป็นรายการรูปแบบ {subject: ชื่อ module, action: [...]} ให้ cms-web นำไปตัดสินใจว่าจะแสดงเมนูใดบ้าง

ModuleGate (บังคับใช้ฝั่ง server)

  1. middleware อ่านค่า user.type ของผู้เรียกจาก CLS ผ่าน selfId
  2. หากเป็น onemoby หรือระบุตัวผู้เรียกไม่ได้ จะปล่อยผ่านทันที (governors bypass)
  3. หากเป็น customer จะสอบถาม orgmodulesetting.IsModuleEnabledForOrg(orgId, moduleName) ด้วย logic baseline-vs-override ชุดเดียวกับข้อ 3 ด้านบน
  4. หากโมดูลถูกปิดอยู่จะได้ 403 พร้อมข้อความ ModuleDisabled แต่หากเกิด error ระหว่าง lookup ระบบจะ fail OPEN คือปล่อยผ่านพร้อม log เพื่อไม่ให้ query ที่สะดุดทำให้ระบบล่มหรือ lock องค์กรออกจากโมดูล
  5. PlatformOnly ทำตรงกันข้าม คือ fail CLOSED เพราะความเสี่ยงที่นี่คือ privilege escalation ใช้กับ PUT /api/apps/:appId ซึ่งเป็นการเปิดหรือปิด app ให้ organization และต้องทำจาก platform admin console เท่านั้น

ไฟล์และฟังก์ชันหลัก

ไฟล์หน้าที่
internal/modules/auth/policy.goenum PolicyAction และ PolicyModule พร้อม Can() และ CheckPolicies() ซึ่งเป็น no-op
internal/modules/auth/guards.goSuperAdmin() และ InternalApiKey()
internal/modules/modulegate/modulegate.goModuleGate(d, moduleName), PlatformOnly(d), gate() และ dbCallerType()
internal/modules/apps/guard.goAppEnabledGuard(d) ใช้ regex /apps/(\w+) แล้วตรวจด้วย IsAppEnabled
internal/modules/user/service.goFindPermissionByUserID (ราวบรรทัด 405)
internal/modules/orgmodulesetting/service.goIsModuleEnabledForOrg — resolver ที่ทั้ง gate และระบบ permission ใช้ร่วมกัน

Endpoint ที่เกี่ยวข้อง

MethodRouteHandlerหมายเหตุ
GET/api/user/:id/permissionct.findPermissionByUserIdไม่มี @CheckPolicies — service จำกัดให้อ่านของตัวเองเท่านั้น

โมดูลที่ถูกครอบด้วย ModuleGate (ใช้ชื่อโมดูลตามตาราง system_module) ได้แก่ api-key, attribute-master, audiences, auto-response, campaign, customer-database, form-builder, friend-track, import-mapping, report-all-friends, rich-menu, rich-message, template-message, trigger-rule และ workflow

ค่า enum ของ PolicyModule ที่มีอยู่ในโค้ด ได้แก่ line-oa, campaign, rich-message, rich-menu, auto-response, organization, dashboard, user, system_role, system_module, line-user, audiences, report-all-friends, template-message, api-key, form-builder, customer-database, import-mapping, tracking-log, attribute-master, trigger-rule, workflow, friend-track, system-attribute, quick-reply และ otp-config

จุดเชื่อมต่อกับ Service อื่น

  • ตารางsystem_module, system_role, system_role_module, organization_module, user และ line_oa_app
  • CLS — ใช้ค่า selfId, organizationId, roleId และ isSuperAdmin ซึ่งเติมโดย JWT guard
  • ผู้กำหนด override — platform admin ผ่านโมดูล Organization Module Setting
  • app-level gate — เกี่ยวข้องโดยตรงกับโมดูล Apps Platform
  • ฝั่ง frontend — cms-web เป็นผู้บริโภคผลลัพธ์ของ permission นี้ในการแสดงเมนู