شروط الكتابة لقواعد أمان 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، يُرجى الاطّلاع على مستندات ال reference .

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

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

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();
    }
  }
}

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

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

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

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