Skip to content

fix(service-automation): resolve triggering user's grants for runAs:'user' (#3356)#3389

Merged
os-zhuang merged 1 commit into
mainfrom
claude/runas-user-permissions-issue-138lrp
Jul 21, 2026
Merged

fix(service-automation): resolve triggering user's grants for runAs:'user' (#3356)#3389
os-zhuang merged 1 commit into
mainfrom
claude/runas-user-permissions-issue-138lrp

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

背景

修复 #3356(#1888 的后续)。自 #1888 起引擎已消费 flow.runAs(system 提权正常),但 runAs:'user' 的凭证传播是空心的:record_change 触发的 runAs:'user' 流,其数据节点(update_record 等)按只剩 member/everyone 兜底的零权限集用户执行——即便触发用户本身对目标对象有完整授权。病征分两副面孔:private 对象直接 403(not permitted for positions [org_member, everyone]);public_read_write 对象则 readonly/FLS 字段被静默剥离,节点仍报 success

根因

ObjectQL record-change hook session 只携带 userId,从不携带 writer 的 positions / permission sets;buildContext() 只能转发一个「空壳 positions + 永远缺省的 permissions」,而两端(trigger 的 buildContext 与 service-automation 的 resolveRunDataContext)各自「正确」,中间没人负责把触发用户的岗位/权限集查出来。注释承诺的「enforces RLS exactly as the user who made the change」因此落空。

采用 issue 首选方案(修实现):在运行建立时,用 REST 请求同一套解析逻辑解析触发用户的真实授权。

改动

  • @objectstack/core:把 resolveAuthzContext 中由 userId 驱动的解析抽出为新导出 resolveUserAuthzGrants(ql, userId, opts)——它是读取 sys_member / sys_user_position / sys_*_permission_set 并推导 positions、permission-set 名、platform_admin、posture 的唯一实现;HTTP 解析器改为委托它,行为逐字节不变(21 条契约测试仍全绿)。

  • @objectstack/service-automation:新增 AutomationEngine.setUserGrantsResolver,由插件桥接到 resolveUserAuthzGrants。当 runAs:'user' 运行的触发上下文未解析出授权(无 permissions)时,引擎在运行建立处(所有触发类型共享的单点)一次性解析出该用户的 positions + permission sets,注入每个数据节点的 ObjectQL context。已携带 permissions 的上下文(REST 触发、以及 ADR-0090 代理在 on-behalf-of 场景下始终非空的 ceiling)保持原样——绝不把被刻意收窄的身份重新放大;runAs:'system' 不变;解析失败故障安全(告警,保留裸用户,决不提权)。

  • @objectstack/trigger-record-change:buildContext 不再转发误导性的空壳 positions,只转发 userId + 租户,交由引擎权威解析。

未接入 ObjectQL 引擎时(裸引擎 / 测试)解析器不挂载,运行身份与改动前一致。

测试

  • resolveUserAuthzGrants 单测 + 与 resolveAuthzContext同源一致性对拍。
  • 引擎 resolve-at-setup 的护栏单测(已携带 permissions / system / 无 user / 解析失败 fail-safe / 未挂载)。
  • 插件桥接的集成测试:启动插件 + fake objectql,证明 runAs:'user' 运行最终把触发用户解析出的 positions + permission set 带进数据操作的 context(端到端验证 bridge)。
  • core / service-automation / trigger-record-change 三包全量测试通过(386 / 342 / 25)。

未在本 PR 覆盖(建议拆单)

issue 的次级问题——update_record 在 readonly 字段被剥离时仍记 success、缺可观测告警——是独立的可观测性增强(issue 亦标注「可拆单」)。本 PR 修复了授权用户的根因(权限集已正确传播,授权用户不再被剥离),该告警建议作为后续单独跟进。


Generated by Claude Code

…user' (#3356)

Follow-up to #1888. `runAs:'user'` record-change flows ran their data ops with a
zero-grant member/everyone principal — 403 on private objects, silent field
strips on public ones — because the ObjectQL hook session carries only a
`userId`, never the writer's positions/permission sets, and nothing resolved
them.

- core: factor the userId-driven core of `resolveAuthzContext` into a new
  exported `resolveUserAuthzGrants(ql, userId, opts)` (single reader of
  sys_member / sys_user_position / sys_*_permission_set); the HTTP resolver
  delegates to it, behaviour byte-identical.
- service-automation: add `AutomationEngine.setUserGrantsResolver`, wired by the
  plugin to `resolveUserAuthzGrants`. A `runAs:'user'` run whose trigger left the
  authz envelope unresolved now resolves the user's positions + permission sets
  once at run setup and threads them into every data node's context. Contexts
  that already carry `permissions` (REST / on-behalf-of agent ceiling) are left
  untouched; `runAs:'system'` is unchanged; a resolver error fails safe (warns,
  never elevates).
- trigger-record-change: stop forwarding the misleading half-populated
  `positions`; forward `userId` + tenant only and let the engine resolve.

Adds unit coverage (core resolver + engine resolve-at-setup guardrails) and a
plugin-wiring integration test that boots the plugin and proves the bridge
end-to-end.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SU5JeLSqHYqhw1P1vP82bH
@github-actions github-actions Bot added documentation Improvements or additions to documentation tests tooling labels Jul 21, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 3 package(s): @objectstack/core, packages/services, packages/triggers.

27 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:

  • content/docs/ai/actions-as-tools.mdx (via @objectstack/core)
  • content/docs/ai/knowledge-rag.mdx (via @objectstack/core)
  • content/docs/ai/natural-language-queries.mdx (via @objectstack/core)
  • content/docs/automation/webhooks.mdx (via @objectstack/core, packages/services)
  • content/docs/concepts/north-star.mdx (via packages/core)
  • content/docs/deployment/migration-from-objectql.mdx (via @objectstack/core)
  • content/docs/kernel/contracts/index.mdx (via @objectstack/core)
  • content/docs/kernel/runtime-services/audit-service.mdx (via packages/services)
  • content/docs/kernel/runtime-services/examples.mdx (via @objectstack/core)
  • content/docs/kernel/runtime-services/index.mdx (via packages/services)
  • content/docs/kernel/runtime-services/settings-service.mdx (via packages/services)
  • content/docs/kernel/services-checklist.mdx (via @objectstack/core)
  • content/docs/kernel/services.mdx (via @objectstack/core)
  • content/docs/permissions/authentication.mdx (via @objectstack/core)
  • content/docs/permissions/authorization.mdx (via packages/core)
  • content/docs/plugins/anatomy.mdx (via @objectstack/core)
  • content/docs/plugins/development.mdx (via @objectstack/core)
  • content/docs/plugins/index.mdx (via @objectstack/core)
  • content/docs/plugins/packages.mdx (via @objectstack/core, packages/services)
  • content/docs/protocol/kernel/config-resolution.mdx (via @objectstack/core)
  • content/docs/protocol/kernel/i18n-standard.mdx (via packages/services)
  • content/docs/protocol/kernel/index.mdx (via @objectstack/core)
  • content/docs/protocol/kernel/lifecycle.mdx (via @objectstack/core)
  • content/docs/protocol/kernel/plugin-spec.mdx (via @objectstack/core)
  • content/docs/releases/implementation-status.mdx (via @objectstack/core)
  • content/docs/releases/v12.mdx (via @objectstack/core)
  • content/docs/releases/v15.mdx (via @objectstack/core)

Advisory only. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs origin/main → pass the list as args.docs.

@vercel

vercel Bot commented Jul 21, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
spec Ready Ready Preview, Comment Jul 21, 2026 3:10pm

Request Review

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/l tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants