Problem/Motivation

A recurring request is to run Canvas Override without the Canvas patch (issue #3567225, which un-finals Drupal\canvas\Storage\ComponentTreeLoader). This issue records the outcome of a thorough investigation and ships the one safe improvement that came out of it.

Finding: a fully working per-content editor without the patch is not achievable from the module side. ComponentTreeLoader is a final class and six Canvas consumers type-hint the concrete class in their constructors (ClientDataToEntityConverter, ComponentTreeInputExtractor, ComponentTreeEditAccessCheck, ComponentInstanceForm, ApiAutoSaveController, ApiLayoutController). By PHP's type system, any class that changes the loader's behaviour fails those hints (TypeError on Canvas's own hot path), and any class that passes them cannot change behaviour. Registering a non-subclass under the loader's service id compiles but then TypeErrors at the first Canvas route. Two alternatives were evaluated and rejected: a backing canvas_page per node (tried, verified, and reverted in #3620603 — parallel storage, one-way sync, GET-side-effect creation) and forking Canvas's @internal controllers (~1.5k lines including the site-wide publish endpoint, a resync treadmill against an API with no stability guarantee). The durable fix is upstream: land #3567225, and ideally extract a ComponentTreeLoaderInterface and re-hint the six consumers.

Bug this issue fixes: on unpatched Canvas, Canvas Override already disables the per-content feature (the content type checkbox is greyed out with an explanation). But a bundle can still carry the enabled flag from a config import made on a patched site, and its Canvas / Canvas editor links still resolve. Canvas's ComponentTreeEditAccessCheck (behind _canvas_component_tree_edit_access on the editor and canvas.api.* routes) calls ComponentTreeLoader::load(), which throws \LogicException for any node bundle on unpatched Canvas. An uncaught exception in an access check is an HTTP 500 on every such request.

Steps to reproduce

  • Install Drupal CMS 2.1 (Drupal 11.4, unpatched Canvas 1.10.x) + Canvas Override.
  • Give a node bundle the canvas_override.enabled third-party setting and a field_canvas_layout field (as a config import from a patched site would).
  • As an administrator, request /canvas/api/v0/layout/node/{nid} (or open the Canvas editor). Result: HTTP 500, uncaught LogicException: For now Canvas only works if the entity is a canvas_page!

Proposed resolution

Retarget Canvas's ComponentTreeEditAccessCheck service — it is consumed only through the access_check tag, so nothing type-hints the concrete class — to a module check that composes Canvas's final check (its constructor is public) and converts the \LogicException into a cacheable AccessResult::forbidden() with an explanation. ~40 module lines, zero forked Canvas code. Behaviour is byte-identical for canvas_page and for any entity Canvas supports (on patched sites the injected loader is already the module subclass); only the unsupported-entity 500 becomes a clean 403. Verified on fresh Drupal CMS 2.1 (Drupal 11.4.6, unpatched Canvas): upstream check throws (500); the module check returns FORBIDDEN with the message and the API returns 403.

Remaining tasks

  • ✅ File an issue about this project
  • ✅ Addition/Change/Update/Fix to this project
  • ✅ Testing to ensure no regression
  • ✅ Automated unit/functional testing coverage
  • ➖ Developer Documentation support on feature change/addition
  • ➖ User Guide Documentation support on feature change/addition
  • ➖ UX/UI designer responsibilities
  • ➖ Accessibility and Readability
  • ✅ Reviewed by a human
  • ✅ Code review by maintainers
  • ✅ Full testing and approval
  • ✅ Credit contributors
  • ✅ Review with the product owner
  • ✅ Update Release Notes
  • ✅ Release canvas_override-1.0.0

User interface changes

  • N/A

API changes

  • On unpatched Canvas, the Canvas editor and canvas.api.* routes return 403 (was 500) for entities Canvas cannot edit.

Data model changes

  • N/A

Release notes snippet

  • Canvas Override now fails safely with a 403 (instead of a 500) when the Canvas editor or API is reached for an entity Canvas cannot edit on this site, e.g. a node bundle enabled via config import on an unpatched Canvas.
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

rajab natshah created an issue. See original summary.

  • rajab natshah committed daa83026 on 1.0.x
    fix: #3621557 Fail safely with a 403 instead of a 500 when Canvas cannot...
rajab natshah’s picture

Issue summary: View changes
Status: Active » 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.

rajab natshah’s picture

Issue summary: View changes

rajab natshah’s picture

Issue summary: View changes
Issue tags: +canvas_override-1.0.0

  • rajab natshah committed 3924aef5 on 1.0.x
    feat: #3621557 Option 2 - run without the Canvas patch by supplying...

  • rajab natshah committed 9231e712 on 1.0.x
    fix: #3621557 Let a node act as its own Canvas host entity so component...

  • rajab natshah committed d293e104 on 1.0.x
    fix: #3621557 Put a real CSRF token in the HTMX reset URL instead of the...

  • rajab natshah committed f876ea55 on 1.0.x
    feat: #3621557 Offer Canvas's link-to-field affordance on per-content...

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.