Firestore安全规则以限制阅读聊天消息

时间:2020-09-18 14:07:06

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

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

数据库结构:

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

我想添加安全规则,以便只有相关的2位参与者才能对其msgs文档的roomId子集合进行读/写

我编写了以下安全规则。
让我们的参与者在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;
    }

更新:

弗兰克提出了3种解决方案,我尝试了其中两种(1和3),但两种方法都不适合我。

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

enter image description here

读取的操作如下:

enter image description here

这是安全规则:

enter image description here


更新2

如Frank在其回答中的评论中所提到的,它在修复安全规则后起作用。 (由于此评论远在下面,我也在此处添加它)

  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)。所以我也尝试了Frank的解决方案3,但它返回了权限被拒绝的错误。
读取查询可能是错误的,并且需要一些过滤器,但是我无法根据规则的要求在{{1}读取的情况下在$roomId文档数据(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;
}

1 个答案:

答案 0 :(得分:2)

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

因此,如果要允许用户仅阅读其所属的消息,则必须执行查询以选择这些消息。

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

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级别上为房间的安全性建模。这需要两个额外的获取,但除此之外要简单得多

    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;