Firestore安全规则:更新时request.resource.data.<prop>会发生什么?

Dau*_*eDK 14 firebase google-cloud-firestore firebase-security-rules

我的团队最近讨论过这个问题,似乎无法确定实际/预期的行为:

如果您有以下安全规则:

match /categories/{document=**} {
    allow update: if request.auth.uid != null
    && request.resource.data.firstName is string
    && request.resource.data.lastName is string;
}
Run Code Online (Sandbox Code Playgroud)

然后使用以下数据从前端到/ categories /创建更新语句:

{
   firstName: 'A valid firstName'
}
Run Code Online (Sandbox Code Playgroud)

那么安全规则应该通过还是失败?

参考文档中,它说

开发人员提供的数据显示在request.resource.data中,这是一个包含字段和值的映射.资源中存在的请求中未提供的字段将添加到request.resource.data

相关问题:

  1. 这是否意味着成功/失败取决于节点中的现有数据?
  2. 如果有人尝试使用安全规则中未指定的数据进行更新,即会发生什么情况 {age: 28}
  3. 验证更新数据的推荐方法是什么?

问题3有更多细节(架构问题) 假设你有这样的模型:

interface Category {
  firstName: string;
  lastName: string;
  age?: int;
  groupId?: string;
}
Run Code Online (Sandbox Code Playgroud)

现在我们创建一个像这样的安全规则:

match /categories/{document=**} {
    allow update: if request.auth.uid != null
    && request.resource.data.firstName is string
    && request.resource.data.lastName is string;
    && request.resource.data.age is int;
    && request.resource.data.groupId is string;
}
Run Code Online (Sandbox Code Playgroud)

然后我们有以下场景,据我所知: 在此输入图像描述

这些场景都不适合可选属性.因为如果必须提供所有属性(如方案1中所示),那么它实际上不是可选属性.如果您不提供它们,就像在方案2​​中那样,它会失败.

也许我在这里遗漏了一些东西,关于如何使用写入firestore的可选属性来验证数据的基本指南?

可选参数的安全规则,如下所示:

match /categories/{document=**} {
   allow update: if request.auth.uid != null
   && request.resource.data.firstName is string
   && request.resource.data.lastName is string;
   && request.resource.data.age is int; // ignore if NOT provided
   && request.resource.data.groupId is string; // ignore if NOT provided
}
Run Code Online (Sandbox Code Playgroud)

Ros*_*des 8

那么安全规则应该通过还是失败?

如果要更新的文档已经有一个lastName字段,并且该字段是,则该更新将成功string。(我假设您在通过身份验证时正在运行此更新,以便request.auth.uid != null返回true)

回答相关问题:

  1. 是的,有时可能取决于节点上的现有数据。
  2. 如果该文档已经有firstNamelastName设置,则添加age字段将成功。请注意,该规则仅检查这两个值是否为字符串。它没有指定文档中的字段不能超过2个。
  3. 这个问题听起来有点宽泛(有点像XY问题)。请说明您要执行哪种更新验证。根据您的问题,我已经知道您要尝试什么,但是我想100%确定。

更新

从更新后的问题3中我了解到,仅当用户提供名字和姓氏时,才希望更新文档。age和groupId是可选的。

要做到这一点,你可以检查,如果这request.resource.data.firstName不是一个已经使用的数据库:resource.data.firstName != request.resource.data.firstName。因此,您的安全规则如下所示:

match /categories/{document=**} {
   allow update: if request.auth.uid != null
   && (request.resource.data.firstName is string && resource.data.firstName != request.resource.data.firstName)
   && (request.resource.data.lastName is string && resource.data.firstName != request.resource.data.firstName)
   && request.resource.data.age is int
   && request.resource.data.groupId is string
}
Run Code Online (Sandbox Code Playgroud)

现在有了这些规则,对此数据的更新将失败:

{
   firstName: 'A valid firstName'
}
Run Code Online (Sandbox Code Playgroud)

虽然这三个将成功:

{
   firstName: 'A valid firstName',
   lastName: 'A valid lastName'
}

{
   firstName: 'A valid firstName',
   lastName: 'A valid lastName',
   age: 20
}

{
   firstName: 'A valid firstName',
   lastName: 'A valid lastName',
   age: 20,
   groupId: 'groupId'
}
Run Code Online (Sandbox Code Playgroud)

更新2:要具有agegroupId作为可选字段,请使用OR运算符和hasAll()函数检查请求是否具有以下字段:

match /categories/{document=**} {
   allow update: if request.auth.uid != null
   && (request.resource.data.firstName is string && resource.data.firstName != request.resource.data.firstName)
   && (request.resource.data.lastName is string && resource.data.firstName != request.resource.data.firstName)
   || (request.resource.data.keys().hasAll(['age']) && request.resource.data.age is int)
   || (request.resource.data.keys().hasAll(['groupId']) && request.resource.data.groupId is string)
}
Run Code Online (Sandbox Code Playgroud)