विशिष्ट फ़ील्ड तक पहुंच नियंत्रित करें

यह पृष्ठ सुरक्षा नियमों की संरचना और सुरक्षा नियमों के लिए लेखन शर्तों की अवधारणाओं पर आधारित है, यह समझाने के लिए कि आप क्लाउड फायरस्टोर सुरक्षा नियमों का उपयोग ऐसे नियम बनाने के लिए कैसे कर सकते हैं जो ग्राहकों को दस्तावेज़ में कुछ फ़ील्ड पर संचालन करने की अनुमति देते हैं लेकिन अन्य को नहीं।

ऐसे समय हो सकते हैं जब आप किसी दस्तावेज़ में परिवर्तनों को दस्तावेज़ स्तर पर नहीं बल्कि फ़ील्ड स्तर पर नियंत्रित करना चाहते हैं।

उदाहरण के लिए, हो सकता है कि आप किसी क्लाइंट को दस्तावेज़ बनाने या बदलने की अनुमति देना चाहें, लेकिन उन्हें उस दस्तावेज़ में कुछ फ़ील्ड संपादित करने की अनुमति नहीं देना चाहेंगे। या आप यह लागू करना चाह सकते हैं कि क्लाइंट द्वारा बनाए गए किसी भी दस्तावेज़ में हमेशा फ़ील्ड का एक निश्चित सेट होता है। यह मार्गदर्शिका बताती है कि आप क्लाउड फायरस्टोर सुरक्षा नियमों का उपयोग करके इनमें से कुछ कार्यों को कैसे पूरा कर सकते हैं।

केवल विशिष्ट फ़ील्ड के लिए पढ़ने की पहुंच की अनुमति देना

क्लाउड फायरस्टोर में पठन दस्तावेज़ स्तर पर किया जाता है। आप या तो पूरा दस्तावेज़ पुनर्प्राप्त करते हैं, या आप कुछ भी पुनर्प्राप्त नहीं करते हैं। आंशिक दस्तावेज़ को पुनः प्राप्त करने का कोई तरीका नहीं है। उपयोगकर्ताओं को किसी दस्तावेज़ के भीतर विशिष्ट फ़ील्ड पढ़ने से रोकना अकेले सुरक्षा नियमों का उपयोग करना असंभव है।

यदि किसी दस्तावेज़ में कुछ फ़ील्ड हैं जिन्हें आप कुछ उपयोगकर्ताओं से छिपाकर रखना चाहते हैं, तो सबसे अच्छा तरीका उन्हें एक अलग दस्तावेज़ में रखना होगा। उदाहरण के लिए, आप किसी private उपसंग्रह में इस प्रकार दस्तावेज़ बनाने पर विचार कर सकते हैं:

/कर्मचारी/{emp_id}

  name: "Alice Hamilton",
  department: 461,
  start_date: <timestamp>

/कर्मचारी/{emp_id}/निजी/वित्त

    salary: 80000,
    bonus_mult: 1.25,
    perf_review: 4.2

फिर आप सुरक्षा नियम जोड़ सकते हैं जिनमें दो संग्रहों के लिए पहुंच के विभिन्न स्तर हैं। इस उदाहरण में, हम यह कहने के लिए कस्टम प्रमाणीकरण दावों का उपयोग कर रहे हैं कि केवल Finance के बराबर कस्टम प्रमाणीकरण दावा role वाले उपयोगकर्ता ही किसी कर्मचारी की वित्तीय जानकारी देख सकते हैं।

service cloud.firestore {
  match /databases/{database}/documents {
    // Allow any logged in user to view the public employee data
    match /employees/{emp_id} {
      allow read: if request.resource.auth != null
      // Allow only users with the custom auth claim of "Finance" to view
      // the employee's financial data
      match /private/finances {
        allow read: if request.resource.auth &&
          request.resource.auth.token.role == 'Finance'
      }
    }
  }
}

दस्तावेज़ निर्माण पर फ़ील्ड्स को प्रतिबंधित करना

क्लाउड फायरस्टोर स्कीमलेस है, जिसका अर्थ है कि किसी दस्तावेज़ में कौन से फ़ील्ड शामिल हैं, इसके लिए डेटाबेस स्तर पर कोई प्रतिबंध नहीं है। हालांकि यह लचीलापन विकास को आसान बना सकता है, लेकिन कई बार आप यह सुनिश्चित करना चाहेंगे कि ग्राहक केवल ऐसे दस्तावेज़ बना सकते हैं जिनमें विशिष्ट फ़ील्ड हों, या अन्य फ़ील्ड शामिल न हों।

