Problem/Motivation

A webform bound to an Orchestra step through the identity (task) doorway refuses the page that tells the submitter their work was saved. The submission is written, the step is completed and the run advances correctly; then the visitor is shown 403 Access denied for the step they have just completed.

Observed on a request-validation run. The submission saved at the same second as the refusal:

webform    Submission ... updated                   q=binding=orchestra-task:4246&token=...
orchestra  Token 4246 signaled; resuming from n_modify
orchestra  Token 4247 parked on node n_validate
access denied  /form/request-form?binding=orchestra-task:4246&token=...

The refusal names itself: AccessDeniedHttpException: The handle resolves to no active work item. in OrchestraInteractionHandler::refuseUnviewableStep().

Webform serves an inline confirmation by rebuilding the same form with the confirmation as its current page, so the handler's alterForm() runs once more on the way out of a successful submit. By then the step has been completed, so the handle no longer names an active work item. Under the task doorway checkViewAccess() is checkCompleteAccess(), which refuses exactly that. So the gate fires on the page whose whole job is to say the submission worked.

A first submission never meets this, which is what makes it look arbitrary: a start form carries no handle at all, and the gate returns on its first line when there is nothing to check. Every bound submit meets it, the instant it succeeds.

The bearer doorway is unaffected: possession of a capability link authorizes viewing, so its checkViewAccess() allows. That is why no existing test caught this - they all bind with a capability token.

The example webform this module ships, request_form in Orchestra Interaction Webform Examples, sets confirmation_type: inline, so the shipped example is the failing combination.

Proposed resolution

The identity doorway asks the question it means. While a step is open, one nobody may act on is one they may not read: unchanged. Once it is finished, ask who the step was rather than who may act on it, so whoever it was offered to or whoever completed it may still be shown it, and to anybody else it stays a step they were never shown.

Keying this on webform's confirmation page instead would fix one configuration only. Just the inline confirmation type sets that page; unticking Display confirmation when submission is updated, or choosing the message, modal or none type, leaves the visitor on the bound URL after a successful submit, where the refusal is unchanged. An operator toggling a checkbox would bring it back.

Who counts as having worked a step is already written down, in the rule that decides who may read a run afterwards: whoever holds a step, and whoever completed one. Both should share it rather than state it twice, so read access and this gate cannot come to disagree.

The handler still leaves webform's confirmation page alone, so that it carries no submit to disable and no message about a step that has moved on.

Remaining tasks

  • Answer checkViewAccess() from who the step was once it is finished, rather than from who may still act on it.
  • Share the rule that says who worked a step with the one that decides who may read the run, so the two cannot drift.
  • Return from alterForm() when webform is rendering its confirmation, so that page carries no submit to disable and no message about a step that has moved on.
  • Kernel coverage through the task doorway, which is the only one that reproduces it: the assignee who completed the step, the completer who was never assigned it, the assignee whose task another operator finished, a stranger still refused, and an anonymous visitor refused a step pooled to a role and then canceled - the one work item with neither an assignee nor a completer.

User interface changes

The submitter sees the webform's confirmation after updating a bound submission, instead of an access denied page.

API changes

None.

Data model changes

None.

AI-Generated: Yes (Claude Code was used to help draft this issue summary and to write the code. I reviewed both before posting.)

Issue fork orchestra-3624025

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Comments

mably created an issue. See original summary.

mably’s picture

Status: Active » Needs review
mably’s picture

Issue summary: View changes

  • mably committed 54a3aeae on 1.x
    fix: #3624025 A webform step completed through the task doorway answers...
mably’s picture

Status: Needs review » Fixed

Now that this issue is closed, review the contribution record.

As a contributor, attribute any organization that helped you, or if you volunteered your own time.

Maintainers, credit people who helped resolve this issue.