ترجمت واجهة Cloud Translation API‏ هذه الصفحة.
Switch to English

تأمين بياناتك

قواعد قاعدة بيانات الوقت الحقيقي هي التكوين التعريفي لقاعدة البيانات الخاصة بك. وهذا يعني أنه تم تحديد القواعد بشكل منفصل عن منطق المنتج. يحتوي هذا على عدد من المزايا: العملاء ليسوا مسؤولين عن فرض الأمان ، ولن تؤدي عمليات تنفيذ عربات التي تجرها الدواب إلى اختراق بياناتك ، وربما الأهم من ذلك ، ليست هناك حاجة لحكم وسيط ، مثل الخادم ، لحماية البيانات من العالم.

هيكلة القواعد الخاصة بك

تتكون قواعد قاعدة بيانات الوقت الفعلي من تعبيرات تشبه جافا سكريبت المضمنة في مستند JSON. يجب أن يتبع هيكل قواعدك بنية البيانات التي قمت بتخزينها في قاعدة البيانات الخاصة بك. على سبيل المثال ، لنفترض أنك تتبع قائمة الرسائل ولديك بيانات تبدو كما يلي:

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

يجب تنظيم قواعدك بطريقة مماثلة. في ما يلي مثال لمجموعة من القواعد التي قد تكون منطقية لبنية البيانات هذه.

{
  "rules": {
    "messages": {
      "$message": {
        // only messages from the last ten minutes can be read
        ".read": "data.child('timestamp').val() > (now - 600000)",

        // new messages must have a string content and a number timestamp
        ".validate": "newData.hasChildren(['content', 'timestamp']) && newData.child('content').isString() && newData.child('timestamp').isNumber()"
      }
    }
  }
}

أنواع قواعد الأمن

هناك ثلاثة أنواع من القواعد لفرض الأمن: .write ، .read ، و .validate . فيما يلي ملخص سريع لأهدافهم:

أنواع القواعد
.اقرأ يصف ما إذا كان المستخدمون يسمحون بقراءة البيانات ومتى يتم ذلك.
.اكتب يصف ما إذا كان مسموحًا بوقت كتابة البيانات ومتى.
. التحقق من الصحة يحدد كيف ستبدو القيمة المنسقة بشكل صحيح ، سواء كانت لها سمات فرعية ، ونوع البيانات.

المتغيرات المحددة مسبقًا

هناك عدد من المتغيرات المفيدة والمحددة مسبقًا التي يمكن الوصول إليها داخل تعريف قاعدة الأمان. سنستخدم معظمها في الأمثلة أدناه. في ما يلي ملخص موجز لكل منها ورابط إلى مرجع واجهة برمجة التطبيقات المناسب.

المتغيرات المحددة مسبقًا
الآن الوقت الحالي بالمللي ثانية منذ حقبة Linux. يعمل هذا بشكل جيد بشكل خاص للتحقق من صحة الطوابع الزمنية التي تم إنشاؤها باستخدام Firebase.database في SDK.ServerValue.TIMESTAMP.
جذر يمثل RuleDataSnapshot مسار الجذر في قاعدة بيانات Firebase كما كان قبل العملية التي حاولت تنفيذها.
بيانات جديدة يمثل RuleDataSnapshot البيانات كما كانت موجودة بعد العملية التي حاولت تنفيذها. يتضمن البيانات الجديدة التي تتم كتابتها والبيانات الموجودة.
البيانات يمثل RuleDataSnapshot البيانات كما كانت موجودة قبل العملية التي حاولت تنفيذها.
المتغيرات $ مسار حرف بدل يستخدم لتمثيل المعرفات ومفاتيح فرعية ديناميكية.
المصادقة يمثل حمولة رمزية للمستخدم المصدق.

يمكن استخدام هذه المتغيرات في أي مكان في القواعد الخاصة بك. على سبيل المثال ، تضمن قواعد الأمان أدناه أن البيانات المكتوبة على /foo/ العقدة يجب أن تكون سلسلة أقل من 100 حرف:

