Hasura GraphQL Engine 中复用 Insert 权限做 Action 数据校验:RFC 方案解析与工程落地
2026/9/20 3:05:26 网站建设 项目流程
  • 后端
  • API网关
  • 数据库
  • GraphQL

【免费下载链接】graphql-engine

Blazing fast, instant realtime GraphQL APIs on all your data with fine grained access control, also trigger webhooks on database events.

项目地址:https://gitcode.com/gh_mirrors/gr/graphql-engine
点击查看免费下载

本文围绕 rfcs/reuse-insert-permission-in-action.md 这一 RFC 展开:当业务校验无法用 Hasura 的权限表达式或 Postgres 的 check 约束表达时,如何借助 Actions 调用外部 webhook 完成校验,同时不丢失原有 insert 权限(如{"channel": {"members": {"user_id": "x-hasura-user-id"}}}这类基于角色的行级约束)的复用能力。读完本文,你将掌握该 RFC 的问题背景、admin_only提案的完整语义,以及仓库中已落地的输入验证(input validations)机制如何最终解决了同一类问题。

一、RFC 要解决的核心矛盾

1.1 场景:用 Hasura 权限表达不了的校验

设想一张消息表,其结构如下(RFC 原例):

create table message ( id serial primary key, content text not null, channel_id integer not null references channel(id) ) create table channel (..) create table channel_members (..)

业务要求:user角色只有在其所属频道的成员列表中时,才能向该频道发消息。这一约束天然可以用 Hasura 的 insert 权限check表达式表达:

{"channel": {"members": {"user_id": "x-hasura-user-id"}}}

也就是说,用户只能向channel_membersuser_id等于当前会话变量x-hasura-user-id的频道发消息。

但问题来了:如果还要对message.content本身做复杂校验(例如调用外部内容审核服务判断是否违规),这类校验既写不进 Hasura 的权限布尔表达式,也无法用 Postgres 的 check 约束完成(check 约束只能引用当前行的列值,不能调用外部服务)。

1.2 矛盾点:为什么必须删掉 insert 权限

RFC 指出,在这种"权限 + 外部校验"并存的场景下,推荐的做法是使用Actions:开发者移除message表上user角色的 insert 权限,把全部校验逻辑搬进 action 的 webhook handler 里。

但这会带来一个明显的副作用:webhook 现在不仅要做 content 的校验,还必须自己重新实现{"channel": {"members": {"user_id": "x-hasura-user-id"}}}这一原本由 Hasura 权限系统负责的约束——等于把权限逻辑从声明式配置退化成 webhook 里的命令式代码,Hasura 权限系统的可复用性就此丢失。

那么,为什么必须移除 insert 权限,而不能"权限和 action 并存"?RFC 给出了关键原因:

如果 insert 权限被定义,insert_message变更就会被自动生成,任何持有user角色的人都能直接调用它,从而绕过对message内容的校验。

也就是说,只要user角色对message表有 insert 权限,GraphQL schema 的mutation_root上就会暴露insert_message字段,客户端可以绕过 action 直接插入数据,外部校验形同虚设。这正是"复用 insert 权限于 action"这一需求的原始动机。该问题最初在 Hasura 官方 Discord 频道被报告。

二、RFC 提案:insert 权限新增admin_only字段

2.1 提案语义

RFC 提出的解决方案是在 insert permission 中引入一个新字段admin_only

  • 行为一:当某角色对该表的 insert 权限设置了admin_only时,mutation_root不再为该角色生成该表的 insert 变更字段,普通客户端无法直接调用;
  • 行为二:但该 insert 变更在携带admin-secret的请求中仍然可用
  • 行为三:action 的 handler 可以在调用 GraphQL Engine 时带上admin-secret,并把x-hasura-role设置为用户的真实角色,从而以该角色身份执行 insert,使 insert 权限中定义的角色级约束(check 条件)照常生效——权限校验由 Engine 完成,webhook 只需专注内容校验。

2.2 与 GraphiQL"角色模拟"机制的关系

RFC 特别指出:当前 Engine 已经支持通过admin-secret+x-hasura-role在 GraphiQL 中模拟某个角色发起请求。因此admin_only的 insert 变更将同样允许"以admin-secret认证、以指定角色身份执行"的请求通过。

这意味着实现该提案时,需要在文档中明确说明:admin_secret配合x-hasura-role可以访问admin_only的插入变更,这是有意为之的信任模型——只有持有admin-secret的后端(如 action webhook)才能以受约束的角色身份执行插入,普通角色直接访问该字段则会被拒绝。

2.3 设计权衡

从 RFC 的表述可以提炼出该方案的核心权衡:

