Firestore安全规则:批处理时拒绝PERMISSION

时间:2019-07-18 17:06:00

标签: android firebase google-cloud-firestore firebase-security firebase-security-rules

考虑以下简单的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.,但是如果我单独编写更改,则成功了吗?

例如以下代码(使用批处理写入)失败: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
    }
}

没有批处理,该代码可以正常运行。例如这很完美:

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
    }
}

关于我的安全规则,我需要了解一些关于批处理写入/事务的特殊信息吗?我希望这些更新能够自动执行,因此我最初尝试使用批处理写入。但是,我似乎无法让它们与我的安全规则结合使用。

2 个答案:

答案 0 :(得分:3)

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

由于在两种情况下都不会显示现有文档的内容,因此实际上不可能确切地说出这是在哪里破坏代码。但这几乎可以肯定是这里正在发生的事情。更新是来自单个文档写入,批处理更新还是事务都无关紧要。它们都遵循相同的规则。

(您曾经能够使用一个称为writeFields的属性来仅查找正在更新的字段,但已不赞成使用-不要使用它。)

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

Cloud Firestore Security Rules - only allow write to specific key in document

答案 1 :(得分:0)

您的文档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();
    }
  }
}