我正在尝试根据子集合中的文档字段的值设置允许访问集合的安全规则。
当通过id检索单个文档时,这可以正常工作,这是一个get
操作。但是,当查询main_collection
(list
操作)时,这会因“权限被拒绝”错误而失败。由于集合中只有一个文档,因此我不能查看某些要查询的文档,例如this question。
我的数据库结构如下所示。它包含列出的集合(main_collection
),它有一个单独的文档(some_doc
),它有一个单个子集合(sub_collection
),它有一个单独的文档(another_doc
)。
/main_collection/some_doc/sub_collection/another_doc
another_doc
有一个字符串字段someFieldValue
。
对于此示例,我的查询是整个集合,即单个文档。在我的实际应用程序中,它只查询它希望有权访问的文档,但最终结果是相同的,因为我无法从客户端库中过滤文档的子集合。
firestore.collection('main_collection').get()
这些是我的安全规则。
service cloud.firestore {
match /databases/{database}/documents {
match /main_collection/{mainColDoc} {
// This operation works
allow get: if subCollectionDocumentHasField('someFieldValue');
// This operation fails with permission denied
allow list: if subCollectionDocumentHasField('someFieldValue');
// This checks for the existence of a field on the subcollection's document
function subCollectionDocumentHasField(fieldName) {
return get(/databases/$(database)/documents/main_collection/$(mainColDoc)/sub_collection/another_doc).data.keys().hasAny([fieldName]);
//return get(/databases/$(database)/documents/main_collection/some_doc/sub_collection/another_doc).data.keys().hasAny([fieldName]);
}
}
}
}
subCollectionDocumentHasField
函数检查文档someFieldValue
上是否存在another_doc
字段。在此函数中,如果我将$(mainColDoc)
变量替换为硬编码文档id some_doc
,则list
操作成功。由于$(database)
路径变量可以在这种情况下使用,我希望其他人也可以。
这是一个错误还是预期的行为?
这实际上是预期的行为,您无法使用Firebase的规则来过滤查询结果。
典型的场景是收集消息,其中每条消息都指向其创建者。
您不能简单地添加一个规则,其中只允许对创建者是经过身份验证的用户的消息进行读取,以自动过滤当前经过身份验证的用户的消息。
唯一的方法是在客户端(或通过云功能)使用过滤器进行查询。
文档非常清楚:
在编写查询以检索文档时,请记住安全规则不是过滤器 - 查询是全部或全部。为了节省您的时间和资源,Cloud Firestore会针对其潜在结果集评估查询,而不是针对所有文档的实际字段值。如果查询可能返回客户端无权读取的文档,则整个请求将失败。
我向Google开了一张票,并有效地确认了@José从使用中推断出的内容,即安全规则“每次查询只检查一次”。
为了澄清,虽然list
操作的安全规则通常不会查询文档的内容(以避免可能性能不佳),但至少有一个条件是它将查询文档的内容。这是保证安全规则只返回一个文档的时候。当满足此保证时,将查询单个文档的内容,因为可以保持高性能;与get
操作相同。
因此,在我的问题的链接示例中,list
操作的规则引用了父文档,这个保证得到满足,并且将查询父文档的内容。
此外,在我的list
操作规则引用硬编码文档ID的示例中,满足此保证并且将查询硬编码文档的内容。
为了明确说明,对于list
操作,在Firestore无法保证其规则只查询单个文档的任何情况下,访问将被设计自动拒绝。