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

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

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

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

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

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

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

इसलिए, उदाहरण के लिए, अगर हमें किसी child_node को parent_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 and Write Rules Cascade

.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/ में baz मौजूद हो और उसकी वैल्यू true हो. /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 कंसोल में मौजूद सुरक्षा सिम्युलेटर में करते हैं, तो हमें पता चलता है कि पढ़ने की कार्रवाई को अस्वीकार कर दिया गया है. ऐसा इसलिए हुआ, क्योंकि पढ़ने के किसी भी नियम ने /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 रीयलटाइम डेटाबेस के सुरक्षा नियमों के बारे में ज़्यादा जानने के लिए, यहां जाएं:

  • Rules भाषा के अगले मुख्य सिद्धांत, डाइनैमिक शर्तों के बारे में जानें. इनकी मदद से, Rules ये काम कर सकता है: उपयोगकर्ता की अनुमति की जांच करना, मौजूदा और आने वाले डेटा की तुलना करना, आने वाले डेटा की पुष्टि करना, क्लाइंट से आने वाली क्वेरी के स्ट्रक्चर की जांच करना वगैरह.

  • सुरक्षा से जुड़े इस्तेमाल के सामान्य उदाहरण और उनसे जुड़ी Firebase सुरक्षा नियमों की परिभाषाएं देखें.