تستند هذه الصفحة إلى المفاهيم الواردة في
تنظيم قواعد الأمان وكتابة شروط لقواعد الأمان لتوضيح كيفية تفاعل
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 Authentication لضبط الحقل author
على معرّف UID الخاص بالمستخدم الذي أنشأ المستند. تعبئ خدمة Firebase
Authentication أيضًا المتغيّر 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()
تأمين المستندات والبحث فيها استنادًا إلى حقل
لتوضيح التفاعل بين طلبات البحث والقواعد، توسّع قواعد الأمان أدناه إذن الوصول للقراءة إلى المجموعة 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
أعلاه:
/forums/{forumid}/posts/{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;
}
}
}
باستخدام هذه القواعد، يمكن لبرامج Web و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
}
}
}
الخطوات التالية
- للحصول على مثال أكثر تفصيلاً حول التحكّم في الوصول المستند إلى الأدوار، يُرجى الاطّلاع على تأمين الوصول إلى البيانات للمستخدمين والمجموعات.
- اطّلِع على مرجع قواعد الأمان.