维度说明
安全性admin_only从 schema 层面隐藏了角色的 insert 字段,避免绕过外部校验
权限复用通过admin-secret+x-hasura-role继续使用声明式的角色 check 约束,webhook 无需重复实现权限逻辑
信任边界约束的正确性依赖admin-secret只被可信后端(webhook)持有
文档义务需要向开发者说明admin_only变更在 GraphiQL 角色模拟场景下同样可执行

三、仓库中的演进:从 RFC 到输入验证(Input Validations)落地

3.1 当前仓库中的实现现状

需要说明的是:在 graphql-engine 当前仓库的源码与文档中,admin_only字段并未以该名字落地(在server/src-libdocs中均未检索到admin_only)。从源码演进看,该 RFC 所提出的"insert 前执行外部 webhook 校验"诉求,最终由输入验证(Input Validations)机制在 v2.29.0 起落地实现。

该机制允许在 insert/update/delete 权限上挂载validate_input配置,把变更的输入参数在真正写库前路由到一个 HTTP webhook 做校验。其官方文档位于 docs/docs/schema/postgres/input-validations.mdx。

3.2 与 RFC 方案的关系

RFC 与落地机制解决的是同一类问题(复杂数据校验),但路径不同:

  • RFC 方案(admin_only):把"直接 insert"从角色 schema 中隐藏,将插入委托给带admin-secret的 action handler,让 Engine 代为执行角色约束——校验与插入分离在 action 的 webhook 与 Engine 两端;
  • 落地机制(validate_input):把校验 webhook 直接挂在权限上,由 Engine 在变更执行前先调用校验 webhook,校验通过后才开启数据库事务写入——校验与插入统一由 Engine 编排,无需隐藏 insert 字段,也就不需要admin_only的 schema 隐藏语义。

两者都需要一个共识前提:webhook 校验失败时请求被中止并返回错误。RFC 中"必须移除 insert 权限否则可被绕过"的担忧,在validate_input落地后由 Engine 的执行流程直接接管,无需再移除权限。

3.3 输入验证的配置方式(实战)

以 Postgres 为例,在 CLI 管理的 Metadata 文件metadata -> databases -> [database-name] -> tables -> [table-name].yaml中,可以这样为一个角色的 insert 权限挂上校验 webhook:

- table: schema: public name: products insert_permissions: - role: user permission: columns: [] filter: {} validate_input: type: http definition: url: http://www.somedomain.com/validateProducts headers: - name: X-Validate-Input-API-Key value_from_env: VALIDATION_HOOK_API_KEY forward_client_headers: true timeout: 5

各字段含义:

  • type:校验接口类型,当前支持http(webhook URL);
  • definition.url:必填,校验 webhook 的 URL;
  • definition.headers:可选,随请求发送的自定义头,value_from_env支持从环境变量取值;
  • definition.forward_client_headers:是否把客户端请求头转发给校验 webhook;
  • definition.timeout:webhook 超时时间(秒),可理解为该配置项的核心调优参数。

应用 Metadata:

hasura metadata apply

也可以通过 Metadata API 创建(对应pg_create_(insert|update|delete)_permission,见 server/src-lib/Hasura/RQL/DDL/Permission.hs 中buildInsPermInfo/buildUpdPermInfovalidateInput的解析):

POST /v1/metadata HTTP/1.1 Content-Type: application/json X-Hasura-Role: admin { "type": "pg_create_insert_permission", "args": { "source": "<db_name>", "table": "products", "role": "user", "permission": { "columns": "*", "filter": {}, "validate_input": { "type": "http", "definition": { "url": "http://www.somedomain.com/validateProducts" } } } } }

3.4 执行流程

官方文档明确了带校验的 insert 的执行顺序(摘自 docs/docs/schema/postgres/input-validations.mdx 的流程说明):

  1. 变更输入参数会先被发送到校验 webhook(对所有涉及的表都会执行);
  2. 数据库事务只在所有校验成功完成后才开始
  3. 任一 webhook 拒绝数据,请求被中止并返回错误信息。

这意味着校验 webhook 是写入路径上的强制门禁——与 RFC 中"action webhook 负责校验、Engine 负责权限"的分工相比,validate_input把两者统一在 Engine 的变更执行管线中。文档同时提示:涉及校验的变更可能比无校验变更更慢,webhook 执行时间是潜在瓶颈(受timeout限制)。

四、仓库中的测试验证:端到端校验 webhook

仓库的 API 测试套件中有与该主题直接对应的端到端测试:server/lib/api-tests/src/Test/Mutations/Insert/ValidationSpec.hs。

该测试在 Postgres、Citus、CockroachDB 三个后端上运行同一套用例,测试的建表与权限结构如下:

  • user表(id / name / email / phone_number),角色user_1拥有 insert 权限,并挂载/validateUser校验 webhook;
  • tweet表(id / content / user_id),角色user_1拥有 insert 权限,并挂载/validateTweet校验 webhook。