आप request.resource.data ऑब्जेक्ट की keys विधि की जांच करके ये नियम बना सकते हैं। यह उन सभी फ़ील्ड की सूची है जिन्हें क्लाइंट इस नए दस्तावेज़ में लिखने का प्रयास कर रहा है। फ़ील्ड के इस सेट को hasOnly() या hasAny() जैसे फ़ंक्शंस के साथ जोड़कर, आप ऐसे तर्क जोड़ सकते हैं जो उन दस्तावेज़ों के प्रकारों को प्रतिबंधित करता है जिन्हें उपयोगकर्ता क्लाउड फायरस्टोर में जोड़ सकता है।

नए दस्तावेज़ों में विशिष्ट फ़ील्ड की आवश्यकता

मान लीजिए कि आप यह सुनिश्चित करना चाहते हैं कि restaurant संग्रह में बनाए गए सभी दस्तावेज़ों में कम से कम एक name , location और city फ़ील्ड शामिल हो। आप नए दस्तावेज़ में कुंजियों की सूची पर hasAll() कॉल करके ऐसा कर सकते हैं।

service cloud.firestore {
  match /databases/{database}/documents {
    // Allow the user to create a document only if that document contains a name
    // location, and city field
    match /restaurant/{restId} {
      allow create: if request.resource.data.keys().hasAll(['name', 'location', 'city']);
    }
  }
}

यह रेस्तरां को अन्य क्षेत्रों के साथ भी बनाने की अनुमति देता है, लेकिन यह सुनिश्चित करता है कि ग्राहक द्वारा बनाए गए सभी दस्तावेज़ों में कम से कम ये तीन फ़ील्ड हों।

नए दस्तावेज़ों में विशिष्ट फ़ील्ड को प्रतिबंधित करना

इसी प्रकार, आप निषिद्ध फ़ील्ड की सूची के विरुद्ध hasAny() का उपयोग करके ग्राहकों को ऐसे दस्तावेज़ बनाने से रोक सकते हैं जिनमें विशिष्ट फ़ील्ड शामिल हैं। यदि किसी दस्तावेज़ में इनमें से कोई भी फ़ील्ड शामिल है तो यह विधि सत्य का मूल्यांकन करती है, इसलिए आप संभवतः कुछ फ़ील्ड को प्रतिबंधित करने के लिए परिणाम को अस्वीकार करना चाहते हैं।

उदाहरण के लिए, निम्नलिखित उदाहरण में, क्लाइंट को ऐसा दस्तावेज़ बनाने की अनुमति नहीं है जिसमें average_score या rating_count फ़ील्ड शामिल है क्योंकि ये फ़ील्ड बाद में सर्वर कॉल द्वारा जोड़े जाएंगे।

service cloud.firestore {
  match /databases/{database}/documents {
    // Allow the user to create a document only if that document does *not*
    // contain an average_score or rating_count field.
    match /restaurant/{restId} {
      allow create: if (!request.resource.data.keys().hasAny(
        ['average_score', 'rating_count']));
    }
  }
}

नए दस्तावेज़ों के लिए फ़ील्ड की अनुमति सूची बनाना

नए दस्तावेज़ों में कुछ फ़ील्ड्स को प्रतिबंधित करने के बजाय, हो सकता है कि आप केवल उन्हीं फ़ील्ड्स की सूची बनाना चाहें जिनकी नए दस्तावेज़ों में स्पष्ट रूप से अनुमति है। फिर आप यह सुनिश्चित करने के लिए hasOnly() फ़ंक्शन का उपयोग कर सकते हैं कि बनाए गए किसी भी नए दस्तावेज़ में केवल ये फ़ील्ड (या इन फ़ील्ड का एक उपसमूह) शामिल हैं और कोई अन्य नहीं।

service cloud.firestore {
  match /databases/{database}/documents {
    // Allow the user to create a document only if that document doesn't contain
    // any fields besides the ones listed below.
    match /restaurant/{restId} {
      allow create: if (request.resource.data.keys().hasOnly(
        ['name', 'location', 'city', 'address', 'hours', 'cuisine']));
    }
  }
}

आवश्यक और वैकल्पिक फ़ील्ड का संयोजन

आप अपने सुरक्षा नियमों में कुछ फ़ील्ड की आवश्यकता और अन्य को अनुमति देने के लिए hasAll और hasOnly संचालन को एक साथ जोड़ सकते हैं। उदाहरण के लिए, इस उदाहरण के लिए आवश्यक है कि सभी नए दस्तावेज़ों में name , location और city फ़ील्ड हों, और वैकल्पिक रूप से address , hours और cuisine फ़ील्ड की अनुमति हो।

service cloud.firestore {
  match /databases/{database}/documents {
    // Allow the user to create a document only if that document has a name,
    // location, and city field, and optionally address, hours, or cuisine field
    match /restaurant/{restId} {
      allow create: if (request.resource.data.keys().hasAll(['name', 'location', 'city'])) &&
       (request.resource.data.keys().hasOnly(
           ['name', 'location', 'city', 'address', 'hours', 'cuisine']));
    }
  }
}

वास्तविक दुनिया के परिदृश्य में, आप अपने कोड को डुप्लिकेट करने से बचने और वैकल्पिक और आवश्यक फ़ील्ड को एक ही सूची में आसानी से संयोजित करने के लिए इस तर्क को एक सहायक फ़ंक्शन में ले जाना चाह सकते हैं, जैसे:

service cloud.firestore {
  match /databases/{database}/documents {
    function verifyFields(required, optional) {
      let allAllowedFields = required.concat(optional);
      return request.resource.data.keys().hasAll(required) &&
        request.resource.data.keys().hasOnly(allAllowedFields);
    }
    match /restaurant/{restId} {
      allow create: if verifyFields(['name', 'location', 'city'],
        ['address', 'hours', 'cuisine']);
    }
  }
}

अद्यतन पर फ़ील्ड प्रतिबंधित करना

एक सामान्य सुरक्षा प्रथा यह है कि ग्राहकों को केवल कुछ क्षेत्रों को संपादित करने की अनुमति दी जाए, अन्य को नहीं। आप इसे केवल पिछले अनुभाग में वर्णित request.resource.data.keys() सूची को देखकर पूरा नहीं कर सकते, क्योंकि यह सूची संपूर्ण दस्तावेज़ का प्रतिनिधित्व करती है क्योंकि यह अद्यतन की देखभाल करेगी, और इसलिए इसमें वे फ़ील्ड शामिल होंगे जो क्लाइंट ने नहीं किए थे परिवर्तन।

हालाँकि, यदि आप diff() फ़ंक्शन का उपयोग करते हैं, तो आप request.resource.data तुलना resource.data ऑब्जेक्ट से कर सकते हैं, जो अपडेट से पहले डेटाबेस में दस्तावेज़ का प्रतिनिधित्व करता है। यह एक mapDiff ऑब्जेक्ट बनाता है, जो एक ऑब्जेक्ट है जिसमें दो अलग-अलग मानचित्रों के बीच सभी परिवर्तन होते हैं।

इस मैपडिफ़ पर affectedKeys() विधि को कॉल करके, आप उन फ़ील्ड्स का एक सेट प्राप्त कर सकते हैं जिन्हें एक संपादन में बदल दिया गया था। फिर आप यह सुनिश्चित करने के लिए hasOnly() या hasAny() जैसे फ़ंक्शंस का उपयोग कर सकते हैं कि इस सेट में कुछ आइटम हैं (या नहीं हैं)।

कुछ फ़ील्ड्स को बदलने से रोका जा रहा है

affectedKeys() द्वारा उत्पन्न सेट पर hasAny() विधि का उपयोग करके और फिर परिणाम को अस्वीकार करके, आप किसी भी क्लाइंट अनुरोध को अस्वीकार कर सकते हैं जो उन फ़ील्ड को बदलने का प्रयास करता है जिन्हें आप बदलना नहीं चाहते हैं।

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

service cloud.firestore {
  match /databases/{database}/documents {
    match /restaurant/{restId} {
      // Allow the client to update a document only if that document doesn't
      // change the average_score or rating_count fields
      allow update: if (!request.resource.data.diff(resource.data).affectedKeys()
        .hasAny(['average_score', 'rating_count']));
    }
  }
}

केवल कुछ फ़ील्ड्स को बदलने की अनुमति देना

