قوانین امنیتی Cloud Firestore خود را آزمایش کنید

همانطور که در حال ساخت برنامه خود هستید، ممکن است بخواهید دسترسی به پایگاه داده Cloud Firestore خود را قفل کنید. با این حال، قبل از راه‌اندازی، Cloud Firestore Security Rules دقیق‌تری نیاز خواهید داشت. با شبیه‌ساز Cloud Firestore ، علاوه بر نمونه‌سازی اولیه و آزمایش ویژگی‌ها و رفتار کلی برنامه خود، می‌توانید تست‌های واحدی بنویسید که رفتار Cloud Firestore Security Rules شما را بررسی کنند.

شروع سریع

برای چند مورد آزمون اساسی با قوانین ساده، نمونه شروع سریع را امتحان کنید.

Cloud Firestore Security Rules درک کنید

هنگام استفاده از کتابخانه‌های کلاینت موبایل و وب، برای احراز هویت، مجوزدهی و اعتبارسنجی داده‌ها در محیط بدون سرور، Cloud Firestore Security Rules Firebase Authentication و Cloud Firestore را پیاده‌سازی کنید.

Cloud Firestore Security Rules شامل دو بخش است:

  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"

وقتی شبیه‌ساز شروع به کار می‌کند، سعی می‌کند روی پورت پیش‌فرض (۸۰۸۰) اجرا شود. می‌توانید با تغییر بخش "emulators" در فایل firebase.json خود، پورت شبیه‌ساز را تغییر دهید:

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

قبل از اجرای شبیه‌ساز

قبل از شروع استفاده از شبیه‌ساز، موارد زیر را در نظر داشته باشید:

  • شبیه‌ساز در ابتدا قوانین مشخص شده در فیلد firestore.rules از فایل firebase.json شما را بارگذاری می‌کند. این شبیه‌ساز نام یک فایل محلی حاوی Cloud Firestore Security Rules شما را دریافت کرده و آن قوانین را برای همه پروژه‌ها اعمال می‌کند. اگر مسیر فایل محلی را ارائه ندهید یا از متد loadFirestoreRules همانطور که در زیر توضیح داده شده است استفاده نکنید، شبیه‌ساز با همه پروژه‌ها به عنوان پروژه‌هایی که دارای قوانین باز هستند رفتار می‌کند.
  • در حالی که اکثر کیت‌های توسعه نرم‌افزار فایربیس (Firebase SDK) مستقیماً با شبیه‌سازها کار می‌کنند، فقط کتابخانه @firebase/rules-unit-testing از شبیه‌سازی احراز auth در قوانین امنیتی (Security Rules) پشتیبانی می‌کند و تست‌های واحد را بسیار آسان‌تر می‌کند. علاوه بر این، این کتابخانه از چند ویژگی خاص شبیه‌ساز مانند پاک کردن تمام داده‌ها، همانطور که در زیر ذکر شده است، پشتیبانی می‌کند.
  • این شبیه‌سازها همچنین توکن‌های تأیید اعتبار Firebase ارائه شده از طریق SDKهای کلاینت را می‌پذیرند و بر اساس آنها قوانین را ارزیابی می‌کنند، که امکان اتصال مستقیم برنامه شما به شبیه‌سازها را در تست‌های یکپارچه‌سازی و دستی فراهم می‌کند.

اجرای تست‌های واحد محلی

اجرای تست‌های واحد محلی با SDK جاوا اسکریپت نسخه ۹

فایربیس یک کتابخانه تست واحد قوانین امنیتی را هم با نسخه ۹ جاوا اسکریپت SDK و هم با نسخه ۸ SDK خود توزیع می‌کند. APIهای این کتابخانه تفاوت قابل توجهی دارند. ما کتابخانه تست نسخه ۹ را توصیه می‌کنیم که ساده‌تر است و برای اتصال به شبیه‌سازها به تنظیمات کمتری نیاز دارد و بنابراین با خیال راحت از استفاده تصادفی از منابع تولید جلوگیری می‌کند. برای سازگاری با نسخه‌های قبلی، ما همچنان کتابخانه تست نسخه ۸ را در دسترس قرار می‌دهیم.

از ماژول @firebase/rules-unit-testing برای تعامل با شبیه‌ساز محلی استفاده کنید. اگر با خطای timeout یا ECONNREFUSED مواجه شدید، دوباره بررسی کنید که شبیه‌ساز واقعاً در حال اجرا باشد.

