流水线操作的安全规则

借助流水线操作规则,您可以匹配现有规则引擎的过滤功能。虽然流水线操作提供了一组丰富的功能,但规则引擎仅限于识别简单过滤条件,以确保查询的可满足性和安全性。

版本和模式要求

本文档适用于原生模式下的 Firestore 企业版。

支持的过滤表达式

如需通过规则限制查询,查询必须使用标准比较运算符与常量进行比较。规则引擎可识别以下过滤条件类型:

  • 等式和不等式:eq、neq。
  • 比较:gt、gte、lt、lte。
  • 成员:in、arrayContains

下面是一些示例:

  • where(eq("foo", 2))
  • where(lt("foo", 2))
  • documents("/user/1", "/user/2").where(...)

请求属性

您可以继续使用 request 对象来验证身份验证和查询上下文,不过流水线不支持标准查询中的某些属性。

支持的属性

新版引擎继续支持以下属性:

  • request.auth:访问用户 UID 和 token 数据。
  • request.method:标识操作(例如 get、list)。
  • request.path:所访问资源的路径。
  • request.time:请求的服务器端时间戳。

不支持的属性

由于在多阶段查询中确定这些值的复杂性,流水线规则检查不支持 request.query 属性(例如 limit、offset 和 orderBy)。

流水线阶段处理和权限

不同的流水线阶段会映射到安全规则中的特定粒度操作:

  • allow list 权限:由 collection()、collectionGroup() 和 database() 阶段触发。
  • allow get 权限:由 documents() 阶段触发,该阶段的处理方式与批量 get 操作类似。
  • allow create 权限:由 insert() 阶段以及创建新文档时的 upsert() 阶段触发。
  • allow update 权限:由 update() 阶段触发,以及在更新现有文档时由 upsert() 阶段触发。
  • allow delete 权限:由 delete() 阶段触发。
  • allow write 权限:适用于所有 DML 操作(insert()、update()、upsert() 和 delete())。
  • 字段修改阶段:规则仅对存储的数据进行操作,而不对派生值进行操作。如果流水线包含修改字段(例如 AddFields、ReplaceWith、Select)的阶段,则规则引擎在遇到该阶段后会停止应用过滤条件限制。
  • 字面量阶段:literals() 阶段不从数据库读取数据,但可能会产生费用。为防止滥用,它必须与另一个可通过规则验证的阶段(例如 collection())配对。

数据操纵语言 (DML) 阶段和安全规则

执行数据操纵语言 (DML) 阶段(insert()、update()、upsert() 和 delete())时,安全规则评估权限的方式与读取查询不同:

  • 逐文档规则评估:与读取流水线不同,DML 操作会针对每个单独的文档变更评估安全规则,而读取流水线需要预先使用过滤表达式在规划时限制查询。
  • 规则变量:
    • request.resource:包含 insert()、update() 和 upsert() 操作的建议文档数据。
    • resource:包含修改前存储在数据库中的当前文档数据(适用于现有文档的 update()、delete() 和 upsert())。

DML 的系统限制和执行行为

使用安全规则运行 DML 操作时,请注意以下系统限制和执行行为:

get() 和 exists() 的 20 次查找限制

Firebase 安全规则允许在整个请求中最多执行 20 次文档查找(get() 或 exists())。

当 DML 流水线在显式事务之外运行时,文档会分批处理。由于每个批次都在自己的事务中执行,并且文档可能会在批次之间并发修改,因此系统会针对每个写入批次重新评估安全规则和文档查找。

即使您的安全规则是针对单个静态文档进行评估,例如检查用户是否为管理员:

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /posts/{postId} {
      allow write: if get(/databases/$(database)/documents/users/$(request.auth.uid)).data.isAdmin == true;
    }
  }
}

每个文档写入批次都会执行一次查找。因此,在写入所有可能的文档后,请求达到 20 次查找限制,流水线失败并显示 PERMISSION_DENIED 错误。此外,每个写入批次执行的每次查找都会产生各自的读取费用。

部分成功和 PERMISSION_DENIED

在事务外部执行时,DML 操作会按顺序批量提交写入。如果某个批次失败(原因可能是文档不符合规则条件,也可能是超出了 20 次查找的限制),流水线会停止并返回 PERMISSION_DENIED 错误。

不过,已提交的批次不会回滚。数据库仍处于部分更新状态。由于客户端收到的错误不包含已修改文档的列表,因此您必须查询数据以检查哪些写入操作成功。

使用 Cloud Audit Logs 诊断规则失败问题

出于安全原因,面向客户端的 PERMISSION_DENIED 错误不会披露哪个安全规则条件失败或是否达到了 20 次查找限制。

如需诊断规则评估失败问题,项目管理员可以在 Google Cloud 控制台中(在 Firestore > 监控下或在 Logs Explorer 中)检查 Cloud Audit Logs,以查看详细的 EvaluationError 条目。