Firestore 规则和使用电子邮件密钥查询文档映射以与用户共享数据

ral*_*r80 2 firebase firebase-security google-cloud-firestore

用例摘要

  1. 用户 A 创建一个故事
  2. 用户 A 通过电子邮件(通过云功能发送)与未知(向应用程序)用户 B 分享故事
  3. 用户 B 收到有关该故事的电子邮件
  4. 用户 B 访问应用程序并创建一个新帐户
  5. 用户 B 查看/阅读用户 A 创建的故事

注意:故事只能被分享给谁或由谁创建

我正在构建一个基于角色的访问系统。我一直在查看基于角色的访问 firestore 文档,但我遗漏了一件。

考虑一个故事,该故事只能由已共享该故事的用户阅读。大多数示例包括 firestore 示例使用UIDhas 密钥来识别共享用户。但是,该用户当前可能不是 firebase 应用程序的用户,另外用户如何分配该 UID。

故事数据

{
  title: "A Great Story",
  roles: {
    aliceUID: {
      hasRole: true,
      type: "owner",
    },
    bobUID: {
      hasRole: true,
      type: "reader",
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

故事查询

firebase.firestore().collection('stories').where(`roles.${user.uid}.hasRole`, '==', true)
Run Code Online (Sandbox Code Playgroud)

第二部分可能通过维护一个单独的用户集合来解决,然后您可以从他们的电子邮件地址中找到用户,但这并不能解决从未登录过的用户。

打算分享故事的用户可以添加具有电子邮件地址的用户。然后使用 firebase 函数,我们可以发送电子邮件通知用户共享故事,用户可以登录应用程序并阅读该故事。

如果我们继续使用这种方法,那么您将没有 UID,而只有一个电子邮件地址作为密钥。

故事数据

{
  title: "A Great Story",
  roles: {
    alice@yahoo.com: {
      hasRole: true,
      type: "owner",
    },
    bob@gmail.com: {
      hasRole: true,
      type: "reader",
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

故事查询

firebase.firestore().collection('stories').where(`roles.${user.email}.hasRole`, '==', true)
Run Code Online (Sandbox Code Playgroud)

更新的 Firestore 规则- 来自文档

function getRole(rsc) {
  // Read from the "roles" map in the story document.
  return rsc.data.roles[request.auth.uid] || rsc.data.roles[request.auth.token.email];
}
Run Code Online (Sandbox Code Playgroud)

我无法使电子邮件查询工作。这个SO问题提到

不幸的是,点不允许作为地图键。所以电子邮件地址将不起作用。

我不明白为什么这会在规则方面产生冲突。它确实会导致一个可能无效的 where 子句

例如

.where(`roles.${user.email}.hasRole`, '==', true) -> .where(`roles.bob@gmail.com.hasRole`, '==', true)
Run Code Online (Sandbox Code Playgroud)

这看起来像无效的 JS 并且不幸的是[并且]是无效的字符所以我们不能这样做

.where(`roles[${user.email}]hasRole`, '==', true)
Run Code Online (Sandbox Code Playgroud)

我在这个Firebase 演讲中看到的最后一件事是使用类似的东西来转义电子邮件地址

function encodeAsFirebaseKey(string) {
  return string.replace(/\%/g, '%25')
    .replace(/\./g, '%2E')
    .replace(/\#/g, '%23')
    .replace(/\$/g, '%24')
    .replace(/\//g, '%2F')
    .replace(/\[/g, '%5B')
    .replace(/\]/g, '%5D');
};
Run Code Online (Sandbox Code Playgroud)

这似乎修复了查询where子句,它是一个有效的数据结构,但它不是一个有效的 Firestore 规则,这意味着它没有真正的安全实施。

关于如何实现这一点的任何想法?请包括有效的数据结构、firestore 规则和查询。我已经展示并看到了许多示例,其中三个示例中的两个都是无效的解决方案。

谢谢!

ral*_*r80 5

基本问题是我不知道如何正确地制定有效的查询。事实证明,您不需要在一行中创建查询。

您可以使用FieldPath来构造您的查询参数。

var path = new firebase.firestore.FieldPath('roles', email ,'hasRole');
firebase.firestore().collection('stories').where(path, '==', true)
Run Code Online (Sandbox Code Playgroud)

这解决了原始丢失的部分。