Эта страница основана на концепциях структурирования правил безопасности и написания условий для правил безопасности, чтобы объяснить, как Cloud Firestore Security Rules взаимодействуют с запросами. В нем более подробно рассматривается, как правила безопасности влияют на запросы, которые вы можете писать, и описывается, как обеспечить, чтобы ваши запросы использовали те же ограничения, что и ваши правила безопасности. На этой странице также описывается, как писать правила безопасности, разрешающие или запрещающие запросы на основе таких свойств запроса, как limit
и orderBy
.
Правила — это не фильтры
При написании запросов на получение документов имейте в виду, что правила безопасности не являются фильтрами: запросы выполняются по принципу «все или ничего». Чтобы сэкономить ваше время и ресурсы, Cloud Firestore оценивает запрос по его потенциальному набору результатов, а не по фактическим значениям полей для всех ваших документов. Если запрос потенциально может вернуть документы, на чтение которых у клиента нет разрешения, весь запрос завершится неудачей.
Запросы и правила безопасности
Как показывают приведенные ниже примеры, вы должны писать запросы так, чтобы они соответствовали ограничениям ваших правил безопасности.
Защита и запрос документов на основе auth.uid
В следующем примере показано, как написать запрос для получения документов, защищенных правилом безопасности. Рассмотрим базу данных, содержащую коллекцию документов- story
:
/истории/{storyid}
{
title: "A Great Story",
content: "Once upon a time...",
author: "some_auth_id",
published: false
}
Помимо полей title
и content
, в каждом документе хранятся поля author
и published
, которые можно использовать для управления доступом. В этих примерах предполагается, что приложение использует аутентификацию Firebase , чтобы установить в поле author
UID пользователя, создавшего документ. Аутентификация 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()
Защита и запрос документов на основе поля
Чтобы дополнительно продемонстрировать взаимодействие между запросами и правилами, приведенные ниже правила безопасности расширяют доступ для чтения к коллекции 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
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;
}
}
}
С помощью этих правил веб-клиенты, клиенты 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
}
}
}
Следующие шаги
- Более подробный пример управления доступом на основе ролей см. в разделе Защита доступа к данным для пользователей и групп .
- Прочтите справочник правил безопасности .