ทดสอบกฎความปลอดภัยของ Cloud Firestore

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

คู่มือเริ่มใช้งานฉบับย่อ

ลองใช้ตัวอย่างคู่มือเริ่มใช้งานฉบับย่อสำหรับกรณีทดสอบพื้นฐาน 2-3 กรณีที่มีกฎง่ายๆ

ทำความเข้าใจ Cloud Firestore Security Rules

ใช้ Firebase Authentication และ Cloud Firestore Security Rules สำหรับการตรวจสอบสิทธิ์ การให้สิทธิ์ และการตรวจสอบข้อมูลแบบ Serverless เมื่อใช้ไลบรารีของไคลเอ็นต์อุปกรณ์เคลื่อนที่และเว็บ

Cloud Firestore Security Rules ประกอบด้วย 2 ส่วน ได้แก่

  1. คำสั่ง match ที่ระบุเอกสารในฐานข้อมูล
  2. นิพจน์ allow ที่ควบคุมการเข้าถึงเอกสารเหล่านั้น

Firebase Authentication จะยืนยันข้อมูลเข้าสู่ระบบของผู้ใช้และเป็นพื้นฐานสำหรับ ระบบการเข้าถึงตามผู้ใช้และตามบทบาท

ระบบจะประเมินคำขอฐานข้อมูลแต่ละรายการจากไลบรารีของไคลเอ็นต์สำหรับอุปกรณ์เคลื่อนที่/เว็บ Cloud Firestore เทียบกับกฎความปลอดภัยก่อนที่จะอ่านหรือเขียนข้อมูลใดๆ หากกฎปฏิเสธการเข้าถึงเส้นทางเอกสารที่ระบุไว้ คำขอทั้งหมดจะไม่สำเร็จ

ดูข้อมูลเพิ่มเติมเกี่ยวกับ Cloud Firestore Security Rules ได้ที่หัวข้อ เริ่มต้นใช้งาน Cloud Firestore Security Rules

ติดตั้งโปรแกรมจำลอง

หากต้องการติดตั้งโปรแกรมจำลอง Cloud Firestore ให้ใช้ Firebase CLI และเรียกใช้คำสั่งด้านล่าง

firebase setup:emulators:firestore

เรียกใช้โปรแกรมจำลอง

เริ่มต้นด้วยการเริ่มต้นโปรเจ็กต์ Firebase ในไดเรกทอรีงาน ซึ่งเป็นขั้นตอนแรกที่พบบ่อยเมื่อใช้ Firebase CLI

firebase init

เริ่มโปรแกรมจำลองโดยใช้คำสั่งต่อไปนี้ โปรแกรมจำลองจะทำงานต่อไปจนกว่าคุณจะหยุดกระบวนการ

firebase emulators:start --only firestore

ในหลายกรณี คุณอาจต้องการเริ่มโปรแกรมจำลอง เรียกใช้ชุดการทดสอบ แล้วปิดโปรแกรมจำลองหลังจากที่การทดสอบทำงานเสร็จแล้ว คุณทำเช่นนี้ได้อย่างง่ายดายโดยใช้คำสั่ง emulators:exec

firebase emulators:exec --only firestore "./my-test-script.sh"

เมื่อเริ่มต้น โปรแกรมจำลองจะพยายามทำงานในพอร์ตเริ่มต้น (8080) คุณสามารถ เปลี่ยนพอร์ตของโปรแกรมจำลองได้โดยแก้ไขส่วน "emulators" ของไฟล์ firebase.json

{
  // ...
  "emulators": {
    "firestore": {
      "port": "YOUR_PORT"
    }
  }
}

สิ่งที่ควรทราบก่อนเรียกใช้โปรแกรมจำลอง

