กฎความปลอดภัยสำหรับการดำเนินการในไปป์ไลน์

แม้ว่าการดำเนินการของไปป์ไลน์จะมีฟีเจอร์มากมาย แต่เครื่องมือกฎจะจำกัดให้จดจำตัวกรองการเปรียบเทียบ (เช่น >) และตรรกะ (เช่น or) เพื่อให้เป็นไปตามข้อจำกัดและความปลอดภัย

นิพจน์ตัวกรองที่รองรับ

เพื่อให้การดำเนินการของไปป์ไลน์ถูกจำกัดให้อยู่ในขอบเขตที่กฎกำหนดไว้ การดำเนินการดังกล่าวต้องใช้โอเปอเรเตอร์เชิงตรรกะและโอเปอเรเตอร์การเปรียบเทียบกับค่าคงที่ เครื่องมือสร้างกฎจะจดจำ ตัวกรองประเภทต่อไปนี้

  • การเปรียบเทียบ: eq, neq, gt, gte, lt, lte, in, arrayContains
  • ตรรกะ: and, or

ตัวอย่างเช่น

  • where(eq("foo", 2))
  • where(lt("foo", 2))
  • documents("/user/1", "/user/2").where(...)

พร็อพเพอร์ตี้คำขอ

คุณยังคงใช้ออบเจ็กต์ request เพื่อตรวจสอบความถูกต้องของการตรวจสอบสิทธิ์และบริบทการค้นหาได้ แม้ว่าการดำเนินการไปป์ไลน์จะไม่รองรับพร็อพเพอร์ตี้บางรายการที่ใช้ได้ในการค้นหามาตรฐานก็ตาม

พร็อพเพอร์ตี้ที่รองรับ

เครื่องมือใหม่ยังคงรองรับพร็อพเพอร์ตี้ต่อไปนี้

  • request.auth: เข้าถึงข้อมูล UID และโทเค็นของผู้ใช้
  • request.method: ระบุการดำเนินการ (เช่น get, list)
  • request.path: เส้นทางของทรัพยากรที่มีการเข้าถึง
  • request.time: การประทับเวลาฝั่งเซิร์ฟเวอร์ของคำขอ

พร็อพเพอร์ตี้ที่ไม่รองรับ

ระบบไม่รองรับพร็อพเพอร์ตี้ request.query เช่น limit, offset และ orderBy สำหรับการตรวจสอบกฎการดำเนินการไปป์ไลน์เนื่องจากความซับซ้อนในการกำหนดค่าเหล่านี้ในการค้นหาแบบหลายระดับ

การจัดการและสิทธิ์ในขั้นตอนไปป์ไลน์

มีขั้นตอนไปป์ไลน์ที่แตกต่างกันซึ่งแมปกับการดำเนินการแบบละเอียดที่เฉพาะเจาะจงในกฎความปลอดภัย ดังนี้

  • สิทธิ์ allow list: ทริกเกอร์โดยระยะcollection(), collectionGroup() และ database()
  • สิทธิ์ allow get: ทริกเกอร์โดยระยะ documents() ซึ่งได้รับการปฏิบัติคล้ายกับการดำเนินการ get แบบกลุ่ม
  • สิทธิ์ allow create: ทริกเกอร์โดยระยะ insert() และระยะ upsert() เมื่อสร้างเอกสารใหม่
  • สิทธิ์ allow update: ทริกเกอร์โดยขั้นตอน update() และขั้นตอน upsert() เมื่ออัปเดตเอกสารที่มีอยู่
  • สิทธิ์ allow delete: ทริกเกอร์โดยระยะ delete()
  • สิทธิ์ allow write: มีผลกับการดำเนินการ DML ทั้งหมด (insert(), update(), upsert() และ delete())
  • ระยะ Literals: ระยะ literals() ไม่ได้อ่านจากฐานข้อมูล แต่อาจมีค่าใช้จ่าย เพื่อป้องกันการละเมิด ต้องจับคู่กับระยะอื่น (เช่น collection()) ที่สามารถยืนยันได้ด้วยกฎ

ขั้นตอนการแก้ไขฟิลด์

กฎจะทำงานกับข้อมูลที่จัดเก็บไว้เท่านั้น ไม่ใช่ค่าที่ได้มา หากไปป์ไลน์มีขั้นตอนที่แก้ไขฟิลด์ (เช่น add_fields(...), replace_with(...), select(...), remove_fields(...)) เครื่องมือสร้างกฎจะหยุดใช้ข้อจำกัดของตัวกรองหลังจากพบขั้นตอนดังกล่าว เพื่อให้แน่ใจว่ากฎทำงานตามที่คาดไว้ ให้วางตำแหน่งขั้นตอนตัวกรอง (เช่น where) ไว้ก่อนขั้นตอนใดๆ ที่สามารถแก้ไขเอกสารที่จัดเก็บไว้เดิมได้

ตัวอย่างเช่น ลองดูกฎความปลอดภัยต่อไปนี้

match /databases/{database}/documents {
  match /cities/{city} {
    // Allow the user to read data if the document has the 'visibility'
    // field set to 'public'
    allow read: if resource.data.visibility == 'public';
  }
}

ปฏิเสธ: กฎนี้จะปฏิเสธไปป์ไลน์ต่อไปนี้เนื่องจากระยะ addFields เกิดขึ้นก่อนการกรองเอกสารที่ visibility เป็น public:

const results = await db.pipeline()
  .collection("/cities")
  // Filters after a modification stage are ignored by Rules.
  .addFields(constant(1000).as("population"))
  .where(eq(field("visibility"), constant("public")))
  .execute();

