Skip to content

Require the publishable permission to write CWA content (#569) - #579

Merged
silverbackdan merged 3 commits into
mainfrom
feat/569-admin-only-content-writes
Oct 31, 2026
Merged

silverbackdan merged 3 commits into
mainfrom
feat/569-admin-only-content-writes

Conversation

@silverbackdan

@silverbackdan silverbackdan commented Oct 31, 2026 •

Copy link
Copy Markdown
Collaborator

Closes #569.

Once a non-admin could sign in, they could write CWA content straight through the API. The only guard was the template's catch-all IS_AUTHENTICATED_FULLY access rule.

What changes

  • A new metadata decorator, ContentWriteSecurityResourceMetadataCollectionFactory, at decoration priority -1. That makes it the outermost bundle factory, so it also sees the operations the other factories add (/move, uploads). It gives every POST/PUT/PATCH/DELETE operation of Page, Layout, Route, ComponentGroup, ComponentGroupOrder (the order PATCH), ComponentPosition, and every AbstractComponent and AbstractPageData subclass (project classes included) the security publishable.permission. When a class sets its own #[Publishable(isGranted:)], that is used instead, resolved the same way PublishableStatusChecker does it.
  • The check goes in security, not securityPostDenormalize. The expression needs no object, the same way PublishableStatusChecker evaluates it.
  • An operation's own security is ANDed with the permission. It is not left alone. Route's PATCH/PUT/DELETE carry read_route, and RoutableResourceMetadataCollectionFactory gives page and page data writes read_routable. Both are read checks, and both used to let any reader write. If an operation's own security had counted as an opt-out, those writes would have stayed open. Both checks, and routable_security on POST, keep applying.
  • Opt-out: extraProperties: [ContentWriteSecurityResourceMetadataCollectionFactory::OPT_OUT_EXTRA_PROPERTY => false] ('publishable_permission' => false), set on one operation or on #[ApiResource] for the whole resource. Form /submit (POST and the validating PATCH) opts out, so it stays public.
  • Reads are unchanged.

Tests

  • Security\ContentWriteSecurityTest: a ROLE_USER gets 403 on POST/PATCH/DELETE (PUT where the operation exists) for a dummy component, an html content, a page, a route, a layout, a component group, a component position, page data and a form, and on route generation, the order PATCH and /move. Anonymous gets 401. An admin can still create and update components, create pages and reorder groups. Users and anonymous callers can still submit forms.
  • A unit test for the factory.
  • Existing tests that wrote content as a ROLE_USER now log in as an admin. The test bundle's DummyPublishableComponent opts out, so the publishable tests for non-admin writers (and the anonymous POST access rule) still cover that logic.

Docs issue: components-web-app/docs#310

🤖 Generated with Claude Code

@codecov

codecov Bot commented Oct 31, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 97.06%. Comparing base (b17d7fa) to head (acee1f4).

Additional details and impacted files
@@            Coverage Diff            @@
##               main     #579   +/-   ##
=========================================
  Coverage     97.05%   97.06%           
- Complexity     5302     5318   +16     
=========================================
  Files           433      434    +1     
  Lines         13724    13755   +31     
=========================================
+ Hits          13320    13351   +31     
  Misses          404      404           
Flag Coverage Δ
phpunit 97.06% <100.00%> (+<0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@silverbackdan
silverbackdan merged commit 3e0b84c into main Oct 31, 2026
14 of 15 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.

Feature: support non-admin signed-in users (lock CWA content writes to admins by default)

1 participant