提问者:小点点

包含数组的查询的FiRecovery安全规则


我有一个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);
}

共3个答案

匿名用户

目前看来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。