考虑这个简单的Firestore数据库结构:
1:Firestore 结构
cities/
city1/
name:Beijing
likes: 0
dislikes: 0
... more fields
city2/
name: New York
likes: 21
dislikes: 1
... more fields
为保护数据库免受意外操作的影响,我添加了以下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"]);
}
}
}
这基本上允许我强制执行以下要求:
- 已认证的用户可以读取城市数据。
- 已认证的用户可以更新城市的“喜欢”和“不喜欢”字段。
问题
当我尝试使用批量写入时,为什么上述设置会给我一个Exception: PERMISSION_DENIED: Missing or insufficient permissions.
,但如果我单独编写更改,则成功了呢?
例如下面的代码(使用Batch write)失败:PERMISSION_DENIED: Missing or insufficient permissions。
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
}
}
如果不进行批处理,代码可以正常工作。例如,以下代码可以完美运行:
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
}
}
在批量写入/事务方面,是否有什么特殊的需要我在安全规则方面了解的?我希望这些更新能够原子执行,因此一开始尝试使用批量写入。然而,我似乎无法将其与我的安全规则结合使用。
resource
运行的,所以如果我的请求数据匹配任何hasOnly
字段,它总是会返回ok。有没有办法模拟已经存在的数据??在我看来,确实需要一种只针对正在更新的字段进行测试的方法,否则通过管理员界面(安全规则不适用的地方)添加任意字段都将破坏用户界面,除非也更新安全规则。 - som