我有一个Flutter应用程序,用户可以在其中发布帖子并将帖子标记为属于一个组。帖子存储在一个全局集合中,每个帖子都有一个Post. groupId
字段:
/posts/{postId}
根据我的Firer安全规则和查询,用户只有在帖子被标记的组中才能阅读帖子(即帖子的groupId
字段)。批准的组用户存储在:
/groups/{groupId}/users/{userId}
我可以查询来自特定用户组的帖子,例如:
_firestore.collection('posts').where('groupId', isEqualTo: 'groupA')...
上面的这些都正常工作。
我正在尝试进行改进,可以将帖子标记在多个组中,而不仅仅是一个组,因此我将单个Post. groupId
字段替换为Post.groupIds
数组。如果用户是来自Post.groupIds
的任何组的成员,他/她应该能够阅读帖子。我尝试使用来自Flutter应用程序的以下查询来阅读带有特定组标记的所有帖子:
_firestore.collection('posts').where('groupIds', arrayContains: 'groupA')...
我不断收到以下异常缺少或权限不足
使用这些安全规则:
match /posts/{postId} {
allow read: if canActiveUserReadAnyGroupId(resource.data.groupIds);
}
function isSignedIn() {
return request.auth != null;
}
function getActiveUserId() {
return request.auth.uid;
}
function isActiveUserGroupMember(groupId) {
return isSignedIn() &&
exists(/databases/$(database)/documents/groups/$(groupId)/users/$(getActiveUserId()));
}
function canActiveUserReadAnyGroupId(groupIds) {
return groupIds != null && (
(groupIds.size() >= 1 && isActiveUserGroupMember(groupIds[0])) ||
(groupIds.size() >= 2 && isActiveUserGroupMember(groupIds[1])) ||
(groupIds.size() >= 3 && isActiveUserGroupMember(groupIds[2])) ||
(groupIds.size() >= 4 && isActiveUserGroupMember(groupIds[3])) ||
(groupIds.size() >= 5 && isActiveUserGroupMember(groupIds[4]))
);
}
有了这些安全规则,我可以阅读一篇文章,但我不能进行上述查询。是否有可能有允许我进行此查询的安全规则?
更新1
添加了isSignedIn()
和getActiveUserId()
安全规则函数以确保完整性。
更新2
这是我在本地尝试使用Fi恢复模拟器执行此查询时收到的错误:
FirebaseError:
Function not found error: Name: [size]. for 'list' @ L215
第215行对应于此规则中的允许读取
行:
match /posts/{postId} {
allow read: if canActiveUserReadAnyGroupId(resource.data.groupIds);
}
目前看来Fi恢复不支持这种情况的安全规则(感谢您帮助跟踪Doug Stevenson)。我已经想出了一种解决限制的机制,并希望在其他人处理此问题的情况下分享。它需要额外的查询,但使我不必使用管理SDK创建WebAPI来绕过安全规则。
帖子存储如下(简化):
/posts/{postId}
- userId
- timestamp
- groupIds[]
- message
- photo
现在我正在添加一个额外的帖子引用集合,它只存储指针信息:
/postRefs/{postId}
- userId
- timestamp
- groupIds[]
帖子
集合将具有进行所有验证的安全规则,以确保用户至少在帖子被标记的组中的一个组中。FiRecovery能够正确处理简单的get
请求,只是目前没有list
请求。
由于postRefs
集合只存储ID的,而不存储post中可能包含的敏感信息,因此可以放宽其安全规则,只验证用户是否登录。因此,用户将对postRefs
集合执行post查询,以检索要从post
集合中延迟加载的有序postId
列表。
客户端在普通post
集合中添加/删除帖子,然后有一个云函数将ID信息复制到postRefs
集合中。
根据这篇博文,如果您可以维护给定帖子的成员ID索引(基于组分配),那么您可以保护帖子读取访问权限,将成员ID存储在数组数据类型中,并与规则集中的“array-包含”子句中的成员ID进行匹配。在您的Firebase规则中,它看起来像这样:
service cloud.firestore {
match /databases/{database}/documents {
match /posts/{postId} {
allow read: if request.auth.uid in resource.data.members
allow write: if request.auth.uid == resource.data.owner
}
}
}
如果我不得不猜测,我会说groupIds实际上不是List类型的对象,这意味着文档中的字段也不是数组。如果是字符串,则此代码将不起作用,因为字符串在规则语言中没有名为size()
的方法。
如果您不能100%确定字段的类型,则需要检查规则中的类型并确定如何处理它。您可以使用is
运算符来检查类型。例如,groupIds is list
将是布尔值true,如果您实际使用一个。
在您的规则中,您可以使用debug()
函数将某个表达式的值转储到日志中。它将返回相同的值。因此,您可以说debug(groupIds)!=null
来打印值并检查它是否为null。