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

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 इससे यह तय होता है कि सही तरीके से फ़ॉर्मैट की गई वैल्यू कैसी दिखेगी, उसमें चाइल्ड एट्रिब्यूट हैं या नहीं, और उसका डेटा टाइप क्या है.

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

नियमों के सभी स्टेटमेंट, नोड की ओर इशारा करते हैं. कोई स्टेटमेंट, किसी खास नोड की ओर इशारा कर सकता है. इसके अलावा, यह क्रम के किसी लेवल पर नोड के सेट की ओर इशारा करने के लिए, $ वाइल्डकार्ड कैप्चर वैरिएबल का इस्तेमाल कर सकता है. इन कैप्चर वैरिएबल का इस्तेमाल करके, नोड की कुंजियों की वैल्यू सेव करें. इससे, इनका इस्तेमाल नियमों के अगले स्टेटमेंट में किया जा सकेगा. इस तकनीक की मदद से, सुरक्षा नियमों की ज़्यादा जटिल Security 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/ में 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 का यह प्रॉडक्ट, App Clip टारगेट पर उपलब्ध नहीं है.
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 का यह प्रॉडक्ट, App Clip टारगेट पर उपलब्ध नहीं है.
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 का यह प्रॉडक्ट, App Clip टारगेट पर उपलब्ध नहीं है.
FIRDatabaseReference *ref = [[FIRDatabase database] reference];
[[ref child:@"records/rec1"] observeSingleEventOfType:FEventTypeValue withBlock:^(FIRDataSnapshot *snapshot) {
    // SUCCESS!
}];
Swift
ध्यान दें: Firebase का यह प्रॉडक्ट, App Clip टारगेट पर उपलब्ध नहीं है.
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 रीयलटाइम डेटाबेस के सुरक्षा नियमों के बारे में ज़्यादा जानकारी पाने के लिए: