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-actionsgithub-actionsBot 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

vercelBot commented Jul 21, 2026

Copy link
Copy Markdown

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

ProjectDeploymentActionsUpdated (UTC)
specReadyReadyPreview, CommentJul 21, 2026 3:10pm

Request Review

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

Labels

documentationImprovements or additions to documentationsize/lteststooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@os-zhuang@claude