Это руководство дополняет руководство по структурированию правил безопасности и показывает, как добавлять условия в Cloud Firestore Security Rules . Если вы не знакомы с основами Cloud Firestore Security Rules , обратитесь к руководству по началу работы .
Основным элементом Cloud Firestore Security Rules является условие. Условие — это логическое выражение, определяющее, разрешена или запрещена конкретная операция. Используйте правила безопасности для написания условий, проверяющих аутентификацию пользователя, подтверждающих подлинность входящих данных или даже обеспечивающих доступ к другим частям вашей базы данных.
Аутентификация
Один из наиболее распространенных шаблонов правил безопасности — это управление доступом на основе состояния аутентификации пользователя. Например, ваше приложение может разрешить запись данных только авторизованным пользователям:
service cloud.firestore {
match /databases/{database}/documents {
// Allow the user to access documents in the "cities" collection
// only if they are authenticated.
match /cities/{city} {
allow read, write: if request.auth != null;
}
}
}
Ещё один распространённый подход заключается в том, чтобы гарантировать, что пользователи могут читать и записывать только свои собственные данные:
service cloud.firestore {
match /databases/{database}/documents {
// Make sure the uid of the requesting user matches name of the user
// document. The wildcard expression {userId} makes the userId variable
// available in rules.
match /users/{userId} {
allow read, update, delete: if request.auth != null && request.auth.uid == userId;
allow create: if request.auth != null;
}
}
}
Если ваше приложение использует Firebase Authentication или Google Cloud Identity Platform , переменная request.auth содержит информацию для аутентификации клиента, запрашивающего данные. Для получения дополнительной информации о request.auth см. справочную документацию .
Проверка данных
Многие приложения хранят информацию о контроле доступа в виде полей документов в базе данных. Cloud Firestore Security Rules могут динамически разрешать или запрещать доступ на основе данных документа:
service cloud.firestore {
match /databases/{database}/documents {
// Allow the user to read data if the document has the 'visibility'
// field set to 'public'
match /cities/{city} {
allow read: if resource.data.visibility == 'public';
}
}
}
Переменная resource указывает на запрошенный документ, а resource.data представляет собой карту всех полей и значений, хранящихся в документе. Для получения дополнительной информации о переменной resource см. справочную документацию .
При записи данных может потребоваться сравнение входящих данных с существующими. В этом случае, если ваш набор правил разрешает ожидающую запись, переменная request.resource будет содержать будущее состояние документа. Для операций update , которые изменяют только подмножество полей документа, переменная request.resource будет содержать ожидающее состояние документа после операции. Вы можете проверить значения полей в request.resource , чтобы предотвратить нежелательные или несогласованные обновления данных:
service cloud.firestore {
match /databases/{database}/documents {
// Make sure all cities have a positive population and
// the name is not changed
match /cities/{city} {
allow update: if request.resource.data.population > 0
&& request.resource.data.name == resource.data.name;
}
}
}
Доступ к другим документам
Используя функции get() и exists() , ваши правила безопасности могут оценивать входящие запросы по отношению к другим документам в базе данных. Функции get() и exists() ожидают полностью указанных путей к документам. При использовании переменных для построения путей для get() и exists() необходимо явно экранировать переменные с помощью синтаксиса $(variable) .
В приведенном ниже примере переменная database захватывается оператором match /databases/{database}/documents и используется для формирования пути:
service cloud.firestore {
match /databases/{database}/documents {
match /cities/{city} {
// Make sure a 'users' document exists for the requesting user before
// allowing any writes to the 'cities' collection
allow create: if request.auth != null && exists(/databases/$(database)/documents/users/$(request.auth.uid));
// Allow the user to delete cities if their user document has the
// 'admin' field set to 'true'
allow delete: if request.auth != null && get(/databases/$(database)/documents/users/$(request.auth.uid)).data.admin == true;
}
}
}
Для операций записи можно использовать функцию getAfter() , чтобы получить доступ к состоянию документа после завершения транзакции или пакета операций записи, но до их фиксации. Как и get() , функция getAfter() принимает полностью указанный путь к документу. Вы можете использовать getAfter() для определения наборов операций записи, которые должны выполняться вместе в рамках транзакции или пакета.
Ограничения на количество вызовов
Существует ограничение на количество обращений к документам в рамках одной оценки набора правил:
- 10 для запросов отдельных документов и запросов запросов.
20 для чтения нескольких документов, транзакций и пакетной записи. Предыдущее ограничение в 10 также применяется к каждой операции.
Например, представьте, что вы создаете пакетный запрос на запись с 3 операциями записи, и ваши правила безопасности используют 2 вызова доступа к документу для проверки каждой записи. В этом случае каждая операция записи использует 2 из 10 вызовов доступа, а пакетный запрос на запись использует 6 из 20 вызовов доступа.
Превышение любого из этих лимитов приводит к ошибке "доступ запрещен". Некоторые запросы на доступ к документам могут кэшироваться, а кэшированные запросы не учитываются в лимитах.
Подробное объяснение того, как эти ограничения влияют на транзакции и пакетную запись, см. в руководстве по обеспечению безопасности атомарных операций .
Доступ к звонкам и ценам
Использование этих функций выполняет операцию чтения в вашей базе данных, а это значит, что с вас будет взиматься плата за чтение документов, даже если ваши правила отклонят запрос. Более подробную информацию о выставлении счетов см. в разделе «Цены Cloud Firestore .
Пользовательские функции
По мере усложнения правил безопасности может возникнуть необходимость обернуть наборы условий в функции, которые можно повторно использовать во всем наборе правил. Правила безопасности поддерживают пользовательские функции. Синтаксис пользовательских функций немного похож на JavaScript, но функции правил безопасности написаны на предметно-ориентированном языке, который имеет некоторые важные ограничения:
- Функции могут содержать только один оператор
return. Они не могут содержать никакой дополнительной логики. Например, они не могут выполнять циклы или вызывать внешние сервисы. - Функции могут автоматически получать доступ к другим функциям и переменным из той области видимости, в которой они определены. Например, функция, определенная в области видимости
service cloud.firestoreимеет доступ к переменнойresourceи встроенным функциям, таким какget()иexists(). - Функции могут вызывать другие функции, но не могут рекурсивно вызывать другие функции. Общая глубина стека вызовов ограничена 10.
- В версии правил
v2функции могут определять переменные с помощью ключевого словаlet. Функции могут иметь до 10 привязок let, но должны заканчиваться оператором return.
Функция определяется с помощью ключевого слова function и принимает ноль или более аргументов. Например, вы можете захотеть объединить два типа условий, использованных в приведенных выше примерах, в одну функцию:
service cloud.firestore {
match /databases/{database}/documents {
// True if the user is signed in or the requested data is 'public'
function signedInOrPublic() {
return request.auth.uid != null || resource.data.visibility == 'public';
}
match /cities/{city} {
allow read, write: if signedInOrPublic();
}
match /users/{user} {
allow read, write: if signedInOrPublic();
}
}
}
Использование функций в правилах безопасности упрощает их сопровождение по мере роста сложности правил.
Правила — это не фильтры.
После того как вы защитите свои данные и начнете писать запросы, помните, что правила безопасности — это не фильтры. Вы не можете написать запрос ко всем документам в коллекции и ожидать, что Cloud Firestore вернет только те документы, к которым у текущего клиента есть доступ.
Например, рассмотрим следующее правило безопасности:
service cloud.firestore {
match /databases/{database}/documents {
// Allow the user to read data if the document has the 'visibility'
// field set to 'public'
match /cities/{city} {
allow read: if resource.data.visibility == 'public';
}
}
}
Отклонено : Это правило отклоняет следующий запрос, поскольку результирующий набор может включать документы, visibility которых не является public :
Веб
db.collection("cities").get()
.then(function(querySnapshot) {
querySnapshot.forEach(function(doc) {
console.log(doc.id, " => ", doc.data());
});
}); Разрешено : Это правило разрешает следующий запрос, поскольку условие where("visibility", "==", "public") гарантирует, что результирующий набор удовлетворяет условию правила:
Веб
db.collection("cities").where("visibility", "==", "public").get()
.then(function(querySnapshot) {
querySnapshot.forEach(function(doc) {
console.log(doc.id, " => ", doc.data());
});
});Правила безопасности Cloud Firestore оценивают каждый запрос на предмет его потенциального результата и отклоняют запрос, если он может вернуть документ, к чтению которого у клиента нет разрешения. Запросы должны соответствовать ограничениям, установленным вашими правилами безопасности. Дополнительную информацию о правилах безопасности и запросах см. в разделе «Безопасные запросы к данным» .
Следующие шаги
- Узнайте, как правила безопасности влияют на ваши запросы .
- Узнайте, как структурировать правила безопасности .
- Ознакомьтесь со справочником правил безопасности .
- Для приложений, использующих Cloud Storage for Firebase , узнайте, как писать условия Cloud Storage Security Rules , которые обращаются к документам Cloud Firestore .