PERMISSION_DENIED 不违反规则的 Firebase 更新结果 - 附解决方案

Firebase update result with PERMISSION_DENIED without breaking the rules - with solution

我认为这可能是 update() 函数中的错误,在 Firebase 中特定使用规则。

我对 profile 节点有以下规则:

"profile": {
  "$appUserId": {
    ".read": "auth.uid !== null",
    ".write": "!newData.child('photo').exists()"
  }
}

(为清楚起见,我省略了 .write 规则中的身份验证)

用户 profile 包含 photo 但只能通过后端更新。客户无法设置此信息。

我正在使用 update() 函数来设置配置文件数据。当我创建配置文件(新节点)时,一切正常。但是配置文件是在每个 update() 调用结果中创建的 PERMISSION_DENIED。我是在一次调用中更新一个字段还是多个字段都没有关系。自然地,传入对象中没有 photo 键。此外,set() 函数在相同条件下完美运行(但删除任何其他字段 - 包括 photo)。

当我删除 .write 规则(并只保留身份验证检查)时,它正在工作。所以我开始怀疑。对我来说,在 update() 调用期间,它看起来像是在内部读取现有数据,将其与传入数据合并,用新值覆盖旧值,然后尝试将其全部保存。在它实际保存数据之前有一个规则检查所以 - 在这一点上 - 它会导致错误。我想它应该在合并数据之前进行规则检查(假设它像我上面描述的那样工作)。

编辑。

重现问题:

1) 设置代码:

var appId = 'APP ID', appUserIdKey = 'test'; //appUserIdKey can be whatever you want

//data to be saved
var profileData = {
    name: "Pawel"
    age: 31,
    photo: 'image.jpg'
};

2) 由于在我的环境中只有后端使用服务密钥将数据保存到 profile/[appUserIdKey]/photo,因此您必须暂时删除 .write 条规则。

现在规则应该是这样的:

"profile": {
  "$appUserId": {
    ".read": "auth.uid !== null",
    ".write": "auth.uid !== null"
  }
}

3) 运行 创建配置文件的代码

var ref = new Firebase('https://'+appId+'.firebaseio.com/profile/' + appUserIdKey); 
ref.update(profileData);

4) 现在,恢复规则。

"profile": {
  "$appUserId": {
    ".read": "auth.uid !== null",
    ".write": "(auth.uid !== null) && (!newData.child('photo').exists())"
  }
}

5) 再次尝试更新个人资料:

var ref = new Firebase('https://'+appId+'.firebaseio.com/profile/' + appUserIdKey); 
ref.update({'age':18});

您将收到一条错误消息

6) 将规则更改为以下内容:

"profile": {
  "$appUserId": {
    ".read": "auth.uid !== null",
    ".write": "(auth.uid !== null) && (!newData.child('photo').exists()) || newData.child('photo').val() == data.child('photo').val())"
  }
}

现在有效。

此问题的解决方法 是扩展规则,因此如果旧值等于新值,它将 return 为真:

"profile": {
  "$appUserId": {
    ".read": "auth.uid !== null",
    ".write": "(!newData.child('photo').exists() || newData.child('photo').val() == data.child('photo').val())"
  }
}

不过,我觉得不应该这样,应该改一下。

documentation for newData 说:

A RuleDataSnapshot corresponding to the data that will result if the write is allowed.

因此它不仅包含更新,还包含数据,因为如果允许此写入,它将存在于该位置。