โปรดคำนึงถึงสิ่งต่อไปนี้ก่อนที่จะเริ่มใช้โปรแกรมจำลอง

  • โปรแกรมจำลองจะโหลดกฎที่ระบุไว้ในช่อง firestore.rules ของไฟล์ firebase.json ในตอนแรก โดยจะคาดหวังชื่อไฟล์ในเครื่องที่มีCloud Firestore Security Rulesและใช้กฎเหล่านั้นกับทุกโปร101}เจ็กต์ หากคุณไม่ระบุเส้นทางไฟล์ในเครื่องหรือใช้เมธอด loadFirestoreRules ตามที่อธิบายไว้ด้านล่าง โปรแกรมจำลองจะถือว่าทุกโปรเจ็กต์มีกฎแบบเปิด
  • แม้ว่า Firebase SDK ส่วนใหญ่ จะทำงานร่วมกับโปรแกรมจำลองได้โดยตรง แต่มีเพียงไลบรารี @firebase/rules-unit-testing เท่านั้นที่รองรับ การจำลอง auth ในกฎความปลอดภัย ซึ่งทำให้การทดสอบ 1 หน่วยง่ายขึ้นมาก นอกจากนี้ ไลบรารียังรองรับฟีเจอร์เฉพาะของโปรแกรมจำลองบางอย่าง เช่น การล้างข้อมูลทั้งหมด ตามที่ระบุไว้ด้านล่าง
  • นอกจากนี้ โปรแกรมจำลองยังยอมรับโทเค็นการตรวจสอบสิทธิ์ Firebase สำหรับเวอร์ชันที่ใช้งานจริงที่ระบุผ่าน Client SDK และประเมินกฎตามนั้น ซึ่งช่วยให้คุณเชื่อมต่อแอปพลิเคชันกับโปรแกรมจำลองได้โดยตรงในการทดสอบการผสานรวมและการทดสอบด้วยตนเอง

เรียกใช้การทดสอบ 1 หน่วยในเครื่อง

เรียกใช้การทดสอบ 1 หน่วยในเครื่องด้วย JavaScript SDK v9

Firebase จัดจำหน่ายไลบรารีการทำ Unit Test สำหรับกฎความปลอดภัยพร้อมกับ JavaScript SDK เวอร์ชัน 9 และ SDK เวอร์ชัน 8 โดย API ของไลบรารีจะแตกต่างกันอย่างมาก เราขอแนะนำไลบรารีการทดสอบ v9 ซึ่งมีประสิทธิภาพมากขึ้นและต้องมีการตั้งค่าน้อยลงเพื่อเชื่อมต่อกับโปรแกรมจำลอง จึงช่วยหลีกเลี่ยงการใช้ทรัพยากรเวอร์ชันที่ใช้งานจริงโดยไม่ได้ตั้งใจได้อย่างปลอดภัย เราจะยังคงให้บริการ ไลบรารีการทดสอบ v8 ต่อไปเพื่อความเข้ากันได้แบบย้อนกลับ

ใช้โมดูล @firebase/rules-unit-testing เพื่อโต้ตอบกับโปรแกรมจำลองที่ทำงานในเครื่อง หากคุณพบข้อผิดพลาดการหมดเวลาหรือ ECONNREFUSED ให้ตรวจสอบอีกครั้งว่าโปรแกรมจำลองกำลังทำงานอยู่จริง

เราขอแนะนำอย่างยิ่งให้ใช้ Node.js เวอร์ชันล่าสุดเพื่อให้คุณใช้สัญกรณ์ async/await ได้ ลักษณะการทำงานเกือบทั้งหมดที่คุณอาจต้องการทดสอบเกี่ยวข้องกับฟังก์ชันแบบอะซิงโครนัส และโมดูลการทดสอบได้รับการออกแบบมาให้ทำงานร่วมกับโค้ดที่อิงตาม Promise

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

คุณนำเข้าไลบรารีโดยใช้คำสั่งนำเข้าแบบแยกส่วน v9 เช่น

import {
  assertFails,
  assertSucceeds,
  initializeTestEnvironment
} from "@firebase/rules-unit-testing"

// Use `const { … } = require("@firebase/rules-unit-testing")` if imports are not supported
// Or we suggest `const testing = require("@firebase/rules-unit-testing")` if necessary.

เมื่อนำเข้าแล้ว การใช้การทดสอบ 1 หน่วยจะเกี่ยวข้องกับสิ่งต่อไปนี้

  • สร้างและกำหนดค่า RulesTestEnvironment ด้วยการเรียก initializeTestEnvironment
  • ตั้งค่าข้อมูลทดสอบโดยไม่ทริกเกอร์กฎโดยใช้วิธีที่สะดวกซึ่งช่วยให้คุณข้ามกฎได้ชั่วคราว นั่นคือ RulesTestEnvironment.withSecurityRulesDisabled
  • ตั้งค่าชุดการทดสอบและฮุกก่อน/หลังการทดสอบแต่ละรายการด้วยการเรียกเพื่อล้างข้อมูลทดสอบและสภาพแวดล้อม เช่น RulesTestEnvironment.cleanup() หรือ RulesTestEnvironment.clearFirestore()
  • ใช้กรณีทดสอบที่เลียนแบบสถานะการตรวจสอบสิทธิ์โดยใช้ RulesTestEnvironment.authenticatedContext และ RulesTestEnvironment.unauthenticatedContext

เมธอดและฟังก์ชันยูทิลิตีทั่วไป

ดูเมธอดการทดสอบเฉพาะของโปรแกรมจำลองใน SDK v9 ด้วย

initializeTestEnvironment() => RulesTestEnvironment

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

ฟังก์ชันนี้ยอมรับออบเจ็กต์ที่ไม่บังคับซึ่งกำหนด TestEnvironmentConfig ซึ่งประกอบด้วยรหัสโปรเจ็กต์และการตั้งค่าการกำหนดค่าโปรแกรมจำลอง

let testEnv = await initializeTestEnvironment({
  projectId: "demo-project-1234",
  firestore: {
    rules: fs.readFileSync("firestore.rules", "utf8"),
  },
});

RulesTestEnvironment.authenticatedContext({ user_id: string, tokenOptions?: TokenOptions }) => RulesTestContext

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

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

// Assuming a Firestore app and the Firestore emulator for this example
import { setDoc } from "firebase/firestore";

const alice = testEnv.authenticatedContext("alice", {  });
// Use the Firestore instance associated with this context
await assertSucceeds(setDoc(alice.firestore().doc('/users/alice'), { ... });

RulesTestEnvironment.unauthenticatedContext() => RulesTestContext

เมธอดนี้จะสร้าง RulesTestContext ซึ่งทำงานเหมือนกับไคลเอ็นต์ที่ไม่ได้เข้าสู่ระบบผ่านการตรวจสอบสิทธิ์ คำขอที่สร้างผ่านบริบทที่แสดงผลจะไม่มีโทเค็นการตรวจสอบสิทธิ์ Firebase แนบอยู่

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

// Assuming a Cloud Storage app and the Storage emulator for this example
import { getStorage, ref, deleteObject } from "firebase/storage";

const alice = testEnv.unauthenticatedContext();

// Use the Cloud Storage instance associated with this context
const desertRef = ref(alice.storage(), 'images/desert.jpg');
await assertSucceeds(deleteObject(desertRef));

RulesTestEnvironment.withSecurityRulesDisabled()

เรียกใช้ฟังก์ชันการตั้งค่าการทดสอบด้วยบริบทที่ทำงานราวกับว่ากฎความปลอดภัยถูกปิดใช้

เมธอดนี้ใช้ฟังก์ชัน Callback ซึ่งใช้บริบทที่ข้ามกฎความปลอดภัยและแสดงผล Promise ระบบจะทำลายบริบทเมื่อ Promise ได้รับการแก้ไข / ปฏิเสธ

RulesTestEnvironment.cleanup()

เมธอดนี้จะทำลาย RulesTestContexts ทั้งหมดที่สร้างขึ้นในสภาพแวดล้อมการทดสอบและล้างทรัพยากรพื้นฐาน ซึ่งช่วยให้การออกจากการทดสอบเป็นไปอย่างราบรื่น

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

assertSucceeds(pr: Promise<any>)) => Promise<any>

นี่คือฟังก์ชันยูทิลิตีของกรณีทดสอบ

ฟังก์ชันนี้จะยืนยันว่า Promise ที่ให้มาซึ่งครอบการดำเนินการของโปรแกรมจำลองจะได้รับการแก้ไขโดยไม่มีการละเมิดกฎความปลอดภัย

await assertSucceeds(setDoc(alice.firestore(), '/users/alice'), { ... });

assertFails(pr: Promise<any>)) => Promise<any>

นี่คือฟังก์ชันยูทิลิตีของกรณีทดสอบ

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

await assertFails(setDoc(alice.firestore(), '/users/bob'), { ... });

เมธอดเฉพาะของโปรแกรมจำลอง

ดูเมธอดการทดสอบและฟังก์ชันยูทิลิตีทั่วไปใน SDK v9 ด้วย

RulesTestEnvironment.clearFirestore() => Promise<void>

เมธอดนี้จะล้างข้อมูลในฐานข้อมูล Firestore ที่เป็นของ projectId ที่กำหนดค่าไว้สำหรับโปรแกรมจำลอง Firestore

RulesTestContext.firestore(settings?: Firestore.FirestoreSettings) => Firestore;

เมธอดนี้จะรับอินสแตนซ์ Firestore สำหรับบริบทการทดสอบนี้ อินสแตนซ์ Firebase JS Client SDK ที่แสดงผลสามารถใช้กับ Client SDK API (v9 แบบแยกส่วนหรือ v9 แบบเข้ากันได้)

แสดงภาพการประเมินกฎ

โปรแกรมจำลอง Cloud Firestore ช่วยให้คุณแสดงภาพคำขอของไคลเอ็นต์ใน UI ของชุดโปรแกรมจำลอง รวมถึงการติดตามการประเมินสำหรับกฎความปลอดภัยของ Firebase

เปิดแท็บ Firestore > คำขอ เพื่อดูลำดับการประเมินโดยละเอียดสำหรับคำขอแต่ละรายการ

เครื่องมือตรวจสอบคำขอของโปรแกรมจำลอง Firestore ที่แสดงการประเมินกฎความปลอดภัย

สร้างรายงานการทดสอบ

หลังจากเรียกใช้ชุดการทดสอบแล้ว คุณจะเข้าถึงรายงานความครอบคลุมของการทดสอบที่แสดงวิธีประเมินกฎความปลอดภัยแต่ละข้อได้

หากต้องการรับรายงาน ให้ค้นหาปลายทางที่เปิดเผยในโปรแกรมจำลองขณะที่โปรแกรมจำลองกำลังทำงานอยู่ หากต้องการเวอร์ชันที่เหมาะกับเบราว์เซอร์ ให้ใช้ URL ต่อไปนี้

http://localhost:8080/emulator/v1/projects/<project_id>:ruleCoverage.html

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

http://localhost:8080/emulator/v1/projects/<project_id>:ruleCoverage

ความแตกต่างระหว่างโปรแกรมจำลองกับเวอร์ชันที่ใช้งานจริง

  1. คุณไม่จำเป็นต้องสร้างโปรเจ็กต์ Cloud Firestore อย่างชัดแจ้ง โปรแกรมจำลองจะสร้างอินสแตนซ์ที่เข้าถึงโดยอัตโนมัติ
  2. โปรแกรมจำลอง Cloud Firestore ไม่ทำงานร่วมกับโฟลว์ Firebase Authentication ปกติ แต่เราได้จัดเตรียมเมธอด initializeTestApp() ไว้ในไลบรารี rules-unit-testing ใน Firebase Test SDK ซึ่งใช้ช่อง auth แฮนเดิล Firebase ที่สร้างขึ้นโดยใช้เมธอดนี้จะทำงานราวกับว่าได้ตรวจสอบสิทธิ์เป็นเอนทิตีที่คุณระบุไว้เรียบร้อยแล้ว หากคุณส่ง null แฮนเดิลจะทำงานเหมือนกับผู้ใช้ที่ไม่ได้ตรวจสอบสิทธิ์ (auth != null กฎจะไม่สำเร็จ เช่น)

แก้ปัญหาที่ทราบ

ขณะใช้โปรแกรมจำลอง Cloud Firestore คุณอาจพบปัญหาที่ทราบต่อไปนี้ โปรดทำตามคำแนะนำด้านล่างเพื่อแก้ปัญหาลักษณะการทำงานที่ผิดปกติที่คุณพบ หมายเหตุเหล่านี้เขียนขึ้นโดยคำนึงถึงไลบรารีการทำ Unit Test สำหรับกฎความปลอดภัย แต่แนวทางทั่วไปสามารถใช้ได้กับ Firebase SDK ทุกรายการ

ลักษณะการทำงานของการทดสอบไม่สอดคล้องกัน

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

โดยเฉพาะอย่างยิ่ง ให้ตรวจสอบการดำเนินการแบบอะซิงโครนัสต่อไปนี้

  • การตั้งค่ากฎความปลอดภัย เช่น initializeTestEnvironment
  • การอ่านและการเขียนข้อมูล เช่น db.collection("users").doc("alice").get()
  • การยืนยันการดำเนินการ รวมถึง assertSucceeds และ assertFails

การทดสอบจะผ่านเฉพาะครั้งแรกที่คุณโหลดโปรแกรมจำลอง

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

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

การตั้งค่าการทดสอบซับซ้อนมาก

เมื่อตั้งค่าการทดสอบ คุณอาจต้องการแก้ไขข้อมูลในลักษณะที่ Cloud Firestore Security Rules ไม่อนุญาต หากกฎทำให้การตั้งค่าการทดสอบซับซ้อน ให้ลองใช้ RulesTestEnvironment.withSecurityRulesDisabled ในขั้นตอนการตั้งค่า เพื่อให้การอ่านและการเขียนไม่ทริกเกอร์ข้อผิดพลาด PERMISSION_DENIED

หลังจากนั้น การทดสอบจะดำเนินการในฐานะผู้ใช้ที่ผ่านการตรวจสอบสิทธิ์หรือไม่ผ่านการตรวจสอบสิทธิ์โดยใช้ RulesTestEnvironment.authenticatedContext และ unauthenticatedContext ตามลำดับ ซึ่งช่วยให้คุณตรวจสอบได้ว่า Cloud Firestore Security Rules อนุญาต / ปฏิเสธ กรณีต่างๆ ได้อย่างถูกต้อง