I filed this issue has a security issue first and got the ok to file it as a public issue since the configure any layout permission is a restrict access permission. @see https://www.drupal.org/drupal-security-team/security-advisory-process-an...
Problem/Motivation
Steps to reproduce
1. Install the Layout Builder module and enable defaults and overrides for the article content type
2. Create a unpublished article node
2. Login as user with "configure any layout" permission
3. View node view page node/1
4. Get "access denied"
5. Go to node/1/layout
Expected result:
403
Actual result:
The Layout Builder representation of the node, with all field values displayed
Proposed resolution
User must have View access to an entity to create/update a layout override.
Remaining tasks
N/A
User interface changes
N/A
API changes
N/A
Data model changes
N/A
Release notes snippet
Previously, users who didn't have access to view individual entities were granted access to configure the layout for that entity (if per-entity layout configuration was enabled). This implicit access has been removed. Site owners should ensure that all content editor roles have access to view the content that they are configuring the layout for.
| Comment | File | Size | Author |
|---|---|---|---|
| #48 | 3028490-access-48-PASS.patch | 9.48 KB | tim.plunkett |
| #48 | 3028490-access-48-FAIL.patch | 1.83 KB | tim.plunkett |
| #41 | 3028490-access-fix-41-interdiff.txt | 1.18 KB | tim.plunkett |
| #41 | 3028490-access-fix-41-D86.patch | 9.61 KB | tim.plunkett |
| #41 | 3028490-access-fix-41-D87.patch | 8.86 KB | tim.plunkett |
Comments
Comment #2
tim.plunkettComment #3
tedbowRunning tests
Comment #5
r.aubin commentedTested and the passing patch worked for me. Note that the steps to reproduce did not include enabling Layout builder for your type of content as well as per-content-item layouts. These 2 checkboxes are found on the "Manage Display" tab of your content type configuration.
/admin/structure/types/manage/content_type_name/displayComment #6
r.aubin commentedComment #7
tim.plunkettAh yes, these STR predate that opt-in checkbox.
Comment #8
xjmJust documenting here that @tedbow and @tim.plunkett worked on the patch for this issue in the original private issue, so it has had peer code review there as well. Updating issue credit.
Comment #9
xjmAt a minimum, the removal of this method should have a CR. Ideally, it would be deprecated instead of removed. https://www.drupal.org/core/deprecation#internal
When we are considering making internal BC breaks, the question shouldn't be "Is there any reason not to get rid of this?" There are two cases where we should break BC instead of deprecating:
I don't think this method meets either of those criteria. It seems fairly harmless to me, so I'd propose just deprecating it and the EFM service injection. Another advantage of that is that the patch gets a lot smaller.
Comment #10
xjmWhile mulling #9, I thought of something that actually sort of trumps the discussion: This is a security issue. It's a public one because it's an administrative access bypass, but still a security issue. For that reason, we should backport this to 8.6.x; otherwise, 8.6.x will have a publicly disclosed administrative access bypass until its EOL in December. And in order to backport it to a patch release, we should make the patch as non-disruptive as possible (meaning avoiding even internal breaks like constructor service fixes).
Comment #11
xjmAlso needs a release note snippet (basically a sentence linking the CR).
Comment #12
xjmThe current patch does not apply to 8.6.x, but maybe a minimal-er version will. Otherwise we can create the 8.6.x patch after we've finalized the 8.7.x one.
Comment #13
tim.plunkettThis is the smallest possible change.
Still needs CR.
Should apply to both branches
Comment #14
tim.plunkettComment #16
tim.plunketts/discard_changes/cancel for the 8.6 patch, see #3025870: Cancelling changes in the Layout Builder UI should use a confirmation form.
Comment #17
xjm8.6.x is failing though:
Comment #18
xjmOh, I see this is what is mentioned in #17. So NR for he 8.7.x patch and then a backport.
Comment #19
xjmSince this would be a critical issue for a stable module, marking as a stable blocker.
Comment #20
kristen polThanks for the patch. One thing I noticed that maybe should be added?
Add another 'Access denied' assertion for layout page?
==
I tested this and it is showing access denied for both node/1 and node/1/layout now as expected.
Marking back to "Needs work" in case the test should be updated.
Comment #21
tim.plunkettGood point @Kristen Pol, idk why I didn't add that second check in the first place.
Here's an update 8.7 and 8.6 patch
Comment #23
tedbowLooks good!
Comment #24
tim.plunkettAdded a CR https://www.drupal.org/node/3032823 and wrote a release notes snipppet
The 8.7 patch needed a reroll, the 8.6 patch did not but reuploading anyway just to be safe.
Comment #25
tedbowThe CR looks good. and the rerolls looks good.
RTBC! 🤞
Comment #26
tedbowComment #27
tim.plunkettDiscussed with @xjm and @tedbow, specifically about why we need the
elsestatement.Turns out, we don't.
$entity_type->hasLinkTemplate('canonical')is checked before entering into this loop.And the canonical link is used to build the path for this route below.
Comment #28
phenaproximaNice change. This seems a lot less brittle!
Nit: I think it's possible to just do
new ContentEntityType([...]), and not mock anything here. Might be a bit easier to read...but this will work too. That's why it's a nitpick. :)Comment #30
tim.plunkettUghh posted the patch for the wrong version.
#28.2 Yes I considered that, but I decided mocking the specific methods instead of messing with annotation-based array structure was cleaner.
Comment #32
tedbowThe test fail in the previous comment is because of Drupalci weirdness. Submitted retest.
This looks good. RTBC 🎉(drupalci will push back if another test fail.)
I manually tested this with node and user profiles.
Users who don't have access the canonical route/view access can't access the /layout page.
Comment #35
xjmOK, well, those look like real fails... apparently we do need those if checks for uninstallation an may need the fallback for whatever entities have it as NULL. :) And an inline comment explaining about those entities that don't have routes.
Comment #36
tim.plunkettI was slightly wrong in #27. Turns out that saying you have a canonical route and having a canonical route are different things.
Comment #37
tedbowLooks good!
Comment #38
xjmSo what are the cases where it doesn't exist, and what are the consequences of this for those cases? Is access granted or denied?
Comment #39
xjmComment #40
tim.plunkettIn order to have a Layout Builder UI, we require that the entity type specify a canonical route.
When we check that, we don't have the ability to ensure that they actually provided one until we're in this loop.
So, this is the same behavior as if they never claimed to provide one: there will not be a Layout Builder UI provided.
Meaning they will get a 404.
This is reflected in the test coverage I added in the interdiff.
Comment #41
tim.plunkettThe interdiff in #36 illustrates how unhelpful and brittle the current tests are for Layout Builder routing.
I opened #3033439: Refactor OverridesSectionStorageTest::testBuildRoutes() and DefaultsSectionStorageTest::testBuildRoutes() to be functional tests to fix that.
Additionally, the placement of that
continuestatement was misleading. I moved the check to the top of the loop and gave it a better comment.Comment #42
tedbowThis looks good. The comment is much clearer to show that the entity type is totally skipped if it does not provide a canonical route.
Comment #43
xjmSo @tim.plunkett assures me that this test code:
...somehow adds test coverage that ensures that the user gets a 404 when the conditions for the
continueare met. #3033439: Refactor OverridesSectionStorageTest::testBuildRoutes() and DefaultsSectionStorageTest::testBuildRoutes() to be functional tests will definitely help make this clearer.+1 for this comment; it is much clearer and with the new code location, makes it clear we're not adding a different kind of access bypass for entities that don't have a canonical route.
Comment #44
xjmThis looks good to me and I plan to commit it once we are out of code freeze. Thanks!
Comment #47
xjmCommitted to 8.7.x ad 8.6.x, thanks!
Does the bug exist in 8.5.x? (Since this is a security issue there too probably.)
Comment #48
tim.plunkettHere's a backport, the bug does indeed exist in 8.5
Comment #49
tedbowPatch in #48 looks good. Would RTBC but already there
Comment #52
xjmCommitted to 8.5.x. Thanks!