[Meteor in Thai] เล่นกับ Allow และ Methods

สวัสดีครับ วันนี้ผมได้มีโอกาสเล่น Meteor ในงานจริงๆ อีกครั้ง คราวนี้เลยจะมาเล่าเรื่องของ Allow และ Methods ครับ


อะไรคือ Collection.allow?

Collection.allow เป็นกลไกหนึ่งในการควบคุมหรือกรองการกระทำของ Client ที่มีต่อข้อมูลใน Collection (CRUD) ตัวอย่างการใช้งานก็เช่น ยอมให้คนที่ login แล้วเท่านั้นถึงจะโพสต์ข้อความได้ มีแค่ admin เท่านั้นที่ลบกระทู้ได้ ก็ประมาณนี้แหละครับ

Collection.allow นั้นจะรันเฉพาะบน Server เท่านั้น กล่าวคือเมื่อ Client ทำการ insert, update, remove ข้อมูลแล้ว ในฝั่ง Client เองจะกระทำตามคำสั่งนั้นๆ ทันที โดยไม่สนใจ Collection.allow เลย ในขณะเดียวกันที่ฝั่ง Server ก็จะทำงานเช่นกันแต่จะตรวจเงื่อนไขที่ Collection.allow ก่อน แล้วถึงจะทำเนินการ และเมื่อทำคำสั่งเสร็จแล้วก็จะส่ง message ไปหา Client เพื่อบอกว่าผลลัพธ์ที่แท้จริงแล้วเป็นแบบไหน

ข้อควรระวัง: Collection.allow จะทำงานเมื่อ client เป็นผู้กระทำต่อข้อมูลโดยตรงเท่านั้น (insert, update, remove) ไม่นับการกระทำผ่านฝั่ง Server และ Meteor.methods 

*นอกจาก Collection.allow แล้ว ยังมี Collection.deny ที่ใช้คู่กัน เพื่อบอกว่าให้ปฏิเสธเมื่อไหร่แทน

ตัวอย่าง

สมมติว่าผมทำแอพเว็บบอร์ดง่ายๆ อันนึง โดยมีข้อกำหนดดังนี้

  1. ผู้ใช้จำเป็นต้อง login ก่อนจึงจะสามารถตั้งกระทู้ได้

  2. ผู้ใช้สามารถลบกระทู้ของตัวเองได้

กำหนดให้ threads document มีหน้าตาแบบนี้

{
    title: string,
    owner: string,
    createdAt: date,
    updatedAt: date,
    deletedAt: date,
}

แล้วเราก็สร้าง Collection ด้วยคำสั่ง

Threads = new Meteor.Collection('threads');

แล้วก็เขียน allow ในส่วนของการ create ดังนี้ครับ

จากโค้ดผมตรวจสอบว่า User login หรือยังก่อน ถ้ายังก็โยน Meteor.Error ออกไปเลยครับ ต่อมาใช้ Package checks ในการตรวจว่ามีการส่ง doc.title มาไหม และ doc.title เป็น String หรือไม่ ต่อมาก็เช็ค doc.owner ว่าถูกปลอมแปลงหรือไม่ แล้วก็ใส่ค่าให้ owner, createdA, updatedAt, deletedAt แล้ว return true กลับไปเพื่อบอกว่า insert ได้นะ

ต่อไปจะเป็น allow ในส่วนของการ remove ครับ

ส่วนนี้ค่อนข้างง่ายครับเพราะ User ส่งข้อมูลเข้าฟังก์ชัน Collection.remove ได้แค่ _id อย่างเดียว เราก็เช็คแค่เรื่อง login กับ owner ของ Thread ว่าคือ User ที่สั่ง remove หรือเปล่า ถ้าใช่ก็คืนค่า true ถ้าไม่ก็คืนค่า false

ตัวอย่างส่วนนี้ก็คงมีแค่นี้ครับ จริงๆ เหลือเรื่อง update อีกอันแต่ก็คล้ายๆ กันครับ มีเพิ่มมาแค่ fields กับ modifier เพิ่มเข้ามา เพื่อบอกว่ามี field ไหนที่เปลี่ยนแปลงบ้าง และเปลี่ยนยังไง ตามลำดับครับ

จะเห็นว่าความยากของ allow คือเราต้องเดาว่า User จะส่งข้อมูลอะไรมาบ้าง แล้วเราต้องควบคุมความปลอดภัยอย่างไร และเมื่อแอพเราใหญ่ขึ้น มีการทำ mutation กับข้อมูลมาขึ้น โอกาสที่จะผิดพลาดก็จะมากขึ้นไปด้วย ดังนั้นจึงไม่แนะนำให้ Client กระทำการใดๆ กับ Collection โดยตรงครับ

