Skip to content

bpm: the inbox's available bucket lists candidate-user tasks, not only candidate-group (#8077) - #8136

Merged
ThuF merged 3 commits into
eclipse-dirigible:masterfrom
d-velev:issue-8077-candidate-users-inbox
Oct 26, 2026
Merged

ThuF merged 3 commits into
eclipse-dirigible:masterfrom
d-velev:issue-8077-candidate-users-inbox

Conversation

@d-velev

@d-velev d-velev commented Oct 26, 2026

Copy link
Copy Markdown
Contributor

Cause

A user task modelled with flowable:candidateUsers only - no candidate group, no assignee - was returned by neither inbox listing. TaskServiceImpl.prepareQuery built the "available" bucket (type=groups) from taskCandidateGroupIn(roles) alone, and the assignee bucket from taskAssignee, so nothing ever queried taskCandidateUser(...) (git grep taskCandidateUser over components was empty). The named candidates never saw the task, nobody could claim it, and the instance waited forever. The same bucket backs the inbox's claim/complete permission gate (getUserTaskIds), so the task was unreachable even by bare id. The same hole hid a task whose candidate users were added at runtime by a listener (addCandidateUser).

Change

prepareQuery now matches candidate-group OR candidate-user in one query:

return taskQuery.taskCandidateGroupIn(UserFacade.getUserRoles())
                .taskCandidateUser(UserFacade.getName());

Flowable ORs the two conditions and keeps the unassigned-only filter (confirmed in its MyBatis Task.xml mapper: with both set it emits ASSIGNEE_ is null AND (LINK.USER_ID_ = :user OR LINK.GROUP_ID_ IN (:groups))), so the available bucket is unchanged for group tasks and now also surfaces candidate-user tasks. The candidate user follows the REAL user (UserFacade.getName()), beside the groups it sits with - act-as moves only the assignee bucket, as before (the existing CLAIM rule in claimantFor is untouched).

Verification

  • BpmInboxCandidateUserIT (new, HTTP-level, untagged → smoke set): deploys a candidateUsers-only task, lists it in the available bucket (type=groups), then claims and completes it through the candidate. It fails without the fix (the listing returns the task nowhere - Expected: a collection with size <1>) and passes with it.
  • engine-bpm-flowable unit suite: 77 green.
  • BpmInboxConcurrentCompletionIT (the candidate-group listing): green - no regression to the group bucket from the added OR clause.
  • mvn formatter:validate clean (cache wiped).
  • Not run locally: the PostgreSQL leg and the javadoc release profile (no javadoc changed) - PR CI covers both.

Note - stacked on #8078

This branch is cut from #8078 (PR #8093, bpm: validate tenant on process-instance variable writes...), which is not yet merged. Until #8078 lands, this PR's diff also shows that commit; it reduces to the two files here once #8078 merges. Happy to rebase onto master after #8078 merges if preferred.

Fixes #8077

🤖 Generated with Claude Code

d-velev and others added 3 commits October 23, 2026 16:13
…ter retry (eclipse-dirigible#8078)

POST /bpm-processes/instance/{id}/variables and the SKIP action wrote to a
process instance of another tenant. BpmProviderFlowable validates the current
tenant on nearly every instance-scoped method, but addProcessInstanceVariable
and retryDeadLetterJob did not, so an operator of one tenant could set a
variable on - and plant the internal SKIP marker on - another tenant's running
instance given its id.

- addProcessInstanceVariable now calls validateProcessInstanceId first, so the
  variable endpoint and the SKIP write both refuse a foreign instance.
- retryDeadLetterJob now calls a new validateDeadLetterJob, which checks the
  dead-letter job belongs to the current tenant.
- BpmFlowableEndpoint maps the validator's failure to 404 (not a 500, and not
  disclosing that the instance exists for another tenant).

BpmnMultitenancyIT#testCrossTenantProcessVariableWriteIsRejected reproduces it:
tenant A starts an instance that parks at a user task, tenant B writes a
variable and posts SKIP against A's instance id. Before the fix the write
returned 200 and both the variable and the SKIP marker landed on A's instance;
after the fix both routes answer 404 and A's variables are unchanged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…y candidate-group (eclipse-dirigible#8077)

A user task modelled with flowable:candidateUsers only - no candidate group, no
assignee - was returned by neither inbox listing. TaskServiceImpl.prepareQuery
built the "available" bucket (type=groups) from taskCandidateGroupIn(roles) alone,
and the assignee bucket from taskAssignee, so nothing ever queried
taskCandidateUser(...). The named candidates never saw the task, nobody could
claim it, and the instance waited forever. The same bucket backs the inbox's
claim/complete permission gate (getUserTaskIds), so the task was unreachable even
by bare id.

prepareQuery now matches candidate-group OR candidate-user in one query -
taskCandidateGroupIn(getUserRoles()).taskCandidateUser(getName()). Flowable ORs
the two conditions and keeps the unassigned-only filter, so the available bucket
is unchanged for group tasks and now also surfaces candidate-user tasks. The
candidate user follows the REAL user (UserFacade.getName()), beside the groups it
sits with - act-as moves only the assignee bucket, as before.

Verified: BpmInboxCandidateUserIT (new, HTTP-level) deploys a candidateUsers-only
task, lists it in the available bucket, claims and completes it through the
candidate; it fails without the fix (the listing returns the task nowhere) and
passes with it. engine-bpm-flowable unit suite (77) green. BpmInboxConcurrent
CompletionIT (the candidate-group listing) still green - no regression to the
group bucket. formatter:validate clean.

Fixes eclipse-dirigible#8077

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@ThuF
ThuF merged commit bcf69bb into eclipse-dirigible:master Oct 26, 2026
16 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

inbox: a user task addressed by flowable:candidateUsers only (no candidate group, no assignee) is invisible to its candidates

2 participants