{
  "rules": {
    "foo": {
      // /foo is readable by the world
      ".read": true,

      // /foo is writable by the world
      ".write": true,

      // data written to /foo must be a string less than 100 characters
      ".validate": "newData.isString() && newData.val().length < 100"
    }
  }
}

البيانات الموجودة مقابل البيانات الجديدة

يتم استخدام متغير data المحدد مسبقًا للإشارة إلى البيانات قبل حدوث عملية الكتابة. على العكس ، يحتوي متغير newData على البيانات الجديدة التي ستكون موجودة إذا نجحت عملية الكتابة. يمثل newData النتيجة المدمجة للبيانات الجديدة التي تتم كتابتها والبيانات الموجودة.

للتوضيح ، ستسمح لنا هذه القاعدة بإنشاء سجلات جديدة أو حذف السجلات الموجودة ، ولكن ليس لإجراء تغييرات على البيانات الموجودة غير الخالية:

// we can write as long as old data or new data does not exist
// in other words, if this is a delete or a create, but not an update
".write": "!data.exists() || !newData.exists()"

الرجوع إلى البيانات في المسارات الأخرى

يمكن استخدام أي بيانات كمعيار للقواعد. باستخدام root المتغيرات المحددة مسبقًا data newData ، يمكننا الوصول إلى أي مسار كما كان قبل أو بعد حدث الكتابة.

ضع في اعتبارك هذا المثال ، الذي يسمح بعمليات الكتابة طالما كانت قيمة /allow_writes/ العقدة true ، وليس لدى العقدة readOnly مجموعة إشارة readOnly ، وهناك طفل اسمه foo في البيانات المكتوبة حديثًا:

".write": "root.child('allow_writes').val() === true &&
          !data.parent().child('readOnly').exists() &&
          newData.child('foo').exists()"

قراءة وكتابة القواعد تتالي

.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 . ".read": false القاعدة ".read": false تحت /foo/bar/ ليس لها تأثير هنا ، لأنه لا يمكن إبطال الوصول بواسطة مسار فرعي.

على الرغم من أنه قد لا يبدو بديهيًا على الفور ، إلا أن هذا جزء قوي من لغة القواعد ويسمح بتنفيذ امتيازات الوصول المعقدة للغاية بأقل جهد. سيتم توضيح ذلك عندما نصل إلى الأمان المستند إلى المستخدم لاحقًا في هذا الدليل.

لاحظ أن قواعد .validate لا تتالي. يجب استيفاء جميع قواعد التحقق على جميع مستويات التسلسل الهرمي من أجل السماح بالكتابة.

القواعد ليست مرشحات

يتم تطبيق القواعد بطريقة ذرية. وهذا يعني فشل عملية القراءة أو الكتابة على الفور إذا لم تكن هناك قاعدة في هذا الموقع أو في موقع أصل يمنح حق الوصول. حتى إذا كان من الممكن الوصول إلى كل مسار طفل متأثر ، فستفشل القراءة في الموقع الأصلي تمامًا. النظر في هذه البنية:

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

بدون فهم أن القواعد يتم تقييمها تلقائيًا ، قد يبدو أن جلب /records/ المسار سيعيد rec1 ولكن ليس rec2 . النتيجة الفعلية ، مع ذلك ، خطأ:

جافا سكريبت
var db = firebase.database();
db.ref("records").once("value", function(snap) {
  // success method is not called
}, function(err) {
  // error callback triggered with PERMISSION_DENIED
});
ج موضوعية
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
}];
سويفت
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
})
جافا
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
  });
});
راحة
curl https://docs-examples.firebaseio.com/rest/records/
# response returns a PERMISSION_DENIED error

نظرًا لأن عملية القراءة في /records/ هي عملية ذرية ، ولا توجد قاعدة قراءة تتيح الوصول إلى جميع البيانات ضمن /records/ ، فسيؤدي ذلك إلى ظهور خطأ PERMISSION_DENIED . إذا قمنا بتقييم هذه القاعدة في محاكي الأمان في وحدة تحكم Firebase ، يمكننا أن نرى أنه تم رفض عملية القراءة:

Attempt to read /records with auth=Success(null)
    /
    /records

No .read rule allowed the operation.
Read was denied.

تم رفض العملية نظرًا لعدم وجود قاعدة قراءة تسمح بالوصول إلى /records/ المسار ، ولكن لاحظ أنه لم يتم تقييم قاعدة rec1 مطلقًا لأنها لم تكن في المسار الذي rec1 . لجلب rec1 ، نحتاج إلى الوصول إليه مباشرة:

جافا سكريبت
var db = firebase.database();
db.ref("records/rec1").once("value", function(snap) {
  // SUCCESS!
}, function(err) {
  // error callback is not called
});
ج موضوعية
FIRDatabaseReference *ref = [[FIRDatabase database] reference];
[[ref child:@"records/rec1"] observeSingleEventOfType:FEventTypeValue withBlock:^(FIRDataSnapshot *snapshot) {
    // SUCCESS!
}];
سويفت
var ref = FIRDatabase.database().reference()
ref.child("records/rec1").observeSingleEventOfType(.Value, withBlock: { snapshot in
    // SUCCESS!
})
جافا
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
  }
});
راحة
curl https://docs-examples.firebaseio.com/rest/records/rec1
# SUCCESS!

قواعد قائمة على الاستعلام

على الرغم من أنه لا يمكنك استخدام القواعد كفلاتر ، يمكنك تقييد الوصول إلى مجموعات فرعية من البيانات باستخدام معلمات الاستعلام في القواعد الخاصة بك. استخدم query. التعبيرات في القواعد الخاصة بك لمنح حق الوصول للقراءة أو الكتابة بناءً على معلمات الاستعلام.

على سبيل المثال، يستخدم المستند إلى استعلام القاعدة التالية قواعد الأمان المستند إلى المستخدم وقواعد المستند إلى استعلام لتقييد الوصول إلى البيانات في baskets جمع فقط لسلال التسوق يملك المستخدم النشط:

 "baskets": {
  ".read": "auth.uid != null &&
            query.orderByChild == 'owner' &&
            query.equalTo == auth.uid" // restrict basket access to owner of basket
}
 

ينجح الاستعلام التالي ، الذي يتضمن معلمات الاستعلام في القاعدة:

 db.ref("baskets").orderByChild("owner")
                 .equalTo(auth.currentUser.uid)
                 .on("value", cb)                 // Would succeed
 

ومع ذلك ، ستفشل الاستعلامات التي لا تتضمن المعلمات في القاعدة مع وجود خطأ PermissionDenied :

 db.ref("baskets").on("value", cb)                 // Would fail with PermissionDenied
 

يمكنك أيضًا استخدام القواعد المستندة إلى الاستعلام لتحديد كمية البيانات التي يقوم العميل بتنزيلها من خلال عمليات القراءة.

على سبيل المثال ، تحدد القاعدة التالية إمكانية الوصول للقراءة إلى أول 1000 نتيجة فقط للاستعلام ، حسب الترتيب حسب الأولوية:

 messages: {
  ".read": "query.orderByKey &&
            query.limitToFirst <= 1000"
}

// Example queries:

db.ref("messages").on("value", cb)                // Would fail with PermissionDenied

db.ref("messages").limitToFirst(1000)
                  .on("value", cb)                // Would succeed (default order by key)

 

query. التالي query. التعبيرات متوفرة في قواعد بيانات Realtime Database.

تعبيرات القاعدة المستندة إلى الاستعلام
التعبير نوع وصف
query.orderByKey
query.orderByPriority
query.orderByValue
منطقي صحيح للاستعلامات مرتبة حسب المفتاح أو الأولوية أو القيمة. خطأ خلاف ذلك.
استعلام. orderByChild خيط
لا شيء
استخدم سلسلة لتمثيل المسار النسبي للعقدة التابعة. على سبيل المثال ، query.orderByChild == "address/zip" . إذا لم يتم طلب الاستعلام بواسطة عقدة فرعية ، فستكون هذه القيمة فارغة.
query.startAt
query.endAt
الاستعلام
خيط
رقم
منطقي
لا شيء
يقوم باسترجاع حدود الاستعلام المنفذ ، أو يقوم بإرجاع قيمة فارغة إذا لم يكن هناك مجموعة منضم.
الاستعلام
الاستعلام
رقم
لا شيء
يقوم باسترجاع الحد على الاستعلام المنفذ ، أو يقوم بإرجاع قيمة فارغة إذا لم يكن هناك حد معين.