อะไรคือ Meteor.methods?

Meteor.methods เป็นกลไกการสร้าง remote functions เพื่อให้ Client เรียกใช้ฟังก์ชันบนฝั่ง Server ได้ และด้วยความที่ Meteor เน้นเรื่ง Optimistic UI ทำให้เราสามารถสร้างฟังก์ชันเดียวกันไว้รันแบบ Simulate บน Client คู่กันไปเพื่อให้ User รู้สึกว่าการทำงานลื่นไหลอีกด้วย

ข้อควรรู้: insert, update, และ remove ของ Collection สร้างขึ้นจาก Methods เช่นกัน

ตัวอย่าง

ผมขอใช้ตัวอย่างเดิมครับ จะได้เปรียบเทียบง่ายๆ ด้วย เริ่มจากสร้าง Method create กันก่อนครับ

ในการสร้าง Method เราสามารถระบุได้เลยว่าต้องการรับ parameter อะไรบ้าง ดังนั้นการตรวจสอบจะค่อนข้างง่ายครับ ตรวจแค่ว่า User login หรือยัง แล้วก็ตรวจ Parameter เสร็จแล้วก็สั่ง insert, update, หรือ remove ตามปกติเลย แล้วก็คืนค่าอะไรไปก็ได้ครับ

ต่อไปจะเป็นส่วนของการ delete ครับ

อันนี้จะยาวกว่าแบบ allow หน่อยครับ เพราะเราต้องเช็ค Parameter ด้วย แล้วก็สั่ง remove แล้วก็คืนค่าอะไรไปก็ได้ครับ

จากตัวอย่างจะเห็นว่าวิธีนี้ค่อนข้างปลอดภัยกว่า Collection.allow เพราะเรากำหนดเองว่าให้ User ส่งอะไรมาบ้าง ข้อเสียอย่างเดียวเลยคือพิมพ์ยาว ><

ข้อควรระวัง: เนื่องจาก Meteor.methods นั้นรันได้ทั้งบน Server และ Client เราจึงมักวางไว้ใน common directory (นอก server หรือ client) แต่ต้องไม่ลืมว่ามีบาง business logic ที่ต้องเป็นความลับ เช่น key สำหรับเชื่อมต่อบริการอื่น โค้ดคำนวณคะแนน เป็นต้น ดังนั้นถ้าส่วนไหนที่ต้องการให้เป็นความลับควรประกาศไว้ที่ Server แล้วอาจจะสร้าง simulation stub แบบง่ายๆ เท่าที่เปิดเผยได้เป็นฟังก์ชันเดียวกัน (Meteor.methods ชื่อเดียวกัน แต่เปลี่ยน logic) ไว้ในส่วนของ Client เพื่อทำ Optimistic UI ก็ได้ (ไม่จำเป็นต้องทำ แต่ถ้าทำ User ก็จะได้รับประสบการณ์ที่ลื่นไหลกว่า)

Allow และ Methods ใน Prodution Apps

อย่างที่ผมกล่าวไปแล้วว่า Collection.allow นั้นมีปัญหาเรื่องความปลอดภัยที่ควบคุมได้ยากดังนั้นจึงควรหลีกเลี่ยงการใช้งานบน Prodution Apps และให้หันไปใช้ Meteor.methods แทน อย่างไรก็ตาม Meteor.methods ไม่ได้เพอร์เฟคไปซะทุกอย่าง เราจึงควรระวังดังนี้

  1. หลีกเลี่ยงการส่ง userId เป็นพารามิเตอร์ เว้นแต่ในกรณีที่เลี่ยงไม่ได้เช่น ใช้ให้ admin กระทำบางอย่างกับ User หรือ ใช้ในระบบเพื่อนหรือการ invite เป็นต้น

  2. 1 Method 1 หน้าที่ เราควรระบุหน้าที่ของ Method นั้นๆ ให้ชัดเจน เพื่อให้ง่ายต่อการตรวจสอบ และป้องกันความผิดพลาด

  3. ตรวจสอบ type และค่าของพารามิเตอร์ก่อนทำงานเสมอ บางครั้งเราอาจจะให้ Client ส่งข้อ _id ของ document มาเป็นพารามิเตอร์ เพื่อไปใช้ในการลบข้อมูล แต่ถ้า Client ส่ง {} มาแทน _id แล้วเราไม่ได้ตรวจอาจจะทำให้ข้อมูลทั้งหมดใน Collection ถูกลบ (ในฝั่ง Server นั้นถือว่าเป็น Trusted source ทำให้ insert, update, remove ยอมให้กระทำอะไรหลายๆ อย่าง ที่อันตรายได้)

Full source code

[varavut/MeteorAllowAndMerhodSample

References & Further reading