شروط الكتابة لقواعد أمان Cloud Firestore

يستند هذا الدليل إلى دليل تنظيم قواعد الأمان لتوضيح كيفية إضافة شروط إلى Cloud Firestore Security Rules. إذا لم تكن على دراية بأساسيات Cloud Firestore Security Rules، يمكنك الاطّلاع على دليل البدء.

الوحدة الأساسية في Cloud Firestore Security Rules هي الشرط. الشرط هو تعبير منطقي يحدّد ما إذا كان يجب السماح بعملية معيّنة أو رفضها. استخدِم قواعد الأمان لكتابة شروط تتحقّق من مصادقة المستخدم أو تتحقّق من صحة البيانات الواردة أو تتيح الوصول إلى أجزاء أخرى من قاعدة البيانات.

المصادقة

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

service cloud.firestore {
  match /databases/{database}/documents {
    // Allow the user to access documents in the "cities" collection
    // only if they are authenticated.
    match /cities/{city} {
      allow read, write: if request.auth != null;
    }
  }
}

هناك نمط شائع آخر يتمثل في التأكّد من أنّ المستخدمين يمكنهم قراءة بياناتهم وكتابتها فقط:

service cloud.firestore {
  match /databases/{database}/documents {
    // Make sure the uid of the requesting user matches name of the user
    // document. The wildcard expression {userId} makes the userId variable
    // available in rules.
    match /users/{userId} {
      allow read, update, delete: if request.auth != null && request.auth.uid == userId;
      allow create: if request.auth != null;
    }
  }
}

إذا كان تطبيقك يستخدم خدمة Firebase Authentication أو Google Cloud Identity Platform، يحتوي المتغيّر request.auth على معلومات المصادقة الخاصة بالعميل الذي يطلب البيانات. لمزيد من المعلومات حول request.auth، يُرجى الاطّلاع على المستندات المرجعية.

التحقّق من صحة البيانات

تخزّن العديد من التطبيقات معلومات التحكّم في الوصول كحقول في المستندات في قاعدة البيانات. يمكن لـ Cloud Firestore Security Rules السماح بالوصول أو رفضه بشكل ديناميكي استنادًا إلى بيانات المستند:

service cloud.firestore {
  match /databases/{database}/documents {
    // Allow the user to read data if the document has the 'visibility'
    // field set to 'public'
    match /cities/{city} {
      allow read: if resource.data.visibility == 'public';
    }
  }
}

يشير المتغيّر resource إلى المستند المطلوب، بينما resource.data هو خريطة لجميع الحقول والقيم المخزّنة في المستند. لمزيد من المعلومات حول المتغيّر resource، اطّلِع على المستندات المرجعية.

عند كتابة البيانات، قد تحتاج إلى مقارنة البيانات الواردة بالبيانات الحالية. في هذه الحالة، إذا كانت مجموعة القواعد تسمح بعملية الكتابة المعلّقة، سيحتوي المتغيّر request.resource على حالة المستند المستقبلية. بالنسبة إلى عمليات update التي تعدّل مجموعة فرعية فقط من حقول المستند، سيحتوي المتغيّر request.resource على حالة المستند المعلق بعد العملية. يمكنك التحقّق من قيم الحقول في request.resource لمنع تعديلات البيانات غير المرغوب فيها أو غير المتّسقة:

service cloud.firestore {
  match /databases/{database}/documents {
    // Make sure all cities have a positive population and
    // the name is not changed
    match /cities/{city} {
      allow update: if request.resource.data.population > 0
                    && request.resource.data.name == resource.data.name;
    }
  }
}

الوصول إلى مستندات أخرى

باستخدام الدالتَين get() وexists()، يمكن لقواعد الأمان تقييم الطلبات الواردة مقارنةً بالمستندات الأخرى في قاعدة البيانات. تتطلّب الدالتان get() وexists() مسارات مستندات محدّدة بالكامل. عند استخدام المتغيرات لإنشاء مسارات get() وexists()، عليك تخطّي المتغيرات بشكل صريح باستخدام بنية $(variable).

في المثال أدناه، يتم تسجيل المتغيّر database من خلال عبارة المطابقة match /databases/{database}/documents واستخدامه لتكوين المسار:

service cloud.firestore {
  match /databases/{database}/documents {
    match /cities/{city} {
      // Make sure a 'users' document exists for the requesting user before
      // allowing any writes to the 'cities' collection
      allow create: if request.auth != null && exists(/databases/$(database)/documents/users/$(request.auth.uid));

      // Allow the user to delete cities if their user document has the
      // 'admin' field set to 'true'
      allow delete: if request.auth != null && get(/databases/$(database)/documents/users/$(request.auth.uid)).data.admin == true;
    }
  }
}

بالنسبة إلى عمليات الكتابة، يمكنك استخدام الدالة getAfter() للوصول إلى حالة مستند بعد اكتمال عملية أو مجموعة من عمليات الكتابة ولكن قبل تنفيذ العملية أو المجموعة. على غرار الدالة get()، تستخدم الدالة getAfter() مسار مستند محدّدًا بالكامل. يمكنك استخدام getAfter() لتحديد مجموعات من عمليات الكتابة التي يجب أن تتم معًا كمعاملة أو دفعة.

