สมอง SML

คลังความรู้ / การตัดสินใจ (ADR)

#backend#adr#query-gateway#security#write-policy

ADR-0011 ล็อกการเขียนผ่าน /query/exec จาก browser

สถานะ: accepted และเป็น ADR ล่าสุด/ปัจจุบัน ที่แทน ADR-0006 /query/exec รับเฉพาะ DML + ตารางระบบต้อง level>=9 (ถูกแทนด้วย ADR-0011) (backend/docs/adr/0011-query-exec-write-lockdown.md:3). แนวคิด: กฎธุรกิจ/GL/สต๊อกอยู่ใน Go backend, browser เป็น thin client — จึงห้าม browser ใช้ SQL เขียนตารางเงินตรง ๆ.

ทำงานอย่างไร

  1. /query/list|limit|batch รับ SELECT/WITH ทุก session; /query/exec|execBatch รับเฉพาะ INSERT/UPDATE/DELETE (หรือ WITH ที่ห่อ DML) อย่างเดียว — อย่างอื่นรวมถึง SELECT ได้ 400 ErrNotWriteOnly (...0011...md:15-17).
  2. handler ตั้ง ExecInput.ClientRequest = true (query/handler.go:245,299); service เรียก WriteStatement.ValidateClientPolicy(userLevel) เมื่อ ClientRequest (application/query/service.go:105,146; write_policy.go:77).
  3. กฎเรียงตามลำดับ:
    • ตารางเงิน/สต๊อก/GL ถูกปฏิเสธ ทุกคนแม้ level 9 → ErrMoneyTableViaExec (HTTP 403): ic_trans*, as_trans*, ap_ar_trans*, ap_ar_debt_*, ap_ar_deposit_money, ap_ar_pay_bill*, cb_trans*, cb_chq_list, gl_journal* (ยกเว้นมาสเตอร์ gl_journal_book), gl_trans*, gl_account_close*, gl_summary_*, gl_wht_list*, etax_document_log, e_tax_transaction, ic_lot_manage, และ ic_inventory ที่แตะคอลัมน์ derived (balance_qty, accrued_*_qty, book_out_qty, average_cost*, item_in_stock, last_movement_date) — write_policy.go:24-31.
    • level >= 9 (SQL Studio) เขียนตารางอื่นได้.
    • level < 9 เขียนได้เฉพาะ allow-list legacyClientWritable (มาสเตอร์ลูกค้า/ผู้ขาย/คลัง/หน่วย/โปรไฟล์บริษัท ฯลฯ) — write_policy_legacy.go:9.
    • ตารางระบบ erp_option, sml_user_list, sml_database_list* เป็น admin-only ผ่าน ValidateUserLevel; statement ที่หาเป้าหมายเขียนไม่เจอ → admin-only.
    • DDL, MERGE และชื่อ identifier แบบ U&"..." ถูกปฏิเสธ/จำกัด admin (write_policy.go:35).
  4. caller ภายใน Go (crm, etax, market, monitormap, procure, tms, wms) ใช้ ClientRequest = false จึงไม่ถูกกระทบ (...0011...md:39).

ที่ย้ายออกจาก SQL ในเบราว์เซอร์แล้ว

stock beginning, point docs (flag 801/802), check price, ฟังก์ชัน system process (check-ic-trans, point-recal, import-transport-label), และ docnonext → ทั้งหมดเป็น /api/process/... (ตารางใน ...0011...md:44-50) — โค้ดอยู่ใน application/process ซึ่งอยู่นอกโน้ตชุดนี้ ดู ภาพรวมกลุ่ม process ของ Backend (Process overview).

วิธีหดรายการ allow-list (ratchet)

ย้ายหน้าจอไป endpoint Go (parameterized, 1 transaction, เงินเป็น string) → เปลี่ยนหน้าจอเป็น apiPost → ลบตารางออกจาก legacyClientWritable → เพิ่มเคสใน write_policy_test.go (...0011...md:54-56). ตารางใหม่เป็น admin-only จนกว่าจะมีคนเพิ่มโดยตั้งใจ.

ข้อควรระวัง

  • erp_user_datalist_config, erp_option_etax_inet, erp_option_mobillyst, erp_thai_qr_config ตั้งใจ ไม่ อยู่ใน allow-list.
  • ตารางใหม่ที่ขึ้นต้นเหมือนตารางเงินจะถูกบล็อกโดย regex อัตโนมัติ (moneyTablePattern, write_policy.go:24) — ถ้าต้องการเขียนจริงต้องทำ endpoint ใน /api/process.
  • แยกจาก /blob/insert ซึ่งใช้ ValidateClientInsert เช็กกฎเดียวกันที่ระดับตาราง/คอลัมน์ (แทรกแถวพร้อมรูปภาพ: POST /blob/insert + MinIO (Blob insert)).

ที่มาในโค้ด

  • backend/docs/adr/0011-query-exec-write-lockdown.md:15 — ส่วน Decision.
  • backend/internal/domain/query/write_policy.go:14,24,77 — error ใหม่, regex ตารางเงิน, ValidateClientPolicy.
  • backend/internal/domain/query/write_policy_legacy.go:9 — legacyClientWritable.
  • backend/internal/adapter/inbound/http/query/handler.go:245 — ตั้ง ClientRequest: true.

ดูเพิ่ม

นโยบายล็อกการเขียนผ่าน Query Gateway (Query write policy / lockdown) · Query Gateway: /query/list, limit, batch, exec, execBatch · สิทธิ์เมนูและ middleware RequireRight (Menu rights / permission) · สารบัญ ADR ของ backend (Architecture Decision Records)