我有一个Flutter应用,用户可以在其中发布帖子,并将帖子标记为属于某个组。帖子存储在全局集合中,每个帖子都有一个Post.groupId
字段:
/posts/{postId}
根据我的Firestore安全规则和查询,仅允许用户阅读帖子所属的组(即帖子的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')...
根据这些安全规则,我继续收到以下异常Missing or insufficient permissions
:
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
这是我尝试在本地使用Firestore Emulator执行此查询时收到的错误:
FirebaseError:
Function not found error: Name: [size]. for 'list' @ L215
第215行对应于此规则中的allow read
行:
match /posts/{postId} {
allow read: if canActiveUserReadAnyGroupId(resource.data.groupIds);
}
答案 0 :(得分:1)
如果我不得不猜测,我会说groupIds实际上不是List类型的对象,这意味着文档中的字段也不是数组。如果是字符串,则此代码将不起作用,因为字符串在规则语言中没有名为size()
的方法。
如果您不确定100%决定字段的类型,则需要检查规则中的类型并确定如何处理。您可以使用is
运算符检查类型。例如,如果您实际上正在使用groupIds is list
,则布尔值为true。
在规则中,可以使用debug()
函数将某些表达式的值转储到日志中。它将返回相同的值。因此,您可以说debug(groupIds) != null
来打印值并检查它是否为空。
答案 1 :(得分:1)
根据此blog post,如果您可以维护给定帖子的成员ID索引(基于组分配),则可以保护以数组数据类型存储成员ID并与之匹配的帖子读取访问权限规则集中带有“ array-contains”子句的成员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
}
}
}
答案 2 :(得分:0)
Firestore目前似乎不支持此方案的安全规则(感谢您对Doug Stevenson的跟踪)。我想出了一种解决限制的机制,并希望在其他人正在处理此问题时分享。它需要一个额外的查询,但是使我不必为了避免安全规则而使用Admin SDK创建Web API。
帖子的存储方式如下(简化):
/posts/{postId}
- userId
- timestamp
- groupIds[]
- message
- photo
现在,我要添加一个额外的帖子引用集合,该集合仅存储指针信息:
/postRefs/{postId}
- userId
- timestamp
- groupIds[]
posts
集合将具有安全性规则,该规则将进行所有验证,以确保用户至少在标记帖子的组之一中。 Firestore能够正确处理简单的get
请求,但暂时无法处理list
请求。
由于postRefs
集合仅存储ID,而不存储帖子中可能包含的敏感信息,因此可以放宽其安全规则,以便我仅验证用户已登录。因此,用户将执行发布对postRefs
集合进行查询,以从postId
集合中检索要延迟加载的有序posts
的列表。
客户在普通的posts
集合中添加/删除帖子,然后有一个Cloud Function将ID信息复制到postRefs
集合中。