Declare what a per-viewer verdict rests on once, at the assignment matcher

AssignmentMatcher implements CacheableDependencyInterface so a consumer can merge "what a match rests on". Those three methods take no account, so they report the user cache context — which partitions per uid and never invalidates — and cannot name the account's own tag. Candidacy reads the account's roles, so every consumer was left to know that and add the tag itself.

Five did, each with its own comment saying why: WorkItemManager::checkActionAccess(), OperationAccessCheck, InstanceReadAccessCheck, InboxController::checkReassignAccess() and AssignmentCacheTrait.

The sixth already existed. OrchestraInteractionHandler::refuseSubmitThatCanDoNothing() disables the webform submit and warns the visitor, and declared that decision's cacheability by hand as two lines: the token list tag and the user context. The verdict it declares for is OperationResumer::checkCompleteAccess(), which reads four things — the token's state, the resolved tenant, the account's roles, and standing cover that lapses on the clock. Three of the four were undeclared, so a page cached while a stand-in could not yet complete the step kept telling them it was not theirs after their cover began, and kept offering the submit after it lapsed or their role was revoked. Not a bypass — preSave() re-checks — but the delegation case silently blocks the person brought in to cover.

The resumers carried no cacheability on the stated grounds that the answer is "never merged into a render array", which stopped being true when [#3621674] rendered it into a form.

The fix is one seam

AssignmentMatcher::getAccountCacheability(AccountInterface) returns the whole set for an account, and the five consumers merge that one thing instead of restating part of it. checkCompleteAccess() now carries it, and the webform handler merges whatever the verdict carried rather than naming a subset. The refusal is decided before its cacheability is applied, because deciding is what gathers it — one of the answers is which token is live.

OperationAccessCheckTest already asserted all three things the matcher now supplies, and stays green unchanged, which is what says the seam preserves the contract. A new test pins it at the matcher, so a consumer added later inherits the whole set by asking.

And two tenant checks, from the other direction

InstanceTenantAccessCheck and IncidentTenantAccessCheck both ended in setCacheMaxAge(0) — the safe answer to "what does this rest on?" and the expensive one, since it makes every page behind the check uncacheable in order to say the realm comes from the request. TenantContext is a declared cacheable dependency; the tree already treats it as one in TenantCacheContext, in the matcher, and in this very webform handler.

The second half is the part the max-age was hiding: the administrator branch declared cachePerPermissions() and the tenant branch declared nothing, so a verdict stored for an account without the permission would not have moved when it was granted. Removing one without the other would have been the wrong half, so both branches now share one dependency set.

A missed site of the lane [#3621292] closed.

Tests

Three, each proven red without its fix and green with it:

  • the matcher answers for an account, and the verdict carries exactly what the matcher gave it, so the two cannot drift;
  • the built webform form declares the realm, the account's tag and the actor context — asserted on the assignee's own build, the one a cache stores, since a refused build throws;
  • all six tenant verdicts (run and token through one check, incident through the other, each as an administrator and as a plain account) are cacheable, name the realm and declare the permission context — plus a second test that the confinement itself still refuses another realm, so the cacheability was not bought by allowing everything.

The tenant test creates user 1 first and uses it as the administrator, because a plain account created first would be the superuser and every verdict would take the administer branch and prove nothing.

Merge request reports

Loading
Loading