安全规则中没有明确的方式来验证正在发生的更新。但是您可以做的是在写操作之前和之后验证文档中的数据。通过比较这两者,并了解文档可以包含哪些字段,您可以确保只有特定字段可以更新。
我经常在我的安全规则中使用这个小助手功能:
function isUnmodified(key) {
return request.resource.data[key] == resource.data[key]
}
顾名思义,它确保在此写入请求中不修改某个键/字段。例如,此规则只允许用户更新他们的个人资料文档,只要他们不修改 name 字段(除非他们是管理员):
allow update: if isAdmin(request) ||
(request.auth.uid == uid && isUnmodified(request, resource, 'name'));
我也有这个辅助函数,它检查特定字段是否存在:
function isNotExisting(key) {
return !(key in request.resource.data) && (!exists(resource) || !(key in resource.data));
}
这很重要,因为有时您希望允许一个字段只写入一次,或者只允许在它已经存在的情况下对其进行更新。有时我为此使用isNotExisting,但我发现自己这些天更多地使用更精细的操作(create、update)而不是聚合write 规则。
最后,您可以要求某些字段,如以下创建规则:
allow create: if request.auth.uid == uid &&
request.resource.data.keys().hasOnly(['lastIndex', 'lastUpdated']) &&
request.resource.data.keys().hasAll(['lastIndex', 'lastUpdated'])
因此,如果用户指定了lastIndex 和lastUpdated 字段,则他们只能创建配置文件文档。如果他们指定了任何额外的字段,或者指定了更少的字段,则创建将被拒绝。
现在有了这些知识,我们可以回到您的需求,看看如何实现它。如前所述,您需要在每个单独的字段上做出声明,而不需要在其中使用通配符。因此,如果您的文档包含三个字段(field1、field2 和 field3),它们必须全部存在,并且用户只能更新 field2,则类似于:
allow update: if request.resource.data.keys().hasAll(['field1', 'field2', 'field2']) &&
isUnmodified('field1')) && isUnmodified('field3'));
现在可能有一种更短的方法来执行此操作,方法是使用此处显示的 set 和 map diff 操作:https://firebase.google.com/support/release-notes/security-rules#february_13_2020 like:
// This rule only allows updates where "a" is the only field affected
allow update: if request.resource.data.diff(resource.data).affectedKeys().hasOnly(["a"]);