شروط الكتابة لقواعد أمان 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 أو 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 عمليات كتابة وأنّ قواعد الأمان تستخدم مكالمتَي وصول إلى المستند للتحقّق من صحة كل عملية كتابة. في هذه الحالة، تستخدم كل عملية كتابة مكالمتَي وصول من أصل 10 مكالمات، ويستخدم طلب الكتابة المجمّع 6 مكالمات وصول من أصل 20 مكالمة.

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

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

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

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

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

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

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

يتم تعريف الدالة باستخدام الكلمة الرئيسية 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 كل طلب بحث مقابل نتيجته المحتمَلة وترفض الطلب إذا كان بإمكانه عرض مستند ليس لدى العميل إذن بقراءته. يجب أن تلتزم طلبات البحث بالقيود التي تحدّدها قواعد الأمان. لمزيد من المعلومات عن قواعد الأمان وطلبات البحث، يُرجى الاطّلاع على المقالة طلب البيانات بشكلٍ آمن.

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