Безопасность Firestore - разрешить только известные поля

Я не могу понять, как правильно установить правило «.validate» в Firestore. По сути, я хочу, чтобы пользовательский документ содержал только известные мне поля:

user {
 name: "John"
 phone: "2342222"
 address: "5th Avenue"
}

Мне не нужны никакие другие поля, кроме трех (имя, телефон, адрес).

Поля НЕ будут сохраняться одновременно. имя и телефон будут сохранены первыми, а адрес будет сохранен только тогда, когда пользователь захочет отредактировать свой профиль.

Я пробовал следовать приведенным ниже правилам, но, похоже, они не работают:

allow read: if request.auth.uid == uid;
allow write: if request.auth.uid == uid && 
 request.resource.data.keys() in ["name", "phone", "address"]

Спасибо за помощь.


person Fabio Berger    schedule 06.10.2017    source источник


Ответы (3)


Вы можете разделить свои правила, чтобы включить различную логику create и update (а также delete):

// allows for creation with name and phone fields
allow create: if request.resource.data.size() == 2
              && request.resource.data.hasAll(['name', 'phone'])
              && request.resource.data.name is string
              && request.resource.data.phone is string;
// allows a single update adding the address field
// OR (||) in additional constraints
allow update: if request.resource.data.size() == resource.data.size() + 1
              && !('address' in resource.data)
              && request.resource.data.address is string;
person Mike McDonald    schedule 06.10.2017
comment
Прохладный! Хотя это не так просто, как мое предыдущее правило базы данных реального времени, оно единственное, что смогло реализовать эту идею. Спасибо - person Fabio Berger; 06.10.2017
comment
Как указано выше, ваши правила RTDB не могут выполнять такое поведение. - person Mike McDonald; 06.10.2017
comment
как установить правило безопасности при удалении значения поля? - person Vishu; 18.10.2017
comment
@Vishu, ты можешь использовать allow delete - person Mike McDonald; 18.10.2017
comment
Удалить помогает удалить весь документ со всеми полями. Я просто хочу удалить определенное поле в документе, и для этого я использовал этот код updates.put (capital, FieldValue.delete ()); Но как установить для этого правило безопасности? - person Vishu; 18.10.2017
comment
Технически это update, поскольку вы удаляете определенное поле. Вы должны написать условие: request.resource.data.size() == resource.data.size() - 1 && !('field' in request.resource.data), чтобы гарантировать, что удаляется только одно свойство (поскольку мы гидратируем другие свойства документа на update). - person Mike McDonald; 19.10.2017
comment
На самом деле это не сработает, поскольку речь идет об обновлении поля адреса. Таким образом, если адрес уже находится в документе, это правило не позволяет пользователю обновить свой адрес. И даже если вы добавите OR address' in resource.data, он откроет добавление, обновив любое поле, если поле адреса уже есть в документе. - person Gerardlamo; 07.05.2019
comment
не работает с несколькими полями, т.е. скажем, у вас есть более двух полей, которые можно добавить, тогда этого недостаточно - person Harkal; 02.06.2019
comment
Я бы посмотрел github.com/FirebaseExtended/protobuf-rules-gen, который генерировать все перестановки обязательных и необязательных полей для данного объекта. - person Mike McDonald; 19.06.2019
comment
Привет, Майк, спасибо за ответ. Я считаю, что у resource.data нет hasAll() функции, упомянутой в документации - person frunkad; 16.05.2020

Вам нужны оба метода: size() и hasOnly().

allow write: if request.resource.data.size() == 3 
             && request.resource.data.keys().hasOnly(['name', 'phone', 'address'])

Использование size() позволяет гарантировать точное количество полей. Сочетание этого с hasOnly() позволяет заблокировать его для этих конкретных полей.

Дополнительную информацию можно найти в справочных документах Cloud Firestore Rules.

person Dan McGrath    schedule 06.10.2017
comment
Привет, Дэн, спасибо за ответ. Это не сработало, потому что эти поля НЕ будут сохранены одновременно. сначала будут сохранены имя и телефон, а позже будет сохранен адрес. Итак, когда я пытаюсь сохранить только «адрес», правило не срабатывает. - person Fabio Berger; 06.10.2017
comment
Я в основном пытаюсь выполнить то же правило, что и в базе данных реального времени: имя: {.validate: true}, телефон: {.validate: true}, адрес: {.validate: true}, $ other: {.validate: ложный } - person Fabio Berger; 06.10.2017
comment
Я не верю, что эти Правила делают то, что вы от них ожидаете. База данных RT не отличает создание от обновлений от удалений (все они установлены), поэтому, хотя вы обеспечиваете возможность записи только нескольких свойств, вы ничего не говорите о порядке, в котором эти свойства обновляются. - person Mike McDonald; 06.10.2017
comment
в настоящее время вам нужно работать с ключами, а не с данными. Теперь правильным условием является ´request.resource.data.keys (). Size () == 3 && request.resource.data.key () s.hasAll (['name', 'phone', 'address']) ´ - person MiguelSlv; 03.03.2018
comment
Привет, Дэн! Просто хочу сообщить, что этот набор правил не работает и на самом деле все еще широко открыт для злоупотреблений. Причина в том, что при создании необходимо указать все три и только 3 поля (имя, телефон, адрес), но при обновлении я мог бы указать любые 3 поля, т.е. (возраст, пароль, роль), и это правило пройдет, поскольку документ уже содержит 3 проверяемых поля. Чтобы исправить это, просто используйте hasOnly вместо hasAll, но тогда вы ограничиваете, какие другие поля вы можете хранить в документе, а не то, что пользователь может отправлять. - person Gerardlamo; 07.05.2019

Чтобы добавить к ответу Майка Макдональда, чтобы проверить конкретные ключи, форма теперь выглядит так:

request.resource.data.keys().hasAll

вместо того

request.resource.data.hasAll

Полный пример:

// allows for creation with name and phone fields
allow create: if request.resource.data.size() == 2
              && request.resource.data.keys().hasAll(['name', 'phone'])
              && request.resource.data.name is string
              && request.resource.data.phone is string;
// allows a single update adding the address field
// OR (||) in additional constraints
allow update: if request.resource.data.size() == resource.data.size() + 1
              && !('address' in resource.data)
              && request.resource.data.address is string;

Дополнительная информация здесь: https://firebase.google.com/docs/reference/rules/rules.Map

person Mufasa    schedule 18.05.2018