Правило Firestore, разрешающее перезапись

У меня есть коллекция пользователей, в которой каждый документ содержит поле имени и карту доступа.

"users" :[
   {
     "mHbVq5TUY7brlleejClKm71NBGI2": {
         "name": "Bob Johnson",
         "access": {
             "X0w1VaVIljR1Nc5u3Sbo" : true
         }
   }
]

Я хотел бы, чтобы правила Firestore разрешали создание нового документа только в том случае, если он еще не существует, и только если лицо, выполняющее действие, подтвердило свой адрес электронной почты. Для обновления только пользователь, владеющий этим узлом, может выполнить обновление, имя должно быть строкой, а карту доступа нельзя изменить. Я протестировал свое обновление и создал правила в симуляторе, и они работали, как ожидалось. Однако, когда я запускаю .set (), он полностью перезаписывает весь мой узел и удаляет карту доступа, чего у меня не могло быть. Я предполагаю, что .set () на самом деле выполняет обновление и, таким образом, соответствует моим критериям обновления. Итак, как мне предотвратить полную перезапись моего узла. Заранее спасибо ... код ниже.

--- КОД ВЫПОЛНЯЕТ ПЕРЕЗАПИСЬ

db.collection("users").doc("mHbVq5TUY7brlleejClKm71NBGI2").set(
  {
    name: "Bill Swanson",
  }
).catch(err => {
  console.info(err.message)
})

---ПРАВИЛА

function incomingData() {
  return request.resource.data
}

function emailVerified() {
  return request.auth.token.email_verified;
}

service cloud.firestore {
  match /databases/{database}/documents {
    match /users/{userId} {

      function userExists(user_Id) {
        return exists(/databases/$(database)/documents/users/$(user_Id));
      }

        allow create: if !userExists(userId) && emailVerified();
        allow update: if request.auth.uid == userId
                      && !('access' in incomingData())
                      && request.resource.data.name is string;
         allow read: if request.auth.uid != null;
    }
  }
}

Согласно этой ссылке (cloud.google.com/nodejs/docs/reference/firestore/0.17.x/…) .set действительно выполняет обновление, если документ уже существует, но он, очевидно, игнорирует мое правило обновления и перезаписывает весь документ вместо слияния. Хотелось бы узнать, есть ли у кого-нибудь обходной путь для этого? Я видел много примеров, использующих этот подход для предотвращения перезаписи, и похоже, что они будут иметь аналогичные результаты (gist.github.com/mcdonamp/f18b1991f74f7a266e0d15f31d46d637)

Chris Mancini 25.10.2018 17:46

Прокрутив колеса, я обнаружил очень тонкое различие в документации (firebase.google.com/docs/firestore/security/rules-condition‌ s). «Для операций обновления, которые изменяют только подмножество полей документа, переменная request.resource будет содержать состояние отложенного документа после операции». Поэтому, когда я сравниваю входящие данные с существующими данными, это действительно не входящие данные. Почему больше экспертов не проводят это различие? Этот тип вопросов возникает повсюду, и эксперты говорят, что request.resource.data - это данные, которые отправляются?

Chris Mancini 25.10.2018 20:53

Единственный верный способ получить фактические входящие данные из запроса - это просмотреть request.writeFields, который устарел и не может использоваться в симуляторе. Я бы на самом деле написал целый пост о своем опыте отслеживания этого, но, очевидно, я не могу инициировать какие-либо новые вопросы, потому что я ограничен. Не знаю, почему именно так. Я также не могу ответить на множество вопросов, подобных моему, потому что у меня нет очков репутации. Что ж, я бы очень хотел, чтобы некоторые из тех, кто это сделал, ответили на эти вопросы. Люди сбиваются с пути плохими советами и демонстрациями.

Chris Mancini 25.10.2018 20:58
Интеграция Angular - Firebase Analytics
Интеграция Angular - Firebase Analytics
Узнайте, как настроить Firebase Analytics и отслеживать поведение пользователей в вашем приложении Angular.
1
3
708
1

Ответы 1

При использовании set (), если вы не уверены, существует ли документ, передайте опцию объединения новых данных с любым существующим документом, чтобы избежать перезаписи всего документа.

Вот как передать возможность объединить обновление с существующим документом.

db.collection("users")
.doc("mHbVq5TUY7brlleejClKm71NBGI2")
.set(
   {
      name: "Bill Swanson"
   },
   {
      merge: true
   }
).catch(err => {
   console.info(err.message)
});

Надеюсь это поможет.

Другие вопросы по теме