This page builds on the concepts in Structuring Security Rules and Writing Conditions for Security Rules to explain how Cloud Firestore Security Rules interact with queries. It takes a closer look at how security rules affect the queries you can write and describes how to ensure your queries use the same constraints as your security rules. This page also describes how to write security rules to allow or deny queries based on query properties like limit and orderBy .
Правила — это не фильтры.
When writing queries to retrieve documents, keep in mind that security rules are not filters—queries are all or nothing. To save you time and resources, Cloud Firestore evaluates a query against its potential result set instead of the actual field values for all of your documents. If a query could potentially return documents that the client does not have permission to read, the entire request fails.
Запросы и правила безопасности
Как показывают приведенные ниже примеры, ваши запросы должны соответствовать ограничениям ваших правил безопасности.
Защита и запрос документов на основе 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;
}
}
}
Suppose that your app includes a page that shows the user a list of story documents that they authored. You might expect that you could use the following query to populate this page. However, this query will fail, because it does not include the same constraints as your security rules:
Недопустимо : ограничения запроса не соответствуют ограничениям правил безопасности.
// This query will fail
db.collection("stories").get()
The query fails even if the current user actually is the author of every story document. The reason for this behavior is that when Cloud Firestore applies your security rules, it evaluates the query against its potential result set, not against the actual properties of documents in your database. If a query could potentially include documents that violate your security rules, the query will fail.
Напротив, следующий запрос выполняется успешно, поскольку он включает то же ограничение на поле 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 , `or`, ` 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
}
}
}
Следующие шаги
- Более подробный пример управления доступом на основе ролей см. в разделе «Защита доступа к данным для пользователей и групп» .
- Ознакомьтесь со справочником правил безопасности .