Google cloud firestore 允许用户使用Firestore安全规则更新自己的文档(但不包括角色)

Google cloud firestore 允许用户使用Firestore安全规则更新自己的文档(但不包括角色),google-cloud-firestore,firebase-security,Google Cloud Firestore,Firebase Security,我的应用程序将用户的角色(订户、编辑或管理员)存储为Firestore用户集合中用户文档中的“角色”映射。角色映射如下所示: // Roles map for a subscriber { subscriber: true } // Roles map for a editor { editor: true } // Roles map for administrator { administrator: true } function isOwner(uid) { retu

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

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

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

// Roles map for administrator
{
administrator: true 
}
function isOwner(uid) {
    return (isSignedIn() && (request.auth.uid == uid));
}
我还可以根据需要在用户文档的角色映射中放置多个(或全新的)角色字段

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

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)
条件如下所示:

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

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

// Roles map for administrator
{
administrator: true 
}
function isOwner(uid) {
    return (isSignedIn() && (request.auth.uid == uid));
}
我相信这一部分是有效的,因为当我运行它时,只有它,它的工作

我怀疑我的问题可能是,如果其中一个角色字段(例如subscriber)在写入之前或之后不存在,则无法通过平等性检查。因此,我还尝试添加一个条件,以允许现有对象没有该字段,但它仍然不起作用:

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

我使用
writeFields
提供了一个更简单的解决方案,该方案可行,但不推荐使用writeFields,因此这不是一个长期的解决方案:

allow update: if isOwner(userUid) && !('roles' in request.writeFields)
同样,不推荐使用writeFields,因此不应使用它


更新2


此解决方案最终对我有效,将角色替换为与我的案例匹配的角色:

请求。资源。数据是一个映射类型的对象。正如您所说的,您可以确保属性始终存在,也可以在使用它们之前检查映射中是否存在该属性。对贴图对象执行
中的
操作将让您知道是否存在属性。有关更多详细信息,请参阅上的文档

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

您现在可以使用映射差异来解决此问题,而无需使用不推荐使用的“writeFields”

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

查看整个文档内容(可能是屏幕截图?)和整个规则(包括匹配规范)以及失败查询的代码会很有帮助。我们应该能够完全重现您所给出的情况,并看到它完全像您一样失败。另外,您是否在控制台模拟器中测试了这一点?它是否向您提供了一条更具体的错误消息,可能会指出具体失败的原因?谢谢您提出的好问题。我没有试过模拟器,当然应该试过。当我这样做时,它会给出错误:“对象上的属性管理员未定义”。这就解释了。我需要在角色映射中标识所有角色,但只为应分配给用户的角色设置“true”。此外,更新过程必须包括这些相同角色中的每一个,以便通过对每个角色的平等性检查(确保没有角色发生更改)。除非有另一种方法来写逻辑?我更新了原始帖子,加入了匹配规范。谢谢道格。在模拟器中,如果传入文档中未包含
角色
,则规则将跳过对角色属性的所有平等性检查(例如
request.resource.data.roles.administrator==resource.data.roles.administrator
)。但是,当
角色
包含在传入文档中时,检查就会运行。因此,由于只有管理员才应该在传入文档中包含角色,因此我将规则更改为
允许更新if:isOwner(userUid)&&request.resource.data.roles.administrator==resource.data.roles.administrator
,并且该规则有效(如果用户不添加角色,则跳过相等)。如果尝试引用不存在的文档属性,则会导致错误,并且您的规则将拒绝访问。这就是为什么在使用它之前应该检查它是否存在的原因。我使用了一个我不知道存在的规则,找到了一个更简单的解决方案:
writeFields
。现在我的规则很简单:
allowupdate:if-isOwner(userUid)&&!('roles'在request.writeFields中)
。奇怪的是,文档中没有对
writeFields
的引用。我搜索了一下,只在上提到了它,没有详细信息。writeFields没有文档记录,不推荐使用,您不应该再使用它了。它不能保证按你期望的方式工作。那太糟糕了。有没有类似的方法可以让我简单地检查传入文档是否有roles对象?