उन फ़ील्ड्स को निर्दिष्ट करने के बजाय जिन्हें आप बदलना नहीं चाहते हैं, आप उन फ़ील्ड्स की सूची निर्दिष्ट करने के लिए hasOnly() फ़ंक्शन का भी उपयोग कर सकते हैं जिन्हें आप बदलना चाहते हैं। इसे आम तौर पर अधिक सुरक्षित माना जाता है क्योंकि किसी भी नए दस्तावेज़ फ़ील्ड में लिखना डिफ़ॉल्ट रूप से अस्वीकृत होता है जब तक कि आप स्पष्ट रूप से उन्हें अपने सुरक्षा नियमों में अनुमति नहीं देते हैं।

उदाहरण के लिए, average_score और rating_count फ़ील्ड को अस्वीकार करने के बजाय, आप सुरक्षा नियम बना सकते हैं जो ग्राहकों को केवल name , location , city , address , hours और cuisine फ़ील्ड बदलने की अनुमति देते हैं।

service cloud.firestore {
  match /databases/{database}/documents {
    match /restaurant/{restId} {
    // Allow a client to update only these 6 fields in a document
      allow update: if (request.resource.data.diff(resource.data).affectedKeys()
        .hasOnly(['name', 'location', 'city', 'address', 'hours', 'cuisine']));
    }
  }
}

इसका मतलब यह है कि यदि, आपके ऐप के किसी भविष्य के पुनरावृत्ति में, रेस्तरां दस्तावेज़ों में एक telephone फ़ील्ड शामिल है, तो उस फ़ील्ड को संपादित करने का प्रयास तब तक विफल रहेगा जब तक आप वापस नहीं जाते और उस फ़ील्ड को अपने सुरक्षा नियमों में hasOnly() सूची में नहीं जोड़ते।

फ़ील्ड प्रकार लागू करना

क्लाउड फायरस्टोर के स्कीमलेस होने का एक और प्रभाव यह है कि विशिष्ट क्षेत्रों में किस प्रकार के डेटा को संग्रहीत किया जा सकता है, इसके लिए डेटाबेस स्तर पर कोई प्रवर्तन नहीं है। हालाँकि, यह कुछ ऐसा है जिसे आप सुरक्षा नियमों में लागू कर सकते हैं, ऑपरेटर के is

उदाहरण के लिए, निम्नलिखित सुरक्षा नियम यह लागू करता है कि समीक्षा का score फ़ील्ड एक पूर्णांक होना चाहिए, headline , content और author_name फ़ील्ड स्ट्रिंग हैं, और review_date एक टाइमस्टैम्प है।

service cloud.firestore {
  match /databases/{database}/documents {
    match /restaurant/{restId} {
      // Restaurant rules go here...
      match /review/{reviewId} {
        allow create: if (request.resource.data.score is int &&
          request.resource.data.headline is string &&
          request.resource.data.content is string &&
          request.resource.data.author_name is string &&
          request.resource.data.review_date is timestamp
        );
      }
    }
  }
}

is ऑपरेटर के लिए मान्य डेटा प्रकार bool , bytes , float , int , list , latlng , number , path , map , string और timestampis ऑपरेटर constraint , duration , set और map_diff डेटा प्रकारों का भी समर्थन करता है, लेकिन चूंकि ये सुरक्षा नियम भाषा द्वारा ही उत्पन्न होते हैं और ग्राहकों द्वारा उत्पन्न नहीं होते हैं, आप शायद ही कभी इन्हें अधिकांश व्यावहारिक अनुप्रयोगों में उपयोग करते हैं।

list और map डेटा प्रकारों में जेनेरिक, या प्रकार के तर्कों के लिए समर्थन नहीं है। दूसरे शब्दों में, आप यह लागू करने के लिए सुरक्षा नियमों का उपयोग कर सकते हैं कि एक निश्चित फ़ील्ड में एक सूची या एक मानचित्र शामिल है, लेकिन आप यह लागू नहीं कर सकते कि किसी फ़ील्ड में सभी पूर्णांकों या सभी स्ट्रिंग्स की सूची शामिल है।

इसी तरह, आप किसी सूची या मानचित्र में विशिष्ट प्रविष्टियों के लिए प्रकार मान लागू करने के लिए सुरक्षा नियमों का उपयोग कर सकते हैं (क्रमशः ब्रेकेट नोटेशन या कुंजी नामों का उपयोग करके), लेकिन मानचित्र या सूची में सभी सदस्यों के डेटा प्रकारों को लागू करने के लिए कोई शॉर्टकट नहीं है एक बार।

उदाहरण के लिए, निम्नलिखित नियम यह सुनिश्चित करते हैं कि किसी दस्तावेज़ में tags फ़ील्ड में एक सूची हो और पहली प्रविष्टि एक स्ट्रिंग हो। यह यह भी सुनिश्चित करता है कि product फ़ील्ड में एक मानचित्र होता है जिसमें बदले में एक उत्पाद का नाम होता है जो एक स्ट्रिंग होता है और एक मात्रा होती है जो एक पूर्णांक होती है।

service cloud.firestore {
  match /databases/{database}/documents {
  match /orders/{orderId} {
    allow create: if request.resource.data.tags is list &&
      request.resource.data.tags[0] is string &&
      request.resource.data.product is map &&
      request.resource.data.product.name is string &&
      request.resource.data.product.quantity is int
      }
    }
  }
}

दस्तावेज़ बनाते और अद्यतन करते समय फ़ील्ड प्रकारों को लागू करने की आवश्यकता होती है। इसलिए, हो सकता है कि आप एक सहायक फ़ंक्शन बनाने पर विचार करना चाहें जिसे आप अपने सुरक्षा नियमों के निर्माण और अद्यतन दोनों अनुभागों में कॉल कर सकें।

service cloud.firestore {
  match /databases/{database}/documents {

  function reviewFieldsAreValidTypes(docData) {
     return docData.score is int &&
          docData.headline is string &&
          docData.content is string &&
          docData.author_name is string &&
          docData.review_date is timestamp;
  }

   match /restaurant/{restId} {
      // Restaurant rules go here...
      match /review/{reviewId} {
        allow create: if reviewFieldsAreValidTypes(request.resource.data) &&
          // Other rules may go here
        allow update: if reviewFieldsAreValidTypes(request.resource.data) &&
          // Other rules may go here
      }
    }
  }
}

वैकल्पिक फ़ील्ड के लिए प्रकार लागू करना

यह याद रखना महत्वपूर्ण है कि किसी दस्तावेज़ पर request.resource.data.foo कॉल करने पर जहां foo मौजूद नहीं है, एक त्रुटि उत्पन्न होती है, और इसलिए उस कॉल को करने वाला कोई भी सुरक्षा नियम अनुरोध को अस्वीकार कर देगा। आप request.resource.data पर get विधि का उपयोग करके इस स्थिति को संभाल सकते हैं। get विधि आपको उस फ़ील्ड के लिए एक डिफ़ॉल्ट तर्क प्रदान करने की अनुमति देती है जिसे आप मानचित्र से पुनर्प्राप्त कर रहे हैं यदि वह फ़ील्ड मौजूद नहीं है।

उदाहरण के लिए, यदि समीक्षा दस्तावेजों में एक वैकल्पिक photo_url फ़ील्ड और एक वैकल्पिक tags फ़ील्ड भी शामिल है जिसे आप सत्यापित करना चाहते हैं कि क्रमशः स्ट्रिंग और सूचियां हैं, तो आप निम्न की तरह reviewFieldsAreValidTypes फ़ंक्शन को फिर से लिखकर इसे पूरा कर सकते हैं:

  function reviewFieldsAreValidTypes(docData) {
     return docData.score is int &&
          docData.headline is string &&
          docData.content is string &&
          docData.author_name is string &&
          docData.review_date is timestamp &&
          docData.get('photo_url', '') is string &&
          docData.get('tags', []) is list;
  }

यह उन दस्तावेज़ों को अस्वीकार कर देता है जहां tags मौजूद हैं, लेकिन सूची नहीं है, जबकि अभी भी उन दस्तावेज़ों को अनुमति देता है जिनमें tags (या photo_url ) फ़ील्ड नहीं है।

आंशिक लेखन की कभी अनुमति नहीं है

क्लाउड फायरस्टोर सुरक्षा नियमों के बारे में एक अंतिम नोट यह है कि वे या तो क्लाइंट को दस्तावेज़ में बदलाव करने की अनुमति देते हैं, या वे संपूर्ण संपादन को अस्वीकार कर देते हैं। आप ऐसे सुरक्षा नियम नहीं बना सकते जो आपके दस्तावेज़ में कुछ फ़ील्ड में लिखने को स्वीकार करते हों जबकि उसी ऑपरेशन में अन्य को अस्वीकार करते हों।