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

问题描述 投票:15回答:7

[为了我的一生,我不明白为什么以下内容会导致允许写入的false。假设我的users集合是空的,并且正在从Angular前端编写以下形式的文档:

{
  displayName: 'FooBar',
  email: '[email protected]'
}

我当前的安全规则:

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());
    }
  }
}

通常,我希望没有用户能够编辑自己的角色。普通用户可以编辑自己的文档,否则管理员可以编辑任何人的文档。

isEditingRole()插入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.fooresource.data.foo来测试字段是否被修改,知道即使resource中的每个字段也都存在于request.resource中,即使它不是在写入请求中提交的。

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

firebase firebase-security google-cloud-firestore
7个回答
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");
    }
  }
}

4
投票

我使用writeFields解决了。请尝试此规则。

allow write: if !('role' in request.writeFields);

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

allow update: if !(['leader', '_created'] in request.writeFields);

3
投票

所以最后,我似乎以为resource.data.nonExistentField == null会在实际返回false时返回Error(根据this和我的测试)。所以我原来的解决方案可能已经遇到了。令人费解的是,相反的内容应根据the docs起作用,但也许文档所指的是“不存在”的值,而不是键-细微的区别。]

我仍然没有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自动插入。尽管事实并非如此,但删除它会导致我的写入因权限问题而失败。

[可能通过将写入分为createupdatedelete部分,而不只是allow write: if !isEditingOwnRole() && (isOwnDocument() || isAdmin());而得到澄清/改进。


2
投票

由于文档中对writeFields的引用已经消失,所以我不得不想出一种新的方法来完成我们对writeFields的处理。


2
投票

使用此功能,您可以检查是否/未创建/修改字段。


2
投票

汤姆·贝利(https://stackoverflow.com/a/48177722/5727205)的解决方案确实很有希望。


1
投票

这看起来似乎是致命的,但是对于更新文档,其中您可能具有非用户生成的其他字段,例如。角色,创建的角色等。您需要可以测试这些字段是否不变的功能。因此满足这三个FN。

© www.soinside.com 2019 - 2024. All rights reserved.