تعتمد هذه الصفحة على المفاهيم الواردة في هيكلة قواعد الأمان وشروط الكتابة لقواعد الأمان لتوضيح كيفية تفاعل Cloud Firestore Security Rules مع طلبات البحث. ويلقي نظرة عن كثب على كيفية تأثير
قواعد الأمان في طلبات البحث التي يمكنك كتابتها، ويوضّح كيفية التأكّد من أنّ
طلبات البحث تستخدِم القيود نفسها المستخدَمة في قواعد الأمان. توضِّح هذه الصفحة أيضًا
كيفية كتابة قواعد أمان للسماح بطلبات البحث أو رفضها استنادًا إلى سمات
طلبات البحث، مثل limit
وorderBy
.
القواعد ليست فلاتر
عند كتابة طلبات بحث لاسترداد المستندات، تذكَّر أنّ قواعد الأمان ليست فلاتر، بل هي طلبات بحث كلها أو لا شيء. لتوفير الوقت والموارد، يُقيّمCloud Firestore طلب البحث مقارنةً بمجموعة النتائج المحتملة بدلاً من قيم الحقول الفعلية لجميع مستنداتك. إذا كان من المحتمل أن يعرض الاستعلام مستندات ليس لدى العميل إذن بقراءتها، ففشل الطلب بأكمله.
الطلبات وقواعد الأمان
وكما توضح الأمثلة أدناه، يجب كتابة استعلاماتك لتلائم قيود قواعد الأمان.
تأمين المستندات وطلب البحث استنادًا إلى auth.uid
يوضح المثال التالي كيفية كتابة استعلام لاسترداد مستندات
محمية بقاعدة أمان. لنفترض أنّ قاعدة بيانات تحتوي على مجموعة من مستندات
story
:
/stories/{storyid}
{
title: "A Great Story",
content: "Once upon a time...",
author: "some_auth_id",
published: false
}
بالإضافة إلى الحقلين title
وcontent
، يخزِّن كل مستند الحقلين
author
وpublished
لاستخدامهما للتحكم في الوصول. تفترض هذه الأمثلة أنّ التطبيق يستخدم مصادقة Firebase لضبط الحقل author
على المعرّف الفريد للمستخدم الذي أنشأ المستند. تعمل مصادقة Firebase أيضًا على تعبئة المتغيّر request.auth
في
قواعد الأمان.
تستخدم قاعدة الأمان التالية متغيّرَي request.auth
وresource.data
لفرض قيود على إذن الوصول للقراءة والكتابة لكل
story
إلى مؤلفه:
service cloud.firestore {
match /databases/{database}/documents {
match /stories/{storyid} {
// Only the authenticated user who authored the document can read or write
allow read, write: if request.auth != null && request.auth.uid == resource.data.author;
}
}
}
لنفترض أنّ تطبيقك يتضمّن صفحة تعرض للمستخدم قائمة بمستندات story
التي كتبها. قد تتوقع أنه يمكنك استخدام الاستعلام
التالي لتعبئة هذه الصفحة. ومع ذلك، سيتعذّر تنفيذ هذا الطلب، لأنّه لا يضمّن القيود نفسها التي تنطبق على قواعد الأمان:
غير صالح: لا تتطابق قيود طلبات البحث مع قيود قواعد الأمان
// This query will fail
db.collection("stories").get()
لا يتم تنفيذ طلب البحث حتى إذا كان المستخدم الحالي هو مؤلف كل مستند
story
. يرجع سبب هذا السلوك إلى أنّه عندما تطبّق
Cloud Firestore قواعد الأمان، فإنّها تقيِّم طلب البحث
استنادًا إلى مجموعة النتائج المحتمَلة، وليس استنادًا إلى السمات الحالية
للمستندات في قاعدة بياناتك. إذا كان من المحتمل أن يتضمّن طلب البحث مستندات
تخالف قواعد الأمان، سيتعذّر تنفيذ الطلب.
في المقابل، ينجح الاستعلام التالي، لأنه يتضمّن القيد نفسه في الحقل author
مثل قواعد الأمان:
صالح: تتطابق قيود طلبات البحث مع قيود قواعد الأمان
var user = firebase.auth().currentUser;
db.collection("stories").where("author", "==", user.uid).get()
تأمين المستندات وإجراء طلب بحث عنها استنادًا إلى حقل
لمزيد من توضيح التفاعل بين طلبات البحث والقواعد، توسّع قواعد security
أدناه إذن الوصول للقراءة إلى مجموعة stories
للسماح لأي مستخدم بقراءة story
مستندات تم ضبط الحقل published
فيها على true
.
service cloud.firestore {
match /databases/{database}/documents {
match /stories/{storyid} {
// Anyone can read a published story; only story authors can read unpublished stories
allow read: if resource.data.published == true || (request.auth != null && request.auth.uid == resource.data.author);
// Only story authors can write
allow write: if request.auth != null && request.auth.uid == resource.data.author;
}
}
}
يجب أن يتضمّن طلب البحث عن الصفحات المنشورة القيود نفسها التي تفرضها قواعد الأمان:
db.collection("stories").where("published", "==", true).get()
يضمن قيد الطلب .where("published", "==", true)
أنّه
resource.data.published
هو true
لأي نتيجة. وبالتالي، يفي هذا الاستعلام بقواعد الأمان
ويُسمح له بقراءة البيانات.
OR
طلب بحث
عند تقييم طلب بحث OR
منطقي (or
أو in
أو array-contains-any
) مقابل مجموعة قواعد، يقيّم Cloud Firestore كل قيمة مقارنة بشكل منفصل. يجب أن تستوفي كل قيمة مقارنة قيود قاعدة الأمان. على سبيل المثال، بالنسبة
للقاعدة التالية:
match /mydocuments/{doc} {
allow read: if resource.data.x > 5;
}
غير صالح: لا يضمن طلب البحث إدخال
x > 5
في جميع المستندات المحتملة
// These queries will fail
query(db.collection("mydocuments"),
or(where("x", "==", 1),
where("x", "==", 6)
)
)
query(db.collection("mydocuments"),
where("x", "in", [1, 3, 6, 42, 99])
)
صالحة: يضمن طلب البحث أنّ
x > 5
لجميع المستندات المحتمَلة
query(db.collection("mydocuments"),
or(where("x", "==", 6),
where("x", "==", 42)
)
)
query(db.collection("mydocuments"),
where("x", "in", [6, 42, 99, 105, 200])
)
تقييم القيود على الاستعلامات
يمكن أن تقبل قواعد الأمان أيضًا طلبات البحث أو ترفضها استنادًا إلى قيودها.
يحتوي المتغيّر request.query
على سمات limit
وoffset
وorderBy
للاستعلام. على سبيل المثال، يمكن لقواعد الأمان
رفض أي طلب بحث لا يحدّد الحد الأقصى لعدد المستندات
المسترجعة ضمن نطاق معيّن:
allow list: if request.query.limit <= 10;
توضح مجموعة القواعد التالية كيفية كتابة قواعد أمان تعمل على تقييم القيود الموضوعة على طلبات البحث. يوسّع هذا المثال مجموعة قواعد stories
السابقة بالتغييرات التالية:
- تفصل مجموعة القواعد قاعدة القراءة إلى قواعد
get
وlist
. - تحدّ قاعدة
get
من استرداد المستندات الفردية إلى المستندات العلنية أو المستندات التي أنشأها المستخدم. - تفرض قاعدة
list
القيود نفسها التي تفرضها قاعدةget
، ولكن على طلبات البحث. يتحقّق أيضًا من حد طلبات البحث، ثم يرفض أي طلب بحث بدون حد أو يزيد عن 10 طلبات بحث. - تحدِّد مجموعة القواعد دالة
authorOrPublished()
لتجنُّب تكرار الرموز.
service cloud.firestore {
match /databases/{database}/documents {
match /stories/{storyid} {
// Returns `true` if the requested story is 'published'
// or the user authored the story
function authorOrPublished() {
return resource.data.published == true || request.auth.uid == resource.data.author;
}
// Deny any query not limited to 10 or fewer documents
// Anyone can query published stories
// Authors can query their unpublished stories
allow list: if request.query.limit <= 10 &&
authorOrPublished();
// Anyone can retrieve a published story
// Only a story's author can retrieve an unpublished story
allow get: if authorOrPublished();
// Only a story's author can write to a story
allow write: if request.auth.uid == resource.data.author;
}
}
}
طلبات بحث مجموعات المجموعات وقواعد الأمان
تقتصر طلبات البحث تلقائيًا على مجموعة واحدة، ويتم استرداد النتائج من تلك المجموعة فقط. وباستخدام طلبات بحث مجموعات المجموعات، يمكنك استرداد النتائج من مجموعة مجموعات تتألف من جميع المجموعات برقم التعريف نفسه. يوضّح هذا القسم كيفية تأمين طلبات بحث مجموعات المجموعات باستخدام قواعد الأمان.
تأمين المستندات وإجراء استعلامات عنها استنادًا إلى مجموعات المجموعات
في قواعد الأمان، يجب السماح صراحةً بطلبات بحث مجموعة المجموعات من خلال كتابة قاعدة لمجموعة المجموعات:
- تأكَّد من أنّ
rules_version = '2';
هو السطر الأول من مجموعة القواعد. تتطلّب طلبات بحث مجموعة المجموعات استخدام سلوك العنصر النائب المتكرّر الجديد{name=**}
في الإصدار 2 من قواعد الأمان. - اكتب قاعدة لمجموعة المجموعات باستخدام
match /{path=**}/[COLLECTION_ID]/{doc}
.
على سبيل المثال، لنفترض أنّ لدينا منتدى منظَّمًا في forum
مستندًا يحتوي على
posts
مجموعة فرعية:
/forums/{forumid}/posts/{postid}
{
author: "some_auth_id",
authorname: "some_username",
content: "I just read a great story.",
}
في هذا التطبيق، يمكن لمالكي المشاركات تعديلها ويمكن لمستخدمين مصادقة قراءة مشاركاتهم:
service cloud.firestore {
match /databases/{database}/documents {
match /forums/{forumid}/posts/{post} {
// Only authenticated users can read
allow read: if request.auth != null;
// Only the post author can write
allow write: if request.auth != null && request.auth.uid == resource.data.author;
}
}
}
يمكن لأي مستخدم تمّت مصادقته استرداد المشاركات في أي منتدى واحد:
db.collection("forums/technology/posts").get()
ولكن ماذا لو كنت تريد عرض مشاركاته للمستخدم الحالي عبر جميع المنتديات؟
يمكنك استخدام طلب بحث عن مجموعة مجموعات لاسترداد
النتائج من جميع مجموعات posts
:
var user = firebase.auth().currentUser;
db.collectionGroup("posts").where("author", "==", user.uid).get()
في قواعد الأمان، يجب السماح بطلب البحث هذا من خلال كتابة قاعدة قراءة أو إدراج لمجموعة المجموعات posts
:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
// Authenticated users can query the posts collection group
// Applies to collection queries, collection group queries, and
// single document retrievals
match /{path=**}/posts/{post} {
allow read: if request.auth != null;
}
match /forums/{forumid}/posts/{postid} {
// Only a post's author can write to a post
allow write: if request.auth != null && request.auth.uid == resource.data.author;
}
}
}
يُرجى العِلم أنّ هذه القواعد ستنطبق على جميع المجموعات التي تحمل رقم التعريف posts
،
بصرف النظر عن التسلسل الهرمي. على سبيل المثال، تنطبق هذه القواعد على جميع مجموعات
posts
التالية:
/posts/{postid}
/forums/{forumid}/posts/{postid}
/forums/{forumid}/subforum/{subforumid}/posts/{postid}
طلبات بحث مجموعة مجموعات آمنة استنادًا إلى حقل
على غرار طلبات البحث الخاصة بمجموعة واحدة، يجب أن تستوفي طلبات البحث الخاصة بمجموعات المجموعات أيضًا
القيود التي تحدّدها قواعد الأمان. على سبيل المثال، يمكننا إضافة حقل published
إلى كل مشاركة في المنتدى كما فعلنا في مثال stories
أعلاه:
/منتديات/{forumid}/post/{postid}
{
author: "some_auth_id",
authorname: "some_username",
content: "I just read a great story.",
published: false
}
يمكننا بعد ذلك كتابة قواعد لمجموعة المختارات posts
استنادًا إلى
الحالة published
والمشاركة author
:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
// Returns `true` if the requested post is 'published'
// or the user authored the post
function authorOrPublished() {
return resource.data.published == true || request.auth.uid == resource.data.author;
}
match /{path=**}/posts/{post} {
// Anyone can query published posts
// Authors can query their unpublished posts
allow list: if authorOrPublished();
// Anyone can retrieve a published post
// Authors can retrieve an unpublished post
allow get: if authorOrPublished();
}
match /forums/{forumid}/posts/{postid} {
// Only a post's author can write to a post
allow write: if request.auth.uid == resource.data.author;
}
}
}
باستخدام هذه القواعد، يمكن لعملاء الويب وApple وAndroid إجراء طلبات البحث التالية:
يمكن لأي مستخدم استرداد المشاركات المنشورة في منتدى:
db.collection("forums/technology/posts").where('published', '==', true).get()
يمكن لأي مستخدم استرداد المشاركات المنشورة للمؤلف في جميع المنتديات:
db.collectionGroup("posts").where("author", "==", "some_auth_id").where('published', '==', true).get()
يمكن للمؤلفين استرداد جميع مشاركاتهم المنشورة وغير المنشورة في جميع المنتديات:
var user = firebase.auth().currentUser; db.collectionGroup("posts").where("author", "==", user.uid).get()
تأمين المستندات وطلب البحث عنها استنادًا إلى مجموعة المجموعات ومسار المستند
في بعض الحالات، قد تحتاج إلى حصر طلبات البحث عن مجموعات المجموعات استنادًا إلى مسار المستند. لإنشاء هذه القيود، يمكنك استخدام الأساليب نفسها ل تأمين المستندات وإجراء طلبات بحث فيها استنادًا إلى حقل.
لنفترض أنّ هناك تطبيقًا يتتبّع معاملات كل مستخدم في عدة بورصات أسهم وعملات مشفّرة:
/users/{userid}/exchange/{exchangeid}/transactions/{transaction}
{
amount: 100,
exchange: 'some_exchange_name',
timestamp: April 1, 2019 at 12:00:00 PM UTC-7,
user: "some_auth_id",
}
لاحِظ الحقل user
. على الرغم من أنّنا نعرف المستخدم الذي يمتلك مستند transaction
من مسار المستند، نكرّر هذه المعلومات في كل مستند transaction
لأنّها تتيح لنا تنفيذ أمرَين:
كتابة استعلامات لمجموعة المجموعات المقصورة على المستندات التي تشتمل على
/users/{userid}
محدّد في مسار المستند. على سبيل المثال:var user = firebase.auth().currentUser; // Return current user's last five transactions across all exchanges db.collectionGroup("transactions").where("user", "==", user).orderBy('timestamp').limit(5)
يمكنك فرض هذا القيد على جميع طلبات البحث في مجموعة
transactions
لكي لا يتمكّن مستخدم واحد من استرداد مستنداتtransaction
الخاصة بمستخدم آخر.
نفرض هذا القيد في قواعد الأمان وندرج التحقق من صحة البيانات
في حقل user
:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /{path=**}/transactions/{transaction} {
// Authenticated users can retrieve only their own transactions
allow read: if resource.data.user == request.auth.uid;
}
match /users/{userid}/exchange/{exchangeid}/transactions/{transaction} {
// Authenticated users can write to their own transactions subcollections
// Writes must populate the user field with the correct auth id
allow write: if userid == request.auth.uid && request.data.user == request.auth.uid
}
}
}
الخطوات التالية
- للحصول على مثال أكثر تفصيلاً على التحكّم في الوصول بالاستناد إلى الأدوار، يُرجى الاطّلاع على مقالة تأمين الوصول إلى البيانات للمستخدمين والمجموعات.
- اطّلِع على مرجع قواعد الأمان.