حدود الوصول إلى المكالمات

هناك حدّ أقصى لطلبات الوصول إلى المستند لكل عملية تقييم لمجموعة القواعد:

  • ‫10 لطلبات المستندات الفردية وطلبات البحث
  • ‫20 لعمليات القراءة والمعاملات والكتابة المجمّعة في مستندات متعددة. وينطبق الحدّ السابق البالغ 10 على كل عملية أيضًا.

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

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

للحصول على شرح تفصيلي حول كيفية تأثير هذه الحدود على المعاملات وعمليات الكتابة المجمّعة، راجِع دليل تأمين العمليات الذرية.

الوصول إلى المكالمات والأسعار

يؤدي استخدام هذه الدوال إلى تنفيذ عملية قراءة في قاعدة البيانات، ما يعني أنّه سيتم تحصيل رسوم منك مقابل قراءة المستندات حتى إذا رفضت القواعد الطلب. يمكنك الاطّلاع على Cloud Firestore الأسعار للحصول على معلومات أكثر تحديدًا عن الفوترة.

الدوال المخصّصة

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

  • يمكن أن تحتوي الدوال على عبارة return واحدة فقط. ولا يمكن أن تتضمّن أي منطق إضافي. على سبيل المثال، لا يمكنهم تنفيذ حلقات أو طلب خدمات خارجية.
  • يمكن للدوال الوصول تلقائيًا إلى الدوال والمتغيرات من النطاق الذي تم تعريفها فيه. على سبيل المثال، يمكن لدالة تم تحديدها ضِمن النطاق service cloud.firestore الوصول إلى المتغيّر resource والدوال المضمّنة، مثل get() وexists().
  • يمكن أن تستدعي الدوال دوال أخرى، ولكن لا يمكن أن تتكرر. يقتصر إجمالي عمق تسلسل استدعاء الدوال البرمجية على 10.
  • في إصدار القواعد v2، يمكن للدوال تحديد المتغيرات باستخدام الكلمة الرئيسية let. يمكن أن تتضمّن الدوال ما يصل إلى 10 عمليات ربط متغيرة، ولكن يجب أن تنتهي بعبارة إرجاع.

يتم تعريف الدالة باستخدام الكلمة الرئيسية function، وتتضمّن وسيطات صفرية أو أكثر. على سبيل المثال، قد تحتاج إلى دمج نوعَي الشروط المستخدَمة في الأمثلة أعلاه في دالة واحدة:

service cloud.firestore {
  match /databases/{database}/documents {
    // True if the user is signed in or the requested data is 'public'
    function signedInOrPublic() {
      return request.auth.uid != null || resource.data.visibility == 'public';
    }

    match /cities/{city} {
      allow read, write: if signedInOrPublic();
    }

    match /users/{user} {
      allow read, write: if signedInOrPublic();
    }
  }
}

يؤدي استخدام الدوال في قواعد الأمان إلى تسهيل صيانتها مع زيادة تعقيد القواعد.

القواعد ليست فلاتر

بعد تأمين بياناتك والبدء في كتابة الاستعلامات، تذكَّر أنّ قواعد الأمان ليست فلاتر. لا يمكنك كتابة طلب بحث عن جميع المستندات في مجموعة وتتوقّع أن يعرض Cloud Firestore المستندات التي يملك العميل الحالي إذنًا بالوصول إليها فقط.

على سبيل المثال، خذ قاعدة الأمان التالية:

service cloud.firestore {
  match /databases/{database}/documents {
    // Allow the user to read data if the document has the 'visibility'
    // field set to 'public'
    match /cities/{city} {
      allow read: if resource.data.visibility == 'public';
    }
  }
}

مرفوضة: ترفض هذه القاعدة طلب البحث التالي لأنّه يمكن أن تتضمّن مجموعة النتائج مستندات لا تكون فيها قيمة visibility هي public:

الويب
db.collection("cities").get()
    .then(function(querySnapshot) {
        querySnapshot.forEach(function(doc) {
            console.log(doc.id, " => ", doc.data());
    });
});

مسموح به: تسمح هذه القاعدة بطلب البحث التالي لأنّ عبارة where("visibility", "==", "public") تضمن أنّ مجموعة النتائج تستوفي شرط القاعدة:

الويب
db.collection("cities").where("visibility", "==", "public").get()
    .then(function(querySnapshot) {
        querySnapshot.forEach(function(doc) {
            console.log(doc.id, " => ", doc.data());
        });
    });

تُقيّم قواعد الأمان في Cloud Firestore كل طلب بحث مقارنةً بالنتيجة المحتملة، وترفض الطلب إذا كان من الممكن أن تعرض مستندًا لا يملك العميل إذنًا بقراءته. يجب أن تلتزم طلبات البحث بالقيود التي تفرضها قواعد الأمان. لمزيد من المعلومات حول قواعد الأمان وطلبات البحث، راجِع الاستعلام عن البيانات بشكل آمن.

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