ما اکیداً توصیه می‌کنیم از نسخه اخیر Node.js استفاده کنید تا بتوانید از نمادگذاری async/await استفاده کنید. تقریباً تمام رفتارهایی که ممکن است بخواهید آزمایش کنید شامل توابع ناهمزمان است و ماژول آزمایش برای کار با کد مبتنی بر Promise طراحی شده است.

کتابخانه تست واحد قوانین نسخه ۹ همیشه از شبیه‌سازها آگاه است و هرگز به منابع تولید شما دست نمی‌زند.

شما کتابخانه را با استفاده از دستورات import ماژولار نسخه ۹ وارد می‌کنید. برای مثال:

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 تقلید می‌کنند.

روش‌های رایج و توابع سودمندی

همچنین به روش‌های تست مخصوص شبیه‌ساز در 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 ایجاد می‌کند که مانند یک کاربر احراز هویت‌شده‌ی احراز هویت رفتار می‌کند. درخواست‌های ایجاد شده از طریق زمینه‌ی برگردانده شده، یک توکن احراز هویت ساختگی (mock Authentication token) ضمیمه خواهند داشت. در صورت تمایل، می‌توانید یک شیء تعریف‌کننده‌ی ادعاها یا لغوهای سفارشی برای بارهای توکن احراز هویت ارسال کنید.

از شیء زمینه تست برگشتی در تست‌های خود برای دسترسی به هر نمونه شبیه‌ساز پیکربندی‌شده، از جمله مواردی که با 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 ایجاد می‌کند که مانند کلاینتی رفتار می‌کند که از طریق احراز هویت وارد سیستم نشده است. درخواست‌هایی که از طریق context برگردانده می‌شوند، توکن‌های 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()

یک تابع راه‌اندازی آزمایشی را با زمینه‌ای اجرا کنید که طوری رفتار کند که انگار قوانین امنیتی غیرفعال شده‌اند.

این متد یک تابع فراخوانی (callback) می‌گیرد که زمینه‌ی دور زدن قوانین امنیتی (Security-Rules-bypassing context) را می‌گیرد و یک 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'), { ... });

روش‌های خاص شبیه‌ساز

همچنین به روش‌های تست رایج و توابع کاربردی در v9 SDK مراجعه کنید.

RulesTestEnvironment.clearFirestore() => Promise<void>

این متد داده‌های موجود در پایگاه داده Firestore که متعلق به projectId پیکربندی شده برای شبیه‌ساز Firestore هستند را پاک می‌کند.

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

این متد یک نمونه Firestore برای این زمینه آزمایشی دریافت می‌کند. نمونه Firebase JS Client SDK برگردانده شده می‌تواند با APIهای SDK کلاینت (v9 modular یا v9 compat) استفاده شود.

ارزیابی قوانین را تجسم کنید

شبیه‌ساز Cloud Firestore به شما امکان می‌دهد درخواست‌های کلاینت را در رابط کاربری Emulator Suite تجسم کنید، از جمله ردیابی ارزیابی برای قوانین امنیتی Firebase.

برای مشاهده جزئیات ارزیابی هر درخواست، تب Firestore > Requests را باز کنید.

مانیتور درخواست‌های شبیه‌ساز Firestore که ارزیابی‌های قوانین امنیتی را نشان می‌دهد

تولید گزارش‌های تست

پس از اجرای مجموعه‌ای از آزمایش‌ها، می‌توانید به گزارش‌های پوشش آزمایش دسترسی داشته باشید که نشان می‌دهد هر یک از قوانین امنیتی شما چگونه ارزیابی شده‌اند.

برای دریافت گزارش‌ها، در حالی که شبیه‌ساز در حال اجرا است، از یک نقطه پایانیِ در معرض دید، کوئری بگیرید. برای نسخه‌ای که با مرورگر سازگار باشد، از URL زیر استفاده کنید:

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

این دستور، قوانین شما را به عبارات و زیرعبارات تقسیم می‌کند که می‌توانید با نگه داشتن ماوس روی آنها، اطلاعات بیشتری از جمله تعداد ارزیابی‌ها و مقادیر برگشتی را مشاهده کنید. برای نسخه خام JSON این داده‌ها، آدرس اینترنتی زیر را در کوئری خود وارد کنید:

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

تفاوت‌های بین شبیه‌ساز و محیط تولید

  1. لازم نیست صریحاً یک پروژه Cloud Firestore ایجاد کنید. شبیه‌ساز به‌طور خودکار هر نمونه‌ای را که مورد دسترسی قرار گیرد، ایجاد می‌کند.
  2. شبیه‌ساز Cloud Firestore با جریان عادی Firebase Authentication کار نمی‌کند. در عوض، در Firebase Test SDK، ما متد initializeTestApp() را در کتابخانه rules-unit-testing ارائه کرده‌ایم که یک فیلد auth می‌گیرد. هندل Firebase ایجاد شده با استفاده از این متد، طوری رفتار خواهد کرد که انگار با موفقیت به عنوان هر موجودیتی که ارائه می‌دهید، احراز هویت شده است. اگر null را وارد کنید، مانند یک کاربر احراز هویت نشده رفتار خواهد کرد (برای مثال، auth != null rules fail خواهد شد).

عیب‌یابی مشکلات شناخته‌شده

هنگام استفاده از شبیه‌ساز Cloud Firestore ، ممکن است با مشکلات شناخته‌شده‌ی زیر مواجه شوید. برای عیب‌یابی هرگونه رفتار غیرعادی که تجربه می‌کنید، از راهنمایی‌های زیر پیروی کنید. این یادداشت‌ها با در نظر گرفتن کتابخانه‌ی تست واحد Security Rules نوشته شده‌اند، اما رویکردهای کلی برای هر SDK فایربیس قابل اجرا هستند.

رفتار آزمون متناقض است

اگر تست‌های شما گهگاه با موفقیت و شکست مواجه می‌شوند، حتی بدون هیچ تغییری در خود تست‌ها، ممکن است لازم باشد تأیید کنید که توالی آنها به درستی انجام شده است. بیشتر تعاملات با شبیه‌ساز ناهمزمان است، بنابراین دوباره بررسی کنید که تمام کدهای ناهمزمان به درستی توالی یافته باشند. می‌توانید این توالی را با زنجیره‌سازی promiseها یا استفاده‌ی آزادانه از نمادگذاری await اصلاح کنید.

به طور خاص، عملیات async زیر را بررسی کنید:

  • تنظیم قوانین امنیتی، برای مثال با initializeTestEnvironment .
  • خواندن و نوشتن داده‌ها، برای مثال با db.collection("users").doc("alice").get() .
  • دستورات عملیاتی، شامل assertSucceeds و assertFails .

تست‌ها فقط در اولین باری که شبیه‌ساز را بارگذاری می‌کنید، با موفقیت انجام می‌شوند

این شبیه‌ساز دارای قابلیت تنظیم وضعیت (stateful) است. تمام داده‌های نوشته شده در آن را در حافظه ذخیره می‌کند، بنابراین هر زمان که شبیه‌ساز خاموش شود، هرگونه داده‌ای از بین می‌رود. اگر چندین تست را روی یک شناسه پروژه اجرا می‌کنید، هر تست می‌تواند داده‌هایی تولید کند که ممکن است بر تست‌های بعدی تأثیر بگذارد. می‌توانید از هر یک از روش‌های زیر برای دور زدن این رفتار استفاده کنید:

  • برای هر تست از شناسه‌های پروژه منحصر به فرد استفاده کنید. توجه داشته باشید که اگر این کار را انجام دهید، باید initializeTestEnvironment به عنوان بخشی از هر تست فراخوانی کنید؛ قوانین فقط برای شناسه پروژه پیش‌فرض به طور خودکار بارگذاری می‌شوند.
  • تست‌های خود را طوری بازسازی کنید که با داده‌های نوشته‌شده‌ی قبلی تعامل نداشته باشند (برای مثال، برای هر تست از یک مجموعه‌ی متفاوت استفاده کنید).
  • تمام داده‌های نوشته شده در طول یک آزمایش را حذف کنید.

تنظیمات تست بسیار پیچیده است

هنگام تنظیم تست خود، ممکن است بخواهید داده‌ها را به گونه‌ای تغییر دهید که Cloud Firestore Security Rules شما در واقع اجازه آن را نمی‌دهند. اگر قوانین شما تنظیم تست را پیچیده می‌کنند، سعی کنید از RulesTestEnvironment.withSecurityRulesDisabled در مراحل تنظیم خود استفاده کنید، بنابراین خواندن و نوشتن باعث ایجاد خطاهای PERMISSION_DENIED نمی‌شود.

پس از آن، تست شما می‌تواند عملیات را به عنوان یک کاربر احراز هویت شده یا احراز هویت نشده به ترتیب با استفاده از RulesTestEnvironment.authenticatedContext و unauthenticatedContext انجام دهد. این به شما امکان می‌دهد تا اعتبارسنجی کنید که Cloud Firestore Security Rules شما موارد مختلف را به درستی مجاز/رد می‌کند.