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

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

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

المصادقة

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

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 السماح بالوصول أو رفضه ديناميكيًا استنادًا إلى بيانات المستند:

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 روابط السماح، ولكن يجب أن تنتهي ببيان إرجاع.

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

الخطوات اللاحقة