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

时间:2019-04-20 18:54:29

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

我的应用程序将用户的角色(订阅者,编辑者或管理员)存储为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

我使用writeFields found是一个更简单的解决方案,该解决方案有效,但是不建议使用writeFields,因此这不是一个长期解决方案:

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

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


更新2

此解决方案最终为我工作,用符合我的情况的角色替换了角色:https://stackoverflow.com/a/48214390/4407512

2 个答案:

答案 0 :(得分:1)

您现在可以使用地图差异来解决此问题,而无需使用不推荐使用的'writeFields'

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

答案 1 :(得分:0)

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

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