अपने Cloud Firestore के सुरक्षा नियमों की जांच करें

ऐप्लिकेशन बनाते समय, हो सकता है कि आपको अपने Cloud Firestore डेटाबेस के ऐक्सेस को लॉक करना पड़े. हालांकि, ऐप्लिकेशन लॉन्च करने से पहले, आपको बेहतर तरीके से सेट अप करना होगा Cloud Firestore Security Rules. Cloud Firestore एम्युलेटर की मदद से, अपने ऐप्लिकेशन की सामान्य सुविधाओं और उसके काम करने के तरीके को प्रोटोटाइप और टेस्ट किया जा सकता है. इसके अलावा, यूनिट टेस्ट भी लिखे जा सकते हैं. इनकी मदद से, Cloud Firestore Security Rules के काम करने के तरीके की जांच की जा सकती है.

क्विकस्टार्ट

आसान नियमों वाले कुछ बुनियादी टेस्ट केस के लिए, क्विकस्टार्ट सैंपल आज़माएं.

के बारे में जानकारीCloud Firestore Security Rules

मोबाइल और वेब क्लाइंट लाइब्रेरी का इस्तेमाल करते समय, सर्वरलेस पुष्टि, अनुमति, और डेटा की पुष्टि के लिए, Firebase Authentication और Cloud Firestore Security Rules को लागू करें.

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"

एम्युलेटर शुरू होने पर, यह डिफ़ॉल्ट पोर्ट (8080) पर चलने की कोशिश करेगा. अपनी firebase.json फ़ाइल के "emulators" सेक्शन में बदलाव करके, एम्युलेटर का पोर्ट बदला जा सकता है:

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

एम्युलेटर चलाने से पहले

एम्युलेटर का इस्तेमाल शुरू करने से पहले, इन बातों का ध्यान रखें:

लोकल यूनिट टेस्ट चलाना

JavaScript SDK के v9 वर्शन के साथ लोकल यूनिट टेस्ट चलाना

Firebase, सुरक्षा नियमों की यूनिट टेस्टिंग लाइब्रेरी को JavaScript SDK के वर्शन 9 और SDK के वर्शन 8, दोनों के साथ डिस्ट्रिब्यूट करता है. लाइब्रेरी के एपीआई में काफ़ी अंतर है. हमारा सुझाव है कि टेस्टिंग लाइब्रेरी के 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.

इंपोर्ट करने के बाद, यूनिट टेस्ट लागू करने के लिए:

  • initializeTestEnvironment को कॉल करके, RulesTestEnvironment बनाना और कॉन्फ़िगर करना.
  • नियमों को ट्रिगर किए बिना, टेस्ट डेटा सेट अप करना. इसके लिए, 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 बनाता है. यह पुष्टि किए गए Authentication उपयोगकर्ता की तरह काम करता है. इससे बनाए गए अनुरोधों में, मॉक Authentication टोकन अटैच होगा. ज़रूरत पड़ने पर, कस्टम दावे या 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().doc('/users/alice'), { ... });

RulesTestEnvironment.unauthenticatedContext() => RulesTestContext

यह तरीका, RulesTestContext बनाता है. यह ऐसे क्लाइंट की तरह काम करता है जिसने Authentication के ज़रिए लॉगिन नहीं किया है. इससे बनाए गए अनुरोधों में, 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 डेटाबेस में मौजूद उस डेटा को मिटाता है जो Firestore एम्युलेटर के लिए कॉन्फ़िगर किए गए projectId से जुड़ा है.

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

यह तरीका, इस टेस्ट कॉन्टेक्स्ट के लिए Firestore इंस्टेंस हासिल करता है. Firebase JS क्लाइंट SDK के दिखाए गए इंस्टेंस का इस्तेमाल, क्लाइंट SDK के एपीआई (v9 मॉड्यूलर या v9 कंपैट) के साथ किया जा सकता है.

नियमों के आकलन को विज़ुअलाइज़ करना

Cloud Firestore एम्युलेटर की मदद से, Emulator Suite के यूज़र इंटरफ़ेस (यूआई) में क्लाइंट के अनुरोधों को विज़ुअलाइज़ किया जा सकता है. इसमें, Firebase के सुरक्षा नियमों के लिए आकलन की ट्रेसिंग भी शामिल है.

हर अनुरोध के लिए, आकलन का क्रम देखने के लिए, Firestore > अनुरोध टैब खोलें.

Firestore Emulator Requests Monitor में सुरक्षा के नियमों का आकलन दिखाया गया है

टेस्ट रिपोर्ट जनरेट करना

टेस्ट का सुइट चलाने के बाद, टेस्ट कवरेज रिपोर्ट ऐक्सेस की जा सकती हैं. इनमें यह दिखाया जाता है कि आपके हर सुरक्षा नियम का आकलन कैसे किया गया.

रिपोर्ट पाने के लिए, एम्युलेटर के चालू होने के दौरान, उसके किसी ऐसे एंडपॉइंट के बारे में क्वेरी करें जिसे सार्वजनिक किया गया हो. ब्राउज़र के साथ काम करने वाले वर्शन के लिए, यह यूआरएल इस्तेमाल करें:

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 में, हमने rules-unit-testing लाइब्रेरी में initializeTestApp() तरीका उपलब्ध कराया है. यह auth फ़ील्ड लेता है. इस तरीके का इस्तेमाल करके बनाया गया Firebase हैंडल, इस तरह काम करेगा जैसे आपने जिस भी इकाई की जानकारी दी है उसकी पुष्टि हो गई हो. अगर null पास किया जाता है, तो यह ऐसे उपयोगकर्ता की तरह काम करेगा जिसकी पुष्टि नहीं हुई है (auth != null नियम फ़ेल हो जाएंगे, उदाहरण के लिए).

आम तौर पर आने वाली समस्याओं को हल करना

Cloud Firestore एम्युलेटर का इस्तेमाल करते समय, आपको आम तौर पर आने वाली ये समस्याएं हो सकती हैं. अगर आपको कोई समस्या आ रही है, तो उसे हल करने के लिए, यहां दिया गया तरीका अपनाएं. ये नोट, सुरक्षा नियमों की यूनिट टेस्टिंग लाइब्रेरी को ध्यान में रखकर लिखे गए हैं. हालांकि, सामान्य तरीके किसी भी Firebase SDK पर लागू होते हैं.

टेस्ट का व्यवहार एक जैसा नहीं है

अगर आपके टेस्ट कभी पास हो रहे हैं और कभी फ़ेल हो रहे हैं, तब भी जब टेस्ट में कोई बदलाव नहीं किया गया है, तो आपको यह पुष्टि करनी पड़ सकती है कि वे सही क्रम में हैं. एम्युलेटर के साथ ज़्यादातर इंटरैक्शन एसिंक्रोनस होते हैं. इसलिए, पक्का करें कि सभी एसिंक्रोनस कोड सही क्रम में हों. प्रॉमिस को चेन करके या 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 अलग-अलग मामलों में सही तरीके से अनुमति देते हैं या अस्वीकार करते हैं.