اختبر قواعد أمان Cloud Firestore

أثناء إنشاء تطبيقك، قد ترغب في تأمين الوصول إلى قاعدة بيانات Cloud Firestore الخاصة بك. ومع ذلك، قبل الإطلاق، ستحتاج إلى قواعد أمان Cloud Firestore أكثر دقة. باستخدام محاكي Cloud Firestore، بالإضافة إلى إنشاء نماذج أولية واختبار الميزات والسلوك العام لتطبيقك، يمكنك كتابة اختبارات الوحدة التي تتحقق من سلوك قواعد أمان Cloud Firestore الخاصة بك.

بداية سريعة

بالنسبة لبعض حالات الاختبار الأساسية ذات القواعد البسيطة، جرّب نموذج البدء السريع .

فهم قواعد أمان Cloud Firestore

قم بتنفيذ قواعد Firebase Authentication و Cloud Firestore Security للمصادقة والترخيص والتحقق من صحة البيانات بدون خادم عند استخدام مكتبات عملاء الهاتف المحمول والويب.

تتضمن قواعد أمان Cloud Firestore جزأين:

  1. عبارة match تحدد المستندات الموجودة في قاعدة البيانات الخاصة بك.
  2. تعبير allow الذي يتحكم في الوصول إلى تلك المستندات.

تتحقق مصادقة Firebase من بيانات اعتماد المستخدمين وتوفر الأساس لأنظمة الوصول المستندة إلى المستخدم والدور.

يتم تقييم كل طلب قاعدة بيانات من مكتبة عميل 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 وتقييم القواعد وفقًا لذلك، مما يسمح بربط تطبيقك مباشرةً بالمحاكيات في التكامل والاختبارات اليدوية.

تشغيل اختبارات الوحدة المحلية

قم بإجراء اختبارات الوحدة المحلية باستخدام v9 JavaScript SDK

يوزع Firebase مكتبة اختبار وحدة قواعد الأمان مع الإصدار 9 JavaScript SDK والإصدار 8 SDK. تختلف واجهات برمجة تطبيقات المكتبة بشكل كبير. نوصي بمكتبة اختبار الإصدار 9، التي تعتبر أكثر بساطة وتتطلب إعدادًا أقل للاتصال بالمحاكيات وبالتالي تجنب الاستخدام غير المقصود لموارد الإنتاج بشكل آمن. من أجل التوافق مع الإصدارات السابقة، نواصل إتاحة مكتبة اختبار الإصدار الثامن .

استخدم وحدة @firebase/rules-unit-testing للتفاعل مع المحاكي الذي يتم تشغيله محليًا. إذا حصلت على مهلات أو أخطاء ECONNREFUSED ، فتحقق مرة أخرى من تشغيل المحاكي بالفعل.

نوصي بشدة باستخدام إصدار حديث من Node.js حتى تتمكن من استخدام تدوين async/await . تتضمن جميع السلوكيات التي قد ترغب في اختبارها تقريبًا وظائف غير متزامنة، وقد تم تصميم وحدة الاختبار للعمل مع التعليمات البرمجية المستندة إلى Promise.

مكتبة v9 Rules Unit Testing تكون دائمًا على دراية بالمحاكيات ولا تمس موارد الإنتاج الخاصة بك أبدًا.

يمكنك استيراد المكتبة باستخدام بيانات الاستيراد المعيارية 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 .
  • إعداد مجموعة اختبار وكل اختبار قبل/بعد مع استدعاءات لتنظيف بيانات الاختبار والبيئة، مثل 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 ، الذي يتصرف مثل مستخدم المصادقة المصادق عليه. ستحتوي الطلبات التي تم إنشاؤها عبر السياق الذي تم إرجاعه على رمز مصادقة وهمي مرفق. اختياريًا، قم بتمرير كائن يحدد المطالبات المخصصة أو التجاوزات لحمولات رمز المصادقة المميز.

استخدم كائن سياق الاختبار الذي تم إرجاعه في اختباراتك للوصول إلى أي مثيلات محاكي تم تكوينها، بما في ذلك تلك التي تم تكوينها باستخدام 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 الذي تم إرجاعه مع واجهات برمجة تطبيقات SDK للعميل (v9 modular أو v9 compat).

تصور تقييمات القواعد

يتيح لك محاكي Cloud Firestore تصور طلبات العميل في واجهة مستخدم Emulator Suite، بما في ذلك تتبع التقييم لقواعد أمان 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 الخاصة بك تسمح/ترفض الحالات المختلفة بشكل صحيح.