fix(workflow): PublishContext arg order + drop workspace-agnostic agent fallback

This commit is contained in:
matevip 2026-05-08 15:07:32 +08:00
parent 6bcf6b5ee4
commit ed64cda043
4 changed files with 18 additions and 12 deletions

View File

@ -104,8 +104,9 @@ public class WorkflowController {
}
WorkflowCompiler.Result result;
try {
// PublishContext is (workspaceId, publisherId) mind the order.
result = compiler.compile(row.getDraftJson(),
new PublishContext(0L, row.getWorkspaceId()), aclPort);
new PublishContext(row.getWorkspaceId(), 0L), aclPort);
} catch (vip.mate.workflow.compiler.WorkflowParseException e) {
// Malformed JSON / structurally invalid graph render as a
// single-error compile failure so the UI's existing errors

View File

@ -102,8 +102,10 @@ public class WorkflowResumeController {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(R.fail("revision " + run.getRevisionId() + " missing for run " + runId));
}
// PublishContext is (workspaceId, publisherId) mind the order;
// ACL resolution scopes by workspace.
WorkflowCompiler.Result compiled = compiler.compile(revision.getGraphJson(),
new PublishContext(0L, run.getWorkspaceId()), aclPort);
new PublishContext(run.getWorkspaceId(), 0L), aclPort);
if (!compiled.ok()) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(R.fail("revision graph failed to recompile on resume"));

View File

@ -32,18 +32,17 @@ public class DefaultAgentInvoker implements AgentInvoker {
@Override
public Long resolveAgentId(long workspaceId, String agentName) {
if (agentName == null || agentName.isBlank()) return null;
// Workspace-scoped only no fallback to a global lookup. The
// earlier "fall back to workspace-agnostic" branch let an old
// revision (or any code path that bypassed publish-time ACL)
// pull a same-named agent from a different workspace at runtime,
// which is exactly what tenant isolation forbids. The
// publish-time ACL layer is also workspace-scoped, so a draft
// referencing a foreign agent is rejected before it ever runs.
AgentEntity entity = agentMapper.selectOne(new LambdaQueryWrapper<AgentEntity>()
.eq(AgentEntity::getWorkspaceId, workspaceId)
.eq(AgentEntity::getName, agentName.trim())
.eq(AgentEntity::getEnabled, true));
if (entity == null) {
// Fall back to a workspace-agnostic lookup so global agents still
// resolve. This mirrors how the workflow ACL phase counts an agent
// as "resolvable" if it exists anywhere the user can see it.
entity = agentMapper.selectOne(new LambdaQueryWrapper<AgentEntity>()
.eq(AgentEntity::getName, agentName.trim())
.eq(AgentEntity::getEnabled, true));
}
return entity == null ? null : entity.getId();
}
}

View File

@ -134,8 +134,12 @@ public class WorkflowService {
throw new IllegalStateException("cannot publish workflow " + workflowId
+ " without a draft");
}
PublishContext ctx = new PublishContext(publisherId == null ? 0L : publisherId,
workflow.getWorkspaceId());
// PublishContext is (workspaceId, publisherId) mind the order.
// ACL validators read ctx.workspaceId() to scope agent / channel /
// employee resolution; passing the publisherId in that slot
// would silently let cross-workspace references through.
PublishContext ctx = new PublishContext(workflow.getWorkspaceId(),
publisherId == null ? 0L : publisherId);
WorkflowCompiler.Result compileResult = compiler.compile(draft, ctx, aclPort);
compileResult.requireOk();