Firestore 安全规则 - 相同的规则适用于写入但不适用于读取

时间:2020-12-20 01:31:53

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

我正在开发一个使用 Firestore 的 Firebase 项目。 我正在查询 Firestore 以从消息集合中获取聊天消息。我只想要属于某个对话的消息:

const messages = await db
    .collection('messages')
    .where('room', '==', room)
    .onSnapshot(snap => {//stuff})

所以很好。有用。我设置安全规则时出现问题。

如果我做一些简单的事情,例如:

allow read: if request.auth != null;

一切正常。但是,如果我只想允许其 uid 包含在 'partyIDs' 消息对象属性中的用户访问,则会出错:

allow read: if
    request.auth.uid == resource.data.pertiesIDs[0] ||
    request.auth.uid == resource.data.parties[1];

最奇怪的是我有一个非常相似的更新规则,它按预期工作:

allow update: if
    (request.resource.data.diff(resource.data).affectedKeys()
        .hasOnly(['read', 'notified'])) &&
    (request.auth.token.name == resource.data.parties[0] ||
        request.auth.token.name == resource.data.parties[1]);

第二条规则(有效)的查询如下所示:

const update = await db
    .collection('messages')
    .doc(docid)
    .update({read: true, notified: true})

我卡住了!有人能解释一下这个谜团吗?

2 个答案:

答案 0 :(得分:2)

问题在于 Firestore security rules are not filters。我强烈建议阅读该文档。

当您编写规则来为集合中的文档读取设置条件时,客户端必须进行与规则条件完全匹配的查询。当您要求某个字段中必须存在某些数据时,您的查询必须通过仅过滤满足规则要求的字段内容的文档来匹配该要求。 规则不会仅提取匹配的文档以返回它们。您可以将客户端查询视为要求与给定过滤器匹配的完整文档集,以及规则因为条件不满足而拒绝那个需求。

然而,这里有一点问题,因为查询没有指定数组索引的方法。无法进行要求数组字段的索引 0 必须包含特定值的查询。

我建议重新考虑您的文档数据和规则,并以客户端应用程序可以完全符合其要求的方式构建它们。

答案 1 :(得分:0)

解决了!

@Doug Stevenson 感谢您为我指明了正确的方向。

我制定的规则如下:

allow read: if request.auth.token.name == resource.data.receiver || request.auth.token.name == resource.data.sender;
allow create: if request.auth != null;
allow update: if (request.resource.data.diff(resource.data).affectedKeys()
.hasOnly(['read'])) && (request.auth.token.name == resource.data.parties[0] || request.auth.token.name == resource.data.parties[1]);}
allow delete: if false;

根据这些规则,我可以执行以下操作:

  1. 首次加载:通过云功能加载给定房间的聊天消息(它具有管理员权限,因此没有安全规则问题)

  2. 获取传入消息:添加一个快照实时侦听器以获取接收者为登录用户且属性房间等于开放房间的所有消息

    await db.collection('messages').where("receiver","==",user.displayName).where("room", "==", room).orderBy('timestamp'). onSnapshot(snapshot => { //Do Stuff })

  3. 更新读取状态:当显示消息时,接收者更新 firestore 中 mssage 的读取属性:

    const update = await db.collection("messages").doc(id).update({read: true});

  4. 在 UI 中实时呈现读取状态:添加第二个 onsnapshot 实时侦听器以获取发送者为登录用户且房间等于开放房间的所有消息

    const readUpdate = await db.collection('messages') .where("sender", "==", user.displayName) .where("房间", "==", 房间) .orderBy('时间戳') .onSnapshot(snapshot => { //Do Stuff })

  5. 发送消息:用户通过firestore发送新消息。在发送方,新消息通过 JS 本地呈现(不是通过实时侦听器)。 在接收端,新消息通过 2) 中描述的侦听器传递和显示

    const deliveryMSG = await db.collection('messages').doc(newID).set(newMsgObj); //渲染消息 renderMSG({id: newID, data: newMsgObj});

也许这不是处理这个问题的最优雅的方法,但它运作良好并且绝对安全。 如果您有任何额外的提示,我会很乐意阅读。