在 Firestore 文档中实现细粒度数据验证

Ilj*_*lja 3 firebase firebase-security google-cloud-firestore

假设我的数据库中有一个字符文档,即 /characters/{characterID}

该文件的形状可以如下:

{
  username: "Username",
  race: "ORC or ELF",
  gender: "FEMALE or MALE",
  gold: 1000,
  equipement: {
    helmet: "null or string",
    boots: "null or string"
  } 
}
Run Code Online (Sandbox Code Playgroud)

我想允许以下用例:

  1. 用户可以通过提供有效的方式为他/她自己创建角色,username, race, gender一旦他们创建角色,这些字段不应该是可编辑的
  2. 其余字段可由用户一次性更新,每次更新一个或任何字段组合。(一旦更新,就会触发 firebase 函数来验证这一点,但就规则而言,我们希望确保用户发送有效值)

我通过使用以下规则找出了第 1 步,validCreateCharacterData验证username, race and gender.

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

    // Characters
    match /characters/{characterID} {
      allow create: if isOwner(characterID)
                    && validCreateCharacterData();
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

但是我很难弄清楚如何验证其余的数据更新,因为它们可以是 null 或各种字段的混合。我想制定细粒度的规则,但下面的方法不起作用:/

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

    // Characters
    match /characters/{characterID} {
      allow create: if isOwner(characterID)
                    && validCreateCharacterData();

      match /gold {
         allow update: if request.resource.data.gold is number
      }
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

Bry*_*oth 6

当您使用 时match,您实际上是在collection/document方向上又迈进了一步。所以update实际上是无效的,因为它是在 acollection而不是 a上执行的document

您的规则应如下所示:

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

    // Characters
    match /characters/{characterID} {
      allow create: if isOwner(characterID)
                && validCreateCharacterData();
      function isValidEquipment(val) {
        return val is string
          || val == null;
      }
      // don't allow a write request that includes updates to username, race, or gender
      allow update: if !request.writeFields.hasAny(['username','race','gender'])
                && request.resource.data.gold is number
                && isValidEquipment(request.resource.data.equipment.helmet)
                && isValidEquipment(request.resource.data.equipment.boots);
    }
  }
}
Run Code Online (Sandbox Code Playgroud)