Firestore安全规则 - 如何检查字段是否被修改?

men*_*e23 13 firebase firebase-security google-cloud-firestore

对于我的生活,我无法理解为什么以下导致false允许写入.假设我的users集合是空的,我正在从我的Angular前端编写以下表单的文档:

{
  displayName: 'FooBar',
  email: 'foo.bar@example.com'
}
Run Code Online (Sandbox Code Playgroud)

我目前的安全规则:

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());
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

一般来说,我希望没有用户能够编辑自己的角色.普通用户可以编辑自己的文档,管理员可以编辑任何人.

Stubbing isEditingRole()for false给出了预期的结果,所以我把它缩小到那个表达式.

写入不断变回虚假,我无法确定原因.任何想法或修复都会有所帮助!

编辑1

我试过的事情:

function isEditingRole() {
  return request.resource.data.keys().hasAny(['role']);
}
Run Code Online (Sandbox Code Playgroud)

function isEditingRole() {
  return 'role' in request.resource.data;
}
Run Code Online (Sandbox Code Playgroud)

function isEditingRole() {
  return 'role' in request.resource.data.keys();
}
Run Code Online (Sandbox Code Playgroud)

编辑2

请注意,最终,管理员将为用户设置角色,因此角色最终可能存在于文档中.这意味着,根据下面的Firestore文档,请求将具有role密钥,即使不在原始请求中也是如此.

将资源中存在的请求中未提供的字段添加到request.resource.data.规则可以测试一个字段是否通过比较修正request.resource.data.foo,以resource.data.foo知道的是,在各个领域resource也将出现在request.resource即使它不是在写请求提交.

据此,我认为"编辑1"中的三个选项被排除在外.我确实试过了这个建议request.resource.data.role != resource.data.role而且也没有用......我很茫然,我开始怀疑Firestore中是否存在错误.

men*_*e23 6

所以最后,我似乎假设它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();
}
Run Code Online (Sandbox Code Playgroud)

另一件让我感到困惑的事情是,根据文档,我不应该需要 中的&& 'role' in request.resource.data部分isChangingRole(),因为它应该由 Firestore 自动插入。尽管情况似乎并非如此,因为删除它会导致我的写入因权限问题而失败。

它有可能成为澄清/打破写伸到改善createupdate以及delete零件,而不是只allow write: if !isEditingOwnRole() && (isOwnDocument() || isAdmin());


小智 6

我用它来解决它writeFields.请试试这条规则.

allow write: if !('role' in request.writeFields);
Run Code Online (Sandbox Code Playgroud)

就我而言,我list用来限制更新字段.它也有效.

allow update: if !(['leader', '_created'] in request.writeFields);
Run Code Online (Sandbox Code Playgroud)

  • writeFields 不再在文档中并且显然已弃用。/sf/answers/3653473351/ (5认同)

Tom*_*ley 5

如果您创建自定义函数来检查更新,则规则将更具可读性和可维护性。例如:

service cloud.firestore {
  match /databases/{database}/documents {
    function isUpdatingField(fieldName) {
      return (!fieldName in resource.data && fieldName in request.resource.data) || resource.data[fieldName] != request.resource.data[fieldName];
    }

    match /users/{userId} {
      //read rules here...
      allow write: if !isUpdatingField("role") && !isUpdatingField("adminOnlyAttribute");
    }
  }
}
Run Code Online (Sandbox Code Playgroud)