Эта страница основана на концепциях структурирования правил безопасности и написания условий для правил безопасности , чтобы объяснить, как правила безопасности Cloud Firestore взаимодействуют с запросами. В нем более подробно рассматривается, как правила безопасности влияют на запросы, которые вы можете написать, и описывается, как обеспечить, чтобы ваши запросы использовали те же ограничения, что и ваши правила безопасности. На этой странице также описывается, как писать правила безопасности для разрешения или отклонения запросов на основе таких свойств запроса, как limit
и orderBy
.
Правила — это не фильтры
При написании запросов для извлечения документов помните, что правила безопасности не являются фильтрами — запросы — это все или ничего. Чтобы сэкономить ваше время и ресурсы, Cloud Firestore оценивает запрос по его потенциальному набору результатов, а не по фактическим значениям полей для всех ваших документов. Если запрос потенциально может возвращать документы, на чтение которых у клиента нет разрешения, весь запрос завершается ошибкой.
Запросы и правила безопасности
Как показывают приведенные ниже примеры, вы должны писать свои запросы так, чтобы они соответствовали ограничениям ваших правил безопасности.
Защищайте и запрашивайте документы на основе auth.uid
В следующем примере показано, как написать запрос для получения документов, защищенных правилом безопасности. Рассмотрим базу данных, содержащую коллекцию документов story
:
/истории/{идентификатор истории}
{
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
для любого результата. Таким образом, этот запрос удовлетворяет правилам безопасности и может считывать данные.
in
и array-contains-any
запросы
При оценке предложения in
или array-contains-any
относительно набора правил Cloud Firestore оценивает каждое значение сравнения отдельно. Каждое значение сравнения должно соответствовать ограничениям правила безопасности. Например, для следующего правила:
match /mydocuments/{doc} {
allow read: if resource.data.x > 5;
}
Неверно : запрос не гарантирует, что x > 5
для всех потенциальных документов.
// This query will fail
db.collection("mydocuments").where("x", "in", [1, 3, 6, 42, 99]).get()
Действителен : запрос гарантирует, что x > 5
для всех потенциальных документов.
db.collection("mydocuments").where("x", "in", [6, 42, 99, 105, 200]).get()
Оценка ограничений на запросы
Ваши правила безопасности также могут принимать или отклонять запросы в зависимости от их ограничений. Переменная 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;
}
}
}
С помощью этих правил веб-клиенты, клиенты 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
}
}
}
Следующие шаги
- Более подробный пример управления доступом на основе ролей см. в разделе Защита доступа к данным для пользователей и групп .
- Прочитайте справочник правил безопасности .