Android Firestore安全规则:批处理时拒绝权限
考虑以下简单的Firestore数据库结构: 1:Firestore结构Android Firestore安全规则:批处理时拒绝权限,android,firebase,google-cloud-firestore,firebase-security,Android,Firebase,Google Cloud Firestore,Firebase Security,考虑以下简单的Firestore数据库结构: 1:Firestore结构 cities/ city1/ name:Beijing likes: 0 dislikes: 0 ... more fields city2/ name: New York likes: 21 dislikes: 1 ... more fields 为了保护数据库不受意外操作
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"]);
}
}
}
这基本上允许我执行这些要求:
- 经过身份验证的用户可以读取城市数据
- 经过身份验证的用户可以更新城市的“喜欢”和“不喜欢”字段
异常:权限\u拒绝:权限缺失或不足。
,但如果我单独写入更改,则会成功
例如,以下代码(使用批处理写入)失败:权限被拒绝:权限缺失或不足
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
}
}
fun rate(喜欢:列表,不喜欢:列表,完成:(布尔)->单位){
db.runBatch{batch->
likes.map{cityCollection.document(it)}.forEach{doc->
批处理更新(文档“likes”,FieldValue.increment(1))
}
dislikes.map{cityCollection.document(it)}.forEach{doc->
批处理更新(文件“不喜欢”,字段值。增量(1))
}
}.addOnCompleteListener{task->
task.exception()//异常:权限\u被拒绝:缺少权限或权限不足。
完成(task.isSuccessful)//错误
}
}
而没有批处理,代码运行良好。e、 g.这一点非常有效:
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
}
}
覆盖有趣率(喜欢:列表,不喜欢:列表,完成:(布尔)->单位){
val tasks=likes.map{cityCollection.document(it)}.map{doc->
文档更新(“likes”,FieldValue.increment(1))
}.union(dislikes.map{cityCollection.document(it)}.map{doc->
文档更新(“不喜欢”,FieldValue.increment(1))
})
Tasks.whenAllComplete(Tasks).addOnCompleteListener{task->
完成(task.isSuccessful)//true
}
}
关于批量写入/事务,我是否需要了解有关我的安全规则的一些特殊信息?我希望这些更新以原子方式执行,因此我最初尝试使用批处理写入。但是,我似乎无法将它们与我的安全规则结合使用。您应该知道,
request.resource.data.keys()
包含现有文档中的所有密钥,而不仅仅是正在更新的密钥request.resource.data
表示允许写入完成时文档的最终状态。因此,如果您正在更新已经包含其他字段的文档,则您的规则将始终拒绝访问,因为hasOnly
将返回false
因为在这两种情况下都没有显示现有文档的内容,所以实际上不可能确切地说出这是在哪里让代码出错的。但这几乎肯定就是这里发生的事情。更新是否来自单个文档写入、批处理更新或事务都无关紧要。他们都受到同样的规则限制
(过去,您可以使用名为writeFields的属性来查找只更新了哪些字段,但这是不推荐使用的-不要使用它。)
如果您想执行每个字段的限制,它实际上比您现在编写的要复杂得多。您必须检查是否(且仅当)某个特定字段已被修改,同时还要检查其他字段是否已被修改。有关更多详细信息,请参见此问题:
您的文档
request.resource.data
对象可以(也可能会)有更多的“喜欢”和“不喜欢”,这将导致规则失败。检查哪些字段已更改的新规范方法是将文档与现有文档区分开来(这将替换“writeFields”)。我已编辑了您的规则以显示以下内容:
service cloud.firestore {
match /databases/{database}/documents {
match /cities/{cityId} {
function onlyLikedFieldUpdated() {
return request.resource.data.diff(resource.data).affectedKeys().hasOnly(["likes", "dislikes"]);
}
allow read: if request.auth.uid != null;
allow update: if request.auth.uid != null && onlyLikedFieldUpdated();
}
}
}
谢谢我已经在考虑将
喜欢的
和不喜欢的
转移到他们自己的子集合中(这可能会更容易)。我只是花了几个小时在这上面挠头。不断获得假阳性,模拟针对不存在的UID的“更新”;测试显然是针对空的资源运行的,因此如果我的请求数据与任何hasOnly
字段匹配,则总是返回ok。有没有办法模拟已经存在的数据??IMHO确实需要有一种只针对正在更新的字段进行测试的方法,否则通过管理员界面(安全规则不适用的地方)添加任何任意字段将破坏用户界面,除非安全规则也被更新。将来会有一种更简单的方法,但是今天你必须检查所有已知的字段。现在多久?严格地说,“现在”就是这个时刻但是如果你问什么时候事情会改变,我不知道未来。我只知道已经讨论过了。