Firestore 安全规则:批处理时权限被拒绝

Ent*_*eco 5 android firebase firebase-security google-cloud-firestore

考虑这个简单的 Firestore 数据库结构:

1:Firestore结构

cities/
    city1/
        name:Beijing
        likes: 0
        dislikes: 0
        ... more fields 
    city2/
        name: New York
        likes: 21
        dislikes: 1
        ... more fields 
Run Code Online (Sandbox Code Playgroud)

为了保护数据库免受意外操作,我添加了以下 Firestore 安全规则

2 Firestore 安全规则:

// Allow read/write access to all users under any conditions
service cloud.firestore {
  match /databases/{database}/documents {    
    match /cities/{cityId} {
      allow read: if request.auth.uid != null;
      allow update: if request.auth.uid != null && 
request.resource.data.keys().hasOnly(["likes", "dislikes"]); 
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

这基本上允许我执行这些要求:

  • 经过身份验证的用户可以读取城市的数据。
  • 经过身份验证的用户可以更新城市的“喜欢”和“不喜欢”字段。

问题

为什么Exception: PERMISSION_DENIED: Missing or insufficient permissions.当我尝试使用批量写入时,上面的设置给了我一个, 但如果我单独写入更改则成功?

例如,下面的代码(使用批量写入)失败:PERMISSION_DENIED:权限缺失或不足。

fun rate(likes: List<String>, dislikes: List<String>, done: (Boolean) -> Unit) {

    db.runBatch { batch ->
        likes.map { cityCollection.document(it) }.forEach { doc ->
            batch.update(doc,"likes", FieldValue.increment(1))
        }
        dislikes.map { cityCollection.document(it) }.forEach { doc ->
            batch.update(doc,"dislikes", FieldValue.increment(1))
        }
    }.addOnCompleteListener { task ->
        task.exception() // Exception: PERMISSION_DENIED: Missing or insufficient permissions.
        done(task.isSuccessful) // false
    }
}
Run Code Online (Sandbox Code Playgroud)

如果没有 Batching,代码就可以正常工作。例如,这完美地工作:

override fun rate(likes: List<String>, dislikes: List<String>, done: (Boolean) -> Unit) {

    val tasks = likes.map { cityCollection.document(it) }.map { doc ->
        doc.update("likes", FieldValue.increment(1))
    }.union(dislikes.map { cityCollection.document(it) }.map { doc ->
        doc.update("dislikes", FieldValue.increment(1))
    })

    Tasks.whenAllComplete(tasks).addOnCompleteListener { task ->
        done(task.isSuccessful) // true
    }
}
Run Code Online (Sandbox Code Playgroud)

关于我的安全规则,我需要了解批量写入/事务有什么特别之处吗?我希望这些更新以原子方式执行,因此我最初尝试使用批量写入。但是,我似乎无法让它们与我的安全规则结合使用。

Dou*_*son 4

您应该知道它request.resource.data.keys()包含现有文档中的所有键,而不仅仅是正在更新的键。 request.resource.data代表文档的最终状态(如果您允许写入完成)。因此,如果您要更新已包含其他字段的文档,您的规则将始终拒绝访问,并hasOnly返回 false。

由于在这两种情况下您都没有显示现有文档的内容,因此实际上不可能确切地说出这在哪里使您的代码出错。但这几乎肯定就是这里发生的事情。更新是否来自单个文档写入、批量更新或事务都无关紧要。它们都受到相同规则的限制。

(您过去可以使用名为 writeFields 的属性来仅查找正在更新的字段,但该属性已被弃用 - 不要使用它。)

如果您想执行每个字段的限制,它实际上比您现在编写的内容要复杂得多。您必须检查(且仅当)特定字段是否已被修改,同时还要检查是否没有其他字段被更改。请参阅此问题了解更多详细信息:

Cloud Firestore 安全规则 - 只允许写入文档中的特定密钥