التحقق من البيانات

يجب أن يتم تنفيذ هياكل البيانات والتحقق من صحة تنسيق البيانات .validate باستخدام قواعد التحقق من الصحة ، التي يتم تشغيلها فقط بعد نجاح قاعدة .write في منح الوصول. في ما يلي نموذج .validate قاعدة .validate الذي يسمح فقط بالتواريخ بتنسيق YYYY-MM-DD بين الأعوام 1900-2099 ، والذي يتم التحقق منه باستخدام تعبير عادي.

".validate": "newData.isString() &&
              newData.val().matches(/^(19|20)[0-9][0-9][-\\/. ](0[1-9]|1[012])[-\\/. ](0[1-9]|[12][0-9]|3[01])$/)"

قواعد .validate هي النوع الوحيد لقاعدة الأمان التي لا تتالي. إذا فشلت أي قاعدة تحقق من الصحة في أي سجل فرعي ، فسيتم رفض عملية الكتابة بالكامل. بالإضافة إلى ذلك ، يتم تجاهل تعريفات التحقق عند حذف البيانات (أي عندما تكون القيمة الجديدة التي تتم كتابتها null ).

قد تبدو هذه نقاطًا تافهة ، ولكنها في الواقع ميزات مهمة لكتابة قواعد بيانات Firebase Realtime القوية. خذ بعين الاعتبار القواعد التالية:

{
  "rules": {
    // write is allowed for all paths
    ".write": true,
    "widget": {
      // a valid widget must have attributes "color" and "size"
      // allows deleting widgets (since .validate is not applied to delete rules)
      ".validate": "newData.hasChildren(['color', 'size'])",
      "size": {
        // the value of "size" must be a number between 0 and 99
        ".validate": "newData.isNumber() &&
                      newData.val() >= 0 &&
                      newData.val() <= 99"
      },
      "color": {
        // the value of "color" must exist as a key in our mythical
        // /valid_colors/ index
        ".validate": "root.child('valid_colors/' + newData.val()).exists()"
      }
    }
  }
}

مع أخذ هذا المتغير في الاعتبار ، انظر إلى نتائج عمليات الكتابة التالية:

جافا سكريبت
var ref = db.ref("/widget");

// PERMISSION_DENIED: does not have children color and size
ref.set('foo');

// PERMISSION DENIED: does not have child color
ref.set({size: 22});

// PERMISSION_DENIED: size is not a number
ref.set({ size: 'foo', color: 'red' });

// SUCCESS (assuming 'blue' appears in our colors list)
ref.set({ size: 21, color: 'blue'});

// If the record already exists and has a color, this will
// succeed, otherwise it will fail since newData.hasChildren(['color', 'size'])
// will fail to validate
ref.child('size').set(99);
ج موضوعية
FIRDatabaseReference *ref = [[[FIRDatabase database] reference] child: @"widget"];

// PERMISSION_DENIED: does not have children color and size
[ref setValue: @"foo"];

// PERMISSION DENIED: does not have child color
[ref setValue: @{ @"size": @"foo" }];

// PERMISSION_DENIED: size is not a number
[ref setValue: @{ @"size": @"foo", @"color": @"red" }];

// SUCCESS (assuming 'blue' appears in our colors list)
[ref setValue: @{ @"size": @21, @"color": @"blue" }];

// If the record already exists and has a color, this will
// succeed, otherwise it will fail since newData.hasChildren(['color', 'size'])
// will fail to validate
[[ref child:@"size"] setValue: @99];
سويفت
var ref = FIRDatabase.database().reference().child("widget")

// PERMISSION_DENIED: does not have children color and size
ref.setValue("foo")

// PERMISSION DENIED: does not have child color
ref.setValue(["size": "foo"])

