用于查询包含数组的Firestore安全规则

时间:2019-07-16 19:07:19

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

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

3 个答案:

答案 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集合中。