【问题标题】:Allow user to update his own document (but not the roles) using Firestore security rules允许用户使用 Firestore 安全规则更新他自己的文档(但不是角色)
【发布时间】:2019-09-10 14:28:56
【问题描述】:

我的应用程序将用户的角色(订阅者、编辑者或管理员)作为“角色”映射存储在 Firestore 的用户集合中的用户文档中。角色映射如下所示:

// Roles map for a subscriber
{
subscriber: true 
}

// Roles map for a editor
{
editor: true 
}

// Roles map for administrator
{
administrator: true 
}

我还可以根据需要在用户文档的角色映射中放置多个(或全新的)角色字段。

我希望用户能够在不更改角色映射的情况下更新他们的个人资料。因此,对于安全规则,我尝试通过检查用户是否是所有者以及之前和之后的角色都相同来确保这一点。但是我在尝试更新时总是遇到权限错误。规则如下:

match /users/{userUid} {
  allow update: if 
  (
    isOwner(userUid) &&
    (
      (request.resource.data.roles.administrator == resource.data.roles.administrator) &&
      (request.resource.data.roles.editor == resource.data.roles.editor) &&
      (request.resource.data.roles.subscriber == resource.data.roles.subscriber)
    )
  );
}

第一个isOwner(user) 条件如下所示:

function isOwner(uid) {
    return (isSignedIn() && (request.auth.uid == uid));
}

我相信这部分是有效的,因为当我只用它运行它时,它就有效。

我怀疑我的问题可能是在写入之前或之后不存在角色字段之一(例如订阅者)的情况下,它无法通过相等性检查。所以我也尝试添加一个条件来允许如果现有对象没有该字段,但它仍然不起作用:

match /users/{userUid} {
  allow update: if 
  (
    isOwner(userUid) &&
    (
      (!request.resource.data.roles.administrator || request.resource.data.roles.administrator == resource.data.roles.administrator) &&
      (!request.resource.data.roles.editor || request.resource.data.roles.editor == resource.data.roles.editor) &&
      (!request.resource.data.roles.subscriber || request.resource.data.roles.subscriber == resource.data.roles.subscriber)
    )
  );
}

提前感谢您的帮助。


更新 1

found 使用 writeFields 的一个更简单的解决方案,但 writeFields 已被弃用,因此这不是一个长期解决方案:

allow update: if isOwner(userUid) && !('roles' in request.writeFields)

同样,writeFields 已被弃用,因此不应使用。


更新 2

这个解决方案最终为我工作,用角色替换角色以匹配我的情况:https://stackoverflow.com/a/48214390/4407512

【问题讨论】:

  • 查看整个文档内容(可能是屏幕截图?)和整个规则(包括匹配规范)会很有帮助,以及失败的查询的代码。我们应该能够完全重现您所提供的情况,并看到它完全像您一样失败。
  • 另外,您是否在控制台模拟器中对此进行了测试?它是否为您提供了更具体的错误消息,可能表明具体失败的原因?
  • 感谢您提出的好问题。我没有尝试过模拟器,当然应该有。当我这样做时,它会给出错误:“物业管理员未定义对象”。所以这就解释了。我需要在角色映射中标识 all 角色,但只为应该分配给用户的角色设置“true”。此外,更新过程必须包括每个相同的角色,以便对每个角色的相等性检查(确保没有角色更改)将通过。除非有另一种写逻辑的方法?我更新了原始帖子以包含匹配规范。

标签: google-cloud-firestore firebase-security


【解决方案1】:

您现在可以使用地图差异来解决此问题,而无需使用已弃用的“writeFields”

allow update: if isOwner(userUid) && !('roles' in request.resource.data.diff(resource.data).affectedKeys());

【讨论】:

    【解决方案2】:

    request.resource.data 是一个 Map 类型的对象。正如您所说,您可以确保属性始终存在,或者您可以在使用之前检查属性是否存在于 Map 中。对 Map 对象的 in 操作将让您知道属性是否存在。有关详细信息,请参阅Map 上的文档。

    "administrator" in request.resource.data   // true if administrator property exists
    

    【讨论】:

    • 谢谢道格。在模拟器中,如果 roles 未包含在传入文档中,则规则将跳过对角色属性的所有相等检查(例如 request.resource.data.roles.administrator == resource.data.roles.administrator)。但是当roles 包含在传入文档中时,就会运行检查。因此,由于只有管理员应该在传入文档中包含角色,我将规则更改为 allow update if: isOwner(userUid) && request.resource.data.roles.administrator == resource.data.roles.administrator 并且它有效(如果用户不添加角色,则跳过平等)。
    • 如果您尝试引用不存在的文档的属性,则会导致错误,并且您的规则将拒绝访问。这就是为什么你应该在使用它之前检查它是否存在。
    • found 使用了一个我不知道存在的规则的更简单的解决方案:writeFields。现在我的规则很简单:allow update: if isOwner(userUid) && !('roles' in request.writeFields)。奇怪的是文档中没有提到writeFields。我搜索了一下,它只在index page 上被提及,没有详细信息。
    • writeFields 未记录,已弃用,您不应再使用它。不能保证按您期望的方式工作。
    • 那太糟糕了。有没有类似的方法可以让我简单地检查传入的文档是否有角色对象?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-06-19
    • 1970-01-01
    • 2020-02-07
    • 1970-01-01
    • 2023-03-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多