// PERMISSION_DENIED: size is not a number
ref.setValue(["size": "foo", "color": "red"])

// SUCCESS (assuming 'blue' appears in our colors list)
ref.setValue(["size": 21, "color": "blue"])

// If the record already exists and has a color, this will
// succeed, otherwise it will fail since newData.hasChildren(['color', 'size'])
// will fail to validate
ref.child("size").setValue(99);
جافا
FirebaseDatabase database = FirebaseDatabase.getInstance();
DatabaseReference ref = database.getReference("widget");

// PERMISSION_DENIED: does not have children color and size
ref.setValue("foo");

// PERMISSION DENIED: does not have child color
ref.child("size").setValue(22);

// PERMISSION_DENIED: size is not a number
Map<String,Object> map = new HashMap<String, Object>();
map.put("size","foo");
map.put("color","red");
ref.setValue(map);

// SUCCESS (assuming 'blue' appears in our colors list)
map = new HashMap<String, Object>();
map.put("size", 21);
map.put("color","blue");
ref.setValue(map);

// If the record already exists and has a color, this will
// succeed, otherwise it will fail since newData.hasChildren(['color', 'size'])
// will fail to validate
ref.child("size").setValue(99);
راحة
# PERMISSION_DENIED: does not have children color and size
curl -X PUT -d 'foo' \
https://docs-examples.firebaseio.com/rest/securing-data/example.json

# PERMISSION DENIED: does not have child color
curl -X PUT -d '{"size": 22}' \
https://docs-examples.firebaseio.com/rest/securing-data/example.json

# PERMISSION_DENIED: size is not a number
curl -X PUT -d '{"size": "foo", "color": "red"}' \
https://docs-examples.firebaseio.com/rest/securing-data/example.json

# SUCCESS (assuming 'blue' appears in our colors list)
curl -X PUT -d '{"size": 21, "color": "blue"}' \
https://docs-examples.firebaseio.com/rest/securing-data/example.json

# If the record already exists and has a color, this will
# succeed, otherwise it will fail since newData.hasChildren(['color', 'size'])
# will fail to validate
curl -X PUT -d '99' \
https://docs-examples.firebaseio.com/rest/securing-data/example/size.json

الآن دعونا ننظر في في نفس الهيكل، ولكن باستخدام .write قواعد بدلا من .validate :

{
  "rules": {
    // this variant will NOT allow deleting records (since .write would be disallowed)
    "widget": {
      // a widget must have 'color' and 'size' in order to be written to this path
      ".write": "newData.hasChildren(['color', 'size'])",
      "size": {
        // the value of "size" must be a number between 0 and 99, ONLY IF WE WRITE DIRECTLY TO SIZE
        ".write": "newData.isNumber() && newData.val() >= 0 && newData.val() <= 99"
      },
      "color": {
        // the value of "color" must exist as a key in our mythical valid_colors/ index
        // BUT ONLY IF WE WRITE DIRECTLY TO COLOR
        ".write": "root.child('valid_colors/'+newData.val()).exists()"
      }
    }
  }
}

في هذا البديل ، ستنجح أي من العمليات التالية:

جافا سكريبت
var ref = new Firebase(URL + "/widget");

// ALLOWED? Even though size is invalid, widget has children color and size,
// so write is allowed and the .write rule under color is ignored
ref.set({size: 99999, color: 'red'});

// ALLOWED? Works even if widget does not exist, allowing us to create a widget
// which is invalid and does not have a valid color.
// (allowed by the write rule under "color")
ref.child('size').set(99);
ج موضوعية
Firebase *ref = [[Firebase alloc] initWithUrl:URL];

// ALLOWED? Even though size is invalid, widget has children color and size,
// so write is allowed and the .write rule under color is ignored
[ref setValue: @{ @"size": @9999, @"color": @"red" }];

// ALLOWED? Works even if widget does not exist, allowing us to create a widget
// which is invalid and does not have a valid color.
// (allowed by the write rule under "color")
[[ref childByAppendingPath:@"size"] setValue: @99];
سويفت
var ref = Firebase(url:URL)

