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

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

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

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

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

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

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

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

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