रीयलटाइम डेटाबेस के सुरक्षा नियमों की भाषा का मुख्य सिंटैक्स जानें

Firebase रीयलटाइम डेटाबेस के सुरक्षा नियमों की मदद से, अपने डेटाबेस में सेव किए गए डेटा का ऐक्सेस कंट्रोल किया जा सकता है. नियमों के लिए इस्तेमाल होने वाले सिंटैक्स की मदद से, ऐसे नियम बनाए जा सकते हैं जो किसी भी चीज़ से मैच करते हों. जैसे, आपके डेटाबेस में किए गए सभी बदलावों से लेकर, अलग-अलग नोड पर किए गए ऑपरेशन तक.

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

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

सुरक्षा के नियमों को व्यवस्थित करना

रीयलटाइम डेटाबेस के सुरक्षा नियम, JSON दस्तावेज़ में मौजूद JavaScript जैसे एक्सप्रेशन से बने होते हैं. आपके नियमों का स्ट्रक्चर, आपके डेटाबेस में सेव किए गए डेटा के स्ट्रक्चर के मुताबिक होना चाहिए.

बुनियादी नियमों से, सुरक्षित किए जाने वाले नोड के सेट की पहचान होती है.साथ ही, इनसे यह भी पता चलता है कि ऐक्सेस करने के कौनसे तरीके (जैसे, पढ़ना, लिखना) इस्तेमाल किए जा सकते हैं. इन नियमों से यह भी पता चलता है कि ऐक्सेस करने की अनुमति किन शर्तों के तहत दी जाती है या नहीं दी जाती. नीचे दिए गए उदाहरणों में, हमारी शर्तें, आसान true और false स्टेटमेंट होंगी. हालांकि, अगले विषय में हम शर्तों को बताने के लिए, ज़्यादा डाइनैमिक तरीके बताएंगे.

उदाहरण के लिए, अगर हमें parent_node के तहत child_node को सुरक्षित करना है, तो इस्तेमाल किया जाने वाला सामान्य सिंटैक्स यह है:

{
  "rules": {
    "parent_node": {
      "child_node": {
        ".read": <condition>,
        ".write": <condition>,
        ".validate": <condition>,
      }
    }
  }
}

आइए, इस पैटर्न को लागू करते हैं. उदाहरण के लिए, मान लें कि आप मैसेज की सूची का ट्रैक रख रहे हैं और आपके पास ऐसा डेटा है:

{
  "messages": {
    "message0": {
      "content": "Hello",
      "timestamp": 1405704370369
    },
    "message1": {
      "content": "Goodbye",
      "timestamp": 1405704395231
    },
    ...
  }
}

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

{
  "rules": {
    // For requests to access the 'messages' node...
    "messages": {
      // ...and the individual wildcarded 'message' nodes beneath
      // (we'll cover wildcarding variables more a bit later)....
      "$message": {

        // For each message, allow a read operation if <condition>. In this
        // case, we specify our condition as "true", so read access is always granted.
        ".read": "true",

        // For read-only behavior, we specify that for write operations, our
        // condition is false.
        ".write": "false"
      }
    }
  }
}

बुनियादी नियमों के काम करने का तरीका

डेटा पर किए जा रहे ऑपरेशन के टाइप के आधार पर, सुरक्षा लागू करने के लिए तीन तरह के नियम होते हैं: .write, .read, और .validate. यहां इनके मकसद के बारे में खास जानकारी दी गई है:

नियम के टाइप
.read इससे यह पता चलता है कि उपयोगकर्ताओं को डेटा पढ़ने की अनुमति है या नहीं. साथ ही, यह भी पता चलता है कि उपयोगकर्ताओं को डेटा कब पढ़ने की अनुमति है.
.write इससे पता चलता है कि डेटा को लिखने की अनुमति है या नहीं और कब है.
.validate इससे यह तय होता है कि सही फ़ॉर्मैट में दी गई वैल्यू कैसी दिखेगी, उसमें सब-एट्रिब्यूट हैं या नहीं, और डेटा टाइप क्या है.

वाइल्डकार्ड कैप्चर वैरिएबल

सभी नियमों के स्टेटमेंट, नोड पर ले जाते हैं. कोई स्टेटमेंट किसी खास नोड पर ले जा सकता है या $ वाइल्डकार्ड कैप्चर वैरिएबल का इस्तेमाल करके, हैरारकी के किसी लेवल पर नोड के सेट पर ले जा सकता है. बाद के नियमों के स्टेटमेंट में इस्तेमाल करने के लिए, नोड की वैल्यू को सेव करने के लिए, कैप्चर वैरिएबल का इस्तेमाल करें. इस तकनीक की मदद से, Rules शर्तें ज़्यादा जटिल की जा सकती हैं. इस बारे में अगले लेख में ज़्यादा जानकारी दी जाएगी.

{
  "rules": {
    "rooms": {
      // this rule applies to any child of /rooms/, the key for each room id
      // is stored inside $room_id variable for reference
      "$room_id": {
        "topic": {
          // the room's topic can be changed if the room id has "public" in it
          ".write": "$room_id.contains('public')"
        }
      }
    }
  }
}

डाइनैमिक $ वैरिएबल का इस्तेमाल, पाथ के स्थिर नामों के साथ भी किया जा सकता है. इस उदाहरण में, हम $other वैरिएबल का इस्तेमाल करके, .validate नियम तय कर रहे हैं. इससे यह पक्का होता है कि widget में title और color के अलावा कोई और चाइल्ड एलिमेंट न हो. अगर किसी भी तरह के बदलाव से अतिरिक्त चाइल्ड ऑब्जेक्ट बनते हैं, तो वह बदलाव लागू नहीं होगा.

{
  "rules": {
    "widget": {
      // a widget can have a title or color attribute
      "title": { ".validate": true },
      "color": { ".validate": true },

      // but no other child paths are allowed
      // in this case, $other means any key excluding "title" and "color"
      "$other": { ".validate": false }
    }
  }
}

नियमों के कैस्केड को पढ़ना और उनमें बदलाव करना

.read और .write नियम, ऊपर से नीचे की ओर काम करते हैं. इसमें, कम प्राथमिकता वाले नियम, ज़्यादा प्राथमिकता वाले नियमों को बदल देते हैं. अगर कोई नियम किसी खास पाथ पर पढ़ने या लिखने की अनुमतियां देता है, तो वह उसके नीचे मौजूद सभी चाइल्ड नोड का ऐक्सेस भी देता है. इस स्ट्रक्चर का इस्तेमाल करें:

{
  "rules": {
     "foo": {
        // allows read to /foo/*
        ".read": "data.child('baz').val() === true",
        "bar": {
          /* ignored, since read was allowed already */
          ".read": false
        }
     }
  }
}

सुरक्षा के इस स्ट्रक्चर की मदद से, /bar/ को तब पढ़ा जा सकता है, जब /foo/ में वैल्यू true वाला चाइल्ड baz शामिल हो. /foo/bar/ में दिए गए ".read": false नियम का यहां कोई असर नहीं पड़ता, क्योंकि चाइल्ड पाथ से ऐक्सेस रद्द नहीं किया जा सकता.

ऐसा हो सकता है कि यह आपको तुरंत समझ न आए, लेकिन यह नियमों की भाषा का एक अहम हिस्सा है. इससे, ऐक्सेस से जुड़े बहुत जटिल अधिकारों को कम से कम प्रयास के साथ लागू किया जा सकता है. इस बारे में, इस गाइड में आगे उपयोगकर्ता के हिसाब से सुरक्षा के बारे में बताते समय बताया जाएगा.

ध्यान दें कि .validate नियम कैस्केड नहीं होते. डेटा डालने की अनुमति देने के लिए, पुष्टि करने वाले सभी नियमों को हैरारकी के सभी लेवल पर पूरा करना ज़रूरी है.

नियम, फ़िल्टर नहीं होते

नियम, एक-एक करके लागू किए जाते हैं. इसका मतलब है कि अगर उस जगह या पैरंट लोकेशन पर कोई ऐसा नियम नहीं है जो ऐक्सेस देता है, तो पढ़ने या लिखने का काम तुरंत बंद हो जाता है. भले ही, जिन चाइल्ड पाथ पर असर पड़ा है उन्हें ऐक्सेस किया जा सकता हो, फिर भी पैरंट लोकेशन पर पढ़ने की सुविधा पूरी तरह से काम नहीं करेगी. इस स्ट्रक्चर पर विचार करें:

{
  "rules": {
    "records": {
      "rec1": {
        ".read": true
      },
      "rec2": {
        ".read": false
      }
    }
  }
}

यह समझे बिना कि नियमों का आकलन एक-एक करके किया जाता है, ऐसा लग सकता है कि /records/ पाथ को फ़ेच करने पर rec1 दिखेगा, लेकिन rec2 नहीं. हालांकि, असल नतीजा गड़बड़ी है:

JavaScript
var db = firebase.database();
db.ref("records").once("value", function(snap) {
  // success method is not called
}, function(err) {
  // error callback triggered with PERMISSION_DENIED
});
Objective-C
ध्यान दें: Firebase का यह प्रॉडक्ट, ऐप्लिकेशन क्लिप टारगेट पर उपलब्ध नहीं है.
FIRDatabaseReference *ref = [[FIRDatabase database] reference];
[[_ref child:@"records"] observeSingleEventOfType:FIRDataEventTypeValue withBlock:^(FIRDataSnapshot *snapshot) {
  // success block is not called
} withCancelBlock:^(NSError * _Nonnull error) {
  // cancel block triggered with PERMISSION_DENIED
}];
Swift
ध्यान दें: Firebase का यह प्रॉडक्ट, ऐप्लिकेशन क्लिप टारगेट पर उपलब्ध नहीं है.
var ref = FIRDatabase.database().reference()
ref.child("records").observeSingleEventOfType(.Value, withBlock: { snapshot in
    // success block is not called
}, withCancelBlock: { error in
    // cancel block triggered with PERMISSION_DENIED
})
Java
FirebaseDatabase database = FirebaseDatabase.getInstance();
DatabaseReference ref = database.getReference("records");
ref.addListenerForSingleValueEvent(new ValueEventListener() {
  @Override
  public void onDataChange(DataSnapshot snapshot) {
    // success method is not called
  }

  @Override
  public void onCancelled(FirebaseError firebaseError) {
    // error callback triggered with PERMISSION_DENIED
  });
});
REST
curl https://docs-examples.firebaseio.com/rest/records/
# response returns a PERMISSION_DENIED error

/records/ में, डेटा पढ़ने की प्रोसेस एक साथ होती है. साथ ही, ऐसा कोई पढ़ने का नियम नहीं है जो /records/ में मौजूद सभी डेटा का ऐक्सेस देता हो. इसलिए, इससे PERMISSION_DENIED गड़बड़ी का मैसेज दिखेगा. अगर हम Firebase Console में सुरक्षा सिम्युलेटर में इस नियम का आकलन करते हैं, तो हम देख सकते हैं कि पढ़ने की अनुमति नहीं दी गई थी, क्योंकि पढ़ने से जुड़े किसी भी नियम ने /records/ पाथ को ऐक्सेस करने की अनुमति नहीं दी थी. हालांकि, ध्यान दें कि rec1 के लिए बने नियम का कभी आकलन नहीं किया गया, क्योंकि वह उस पाथ में नहीं था जिसके लिए हमने अनुरोध किया था. rec1 को फ़ेच करने के लिए, हमें इसे सीधे ऐक्सेस करना होगा:

JavaScript
var db = firebase.database();
db.ref("records/rec1").once("value", function(snap) {
  // SUCCESS!
}, function(err) {
  // error callback is not called
});
Objective-C
ध्यान दें: Firebase का यह प्रॉडक्ट, ऐप्लिकेशन क्लिप टारगेट पर उपलब्ध नहीं है.
FIRDatabaseReference *ref = [[FIRDatabase database] reference];
[[ref child:@"records/rec1"] observeSingleEventOfType:FEventTypeValue withBlock:^(FIRDataSnapshot *snapshot) {
    // SUCCESS!
}];
Swift
ध्यान दें: Firebase का यह प्रॉडक्ट, ऐप्लिकेशन क्लिप टारगेट पर उपलब्ध नहीं है.
var ref = FIRDatabase.database().reference()
ref.child("records/rec1").observeSingleEventOfType(.Value, withBlock: { snapshot in
    // SUCCESS!
})
Java
FirebaseDatabase database = FirebaseDatabase.getInstance();
DatabaseReference ref = database.getReference("records/rec1");
ref.addListenerForSingleValueEvent(new ValueEventListener() {
  @Override
  public void onDataChange(DataSnapshot snapshot) {
    // SUCCESS!
  }

  @Override
  public void onCancelled(FirebaseError firebaseError) {
    // error callback is not called
  }
});
REST
curl https://docs-examples.firebaseio.com/rest/records/rec1
# SUCCESS!

ओवरलैप होने वाले स्टेटमेंट

किसी नोड पर एक से ज़्यादा नियम लागू हो सकते हैं. अगर एक से ज़्यादा नियमों के एक्सप्रेशन किसी नोड की पहचान करते हैं, तो ऐक्सेस करने के तरीके को तब अस्वीकार कर दिया जाता है, जब कोई भी शर्त false हो:

{
  "rules": {
    "messages": {
      // A rule expression that applies to all nodes in the 'messages' node
      "$message": {
        ".read": "true",
        ".write": "true"
      },
      // A second rule expression applying specifically to the 'message1` node
      "message1": {
        ".read": "false",
        ".write": "false"
      }
    }
  }
}

ऊपर दिए गए उदाहरण में, message1 नोड को पढ़ने की अनुमति नहीं दी जाएगी, क्योंकि दूसरा नियम हमेशा false होता है. भले ही, पहला नियम हमेशा true हो.

अगले चरण

Firebase रीयलटाइम डेटाबेस के सुरक्षा नियमों के बारे में ज़्यादा जानने के लिए: