डेटा के लिए सुरक्षित रूप से क्वेरी करें

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

नियम, फ़िल्टर नहीं हैं

दस्तावेज़ों को फिर से पाने के लिए क्वेरी लिखते समय, ध्यान रखें कि सुरक्षा के नियम फ़िल्टर नहीं हैं—क्वेरी पूरी होती हैं या कुछ नहीं. आपका समय और संसाधन बचाने के लिए, Cloud Firestore आपके सभी दस्तावेज़ों की असली फ़ील्ड वैल्यू के बजाय, क्वेरी का आकलन उसके संभावित नतीजे के सेट के आधार पर करता है. अगर कोई क्वेरी ऐसे दस्तावेज़ लौटा सकती है जिन्हें पढ़ने की अनुमति क्लाइंट के पास नहीं है, तो पूरा अनुरोध फ़ेल हो जाता है.

क्वेरी और सुरक्षा के नियम

जैसा कि नीचे दिए गए उदाहरणों में बताया गया है, आपको अपने सुरक्षा नियमों की सीमाओं को पूरा करने के लिए अपनी क्वेरी लिखनी चाहिए.

auth.uid पर आधारित दस्तावेज़ सुरक्षित और क्वेरी करें

यहां दिए गए उदाहरण में, सुरक्षा के नियम के तहत सुरक्षित किए गए दस्तावेज़ों को वापस पाने के लिए क्वेरी लिखने का तरीका बताया गया है. ऐसा डेटाबेस देखें जिसमें story दस्तावेज़ों का कलेक्शन हो:

/stories/{storyid}

{
  title: "A Great Story",
  content: "Once upon a time...",
  author: "some_auth_id",
  published: false
}

title और content फ़ील्ड के अलावा, ऐक्सेस कंट्रोल के लिए हर दस्तावेज़ में author और published फ़ील्ड सेव होते हैं. इन उदाहरणों में माना जाता है कि ऐप्लिकेशन, दस्तावेज़ बनाने वाले उपयोगकर्ता के यूआईडी में author फ़ील्ड को सेट करने के लिए, Firebase से पुष्टि करने की सुविधा का इस्तेमाल करता है. Firebase से पुष्टि करने की सुविधा से, सुरक्षा नियमों में request.auth वैरिएबल की जानकारी भी अपने-आप भर जाती है.

नीचे दिया गया सुरक्षा का नियम, request.auth और resource.data वैरिएबल का इस्तेमाल करके, हरstory के लिए रीड और राइट ऐक्सेस को प्रतिबंधित करता है. इसकी जानकारी उसके लेखक को मिलने पर होती है:

service cloud.firestore {
  match /databases/{database}/documents {
    match /stories/{storyid} {
      // Only the authenticated user who authored the document can read or write
      allow read, write: if request.auth != null && request.auth.uid == resource.data.author;
    }
  }
}

मान लें कि आपके ऐप्लिकेशन में एक ऐसा पेज है जो उपयोगकर्ता को उसके लिखे गए story दस्तावेज़ों की सूची दिखाता है. आपको इस पेज को भरने के लिए, इस क्वेरी का इस्तेमाल करना चाहिए. हालांकि, यह क्वेरी फ़ेल हो जाएगी, क्योंकि इसमें वे पाबंदियां शामिल नहीं हैं जो आपके सुरक्षा नियमों में हैं:

अमान्य: क्वेरी की पाबंदियां सुरक्षा नियमों से जुड़ी शर्तों से मेल नहीं खाती हैं

// This query will fail
db.collection("stories").get()

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

इसके उलट, यह क्वेरी सफल होती है, क्योंकि इसमें author फ़ील्ड की वही कंस्ट्रेंट शामिल है जो सुरक्षा के नियमों में है:

मान्य: क्वेरी कंस्ट्रेंट, सुरक्षा के नियमों से मेल खाते हैं

var user = firebase.auth().currentUser;

db.collection("stories").where("author", "==", user.uid).get()

फ़ील्ड के आधार पर, दस्तावेज़ों को सुरक्षित रखें और उनके बारे में क्वेरी करें

क्वेरी और नियमों के बीच इंटरैक्शन को बेहतर तरीके से दिखाने के लिए, नीचे दिए गए सुरक्षा नियमों में stories कलेक्शन के लिए पढ़ने का ऐक्सेस बढ़ाया गया है. इससे कोई भी उपयोगकर्ता उन story दस्तावेज़ों को पढ़ सकता है जिनमें published फ़ील्ड को true पर सेट किया गया है.

service cloud.firestore {
  match /databases/{database}/documents {
    match /stories/{storyid} {
      // Anyone can read a published story; only story authors can read unpublished stories
      allow read: if resource.data.published == true || (request.auth != null && request.auth.uid == resource.data.author);
      // Only story authors can write
      allow write: if request.auth != null && request.auth.uid == resource.data.author;
    }
  }
}

