التعرف على البنية الأساسية للغة قواعد أمان قاعدة البيانات في الوقت الفعلي

تسمح لك "قواعد أمان قاعدة بيانات Firebase في الوقت الفعلي" بالتحكّم في الوصول إلى البيانات المخزَّنة. في قاعدة البيانات لديك. تسمح لك بنية القواعد المرنة بإنشاء من القواعد التي تطابق أي شيء، بدءًا من جميع عمليات الكتابة إلى قاعدة البيانات وحتى العمليات على العُقد الفردية.

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

يصف هذا الموضوع البنية الأساسية لقواعد أمان قاعدة البيانات في الوقت الفعلي وبنيتها. المستخدمة لإنشاء مجموعات قواعد كاملة.

تنظيم قواعد الأمان

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

تحدِّد القواعد الأساسية مجموعة من العقد التي يجب تأمينها، وطرق الوصول (مثل القراءة والكتابة) المُستخدَمة، والشروط التي يتم بموجبها السماح بالوصول أو رفضه. في الأمثلة التالية، ستكون الشروط عبارة عن عبارات true و false بسيطة، ولكن في الموضوع التالي سنتناول طرقًا أكثر ديناميكية ل expressed conditions.

على سبيل المثال، إذا كنا نحاول تأمين 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 تحدّد هذه السمة الشكل الذي ستظهر به القيمة المنسّقة بشكل صحيح، وما إذا كانت تحتوي على سمات فرعية، ونوع البيانات.

متغيّرات تسجيل أحرف البدل

تشير جميع عبارات القواعد إلى العُقد. يمكن أن يشير البيان إلى node معيّنة أو يستخدم $ متغيّرات الالتقاط للأحرف البدل للإشارة إلى مجموعات من العقد في مستوى من التسلسل الهرمي. استخدام متغيرات الالتقاط هذه لتخزين قيمة العقدة للاستخدام داخل عبارات القواعد اللاحقة. يتيح لك هذا الأسلوب كتابة أكثر تعقيدًا 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. لا تحتوي قاعدة ".read": false ضمن /foo/bar/ على تأثير هنا، حيث لا يمكن إبطال الدخول عن طريق مسار فرعي.

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

يُرجى العلم أنّ قواعد .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 هذا في استهداف تطبيق 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
})
جافا
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 هذا في استهداف تطبيق Clip.
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
  }
});
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، وهو الشروط الديناميكية، التي تتيح لتطبيقك التحقّق من تفويض العميل، ومقارنة البيانات الحالية والواردة، والتحقّق من صحة البيانات الواردة، والتحقّق من بنية طلبات البحث الواردة من العميل، وغير ذلك.

  • راجِع حالات استخدام الأمان الشائعة وتعريفات قواعد أمان Firebase التي تعالج هذه الحالات.