fix(runtime): resolve session identity for action body ctx.user (#2701)#2725
Merged
Conversation
chore: release packages (#2304) objectui@7a68d78f2a0c3c1f99dafd39b75b4f117a24917b
The POST /actions/:object/:action route called handleActions directly,
bypassing dispatch() → resolveExecutionContext. The action body sandbox's
ctx.user was therefore hard-coded to { id: 'system' }, so handlers could not
branch on the operator's identity/roles or enforce server-side ownership.
- dispatcher-plugin: action routes now dispatch through dispatch() (like the
automation/AI routes), resolving the session identity + per-project kernel
per request before the action body runs.
- http-dispatcher: handleActions builds ctx.user from the request's
ExecutionContext (id, email, positions/roles, permissions, tenantId),
matching the MCP runAction and record-change trigger paths; falls back to a
system principal only for a genuinely anonymous / self-invoked call.
- Tests: 4 regression cases incl. an end-to-end api-key → dispatch → action
pipeline asserting the principal reaches ctx.user (verified red without the fix).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Contributor
📓 Docs Drift CheckThis PR changes 1 package(s): 15 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
The
POST /actions/:object/:actionroute registered its handler on the raw HTTPserver and called
handleActionsdirectly, bypassingdispatcher.dispatch().Since
resolveExecutionContextonly runs insidedispatch(), the request'sexecutionContextwas never populated, andhandleActionsadditionally builtctx.userfrom_context.user/_context.userId— fieldsHttpProtocolContextnever carries — hard-falling to
{ id: 'system' }.Net effect: the action body sandbox's
ctx.userwas alwayssystem(no id,no roles), so handlers could not branch on the operator's identity/business roles
or enforce server-side ownership. Every other entrypoint (MCP
runAction,record-change trigger) already resolved the session correctly — the action route
was the single miss. (Closes #2701)
Fix
Route action requests through
dispatch()like the automation/AI routes, so theper-request pipeline resolves identity (and swaps to the per-project kernel)
before the action body runs — this eliminates the "bypass dispatch" workaround
rather than papering over it with a fallback.
dispatcher-plugin.ts—registerActionRoutesnow callsdispatch('POST', '/actions/…'). The scoped-URL:environmentIdrides onreq.paramsand is picked up byprepareResolverHints, exactly as automation does.http-dispatcher.ts—handleActionsbuildsctx.userfrom the resolvedExecutionContext(id,email,roles/positions= ADR-0090 business roles,permissions,tenantId), matching the MCP/record-change paths. Falls back to asystemprincipal only for a genuinely anonymous / self-invoked call. The stalekernel-swap comment is updated (the block is now an idempotent fallback for direct
handleActionscallers).No authoring change is required — handlers that always saw
ctx.user.id === 'system'now see the real caller.
Verification
http-dispatcher.test.ts, including an end-to-endcase: mint an api key →
dispatch('/actions/…')→resolveExecutionContext→action, asserting the principal reaches
ctx.user(iduser_9, notsystem).(
expected 'system' to be 'user_42','system' to be 'user_9', …), confirming thetests actually guard the bug.
dispatcher-plugin.routes+ready.integrationtests green (9/9); full
http-dispatcher.test.tsgreen (156/156).🤖 Generated with Claude Code