पब्लिश किए गए पेजों की क्वेरी में वे सभी शर्तें शामिल होनी चाहिए जो सुरक्षा के नियमों में हैं:

db.collection("stories").where("published", "==", true).get()

क्वेरी कंस्ट्रेंट .where("published", "==", true), इस बात की गारंटी देता है कि किसी भी नतीजे के लिए resource.data.published, true है. इसलिए, यह क्वेरी सुरक्षा नियमों का पालन करती है और इसे डेटा पढ़ने की अनुमति है.

OR क्वेरी

किसी नियम-सेट के साथ लॉजिकल OR क्वेरी (or, in या array-contains-any) का आकलन करते समय, Cloud Firestore हर तुलना वैल्यू का अलग-अलग आकलन करता है. तुलना के लिए हर वैल्यू, सुरक्षा के नियम की शर्तों के मुताबिक होनी चाहिए. जैसे कि नीचे दिए नियम के लिए:

match /mydocuments/{doc} {
  allow read: if resource.data.x > 5;
}

अमान्य: क्वेरी इस बात की गारंटी नहीं देती है कि x > 5 सभी संभावित दस्तावेज़ों के लिए

// These queries will fail
query(db.collection("mydocuments"),
      or(where("x", "==", 1),
         where("x", "==", 6)
      )
    )

query(db.collection("mydocuments"),
      where("x", "in", [1, 3, 6, 42, 99])
    )

मान्य: क्वेरी की गारंटी देता है कि x > 5 सभी संभावित दस्तावेज़ों के लिए

query(db.collection("mydocuments"),
      or(where("x", "==", 6),
         where("x", "==", 42)
      )
    )

query(db.collection("mydocuments"),
      where("x", "in", [6, 42, 99, 105, 200])
    )

क्वेरी पर मौजूद कंस्ट्रेंट का आकलन करना

आपके सुरक्षा नियम, क्वेरी को उनकी शर्तों के आधार पर स्वीकार या अस्वीकार भी कर सकते हैं. request.query वैरिएबल में किसी क्वेरी की limit, offset, और orderBy प्रॉपर्टी शामिल होती हैं. उदाहरण के लिए, आपके सुरक्षा नियम किसी ऐसी क्वेरी को अस्वीकार कर सकते हैं जो एक तय सीमा तक वापस लिए जाने वाले दस्तावेज़ों की ज़्यादा से ज़्यादा संख्या को सीमित नहीं करती हो:

allow list: if request.query.limit <= 10;

नीचे दिए गए नियमों-सेट में, उन सुरक्षा नियमों को लिखने का तरीका बताया गया है जो क्वेरी पर लागू की गई सीमाओं का आकलन करते हैं. इस उदाहरण में, नीचे दिए गए बदलावों के साथ पिछले stories नियमों को लागू किया गया है:

  • नियमसेट, पढ़े गए नियम को get और list के नियमों में अलग करता है.
  • get नियम के मुताबिक, किसी एक दस्तावेज़ को ऐसे सार्वजनिक दस्तावेज़ों या दस्तावेज़ों में वापस नहीं पाया जा सकता जिसे उपयोगकर्ता ने लिखा है.
  • क्वेरी के लिए, list नियम get की तरह ही पाबंदियां लागू करता है. यह क्वेरी की सीमा की भी जांच करता है. इसके बाद, बिना सीमा वाली या 10 से ज़्यादा सीमा वाली किसी भी क्वेरी को अस्वीकार करता है.
  • कोड को डुप्लीकेट होने से बचाने के लिए, नियमसेट authorOrPublished() फ़ंक्शन के बारे में बताता है.
service cloud.firestore {

  match /databases/{database}/documents {

    match /stories/{storyid} {

      // Returns `true` if the requested story is 'published'
      // or the user authored the story
      function authorOrPublished() {
        return resource.data.published == true || request.auth.uid == resource.data.author;
      }

      // Deny any query not limited to 10 or fewer documents
      // Anyone can query published stories
      // Authors can query their unpublished stories
      allow list: if request.query.limit <= 10 &&
                     authorOrPublished();

      // Anyone can retrieve a published story
      // Only a story's author can retrieve an unpublished story
      allow get: if authorOrPublished();

      // Only a story's author can write to a story
      allow write: if request.auth.uid == resource.data.author;
    }

  }
}

कलेक्शन ग्रुप की क्वेरी और सुरक्षा के नियम

डिफ़ॉल्ट रूप से, क्वेरी का दायरा किसी एक संग्रह तक सीमित किया जाता है और वे सिर्फ़ उसी संग्रह से नतीजे देती हैं. कलेक्शन ग्रुप की क्वेरी की मदद से, ऐसे कलेक्शन ग्रुप के नतीजे वापस लाए जा सकते हैं जिनमें एक ही आईडी वाले सभी कलेक्शन शामिल हों. इस सेक्शन में बताया गया है कि सुरक्षा के नियमों का इस्तेमाल करके, कलेक्शन ग्रुप की क्वेरी को कैसे सुरक्षित रखा जाए.

कलेक्शन ग्रुप के आधार पर, सुरक्षित और क्वेरी वाले दस्तावेज़

अपने सुरक्षा नियमों में, आपको साफ़ तौर पर कलेक्शन ग्रुप की क्वेरी को अनुमति देनी होगी. इसके लिए, आपको कलेक्शन ग्रुप के लिए एक नियम लिखना होगा:

  1. पक्का करें कि आपके नियमों-सेट की पहली लाइन rules_version = '2'; है. कलेक्शन वाली क्वेरी के लिए, सुरक्षा नियमों के वर्शन 2 के बार-बार होने वाले नए वाइल्डकार्ड {name=**} नियम का पालन करना ज़रूरी है.
  2. match /{path=**}/[COLLECTION_ID]/{doc} का इस्तेमाल करके, अपने कलेक्शन ग्रुप के लिए एक नियम लिखें.

उदाहरण के लिए, posts सब-कलेक्शन वाले forum दस्तावेज़ों में व्यवस्थित फ़ोरम देखें:

/फ़ोरम/{forumid}/पोस्ट/{postid}

{
  author: "some_auth_id",
  authorname: "some_username",
  content: "I just read a great story.",
}

इस ऐप्लिकेशन में, हम पोस्ट को उनके मालिकों से बदलाव करने लायक बनाते हैं और पुष्टि किए गए उपयोगकर्ता उन्हें आसानी से पढ़ सकते हैं:

service cloud.firestore {
  match /databases/{database}/documents {
    match /forums/{forumid}/posts/{post} {
      // Only authenticated users can read
      allow read: if request.auth != null;
      // Only the post author can write
      allow write: if request.auth != null && request.auth.uid == resource.data.author;
    }
  }
}

कोई भी प्रमाणित उपयोगकर्ता किसी भी एक फ़ोरम से पोस्ट पा सकता है:

db.collection("forums/technology/posts").get()

लेकिन, अगर आपको सभी फ़ोरम पर मौजूदा उपयोगकर्ता की पोस्ट दिखानी हों, तो क्या होगा? सभी posts कलेक्शन से नतीजे पाने के लिए, कलेक्शन ग्रुप क्वेरी का इस्तेमाल करें:

var user = firebase.auth().currentUser;

db.collectionGroup("posts").where("author", "==", user.uid).get()

अपने सुरक्षा नियमों में, आपको इस क्वेरी की अनुमति देनी होगी. इसके लिए, आपको posts कलेक्शन ग्रुप के लिए, पढ़ने या सूची बनाने का नियम लिखना होगा:

rules_version = '2';
service cloud.firestore {

  match /databases/{database}/documents {
    // Authenticated users can query the posts collection group
    // Applies to collection queries, collection group queries, and
    // single document retrievals
    match /{path=**}/posts/{post} {
      allow read: if request.auth != null;
    }
    match /forums/{forumid}/posts/{postid} {
      // Only a post's author can write to a post
      allow write: if request.auth != null && request.auth.uid == resource.data.author;

    }
  }
}

हालांकि, ध्यान रखें कि ये नियम posts आईडी वाले सभी कलेक्शन पर लागू होंगे. इस बात से कोई फ़र्क़ नहीं पड़ता कि वे क्रम के हिसाब से हैं या नहीं. उदाहरण के लिए, ये नियम यहां दिए गए सभी posts कलेक्शन पर लागू होते हैं:

  • /posts/{postid}
  • /forums/{forumid}/posts/{postid}
  • /forums/{forumid}/subforum/{subforumid}/posts/{postid}

फ़ील्ड के आधार पर, सुरक्षित कलेक्शन ग्रुप क्वेरी

सिंगल-कलेक्शन क्वेरी की तरह, कलेक्शन ग्रुप क्वेरी भी आपके सुरक्षा नियमों की शर्तों के मुताबिक होनी चाहिए. उदाहरण के लिए, हम ऊपर दिए गए stories उदाहरण की तरह, हर फ़ोरम पोस्ट में published फ़ील्ड जोड़ सकते हैं:

/फ़ोरम/{forumid}/पोस्ट/{postid}

{
  author: "some_auth_id",
  authorname: "some_username",
  content: "I just read a great story.",
  published: false
}

