این صفحه بر پایه مفاهیم ساختار قوانین امنیتی و شرایط نوشتن برای قوانین امنیتی است تا نحوه تعامل 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()
محدودیت query .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;
}
}
}
با استفاده از این قوانین، مشتریان وب، اپل و اندروید می توانند پرس و جوهای زیر را انجام دهند:
هر کسی می تواند پست های منتشر شده در یک انجمن را بازیابی کند:
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
}
}
}
مراحل بعدی
- برای مثال دقیقتری از کنترل دسترسی مبتنی بر نقش، به امنیت دسترسی به دادهها برای کاربران و گروهها مراجعه کنید.
- مرجع قوانین امنیتی را بخوانید.