在我使用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
数组。
写入消息(添加新文档)具有相同的安全规则。
读取的操作如下:
这是安全规则:
更新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;
}
答案 0 :(得分:2)
安全规则不过滤数据。相反,他们只是检查是否允许读取操作。
因此,如果要允许用户仅阅读其所属的消息,则必须执行查询以选择这些消息。
例如:您可以选择用户作为发件人的所有邮件:
db.collection('chatRooms').doc(roomId).collection('msgs')
where('sender._id', '==', firebase.auth().currentUser.uid)
现在,安全规则将检查是否允许此查询,并返回相应的消息。
问题就变成了您无法对sender._id
和partner._id
字段执行OR条件。有一些解决方案:
将每个消息的参与者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
通过在/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);
让/msgs
的访问权限从房间继承:
match /chatRooms/{roomId=**} {
allow read: if request.auth.uid == resource.data.userA.id ||
request.auth.uid == resource.data.userB.id;