// ALLOWED? Even though size is invalid, widget has children color and size,
// so write is allowed and the .write rule under color is ignored
ref.setValue(["size": 9999, "color": "red"])

// ALLOWED? Works even if widget does not exist, allowing us to create a widget
// which is invalid and does not have a valid color.
// (allowed by the write rule under "color")
ref.childByAppendingPath("size").setValue(99)
جافا
Firebase ref = new Firebase(URL + "/widget");

// ALLOWED? Even though size is invalid, widget has children color and size,
// so write is allowed and the .write rule under color is ignored
Map<String,Object> map = new HashMap<String, Object>();
map.put("size", 99999);
map.put("color", "red");
ref.setValue(map);

// ALLOWED? Works even if widget does not exist, allowing us to create a widget
// which is invalid and does not have a valid color.
// (allowed by the write rule under "color")
ref.child("size").setValue(99);
راحة
# ALLOWED? Even though size is invalid, widget has children color and size,
# so write is allowed and the .write rule under color is ignored
curl -X PUT -d '{size: 99999, color: "red"}' \
https://docs-examples.firebaseio.com/rest/securing-data/example.json

# ALLOWED? Works even if widget does not exist, allowing us to create a widget
# which is invalid and does not have a valid color.
# (allowed by the write rule under "color")
curl -X PUT -d '99' \
https://docs-examples.firebaseio.com/rest/securing-data/example/size.json

وهذا يوضح الاختلافات بين .write و .validate القواعد. كما هو موضح ، يجب كتابة جميع هذه القواعد باستخدام .validate ، مع استثناء محتمل newData.hasChildren() ، والتي ستعتمد على ما إذا كان يجب السماح بالحذف.

استخدام متغيرات $ لالتقاط مقاطع المسار

يمكنك التقاط أجزاء من المسار للقراءة أو الكتابة من خلال التصريح عن متغيرات الالتقاط بالبادئة $ . يعمل هذا كحرف شامل ، ويخزن قيمة هذا المفتاح للاستخدام داخل إعلانات القواعد:

{
  "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 على .validate غير 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 }
    }
  }
}

مثال محادثة مجهول

دعنا نجمع القواعد معًا وننشئ تطبيق دردشة مجهول وآمن. سنذكر هنا القواعد ، ويتم تضمين نسخة وظيفية من الدردشة أدناه:

{
  "rules": {
    // default rules are false if not specified
    // setting these to true would make ALL CHILD PATHS readable/writable
    // ".read": false,
    // ".write": false,

    "room_names": {
      // the room names can be enumerated and read
      // they cannot be modified since no write rule
      // explicitly allows this
      ".read": true,

      "$room_id": {
        // this is just for documenting the structure of rooms, since
        // they are read-only and no write rule allows this to be set
        ".validate": "newData.isString()"
      }
    },

    "messages": {
      "$room_id": {
        // the list of messages in a room can be enumerated and each
        // message could also be read individually, the list of messages
        // for a room cannot be written to in bulk
        ".read": true,

        // room we want to write a message to must be valid
        ".validate": "root.child('room_names/'+$room_id).exists()",

        "$message_id": {
          // a new message can be created if it does not exist, but it
          // cannot be modified or deleted
          ".write": "!data.exists() && newData.exists()",
          // the room attribute must be a valid key in room_names/ (the room must exist)
          // the object to write must have a name, message, and timestamp
          ".validate": "newData.hasChildren(['name', 'message', 'timestamp'])",

          // the name must be a string, longer than 0 chars, and less than 20 and cannot contain "admin"
          "name": { ".validate": "newData.isString() && newData.val().length > 0 && newData.val().length < 20 && !newData.val().contains('admin')" },

          // the message must be longer than 0 chars and less than 50
          "message": { ".validate": "newData.isString() && newData.val().length > 0 && newData.val().length < 50" },

          // messages cannot be added in the past or the future
          // clients should use firebase.database.ServerValue.TIMESTAMP
          // to ensure accurate timestamps
          "timestamp": { ".validate": "newData.val() <= now" },

          // no other fields can be included in a message
          "$other": { ".validate": false }
        }
      }
    }
  }
}

الخطوات التالية