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

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

เริ่มต้นอย่างรวดเร็ว

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

ทำความเข้าใจกฎความปลอดภัยของ Cloud Firestore

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

กฎความปลอดภัยของ Cloud Firestore ประกอบด้วยสองส่วน:

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

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

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

เรียนรู้เพิ่มเติมเกี่ยวกับกฎความปลอดภัยของ Cloud Firestore ใน เริ่มต้นใช้งานกฎความปลอดภัยของ Cloud Firestore

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

หากต้องการติดตั้งโปรแกรมจำลอง 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 ของคุณ และใช้กฎเหล่านั้นกับทุกโปรเจ็กต์ หากคุณไม่ได้ระบุเส้นทางของไฟล์ในเครื่องหรือใช้เมธอด loadFirestoreRules ตามที่อธิบายไว้ด้านล่าง โปรแกรมจำลองจะถือว่าโปรเจ็กต์ทั้งหมดมีกฎแบบเปิด
  • แม้ว่า Firebase SDK ส่วนใหญ่ จะทำงานร่วมกับอีมูเลเตอร์โดยตรง แต่มีเพียง @firebase/rules-unit-testing ไลบรารี่เท่านั้นที่รองรับการจำลองการตรวจ auth ในกฎความปลอดภัย ทำให้การทดสอบหน่วยง่ายขึ้นมาก นอกจากนี้ ไลบรารียังรองรับฟีเจอร์เฉพาะของโปรแกรมจำลองบางอย่าง เช่น การล้างข้อมูลทั้งหมด ดังที่แสดงด้านล่าง
  • นอกจากนี้ โปรแกรมจำลองจะยอมรับโทเค็น Firebase Auth ที่ใช้งานจริงที่จัดทำผ่าน Client SDK และประเมินกฎตามลำดับ ซึ่งช่วยให้สามารถเชื่อมต่อแอปพลิเคชันของคุณกับโปรแกรมจำลองได้โดยตรงในการบูรณาการและการทดสอบด้วยตนเอง

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

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

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

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

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

ไลบรารีการทดสอบกฎกฎ 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.

เมื่อนำเข้าแล้ว การดำเนินการทดสอบหน่วยจะเกี่ยวข้องกับ:

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

วิธีการทั่วไปและฟังก์ชันอรรถประโยชน์

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

initializeTestEnvironment() => RulesTestEnvironment

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

ฟังก์ชันยอมรับออบเจ็กต์ทางเลือกที่กำหนด 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 ซึ่งทำงานเหมือนกับผู้ใช้ Authentication ที่ได้รับการรับรองความถูกต้อง คำขอที่สร้างขึ้นผ่านบริบทที่ส่งคืนจะมีโทเค็นการรับรองความถูกต้องจำลองแนบมาด้วย อีกทางหนึ่งคือส่งผ่านออบเจ็กต์ที่กำหนดการอ้างสิทธิ์ที่กำหนดเองหรือการแทนที่สำหรับเพย์โหลดโทเค็นการรับรองความถูกต้อง

ใช้ออบเจ็กต์บริบทการทดสอบที่ส่งคืนในการทดสอบของคุณเพื่อเข้าถึงอินสแตนซ์ตัวจำลองที่กำหนดค่าไว้ รวมถึงอินสแตนซ์ที่กำหนดค่าด้วย 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(), '/users/alice'), { ... });

RulesTestEnvironment.unauthenticatedContext() => RulesTestContext

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

ใช้ออบเจ็กต์บริบทการทดสอบที่ส่งคืนในการทดสอบของคุณเพื่อเข้าถึงอินสแตนซ์ตัวจำลองที่กำหนดค่าไว้ รวมถึงอินสแตนซ์ที่กำหนดค่าด้วย 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()

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

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

RulesTestEnvironment.cleanup()

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

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

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

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

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

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

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

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

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

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

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

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

RulesTestEnvironment.clearFirestore() => Promise<void>

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

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

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

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

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

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

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

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

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

หากต้องการรับรายงาน ให้ค้นหาปลายทางที่เปิดเผยบนโปรแกรมจำลองขณะที่เครื่องกำลังทำงานอยู่ สำหรับเวอร์ชันที่เหมาะกับเบราว์เซอร์ ให้ใช้ 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 ปกติ แต่ใน Firebase Test SDK เราได้จัดเตรียมเมธอด initializeTestApp() ไว้ในไลบรารี rules-unit-testing ซึ่งใช้ฟิลด์ auth หมายเลขอ้างอิง Firebase ที่สร้างขึ้นโดยใช้วิธีนี้จะทำงานเหมือนกับว่าได้รับการรับรองความถูกต้องสำเร็จแล้วว่าเป็นเอนทิตีใดก็ตามที่คุณระบุ หากคุณส่งผ่าน null มันจะทำงานเป็นผู้ใช้ที่ไม่ได้รับการรับรองความถูกต้อง ( auth != null จะล้มเหลว เป็นต้น)

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

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

พฤติกรรมการทดสอบไม่สอดคล้องกัน

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

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

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

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

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

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

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

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

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