อนุญาต: กฎนี้อนุญาตไปป์ไลน์ต่อไปนี้เนื่องจากระยะ where(eq(field("visibility"), constant("public"))) เกิดขึ้นก่อนระยะการแก้ไขใดๆ

const results = await db.pipeline()
  .collection("/cities")
  .where(eq(field("visibility"), constant("public")))
  .addFields(constant(1000).as("population"))
  .execute();

ระยะของภาษาในการจัดการข้อมูลและกฎความปลอดภัย

เมื่อดำเนินการขั้นตอนภาษาในการจัดการข้อมูล (DML) (insert(), update(), upsert() และ delete()) กฎความปลอดภัยจะประเมินสิทธิ์แตกต่างจากการค้นหาเพื่ออ่าน ดังนี้

  • การประเมินกฎต่อเอกสาร: การดำเนินการ DML จะประเมินกฎความปลอดภัยกับการเปลี่ยนแปลงเอกสารแต่ละรายการ ซึ่งแตกต่างจากไปป์ไลน์การอ่านที่ต้องใช้นิพจน์ตัวกรองล่วงหน้าเพื่อจำกัดการค้นหาในเวลาวางแผน
  • ตัวแปรของกฎ
    • request.resource: มีข้อมูลเอกสารที่เสนอสำหรับการดำเนินการ insert(), update() และ upsert()
    • resource: มีข้อมูลเอกสารปัจจุบันที่จัดเก็บไว้ในฐานข้อมูลก่อนการแก้ไข (ใช้ได้กับ update(), delete() และ upsert() ของเอกสารที่มีอยู่)

ขีดจำกัดของระบบและลักษณะการทำงานของการดำเนินการด้วย DML

เมื่อเรียกใช้การดำเนินการ DML ด้วยกฎความปลอดภัย โปรดคำนึงถึงขีดจำกัดของระบบและลักษณะการทำงานต่อไปนี้

ขีดจำกัดการค้นหา 20 ครั้งใน get() และ exists()

กฎความปลอดภัยของ Firebase อนุญาตให้ค้นหาเอกสาร (get() หรือ exists()) ได้สูงสุด 20 รายการในคำขอทั้งหมด

เมื่อไปป์ไลน์ DML ทำงานนอกธุรกรรมที่ชัดเจน Cloud Firestore จะประมวลผลการเขียนเอกสารเป็นกลุ่ม เนื่องจากแต่ละกลุ่มจะดำเนินการในธุรกรรมของตนเอง และเอกสารอาจมีการแก้ไขพร้อมกันระหว่างกลุ่ม ระบบจึงจะประเมินกฎความปลอดภัยและการค้นหาเอกสารอีกครั้งสำหรับการเขียนแต่ละกลุ่ม

แม้ว่ากฎความปลอดภัยจะประเมินกับเอกสารแบบคงที่รายการเดียว (เช่น การตรวจสอบว่าผู้ใช้เป็นผู้ดูแลระบบหรือไม่) ให้ทำดังนี้

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /posts/{postId} {
      allow write: if get(/databases/$(database)/documents/users/$(request.auth.uid)).data.isAdmin == true;
    }
  }
}

การเขียนแบบกลุ่มแต่ละชุดที่มีเอกสาร 10 รายการจะทำการค้นหา ด้วยเหตุนี้ หลังจากเขียนเอกสารที่เป็นไปได้ทั้งหมดแล้ว ระบบจะถึงขีดจำกัดการค้นหา 20 รายการสำหรับคำขอ และไปป์ไลน์จะล้มเหลวพร้อมข้อผิดพลาด PERMISSION_DENIED นอกจากนี้ การค้นหาแต่ละครั้งที่ดำเนินการต่อกลุ่มการเขียนจะทำให้เกิดการเรียกเก็บเงินสำหรับการอ่านของตัวเอง

สำเร็จบางส่วนและ PERMISSION_DENIED

เมื่อดำเนินการนอกธุรกรรม การดำเนินการ DML จะคอมมิตการเขียนเป็นกลุ่มตามลำดับ หากกลุ่มล้มเหลว ไม่ว่าจะเนื่องจากเอกสารไม่เป็นไปตามเงื่อนไขของกฎหรือเนื่องจากเกินขีดจำกัดการค้นหา 20 รายการ ไปป์ไลน์จะหยุดทำงานและแสดงข้อผิดพลาด PERMISSION_DENIED

อย่างไรก็ตาม ระบบจะไม่ย้อนกลับกลุ่มที่คอมมิตแล้ว ฐานข้อมูลจะยังคงอยู่ในสถานะอัปเดตบางส่วน เนื่องจากไคลเอ็นต์ได้รับข้อผิดพลาดโดยไม่มีรายการเอกสารที่แก้ไข คุณจึงต้องค้นหาข้อมูลเพื่อตรวจสอบว่าการเขียนใดสำเร็จ

การวินิจฉัยข้อผิดพลาดของกฎด้วยบันทึกการตรวจสอบระบบคลาวด์

ด้วยเหตุผลด้านความปลอดภัย ข้อผิดพลาด PERMISSION_DENIED ที่แสดงต่อไคลเอ็นต์จะไม่เปิดเผยว่าเงื่อนไขของกฎความปลอดภัยใดไม่สำเร็จ หรือมีการค้นหาถึงขีดจำกัด 20 ครั้งแล้วหรือไม่

หากต้องการวิเคราะห์การประเมินกฎที่ไม่สำเร็จ ผู้ดูแลระบบโปรเจ็กต์สามารถตรวจสอบบันทึกการตรวจสอบของ Cloud ในคอนโซล Google Cloud (ในส่วน Firestore > การตรวจสอบ หรือใน Logs Explorer) เพื่อดูรายการ EvaluationError โดยละเอียด