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

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