इसके बाद, हम published स्थिति और author पोस्ट के आधार पर, posts कलेक्शन ग्रुप के लिए नियम लिख सकते हैं:

rules_version = '2';
service cloud.firestore {

  match /databases/{database}/documents {

    // Returns `true` if the requested post is 'published'
    // or the user authored the post
    function authorOrPublished() {
      return resource.data.published == true || request.auth.uid == resource.data.author;
    }

    match /{path=**}/posts/{post} {

      // Anyone can query published posts
      // Authors can query their unpublished posts
      allow list: if authorOrPublished();

      // Anyone can retrieve a published post
      // Authors can retrieve an unpublished post
      allow get: if authorOrPublished();
    }

    match /forums/{forumid}/posts/{postid} {
      // Only a post's author can write to a post
      allow write: if request.auth.uid == resource.data.author;
    }
  }
}

इन नियमों का इस्तेमाल करके वेब, Apple, और Android क्लाइंट ये क्वेरी कर सकते हैं:

  • कोई भी व्यक्ति किसी फ़ोरम में प्रकाशित पोस्ट को फिर से पा सकता है:

    db.collection("forums/technology/posts").where('published', '==', true).get()
    
  • कोई भी व्यक्ति सभी फ़ोरम में किसी लेखक की प्रकाशित पोस्ट को फिर से पा सकता है:

    db.collectionGroup("posts").where("author", "==", "some_auth_id").where('published', '==', true).get()
    
  • लेखक सभी फ़ोरम में अपनी सभी प्रकाशित और अप्रकाशित पोस्ट फिर से पा सकते हैं:

    var user = firebase.auth().currentUser;
    
    db.collectionGroup("posts").where("author", "==", user.uid).get()
    

कलेक्शन ग्रुप और दस्तावेज़ के पाथ के आधार पर, दस्तावेज़ों को सुरक्षित और क्वेरी करें

कुछ मामलों में, हो सकता है कि आप दस्तावेज़ के पाथ के आधार पर, कलेक्शन ग्रुप की क्वेरी पर पाबंदी लगाना चाहें. इन पाबंदियों को बनाने के लिए, एक जैसी तकनीकों का इस्तेमाल करके, किसी फ़ील्ड के आधार पर दस्तावेज़ों को सुरक्षित किया जा सकता है और क्वेरी की जा सकती है.

ऐसा ऐप्लिकेशन इस्तेमाल करें जो कई स्टॉक और क्रिप्टो करंसी एक्सचेंज में, हर उपयोगकर्ता के लेन-देन की जानकारी को ट्रैक करता हो:

/users/{userid}/exchange/{exchangeid}/transactions/{transaction}

{
  amount: 100,
  exchange: 'some_exchange_name',
  timestamp: April 1, 2019 at 12:00:00 PM UTC-7,
  user: "some_auth_id",
}

user फ़ील्ड पर ध्यान दें. भले ही हमें पता है कि दस्तावेज़ के पाथ से, transaction दस्तावेज़ का मालिकाना हक किस उपयोगकर्ता के पास है, लेकिन हम हर transaction दस्तावेज़ में इस जानकारी की डुप्लीकेट कॉपी बनाते हैं, क्योंकि इससे हमें दो काम करने की अनुमति मिलती है:

  • कलेक्शन ग्रुप की ऐसी क्वेरी लिखें जिनके सिर्फ़ दस्तावेज़ों के पाथ में कोई /users/{userid} शामिल हो. उदाहरण के लिए:

    var user = firebase.auth().currentUser;
    // Return current user's last five transactions across all exchanges
    db.collectionGroup("transactions").where("user", "==", user).orderBy('timestamp').limit(5)
    
  • यह पाबंदी, transactions कलेक्शन ग्रुप की सभी क्वेरी के लिए लागू करें, ताकि एक उपयोगकर्ता किसी अन्य उपयोगकर्ता के transaction दस्तावेज़ों को वापस न पा सके.

हम इस पाबंदी को अपने सुरक्षा नियमों में लागू करते हैं. साथ ही, user फ़ील्ड के लिए डेटा की पुष्टि को शामिल करते हैं:

rules_version = '2';
service cloud.firestore {

  match /databases/{database}/documents {

    match /{path=**}/transactions/{transaction} {
      // Authenticated users can retrieve only their own transactions
      allow read: if resource.data.user == request.auth.uid;
    }

    match /users/{userid}/exchange/{exchangeid}/transactions/{transaction} {
      // Authenticated users can write to their own transactions subcollections
      // Writes must populate the user field with the correct auth id
      allow write: if userid == request.auth.uid && request.data.user == request.auth.uid
    }
  }
}

अगले चरण