Problem/Motivation
A single permission controls access to the Layout Builder.
Ideally access would be more fine-grained.
Proposed resolution
Leave the 'configure any layout' permission
For each bundle that is overridable create 2 new permission
- Configure layout overrides for all entities of the bundle
- Configure layout overrides for entities of the bundle that the user has edit access to.
These 2 bundle level permissions would let you set up different kinds of users
- Some "design" users that can configure Layouts for any articles but don't have any edit the access to other fields.
- when using Landing pages(maybe with no other fields), users who can only configure the layout for their own landing pages(others they are somehow granted access to edit.)
This allows separating out users who are responsible for design/layout and those that are responsible the data entered on a entity.
It also allows some bundles to tied to together the access to layouts and to the structured data.
For instance this allows creation of landing page style content types without other fields where most of the data is enter through inline blocks or gather by placing views.
Remaining tasks
User interface changes

API changes
None
Data model changes
None
Release notes snippet
In earlier versions of Drupal 8, Layout Builder only provided a single permission to access all aspects of Layout Builder (configuring/deleting layouts, adding/removing inline blocks, etc.). In 8.7, more granular permissions have been added (for example, to only allow manipulating the layout on content to which the user has edit access). Review Layout Builder's permissions after updating and change to more granular permissions where appropriate for better access control.
| Comment | File | Size | Author |
|---|---|---|---|
| #162 | 2914486-permissions-162-interdiff.txt | 1.66 KB | tim.plunkett |
| #162 | 2914486-permissions-162.patch | 58.89 KB | tim.plunkett |
| #160 | 2914486-permissions-160-interdiff.txt | 4.52 KB | tim.plunkett |
| #160 | 2914486-permissions-160.patch | 58.87 KB | tim.plunkett |
| #153 | 2914486-permissions-153-interdiff.txt | 656 bytes | tim.plunkett |
Comments
Comment #2
larowlanComment #3
MerryHamster commentedComment #4
MerryHamster commentedadded changes to permissions
Comment #5
MerryHamster commentedComment #7
piggito commentedChanging permission in test.
Comment #8
piggito commentedComment #10
piggito commentedFixing permission access to layout section
Comment #11
piggito commentedComment #12
sharique commented+1 for RTBC
Comment #13
johnwebdev commentedThis one could probably be split into multiple permissions?
Comment #14
tim.plunkettComment #15
kristen polThanks for the patch. Here is some feedback on naming conventions.
1) The first permission has the "active verb" prominent but all the rest are "Can xyz". I took a quick look at other core permissions and it looks like changing these to be something like the following would be good:
* Add and configure sections (or just Configure sections)
* Remove sections
* Add, update, remove, and move blocks in sections
(note on the last one that a) it should have a serial/Oxford comma in the list, and b) I changed "into" to "in".
2) In my quick search, didn't see any existing permissions that use "configure". Instead, "administer" is used heavily. In light of that, for consistency, perhaps these should instead be something like:
* administer entity layout
* administer sections
* remove sections
* administer section blocks
3) Should "remove sections" be there or just handled with the configure/administer permission? Configure/administer might imply that removal is part of the permission.
4) The list of permissions in the patch doesn't line up one-for-one with the issue summary so it would be good to discuss the reasons for collapsing these rather than having more fine-grained permissions
Comment #17
MerryHamster commentedComment #18
MerryHamster commentedAdded changes to permissions and code to hiding links if the user does not have access to remove.
Comment #19
MerryHamster commentedComment #20
MerryHamster commentedComment #22
piggito commentedPermission IDs update were missing so I updated them according to point 2 in #15
I agree with point 3 in #15 as well so I updated "remove sections" usage by "administer sections"
We still need further discussion as point on point 4 in #15
Comment #23
kristen polThanks for the updates.
Was there a compelling reason to move out this code to its own function? Was it to simplify the access check?
I'm also now wondering if there is any value for someone to be able to only "administer sections" or only "administer section blocks". I can't think of a use case of the top of my head. The issue summary has a bunch of granular permissions. I understand that for something like adding, editing, deleting, and viewing content but for working with the layout, if they can use the builder, it seems like they should be able to do anything. I see why someone would argue that deleting things might be more restrictive but I think it would be weird UX if someone can add, move, and update sections and blocks but not be able to delete blocks.
Comment #24
tim.plunkettComment #25
phenaproximaThese titles are a bit vague. Can we say "Administer layout sections" and "Administer layout section blocks" instead? Ideally we would also do this for the actual permission names too.
Comment #26
twfahey commentedRe-rolled the patch in 22 against latest 8.6.x, additionally updated permission label + name per phenaproxima's suggestion.
Comment #28
twfahey commentedUpdated tests w/ new permissions
Comment #29
tedbow+1 for this.
Are we going to something different directly in Layout Builder for adding Landing pages? Is there an existing issue?
Testing the patch
If give a user "administer layout sections" but not "administer layout section blocks" it is very weird experience.
The can add new sections but they can't do anything with them.
They also now can see the "Add block" link but nothing happens b/c they get an access denied.
If the user has "administer layout section blocks" but not "administer layout sections" they can see the "Layout" tab but then get an "access denied" if they click on it.
Comment #30
tedbowFrom the purposed Must Have permissions
How were these chosen? Is this something similar to the Panels contrib modules?
These seem like these permissions could lead to very weird UX for the builder.
If you have only had "add sections" here. What if you only had "add sections"? If added a section but put in the wrong place you couldn't move the section or delete it.
Would we allow moving or deleting of sections that you just added before you clicked "Save Layout"?
Otherwise it seems the common workflow if you only had "add section" permission would be
Of course if you realized that you put the section in the wrong place after you save there would be nothing you could do about it.
Bundle based
Adding a permission per bundle would be allow fine grained control but it would get overwhelming on permissions page.
But what if under "LAYOUT OPTIONS" when "Allow each content item to have its layout customized." was checked there was an additional option "Allow users to configure the layout for any content items they can edit"
I think for many entity types for many sites this would make sense. Of course the global "Configure any layout" would still apply.
Comment #32
bkosborneI agree with #30 - The proposed permissions are too granular IMO and I can't think of a use case.
There's something that's not touched on here that I think is important to address as well: The "administer display" permission (each entity type has one) is used for controlling access to modifying the default layout for each view mode. I can see why this chosen (since LB essentially replaces Field UI), but I think LB introduces a new paradigm for site building that Field UI did not have.
I'd like to give access for site builders to modify the default layouts for a view mode, but I don't want them to have access to turn LB on/off, toggle overrides, or control what view modes an entity has access to.
Comment #33
tedbowHere is try at the "Bundle based" idea in #30
I added testing the permission to the existing test method
\Drupal\Tests\layout_builder\Functional\LayoutBuilderTest::testLayoutBuilderUi()So that override logic is done with a user with the existing permission and then with permission to edit the node.
Comment #34
tedbowComment #35
tedbowI removed the permission on the route and now '_layout_builder_access' will check the permission when needed.
Here is the main part where the user would have to have either entity update access or access via the parent, which will check 'configure any layout'
This is the only place now directly checks the permission
Comment #36
tedbowRe #32 being able to control default without "administer display" permission for the entity type:
While I could see use for that I think more commonly if you have you "administer display" permission for an entity type you should automatically should be able control the layout defaults for that entity type.
Right now we add the "Manage layout" to button to the "Manage display" and don't even check if the user "configure any layout" permission. So if you don't have permission when click the button you get "access denied". The fact that as far as I can tell nobody has failed as an issue points to the probability people are probably not assigning "administer display" without also having "configure any layout".
Also you are still "administering the display", you are organizing field order and configuring widgets.
Comment #37
bkosborneRight, the thing is, I don't want to hand out the "administer display" permission. I just want "configure any layout" to be sufficient for controlling the default layout per view mode, but as I recall that's not currently the case.
I'll have to spend some time on this soon to figure out if I can easily modify these perms with a route modifier, and if so, it's not that big of a deal to me.
Comment #38
tedbow@bkosborne yeah I understand your use case. I was talking about different case. The case where user already does the "administer display" permission.
I think that permission should sufficient enough to access to the Layout builder because they can already turn on the layout builder for the bundle and they have power to allow overrides. These are power users.
If we allow this do we also have the solve the problem in core about how the users would access the default layouts because I think right now if they have the permission then they won't have a way to get to the pages.
but that is true also if you have "administer display" permission and not "administer fields" permission.
That should be pretty easy. Just create a routing subscriber that runs after
\Drupal\layout_builder\Routing\LayoutBuilderRouteswhich hasSo would have to be below -110. And then if you look at
\Drupal\layout_builder\Plugin\SectionStorage\DefaultsSectionStorage::buildRoutes()and then unset
$requirements['_field_ui_view_mode_access']another proposal
My current thinking is we should do:
Having 3 & 4 permissions would be much more powerful than the single permissions now. To give a role the power of the current permission just give the role 3 & 4.
4) would allow the case @bkosborne was #32 and #37
1) would allow power users to continue have same kind of power they currently have just with a new and better tool. It also allows that permission to be given on entity type basis. It also would limit them in the same way in that are not give power to create or update "content".
We have to consider that when we have #2957425: Allow the inline creation of non-reusable Custom Blocks in the layout builder(🤞) giving users the permission to configure layout overrides in a lot of sites is effectively going to give the those users the ability to edit content even if they can't actually edit those content entities. Because sites are going to using inline blocks to create content in a paragraph style way.
Right now our permissions make it very easy give permission edit defaults without editing content and I think we should maintain that.
2) allows for sites set up where configuring layout overrides is effectively editing content to maintain whatever fine grained control they have set up around editing content whether it is setup throw custom code or contrib. The point they won't have to create new logic and code around the layout builder.
It also allows for other sites to have users who can configure the layout override to be separate from content editors. They just don't turn on this permission.
Comment #39
bkosborneComment #40
tim.plunkett#32 has a really good point.
And I was reminded of #2935999: Remove Layout Builder's hard dependency on Field UI
Comment #41
tim.plunkettNW for all the discussion since #34
Comment #42
tedbowreroll
Comment #44
tedbowFixing the tests. If
\Drupal\layout_builder\Plugin\SectionStorage\SectionStorageBase::access()is not give a user it should used the currently logged in user.This is the behavior that is expected with
AccessibleInterfaceComment #45
wim leersComment #46
tedbowThis removes the options on Manage display when Layout builder is enabled for overrides "Allow users to configure the layout of any @entity they can edit"
This was in the previous patches. It is different than how other access are done in core because you don't control access by giving a permission by giving permission to a role.
Instead this patches makes 2 new permissions for each bundle where overrides are enabled.
The second one basically means if you can edit the entity you can edit the override. I am not sure if that would be hard to explain the permission page.
These 2 permissions would let you set up different kinds of users
Comment #47
tedbowUpdated the summary for this new approach.
I talked to @tim.plunkett and @DyanneNova about this this week and I think they were onboard with this.
1 problem with more complicated locating and section level permission is that we hope someday to be able to offer decoupled API access to updating layouts. However complicated we get in the UI we would have to support over the API which could get very complicated with validation.
Comment #48
kristen pol@tedbow What's the best way to test this?
Comment #49
tedbow@Kristen Pol thanks for asking.
layout_admin has "configure any layout"
article layouter has "Configure any layout overrides for Content items of Content type Article" + "Article: Edit own content"
article editor has "Configure layout overrides for editable Content items of Content type Article"
/admin/structure/types/manage/article/display-layout/default
and node/*/layout for both nodes
node/*/layout for both nodes
Not able to access: /admin/structure/types/manage/article/display-layout/default
node/*/layout for only the node they authored. Not for the other node
Not able to access: /admin/structure/types/manage/article/display-layout/default
Needed a reroll
Comment #50
bendeguz.csirmaz commentedRerolled patch #49.
Comment #52
tim.plunkettThis change is at least one of the ones breaking tests. We don't use SectionStorageLocalTaskProviderInterface on this class anymore.
Didn't review the rest of the failures yet.
Comment #53
tedbow@bendeguz.csirmaz thanks for the reroll.
Probably all the fails were from what @tim.plunkett mentioned in #52.
All the kernel and unit tests passed for me locally plus
\Drupal\Tests\layout_builder\Functional\LayoutBuilderTestwhich is the only test that changed.So hopefully this is good.
Comment #54
kristen polAdded "how to test" to issue summary from #49.
Comment #55
bnjmnmReroll
Comment #56
bnjmnmComment #57
bnjmnmThe test steps in #49 will not work - some of the test users require additional permissions and some of the paths used have been changed. I am in the process of rewriting these steps and will add them here once completed.
Comment #58
bnjmnmI updated the test instructions to reflect what was necessary to test locally. My changes are in italics, and comments in parentheses. Before moving these to the IS, I'd like @tedbow to review and make sure that's how the permissions should behave, or if this exposes additional work that needs to be done.
layout_admin has "configure any layout" + Content: Administer display
article layouter has "Configure any layout overrides for Content items of Content type Article" + "Article: Edit own content" +Article: Create new content +
article editor has "Configure layout overrides for editable Content items of Content type Article" + Article: Edit own content (node/*/layout returns Access Denied without this permission, even for nodes created by user)
/admin/structure/types/manage/article/display/default/layout
and node/*/layout for both nodes
node/*/layout for both nodes
Not able to access: /admin/structure/types/manage/article/display/default/layout
node/*/layout for only the node they authored. Not for the other node
Not able to access: /admin/structure/types/manage/article/display/default/layout
Comment #59
tim.plunkettJudging by #2571235: [regression] Roles should depend on objects that are building the granted permissions and #2339487: Static cache permissions, there is no cache to be cleared here.
Do these need to be public?
OH I see later that they're called instead of generating the permission string each time. Hmm, interesting.
The distinction between these two is not very clear. The description on the second one helps slightly...
But I think it should be much clearer that the first permission lets users edit layouts for entities they cannot directly edit.
Also, when comparing the "per-bundle bypass edit-access" one, do we still need the "configure any layout" permission?
Finally, right now Field UI permissions are needed to enable LB itself (defaults and overrides). I think we can punt any perms related to that to #2935999: Remove Layout Builder's hard dependency on Field UI.
Nit: What else would this be? I'd suggest using an inline @var here
Not all bundles are represented by entity type. This should use the
entity_type.bundle.infoserviceNit: Can be combined
Nit, AccountProxyInterface only needs to be specified if you are swapping out the account. AccountInterface is suitable
Nit: End with a colon:
I think the split here may be valuable, but 2 seems confusing when contrasted with 1 (because parent::access is called "configure any layout")
Also, permission is spelled wrong.
Nit, missing second 's' in access
Nit: Can you move this to the top of the method for readability?
Personal preference, but I think the multiple local variable names almost make this more confusing
Nit: Maybe LayoutTestUserTrait? Idk. But should start with a capital letter.
Comment #60
tedbow@bnjmnm re #58 thanks for laying this out in detail!
article layouter should not need ""Article: Edit own content" but does need "View published content". that user can create an article not edit it but still configure the layout. Any for any published article
Yes this is correct for article editor because their access is dependent on edit access(and view but so is everyone's)
@tim.plunkett re #59
Thanks for the review!
Changed for first 1 to
instead of "any"
Change the second to "editable by the user"
Agree they could use more work.
For BC wise I am sure we can get rid of it.
Also these permission only deal with overrides not defaults.
Not sure how that would change anything here.
Add to also making different permission titles and descriptions for entity types that don't have bundles. So it doesn't say "User items of user".
Also had to change the titles because permission are sorted by title and the current ones would not group entity types together.
$access_resultand$edit_only_result. I would like to keep the edit result separate because it is an AND condition nested in an OR.I move checking the layout builder to the end which eliminates the need for another variable.
Comment #62
tedbowI talked this over in the UX meeting. Thanks everyone!(will credit attendees in subsequent comments).
It was decided that we needed to shorten up the descriptions and make them more clear.
Here is what we came up with:
For entity types with bundles, for example articles bundle for nodes
Content - Article: Configure all layout overrides
Content - Article: Configure layout overrides for content items that the user can edit.
The pattern is:
[entity type collection label] - [bundle]: Configure all layout overrides
[entity type collection label] - [bundle]: Configure layout overrides for [entity type plural] that the user can edit.
For entity types without bundles, for example user:
Users: Configure all layout overrides
Users: Configure layout overrides for users that the user can edit.
The pattern is:
[entity type collection label]: Configure all layout overrides
[entity type collection label]: Configure layout overrides for [entity type plural] that the user can edit.
Comment #67
tedbowComment #68
johnwebdev commentedI really like the permissions! They are clear now.
We need a test for testing an entity without bundles.
And there are todos in the patch.
Setting to NW for this.
Comment #69
bendeguz.csirmaz commentedComment #70
bendeguz.csirmaz commentedReroll of patch #61.
Comment #71
bendeguz.csirmaz commentedI have some questions.
- #68
Could you elaborate on what you mean by testing an entity without bundles please?- ah, I realized #62 already described it. Sorry. What should be tested though? The permission labels?I'm not sure what this todo means.
Do we need a test for when an entity display's overridable property changes from true to false, users without the configure any layout permission are no longer able to edit layouts?
Comment #72
johnwebdev commented#71 There are no scenarios in the test where the entity has no bundles, which has separate permissions.
Comment #73
tedbowThis dataProvider could be changed to return entity types and bundle(null for user). So basically you double the test cases.
but you probably would also have to return something like $manage_display_path because the patter would be different for nodes and users.
I think the thought here is to check access to /node/1/layout when it is not actually overridable. That might out of scope of this issue.
Also before in this patch there was a setting on manager display for "Allow users with edit access to entity to configure layout for entity". Since this is not here any more and just standard permission I think we can just remove todo.
Comment #74
bendeguz.csirmaz commentedThe
OverridesSectionStorageTest::testDeriveContextsFromRoute(#70) fails because thecurrent_userservice is set to a prophesized account with the id 999.When
CurrentUserContext::getRuntimeContextstries to load the current user (with the id 999), it obviously returns null.I wonder why this hasn't failed before.
Comment #75
bendeguz.csirmaz commentedFixing the test and changing a type hint.
Comment #76
bendeguz.csirmaz commented#73.1
I think adding more complexity to the LayoutBuilderTest is not a good idea.
This seems like a good opportunity to refactor the testLayoutBuilderUi method into a new class.
Comment #77
tedbowre #74 I think this new fail is because changes committed in #2942907: Entity system does not provide an API for retrieving an entity variant that is safe for editing
Comment #78
tedbowre #76
@bendeguz.csirmaz I think you are correct about not adding more complexity. But I don't refactoring is needed in this issue(though we could in another issue)
Here is my idea
I initially updated `\Drupal\Tests\layout_builder\Functional\LayoutBuilderTest::testLayoutBuilderUi()` with data provider to test with different permissions because I wanted a quick way to test that permissions worked.
But that method is very complicated and tests a lot more that we need to prove the permissions themselves work.
we should probably instead simply create a new test method and/or class that simply tests the permissions .
basically that you get access denied on overrides when you should and that none of the new permissions give you access to defaults.
this test method could use the existing dataProvider but with added cases for user entity which has no bundle.
then we can revert all changes to testLayoutBuilderUi in this issue.
Comment #79
tedbowdoes #78
Comment #80
johnwebdev commentedi really like seeing this being put in its own file!
we should considering making a base class for functional tests, we could definitely re-use some code. (not in this issue though)
Nit: Maybe we can call this testUserLayoutAccess (only User threw me off while reading)
instead of checking for text, we could test the http status code returned 200 vs 403, saves us a variable to manage
Comment #81
tim.plunkettTo me this represents a worrisome BC break.
Any SectionStorage plugins in contrib/custom code will now no longer be protected by this permission unless they refactor to call
parent::accessAnd this is only needed for the case of Overrides, due to wanting to OR the existing permission with the new one.
I think that the default behavior should remain that "configure any layout" should be a required permission, unless the SectionStorage opts out of it.
In order to match the full logic of overrides, this should be:
And possibly contain a comment referencing
\Drupal\layout_builder\Plugin\SectionStorage\OverridesSectionStorage::getEntityTypes()Maybe even consider mimicking that more closely with a protected method to filter the definitions first? Then you won't need the inline @var either
Nit, store a local variable for the entity_view_display storage outside both foreach loops
Why is one static:: and the other self:: ?
Half of these have trailing periods and half don't, they should be consistent
Logging in and out feels odd here.
Can we instead load the EVDs directly and manipulate their values instead?
Comment #83
tim.plunkett#80
1) Fixed this by not using the UI, as I suggested in #81.6
2) Renamed
3) Fixed
#81
1) Fixed
2) Fixed
3) Fixed
4) Fixed
5) Fixed
6) Fixed
Also I chose to restore LayoutSectionAccessCheck to how it is in HEAD, remove all usages, and deprecate it.
It has been unnecessary since #2936358: Layout Builder should be opt-in per display (entity type/bundle/view mode) added a new access check, and we never noticed.
CR placeholder at https://www.drupal.org/node/3039551
Comment #85
tim.plunkettCan't use the
deprecatedtag on a tagged service as it is always instantiated.The access check itself has a proper trigger_error
Comment #86
tim.plunkettIn all other cases, we had the permission check and the access check. Somehow this route was missing the access check!?
This is the replacement for the removed
_permissionsfrom the routing fileWe had completely duplicated access checks ever since #2936358: Layout Builder should be opt-in per display (entity type/bundle/view mode), deprecating the less useful one
The only caller of this in HEAD added the object as a cacheable dependency, but now have additional callers. Adding it multiple times does not have unwanted effects
This is not an API addition. getPluginDefinition is on the parent interface. We're just overriding here to give a better typehint than
mixedActually, I think this one usage can be removed. The other two are needed still.
Comment #87
phenaproximaIt took me a long time to understand this, but @tim.plunkett was kind and patient enough to explain it to me in a call.
Although this seems pretty awkward (and it kind of is), it preserves the behavior we already have in core. Previously, any Layout Builder route required the 'configure any layout' permission in order to work. That's no longer the case -- we're delegating access to the section storage plugin. And that is fine, except that we cannot assume that the section storage is checking for the 'configure any layout' permission, and that discrepancy constitutes a BC break. This stems from the fact that the access checking for the routes is not necessarily going to produce the same result as the access checking for the plugin, and ever thus has it been.
So effectively, this is preserving the behavior in HEAD, whilst also giving section storage plugins a way to take a different path during routing-level access checks. I think this should be made very clear in the change record.
Apropos, I think we might want to mention that this is only applicable during routing-level access checks, and that the 'configure any layout' permission is only required under those circumstances.
I know these are machine-readable permission names, but the phrasing seems odd to me. How about "configure editable $bundle $entity_type_id layout overrides"? That will result in things like "configure editable remote_video media layout overrides", which reads almost like a sentence to me.
Similarly, I wonder if "configure all $bundle $entity_type_id layout overrides" would read better.
Nit: These can be chained.
This needs a comma. "...for user entities, which do not..."
I may be missing something, but this method appears to be used in only one place. Does it really need to be a trait?
Comment #88
tim.plunkett#87
1) CR still hasn't been written, but yeah that's super important to include
2) Tried to clarify this more
3) Agreed, and reworded.
4) Same
5) Changed
6) AFAIK that's not a requirement of English, and I can parse the sentence as written (not by me). Left unchanged.
7) I observed this slightly in #86.6, and you're even more correct. This made me completely refactor the test itself to account for the permissions, instead of blindly assigning the one *special permission*
Finally, I straight up removed the static helpers for the permission machine names. I understand the intention, but as that's not a pre-existing pattern and there's not really a good place for them (if there's pushback against this and they have to live somewhere, I'd vote for OverridesSectionStorage), I think it's best to switch to the strings themselves.
Comment #89
tim.plunkettAfter further discussion of #87.6 I renamed the methods to be clearer about what we are testing.
Comment #90
tim.plunkettCR is in place
Comment #91
phenaproximaTwo nits, then RTBC.
"Asserts expect access" is quasi-broken English. :)
Nit: This can be all one line if we put the ternary inside statusCodeEquals().
Comment #92
tim.plunkett#91
1) Yep. Also renamed the method
2) I prefer the readibility of this way, not changing it
Comment #93
phenaproximaThanks! We're done here.
Comment #94
larowlanshould we mark these two as restrict access given they bypass entity access?
minor nit, we could
continuehere and avoid the elseshould we
->addCacheableDepelency($entity)too?Comment #95
tim.plunkett#94
1) Yes, since we are bypassing update/edit access checks, like the existing perm. Added.
2) We could, but I like the explicitness of this. Especially if we were to eventually add something _after_ the else. Call it personal preference.
3) We add the result of $entity->access() here, which is responsible for specifying the cacheable metadata. Consider
\Drupal\node\NodeAccessControlHandler::access()which specifies which permissions to cache by. Adding the entity itself here would cause more invalidations than necessary.Comment #96
phenaproximaChanges look okay to me.
Comment #98
tim.plunkettAfter further discussion with @xjm, removing the
restrict_accessflag and replacing it with a descriptive message. Wording borrowed from thebypass node accesspermission description.#92 No descriptions at all



#95 Restrict access
#97 Custom description
Comment #99
tim.plunkettAdding the final image to the IS
Comment #100
xjmI think it's not "regardless of permission restrictions". It is still restricted based on view access, right? So maybe "even if they cannot edit the content itself" or such?
We could prefix it with "warning" to raise the visibility without lumping it into the same category as "bypass node access" and "administer views" and such.
BTW, the reasoning for #98 is that IMO we should not introduce a pattern where site owners are encouraged to hand out restricted access permissions to a content reviewer role. If so, any security issues for that permission would be handled in public by default, and I don't think that's what we want in this case. (Also, other, more dangerous permissions than this one still don't have the flag.)
Tagging for usability review of whatever we finalize on as the alternate proposal.
Comment #101
tim.plunkettFair point. Also I discovered there is a different key for this sort of thing:
warningvsdescriptionComment #102
tim.plunkettThis needs tests but here's an additional permission per discussion with @larowlan and @xjm
Comment #104
tim.plunkettComment #106
tim.plunkettMissed one!
Updated the screenshot in the issue summary
Comment #107
xjmMaybe "Manage inline blocks" should be "Create and manage inline blocks" or something to clarify its purpose?
Comment #108
tedbowre #107 I think the problem is that since #2968500: Change inline blocks workflow in Layout Builder to match mocks we don't actually have the phrase "Inline Blocks" anywhere in the UI. We just have at the top of the block listing "Create custom block" the name we get from
getSingularLabel().So maybe "Create and edit custom blocks" because I don't think we want "manage" because you can put reusable/global blocks that in your block library regardless of this permission.
Comment #109
tim.plunkettAgreed with #108
Following the lead of #2862422: Add per-media type creation permissions for media, adding this permission in an update path.
Comment #112
tedbowAdding a couple wording changes and additions after UX meeting today and removing "Needs usability review"
See interdiff for actual wording changes. New "description" was also for inline blocks.
Crediting @rainbreaw and @dyannenova from the meeting. I think everyone else was at the previous meeting and has been credit already.
Comment #114
tedbowunused var
We don't actually need to wait for the ajax request because this is already called inside
addInlineBlockToLayout()This a second use of a contextual link on the same which can cause the random failure. see #2918718: Using ContextualLinkClickTrait::clickContextualLink() multiple times per page can cause random failures
This preventable by just reloading page. Adding with a todo to other issue.
UPDATE: looking down at the rest of the test, I am not why we are clicking the "Configure" contextual link at all here.
We are explicitly testing the permissions directly below this. Removing the click.
unused var
Comment #115
phenaproximaWhen an entity view display's support for overrides is changed, and then the display is saved, will the permissions cache (and/or other relevant caches) be automatically invalidated? If not, should they be?
I would slightly change the phrasing of this: "Allows the user to configure the layout even if they cannot..." Not a big deal, though, since labels can be changed up until RC.
Shouldn't this maybe go in the blockAccess() method? If not, can there be a comment explaining why not?
Comment #116
tim.plunkett1)
Get this: there is no permissions cache!
I learned this above in #59.1
It's recalculated every time you go to
admin/people/permissionsThe actual *assigned* permissions are stored on the role entities.
2)
Funny, that's what I had before. But it went through UX tweaking and feedback and this is what it will be.
3)
The docs on
\Drupal\Core\Block\BlockBase::blockAccess():It's only for view, and is documented as such.
Comment #117
phenaproximaAll good answers. "There is no permissions cache!" sounds like it should be a sticker they give out at DrupalCon.
Comment #118
effulgentsia commentedI haven't reviewed the code here, but FWIW, I was curious to test out how use case 1 worked with content moderation. AFAICT, it worked great. I created 3 roles with one user each: "editor", "designer", "publisher". I gave editor and designer permission to create drafts, and publisher permission to publish. I gave editor permission to create and edit articles, and designer permission to configure all article layout overrides.
As expected, I was able to login as an editor and create and edit articles. For each save, I could only choose "Draft" as the state. I did not have access to the Layout tab.
As expected, I was able to login as a designer, and so long as there already existed a published revision of the article, I was able to access it, and via it, the Layout tab. This worked, even if there was also a forward draft. As expected, when making Layout changes, I could only choose "Draft" as the state, and the newly created revision incorporated the changes from the latest draft created by the editor.
As expected, I was able to login as a publisher, view the revisions created by both the editor and the designer, and publish them.
This issue isn't directly about CM integration and didn't make any changes in regards to that, so I'm just leaving this comment here to confirm that it looks like that all works properly, so that the use case identified in the issue summary is in fact viable.
Comment #119
xjmMix of notes for myself, questions, and feedback. I haven't read the tests yet or drilled down on the existing access checking API in HEAD.
Non-actionable notes
This line is the equivalent of the previous combination of the two route requirements for the
configure any layoutpermission and theviewaccess (by making view accessandIf()the permission).This allows individual storages (in the patch, overrides) to take control of access checking and override the default access checking behavior.
This is the removed usage of the deprecated access check.
Actionable feedback and questions
This should say "deprecated in 8.7.0" (not 8.7.x).
@todo Followup issue to talk about deprecations of access checks (and as compared to tagged services generally).
New services. Can we use the null/static fallback for internal BC?
This might merit an inline comment describing the restrictions.
This class has a very generic name and description,but then the method has knowledge of a particular storage. Should the class be specific to overrides (and named as such)?
Also -- I haven't read the code here yet, but just want to confirm we're not accidentally bypassing delegation again.
Finally, maybe this method should be something like
getLayoutOverridePermissionsMap()or something?I'm wondering if these should be
formatPlural()... I guess not because they don't include a count. However, this might be difficult to translate correctly in some languages where singular/plural is more than just a binary concept. I raised this concern when entity plural labels were introduced originally, so it may be out of scope for this issue. However, it'd be good to check for existing examples in core to ensure we're just following the existing pattern. (Also I should check and see if I can find an existing issue about plural labels to@todo.)So this restricts access to the form. How is access restricted on the backend? Does it currently just fall back to the Custom Block module's permissions? (Which I think we discussed is straight up
administer blocksor something.)Also, for my reference and maybe for reference in the patch, what mechanism do we use to restrict the creation/editing of inline blocks outside LB?
Seems like an existing bug WRT missing the cacheable dependency; does it have tests?
Nit: I think this comment is wrapping too early.
We've referred back to this inline comment a lot. I wonder if it'd be helpful to list examples of the actual permissions under each numbered item in the comment. Not a big deal but I have to read through and line up 1-2-3 each time.
Also, is there a place at a higher level of documentation we could document the access/permission scheme? Maybe even an API docs section. Maybe docs gate, sorta.
I think
{@inheritdoc}here is a lie. This is an API addition ne?Is there a base implementation of this for BC? I don't see anything else being added in the patch.
Comment #120
xjmComment #121
xjmWhat kind of array?
array[]?string[][]? Etc.We should maybe say what the test fixture is for?
Usually we document data provider parameters as well as adding inline comments for each scenario.
"Provides test data for..." ?
I think this should be:
Is this permission addition needed?
Uhhh why are we doing this with an anonymous function? Hard to read. :)
Is this a
string[]rather than justarray?Yah, like this. :)
Except the third arg seems to be an array, not a "whether" (which would imply bool). "Any section data"?
Comment #122
xjm@tim.plunkett, @tedbow, and I discussed that there's still a place where we're not updating the custom block permission correctly for
layout_builder_block_content_access(). At a minimum, we need to use the new permission there instead of "configure any layout".However, having those accessible from the normal custom block form could cause other problems as it's another entry point for things like revisions, maybe translations, etc. We might want to look at why we allowed access to them via that form and see if it's still needed (maybe in a separate issue).
Comment #123
xjm@tim.plunkett and I discussed that a release note about the new permissions structure is a good idea. There's an upgrade path, but also security hardening opportunity and possible change to anything extending the functionality.
Comment #124
tim.plunkett#119
1)
Done (followup still needed)
2)
Done
3)
Done
4)
We are 100% bypassing delegation here.
I will rename the class to better indicate that.
The reason to bypass delegation is that the permissions UI groups the permissions by their providing module. If contrib/custom plugins were to use a delegation-based approach, their permissions would be located under the Layout Builder section of their UI, not their own module.
With the new clarification of the class as only being for overrides, we can simplify the method name to simply
permissions(), which is what is used in other modules (like filter)5)
$entity_type->getPluralLabel() is the best we've got
6)
All of the existing protections for the entity (entity and routing access) will prevent everything else. This is our new permission for our UI.
I believe if edited elsewhere, it requires
administer blocks7)
This is 1:1 replacing the addCacheableDependency call in the now-deprecated and no-longer-used LayoutSectionAccessCheck
8)
Hah, too true. This has been indented and outdented multiple times :)
9)
Wrapped these around their respective lines. I think that's much clearer.
For docs, I think that would be better as handbook docs. With the @see I added to the permission provider pointing here to these docs, I think we're okay in-code.
10)
No, not an API addition. Just for a typehinting improvement. The method is already on PluginInspectionInterface, but claims it returns an array, which has never been true here. Sorta out of scope, so I can remove it, but I added it to help with the
if (!$section_storage->getPluginDefinition()->get('handles_permission_check')) {addition#121
1)
Fixed
2)
Fixed
3)
Sure
4)
Fixed
5)
Yes but I removed the 200/403 because it put it over 80 chars and its clear enough in this 2 line helper method
6)
This is necessary because we have a whole test that tests the innards of locale.module because we forgot a t() once in the UI (#3019333: If you translate the literal "inline blocks" to another language in the layout builder, it stops working.)
I'd be just as happy to remove this whole test and promise to never miss a t() again, because testing the locale.module from within Layout Builder is kind of a lot.
7)
I much prefer this approach! Leaving in as a stylistic thing.
8)
Yes, fixed
9)
Yes. This is unchanged in this patch (just trailing punctuation) but fixed anyway :)
#122
Fixed by swapping to the correct permission.
We do have explicit tests that the custom block form for _inline blocks_ is accessible from outside the Layout Builder UI, in
\Drupal\Tests\layout_builder\FunctionalJavascript\InlineBlockTest::testAccess()Comment #126
tim.plunkettThat test I mentioned in response to #122? I broke it :)
Comment #127
webchickAttempting to write a release notes snippet.
Comment #128
tim.plunkettThanks!
Comment #130
tim.plunkettKeeping up with HEAD
Comment #131
wim leersFor #119.1: #3042376: [Policy, no patch] How to deprecate access checks?.
AFAICT all other feedback has been addressed. Back to RTBC.
Comment #132
xjmThanks @tim.plunkett and @webchick.
The release note snippet is missing an action for the site owner or developer (helpful for sites to determine if they're affected). Does this work?
Or something along those lines?
Comment #133
tim.plunkett+1!
I went to make the change in the IS and was confused until I realized you already did it :D
Unassigning myself for now, but I'm available to make any changes needed.
Comment #134
benjifisherI would strike ", to restrict access further" from the release note snippet. Adding new permissions does not restrict access; it is only when the site admin removes one permission (and perhaps grants a more restrictive one) that access is restricted. Besides, less is more.
I will not edit the IS myself: just make the suggestion.
Comment #135
tim.plunkettAfter discussion with @xjm I am adding a
@defgroup(and the accompanying@ingroups), as well as a link to the handbook in the hook_helpComment #136
xjm@benjifisher I changed it to:
Comment #138
tim.plunkettlol whoops
Comment #139
tim.plunkettRerolled since #3038442: Use hook_entity_form_display_alter() instead of Layout Builder's custom overrides display altering hook removed layout_builder.api.php :)
Comment #140
tim.plunkettUpdating the screenshot
Comment #142
tim.plunkettNot sure why that passed before. But I missed this in my last patch.
Comment #143
effulgentsia commentedIs there a reason that this patch isn't removing the
'configure any layout'permission requirement fromlayout_builder.move_block_form?Comment #144
effulgentsia commentedThe CR doesn't mention this.
Comment #145
tim.plunkett#143
Because that landed on Friday and I didn't notice during my rerolls :)
Nicely spotted!
#144
Updated the CR https://www.drupal.org/node/3039551/revisions/view/11342297/11355617
Comment #146
effulgentsia commentedhttps://www.drupal.org/docs/8/core/modules/layout-builder/security-consi... currently only has a copy/paste of #118. What information do we still plan on adding there?
Comment #147
tim.plunkettCompleted handbook docs are not a stable blocker, but I added that page as a placeholder at the request of @xjm. I think the idea is to use the examples of different personas as a way to help illustrate the differences between the permissions.
Comment #148
effulgentsia commentedAre we worried at all about the possibility that a section storage plugin has the
handles_permission_checkannotation, but in fact doesn't check any permissions? For example, suppose some alters the'overrides'plugin with a different class, with an access() method that does not check permissions (because in 8.6, it didn't have to).What if in the above code we add an
elsethat then checks to see if $access has a 'user.permissions' cache context, and if not, then throw an exception? Since if you declare that you handle permissions, but return an access result that does not vary by permissions, then you did something wrong, right?I'm willing to be convinced that it's not worth adding such an exception, if it doesn't feel right to do so, but then should we add something to the CR and/or to https://www.drupal.org/docs/8/core/modules/layout-builder/security-consi... that specifically calls out the need for anyone overriding section storage plugins to be aware of this annotation and to ensure that the overridden class complies with it? If we decide that that info belongs only in that handbook page, then per #147, that's not a commit blocker.
Comment #149
tim.plunkettThe default behavior for this annotation key is to be FALSE, and it is optional.
A developer would have to explicitly set it to TRUE in their annotation.
There are many reasons to do so, and I don't think it's fair to say that everything must be governed by permissions.
I think adding an exception there is a step too far, and could actively limit the possibilities of contrib/custom code.
The
handles_permission_checkkey is documented in the CR, it has a whole paragraph on the annotation, and it is also explained in the API docs which will be on api.d.oIf all 3 of those sets of docs aren't sufficient, we can always add to the handbook post-stable as you point out
Comment #150
effulgentsia commentedOk to #149.
The rest all looks good to me, so removing the "Needs framework manager review" tag.
I think it would be better to add the permission as a
#accesson the element rather than not create the element at all. That's not a commit blocker though and can be punted to a followup if need be.Let's add an @deprecated to the class, and mention in the CR that
_has_layout_sectionis now deprecated. Should we also add adeprecated:line to theaccess_check.entity.layoutservice?Comment #151
tim.plunkett#150
1)
Agreed, and that's what I had done in InlineBlock::buildForm(). Thanks for pointing that out!
2)
We can't use the
deprecatedflag in the services.yml because this is a tagged service, and it will always be retrieved and instantiated (which is also why we had to put the trigger_error in the ::access() method and not in the class.I put a comment in the services.yml though, and added the @deprecated.
I also expanded the CR: https://www.drupal.org/node/3039551/revisions/view/11355617/11356233
Comment #153
tim.plunkettOkay, that also is prevented because of tagged services.
Making it an @todo for now, this is what #3042376: [Policy, no patch] How to deprecate access checks? is all about.
Comment #154
xjmNice catch on #143. I had made a note about needing to check this issue once that one landed but forgot. :)
I'll see if I can work on the docs some today.
Comment #155
xjmFew notes while I work through the doc:
Missing noun here -- the "handles permission check" what? The annotation key, right? What's the right word to use here?
Typo "a the".
By default, the Layout Builder access check requires the 'configure any layout' permission. Individual section storage plugins may override this by setting the 'handles_permission_check' annotation key to TRUE. Any section storage plugin that uses 'handles_permission_check' must provide its own complete routing access checking to avoid any access bypasses.
(Is the above true?)
And then since "API"'s meaning is contextual, maybe "client API access"? or "API access (e.g. via REST, JSON:API, etc.)".
Then, I think we should add (at a minimum:
(The former is already in the ingroup but it's specifically relevant to clarify the bit about API access restrictions.)
Comment #156
effulgentsia commentedHow about "HTTP API"? We use that elsewhere: e.g., in the docs for
EntityTypeInterface::isInternal().Comment #157
xjmInstead of "security documentation" (which it isn't really; bit different from the JSON:API case), the link text should be something like "documentation on configuring Layout Builder permissions".
I also renamed and retitled the handbook page. The new URL is:
https://www.drupal.org/docs/8/core/modules/layout-builder/configuring-la...
So let's update to use that. Edit: Probably rename the URL placeholder as well for consistency.
Comment #158
xjmOr, better,
Comment #159
xjmIn the CR or above (I forget which), I switched away from the wording "will be fully responsible" because that could be read a couple different ways. I think we need a stronger statement, something like If TRUE, Layout Builder will not enforce any access restrictions for the storage, so the section storage's implementation of access() must perform the routing access checking itself."
In addition to what I said above about this sentence, I wonder if we should explicitly say "enforced on the routing level (not on the entity or field level)". Maybe?
NW for the above several comments. Meanwhile I'm working on https://www.drupal.org/docs/8/core/modules/layout-builder/configuring-la... a bit.
Comment #160
tim.plunkett#155
1)
Obsolete after 3 below
2)
fixed
3)
+1
4)
That is true! Fixed, and the second half is fixed with the suggestion from #156
#157
Great! Fixed all points
#158
I should have read all the feedback first ;)
Fixed again
#159
Changed both
Comment #161
effulgentsia commentedAll of the changes in #160 look great!
The last sentence just sort of leaves me hanging. First of all, we need a "." at the end of the sentence. Secondly, do we also want some other word like "page" or "documentation" either inside or after that last anchor?
Comment #162
tim.plunkettPutting in "online documentation." as that is how the handbook is consistently referred to
Thanks!
Comment #163
xjmThe added docs and misc. other improvements addressed all the specifically release-management-related questions I had, so untagging. (Note that I've not done a full code review of the final patch yet.) Thanks!
Comment #165
effulgentsia commented#162 addresses all of my feedback as well, so RTBC. The test failure is likely a CI problem, so we'll need a new test run once that's resolved.
Comment #167
webchick@tim.plunkett walked me through this patch. The upgrade path (and accompanying text) was surprisingly straight-forward! The "core" part of the patch is well-documented, and that ->andIf()/->orIf() stuff is pretty sweet! :D Also ran through a manual test earlier with the team and confirmed the permissions are operating as they should.
Confirmed with @effulgentsia that he has performed a code review of the patch, and that his feedback has been addressed. Also seems we have addressed all release management feedback, per #163.
Committed and pushed to 8.8.x and cherry-picked to 8.7.x since this is an experimental module. WOOHOO!! :D
Comment #170
xjm