【问题标题】:Firestore Security Rule to restrict reading chat messages限制阅读聊天消息的 Firestore 安全规则
【发布时间】:2021-01-05 11:39:10
【问题描述】:

在我使用 Firestore 的 1-1 聊天应用程序中,2 位参与者将读取/写入 msgs 子集合中的文档,其中每个文档是一条消息。
但是由于安全规则,权限被拒绝而失败。

数据库结构:

chatRooms/
    ${roomId}/
        msgs/
            ${msgId}

我想添加安全规则,以便只有相关的 2 个参与者能够在他们的 roomId 文档的 msgs 子集合中读/写

我编写了以下安全规则。
它让参与者在msgs 子集合中创建一个新文档(即发送消息)。
但用户无法阅读此msgs 子集。

match /chatRooms/{roomId} {
    allow read:   if request.auth.uid == resource.data.userA.id || 
                     request.auth.uid == resource.data.userB.id;
    allow create: if request.auth.uid == request.resource.data.userA.id || 
                     request.auth.uid == request.resource.data.userB.id;

    match /msgs/{msgId} {
        allow read:   if request.auth.uid == resource.data.sender._id ||
                         request.auth.uid == resource.data.partner._id;
        allow create: if request.auth.uid == request.resource.data.sender._id ||
                         request.auth.uid == request.resource.data.partner._id;
    }
}

读取的代码是:

    const roomId = `${userAId}_${userBId}`;
    const query = db.collection('chatRooms').doc(roomId).collection('msgs')
        .orderBy('createdAt', 'desc')
        .startAfter(lastVisible)
        .limit(10)

    query.get()

如果我像下面这样更改读取规则,则读取和写入工作完全正常。
所以我们可以确定问题出在msgs 子集合的读取规则中。 (而不是在其父集合/文档规则中)

    match /msgs/{msgId} {
        allow read:   if request.auth != null;
        allow create: if request.auth.uid == request.resource.data.sender._id ||
                         request.auth.uid == request.resource.data.partner._id;
    }

更新:

Frank 提出了 3 个解决方案,我尝试了其中的两个(1 和 3),但都对我不起作用。

对于解决方案 1,我添加了一个带有参与者 UID 的 ids 数组。
写入消息(添加新文档)使用相同的安全规则。

读取操作如下所示:

这是安全规则:


更新 2

正如弗兰克在他的回答中的评论中提到的那样,它在修复安全规则后起作用。 (因为这个评论在下面,我也在这里添加它)

  match /msgs/{msgId} {
    allow read:   if request.auth.uid in resource.data.ids;
    allow create: if request.auth.uid in request.resource.data.ids;
  }

更新 3

解决方案 1 需要生成一个索引(我也使用orderBy),这需要额外付费。所以我也尝试了弗兰克的解决方案 3,但它返回权限被拒绝错误。
可能读取查询是错误的,它需要一些过滤器,但我不能按照规则的要求在$roomId doc 数据(userA.id / userB.id)上添加过滤器,同时从msgs 集合中读取。

规则:

match /chatRooms/{roomId=**} {
  allow read:   if request.auth.uid == resource.data.userA.id || 
                   request.auth.uid == resource.data.userB.id;
  allow create: if request.auth.uid == request.resource.data.userA.id || 
                   request.auth.uid == request.resource.data.userB.id;
  allow update: if request.auth.uid == resource.data.userA.id || 
                   request.auth.uid == resource.data.userB.id;
}

读取查询:

    const query = dbFirestore.collection('chatRooms').doc(roomId).collection('msgs')
        .orderBy('createdAt', 'desc')
        .limit(10)

    query.onSnapshot( ... )

【问题讨论】:

  • 请不要发布您的代码截图或其他文本内容。而是发布实际文本,并使用 Stack Overflow 的格式化工具进行标记。

标签: javascript google-cloud-firestore firebase-security


【解决方案1】:

安全规则不会过滤数据。相反,他们只是检查是否允许读取操作。

因此,如果您想让用户只阅读他们所属的消息,则必须执行一个查询来选择这些消息。

例如:您可以选择用户作为发件人的所有消息:

db.collection('chatRooms').doc(roomId).collection('msgs')
    where('sender._id', '==', firebase.auth().currentUser.uid)

现在安全规则会检查这个查询是否被允许,并返回相应的消息。


问题就变成了您不能对sender._idpartner._id 字段执行OR 条件。有几个解决方案:

  1. 将每条消息的参与者 UID 存储在一个数组字段中(比如participants),然后使用array-contains 进行查询:

    db.collection('chatRooms').doc(roomId).collection('msgs')
     where('participants', 'array-contains', firebase.auth().currentUser.uid)
    

    然后在规则中使用类似这样的东西来保护它:

    request.auth.uid in resource.data.participants
    
  2. /chatRooms/$roomId 级别上对房间的安全性建模,并在msgs 子集合上进行查找。这需要两个额外的 get,但除此之外要简单得多

    match /msgs/{msgId} {
     allow read: if request.auth != null && 
         (get(/databases/$(database)/documents/chatRooms/$(roomId)).data.userA.id == request.auth.uid
         || get(/databases/$(database)/documents/chatRooms/$(roomId)).data.userB.id == request.auth.uid);
    
  3. /msgs的访问权限从房间继承:

    match /chatRooms/{roomId=**} {
       allow read: if request.auth.uid == resource.data.userA.id || 
                  request.auth.uid == resource.data.userB.id;
    

【讨论】:

  • 谢谢@Frank,你的解释很有用。我想我会采用第一种方法。
  • 抱歉,我想先测试解决方案,然后将其标记为已解决。第三种解决方案对我不起作用,所以我尝试了第一种。但这也不起作用:( --------- 读取操作:query = db.collection('chatRooms').doc(roomId).collection('msgs').where('ids','array-contains',firebase.auth().currentUser.uid) ----------- 规则:match /msgs/{msgId} { allow read: if request.auth.uid == resource.data.ids[0] || request.auth.uid == resource.data.ids[1]; } --------尽管 write 使用相同的规则,但我在生成的文档中使用参与者 ID 确认了 ids 数组
  • 请使用新的查询、规则和文档截图更新您的问题。在 cmets 中读起来太难了。
  • 那些数组比较不起作用,因为查询执行 array-contains 而规则执行两个 == 检查。我希望request.auth.uid in resource.data.ids
  • 嗯...我确实不确定这是否有效。主要是:规则如何根据您传入的信息验证读取操作。因此,方法 3 可能无法实现。
猜你喜欢
  • 2020-09-10
  • 1970-01-01
  • 2021-01-19
  • 2020-08-21
  • 1970-01-01
  • 2018-05-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多