使用Firestore安全规则限制写在嵌套对象上的字段

问题描述 投票:0回答:1

tl; dr:我认为Set需要一种获取元素(set.toList()[0])的方法,但也许我缺少了某些东西!


你好!我正在使用Firestore开发带有大量小对象(信用卡交易)的预算应用程序。为了限制读取次数,将每个事务存储为单独的文档没有意义,因为用户可能一次需要约数百个事务。

相反,我有一个容器来容纳许多看起来像这样的交易:

/user/{user_id}/transactions/{container_id}

container: {
  transactions: {
    transaction_id_1: {
      amount: 8.25,
      note: 'chipotle lunch'
    },
    transaction_id_2: {
      amount: 12.01
    }
  }
}

这很好,但是我认为安全规则不能用于写入。我想允许用户修改某些字段(note),但不能修改其他字段(amount)。如果每笔交易都是一个文档,我们可以使用MapDiff来执行此操作,但是嵌套会增加难度。

由于我们无法编写循环,因此如果每次写入将自己限制为一个更新的事务,那么使用嵌套的MapDiff这样的代码应该完全有可能:

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents/{document=**} {

    function allowTransactionUpdate() {
      let transactionId = <transaction ID of the single transaction being updated>;

      // Limit fields updated for container.
      return request.resource.data.diff(resource.data).changedKeys()
        .hasOnly(['transactions']) &&

      // Make sure only one transaction changed.
      request.resource.data.transactions.diff(resource.data.transactions)
        .changedKeys().hasOnly([transactionId]) &&

      // Verify the transaction already exists.
      transactionId in resource.data.transactions &&

      // Only allow certain fields to be updated on that transaction.
      request.resource.data.transactions[transactionId]
        .diff(resource.data.transactions[transactionId]).affectedKeys()
        .hasOnly(['note']);
    }

    match /transactions/{transMonthId} {
      allow update: if allowTransactionWrite();
    }

    allow read, write: if false;
  }
}

如果我们可以使用MapDiff来获取在container.transactions地图中更改的交易,这将非常有用...

let transactionId = request.resource.data.transactions
  .diff(resource.data.transactions).changedKeys()[0];

关键缺失部分是最后一位:[0]。当前,Sets无法获得元素,这意味着将某些内容转换为Set(以及使用MapDiff进行的任何操作)都是死胡同:您永远无法真正知道Set中的值。它只能与其他集合进行比较。

否则...我想念什么吗?还有另一种方法来限制嵌套更新上的字段吗?


其他选项是:

  • 使用自定义后端来执行此写操作,这是可行的,但令人沮丧,因为Firestore的一大优势是最小化了后端+安全规则中的强制执行。
  • 将用户可编辑属性放在一个容器文档中,将不可编辑属性放在另一个容器文档中,但这会使读取翻倍,并给客户端订阅增加烦人的复杂性。
  • 接受这是不可能的,并且每次事务使用一个文档,这将导致100倍以上的读取。 ;)
google-cloud-firestore firebase-security
1个回答
1
投票

您没有丢失任何东西。安全规则无法实现您要执行的操作。

如果您打算收集数据项,并且要引用这些数据项并使用安全规则进行保护,则它们应该是集合或子集合中的单个文档。建议不要将它们全部塞在一个文档中,也不能扩展。如果您这样做是为了节省文档读取的内容,那么您很快就会发现,在涉及安全规则和管理这些单个项目时,这种“优化”实际上不是非常有用。将数据项作为单个文档保护比在单个文档中进行管理更容易和直接。如果确实必须将所有内容存储在一起,我建议您通过一些后端来限制写访问权限,在这里您可以编写自定义逻辑,并让您的客户端在需要执行写操作时调用后端。请记住,这是不可扩展的,并且您可能会遇到最大1MB的文档大小,这是一个比起初要解决的问题更昂贵的问题。

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