测试用例覆盖的校验行为包括:

用例校验规则期望错误
邮箱格式错误email 必须是合法邮箱Invalid email id "random email"
手机号格式错误phone_number 必须合法Invalid phone number "987654321"
关联插入超过限制一个用户最多关联 1 条 tweetOnly one tweet is allowed to be added with a user
tweet 内容超长content 不超过 30 字符Tweet should not contain more than 30 characters

测试通过携带X-Hasura-Role: user_1请求头发起变更,断言返回code: validation-failed错误,证明校验 webhook 在权限生效的同时被强制执行。

承载这些校验的测试 webhook 实现在 server/lib/test-harness/src/Harness/Webhook.hs 中,其runInputValidationWebhook起了一个本地 Spock 服务,提供/validateUser/validateTweet两个校验端点,从请求体$rows中提取数据后逐条执行校验逻辑,任一失败即返回校验错误。这一实现恰好演示了 RFC 中"action 的 handler / 校验 webhook 需要做内容级校验"的最小落地形态。

五、admin-secret+x-hasura-role:角色模拟的官方语义

RFC 方案成立的关键前提是"admin-secret+x-hasura-role可模拟指定角色执行请求"。这一机制在官方文档 docs/docs/auth/authentication/admin-secret-access.mdx 中有明确记载:

如果请求同时携带X-Hasura-Admin-Secret头,以及X-Hasura-Role等用户特定头(如X-Hasura-User-Id),Hasura GraphQL Engine 将使用该用户与角色对应的访问控制规则处理请求,而不是以 admin 身份处理。

这正是 RFC 中admin_only方案的核心依据:action 的 handler(持有admin-secret)可以"扮演"任意角色发起插入,从而让该角色在 insert 权限里声明的 check 约束(例如频道成员关系)由 Engine 强制执行。

官方文档同时给出了安全警示:admin-secret绝不能在面向用户的客户端暴露,否则恶意用户可以通过检查请求获取它——这与 RFC 方案中"admin_only的可达性依赖 admin-secret 只被可信后端持有"的信任模型完全一致。相关的会话变量传递机制(action 请求体中的session_variables,如x-hasura-user-idx-hasura-role)可参见 docs/docs/actions/action-handlers.mdx 中关于 action 请求负载的说明。

六、方案对比与选型建议

综合 RFC 与仓库现状,可以把两类实现路径放在一起对比:

维度RFC 方案:admin_only+ action落地机制:validate_input输入验证
校验执行者action 的 webhook handler挂在权限上的校验 webhook(Engine 统一编排)
insert 字段暴露角色 schema 中隐藏,仅admin-secret可访问正常暴露,但写库前必经校验门禁
权限约束复用依赖admin-secret+x-hasura-role角色模拟权限 check 与校验 webhook 并存,无需移除权限
信任边界要求admin-secret仅存于可信后端校验 webhook 必须被正确配置且可达
落地状态RFC 提案(当前仓库未以该字段名实现)已实现,文档见 input-validations.mdx,测试见 ValidationSpec.hs

选型建议

  • 如果你的场景只是"在插入/更新/删除前调用外部服务做内容校验",优先使用已经落地的validate_input机制,它无需隐藏 insert 字段,权限与校验天然共存;
  • 如果你的场景是"用 action 承担完整的数据写入(含内容处理),但希望角色级约束仍由 Engine 声明式执行",RFC 的admin_only思路依然有参考价值——它揭示了admin-secret+x-hasura-role角色模拟这一底层能力,可用于在 action handler 内以受约束角色身份执行插入;
  • 无论走哪条路,都要守住同一条底线:校验入口(webhook / action handler)与权限判定(Engine)不能被客户端绕过,这正是 RFC 最初强调"必须移除 insert 权限否则校验被绕过"的教训所在。

七、小结

reuse-insert-permission-in-action.md这份 RFC 记录了一个小而典型的工程问题:外部校验与声明式权限如何共存而不互相绕过。它提出的admin_only方案虽然未在当前仓库中以同名落地,但其问题分析、信任模型(admin-secret+x-hasura-role角色模拟)和"校验失败即中止"的执行语义,都直接反映在仓库现已实现的输入验证机制及其测试中。理解这份 RFC,有助于把握 Hasura 权限系统与 Actions 的分工边界,也能在遇到"权限表达不了 + check 约束做不到"的校验需求时,快速定位到正确的实现路径。

  • 后端
  • API网关
  • 数据库
  • GraphQL

【免费下载链接】graphql-engine

Blazing fast, instant realtime GraphQL APIs on all your data with fine grained access control, also trigger webhooks on database events.

项目地址:https://gitcode.com/gh_mirrors/gr/graphql-engine
点击查看免费下载

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询