Правила безопасности Firestore - как я могу проверить, что поле/не изменяется?
для жизни меня, я не могу понять, почему следующее В результате false
для разрешения пишет. Предположим, мой users
коллекция пуста для начала, и я пишу документ следующей формы из моего углового интерфейса:
{
displayName: 'FooBar',
email: 'foo.bar@example.com'
}
мой правила безопасности:
service cloud.firestore {
match /databases/{database}/documents {
match /users/{userId} {
function isAdmin() {
return resource.data.role == 'ADMIN';
}
function isEditingRole() {
return request.resource.data.role != null;
}
function isEditingOwnRole() {
return isOwnDocument() && isEditingRole();
}
function isOwnDocument() {
return request.auth.uid == userId;
}
allow read: if isOwnDocument() || isAdmin();
allow write: if !isEditingOwnRole() && (isOwnDocument() || isAdmin());
}
}
}
в общем, я не хочу, чтобы пользователи могли редактировать свои собственные роли. Обычные пользователи могут редактировать свой собственный документ иначе, а администраторы могут редактировать никто.
Stubbing isEditingRole()
на false
дает ожидаемый результат, поэтому я сузил его до этого выражения.
запись постоянно возвращается false, и я не могу определить, почему. Любые идеи или исправления были бы полезны!
изменить 1
вещи, которые я пробовал:
function isEditingRole() {
return request.resource.data.keys().hasAny(['role']);
}
и
function isEditingRole() {
return 'role' in request.resource.data;
}
и
function isEditingRole() {
return 'role' in request.resource.data.keys();
}
Изменить 2
обратите внимание, что в конечном итоге, администраторы будут устанавливать роль для пользователей, поэтому роль может в конечном итоге существовать в документе. Это означает, что согласно Firestore docs ниже, запрос будет иметь role
ключ, даже если не было в исходном запросе.
поля, не указанные в запросе, которые существуют в ресурсе, добавляются в
request.resource.data
. Правила могут проверить, изменяется ли поле путем сравненияrequest.resource.data.foo
toresource.data.foo
зная, что каждое поле вresource
также будет присутствовать вrequest.resource
даже если он не был представлен в записи запроса.
в соответствии с этим, я думаю, что три варианта из "Edit 1" исключены. Я попробовал предложение request.resource.data.role != resource.data.role
и это не работает... Я в недоумении и начинаю задаваться вопросом, есть ли на самом деле ошибка в Firestore.
5 ответов
Я решил это с помощью writeFields
. Пожалуйста, попробуйте это правило.
allow write: if !('role' in request.writeFields);
в моем случае, я использую list
для ограничения полей обновления. Это тоже работает.
allow update: if !(['leader', '_created'] in request.writeFields);
запрос.ресурс.ключи.hasAny() - вероятно, ваш лучший выбор здесь. Это позволяет проверить, если конкретный запрос.ресурс имеет любой из указанных ключей. Отрицая логику, вы можете убедиться, что запросы на запись не имеют ваших ключей в черном списке. Например:
service cloud.firestore {
match /databases/{database}/documents {
match /users/{userId} {
//read rules here...
allow write: if !request.resource.keys().hasAny(["role", "adminOnlyAttribute"]);
}
}
}
Итак, в конце концов, кажется, я предполагал, что resource.data.nonExistentField == null
вернутся false
, когда он фактически возвращает Error
(по этой и мои тесты). Таким образом, мое первоначальное решение, возможно, столкнулось с этим. Это озадачивает, потому что противоположное должно работать в соответствии с документы, но, возможно, документы ссылаются на значение, которое "не существует", а не на ключ-тонкое различие.
у меня все еще нет 100% ясности, но это то, что я закончил с этим работал:
function isAddingRole() {
return !('role' in resource.data) && 'role' in request.resource.data;
}
function isChangingRole() {
return 'role' in resource.data && 'role' in request.resource.data && resource.data.role != request.resource.data.role;
}
function isEditingRole() {
return isAddingRole() || isChangingRole();
}
еще одна вещь, которая все еще озадачивает меня, это то, что, согласно документам, мне не нужно && 'role' in request.resource.data
входит в isChangingRole()
, потому что он должен быть вставлен автоматически Firestore. Хотя это не так, поскольку удаление этого приводит к сбою моей записи для проблем с разрешениями.
это, вероятно, может быть уточнено / улучшено путем разрыва записи в create
, update
и delete
запасные части, а не просто allow write: if !isEditingOwnRole() && (isOwnDocument() || isAdmin());
.
С помощью этой единственной функции вы можете проверить, создаются ли/не изменяются поля.
function incomingDataHasFields(fields) {
return ((
request.writeFields == null
&& request.resource.data.keys().hasAll(fields)
) || (
request.writeFields != null
&& request.writeFields.hasAll(fields)
));
}
использование:
match /xxx/{xxx} {
allow create:
if incomingDataHasFields(['foo']) // allow creating a document that contains 'foo' field
&& !incomingDataHasFields(['bar', 'baz']); // but don't allow 'bar' and 'baz' fields to be created
в целях обеспечения соблюдения полей только для чтения в клиенте, используйте writeFields
(https://firebase.google.com/docs/reference/rules/rules.firestore.Request#writeFields) - ... гекиджин предложил это, но синтаксис был немного неправильным.
match /myCollection/{id}
// Readonly fields
allow update: if !(request.writeFields.hasAny(['field1', 'field2']));
}
Firestore будет заполнять поля записи для вас, поэтому вышеуказанная проверка всегда безопасна в вашем update
& create
правила.
EDIT 9 Oct 2018
@dls101 был достаточно любезно сообщить, что Google, похоже, удалил любое упоминание writeFields
документы. Поэтому будьте осторожны с этим решением.