Problem/Motivation
Core has an admin role, which has by design all permissions. To achieve this user.module traditionally assigned all new permissions to the admin role.
This approach is flawed in several ways:
a) Drupal now has dynamic permissions, which are not rebuild, when e.g. a new filter format is imported via config - unless it is installed as a module. That means the admin role. That is a bug, because module installation is not the only thing that can define new permissions.
b) The permissions page allows removing admin permissions of admin role. This is confusing, because when the next module is installed, those permissions are back.
c) user_modules_installed() needs to re-build the router, because some permissions use $entity->uri(), which brings the installer memory wise over what we want.
Proposed resolution
- Create a boolean flag in the Role entity ->isAdminRole()
- Always pass all permissions check for that role.
- Remove user_modules_installed()
- Profit!
Remaining tasks
- Create patch
User interface changes
- (possibly) remove admin role from permissions page
API changes
- Introduce ->isAdminRole() on the Role Entity interface
Original report
dawehner:
Could we mark roles as being admin roles and instead of assigning all permissions to it automatically, adapt
\Drupal\user\RoleStorage::isPermissionInRoles to check for a boolean flag on the admin role entity?
Berdir:
I've thought of that today as well, thought we could check the admin role setting, but a flag on the role itself could indeed be an advantage because then we have nothing additional that we need to load.
Could even help the permissions page because then we could opt out of displaying checkboxes for that role completeley. Sounds like being able to remove certain permissions from the admin role is no longer an option anyway, if user_modules_installed() now works they way it does.
Beta phase evaluation
| Issue priority | Major because user_modules_installed() assigning all permissions is problematic for memory requirements we want to met. |
|---|---|
| Prioritized changes | The main goal is consistency, ensuring the admin role can perform its function and performance by removing the need for user_modules_installed, which calls the router rebuild, which saves 2.5 seconds and around 10 MB. |
| Disruption | Disruptive for existing sites who will need to delete the existing config key user.settings::admin_role and use the UI to set the admin role. |
| Comment | File | Size | Author |
|---|---|---|---|
| #47 | interdiff.txt | 641 bytes | dawehner |
| #47 | 2435075-47.patch | 18.95 KB | dawehner |
| #44 | 2435075-44.patch | 18.98 KB | dawehner |
| #40 | interdiff.txt | 2.46 KB | dawehner |
| #40 | 2435075-40.patch | 29.25 KB | dawehner |
Comments
Comment #1
fabianx commentedComment #2
berdirNote on b) AFAIK the only JS that we have on that page is to prevent changing the permission for any actual role if it is granted to the authenticated role, becaue it is not possible for any other role to not have it then. I just tried it is possible to remove permissions. And it works, until you enable the next module. Claiming that we are able to do this is kind of a bug :)
Comment #3
fabianx commentedComment #4
dawehnerI'll give it a try, sounds like fun.
Comment #5
dawehnerIts not really clear whether getPermissions should now return ALL permissions.
Let's first see whether we have any test failures with that.
Comment #6
berdirNice, I guess the part that is missing is removing the existing setting/UI and providing a UI for this... how do we do this? should we, for simplicity, keep the existing UI and just use that to set the flag on the role, or should we make it part of the role edit form?
Not sure about the permission part, maybe we should explicitly save an empty permissions list and document that somewhere?
Comment #8
dawehnerComment #9
fabianx commentedI like this a lot!
nit - I find array_intersect(array_keys($roles), $account->getRoles())
easier to digest.
Either empty array() or an Exception, not sure we can do an Exception without breaking BC.
I think we should throw an Exception here.
I am not sure we can throw an Exception.
Else I think we need to do a no-op and indeed document that an admin role returns an empty array() for getPermissions().
CNW for the @todos, lets ping catch.
Comment #10
dawehnerQuick question I had while manually testing the patch: Should we allow the anonymous role and authenticated role allow to be an admin role?
Especially the first one leads to odd results, as you still see the login block, but you can do everything.
Comment #11
catchI'm responsible for the original implementation of the admin role that went into core, but it's never worked with dynamic permissions.
I do think the ability to remove permissions from the admin role is potentially useful, but contrib could do its own implementation for that.
Whatever we do here is probably going to require some usability review, so tagging.
Not sure we want to throw an exception, the no-op seems more in-keeping.
Not sure a checkbox on role edit form is a good idea - won't that mean you can assign multiple admin roles then?
Comment #12
dawehnerFair point
I went with the empty array approach, I would hate to require breaking the API for just that.
Feel free to disagree.
On top of that I disabled the checkbox for anonymous roles.
Mh that is a fair point. You could argue that you might want to be able to check additional behaviour based upon the role name itself, but I think this
was always a bad idea.
Comment #13
catchBerdir pointed out that user module is assigning all permissions on the system every time now, not just new permissions added by a module, which breaks the 'remove a permission' aspect of the current approach anyway. So even if we didn't drop that, we'd have to figure out how to bring it back.
Comment #14
dawehnerEven its a bit annoying work, I think a contrib module could easily alter the permissions form, change the way how Role::hasPermission is implemented by storing explicit the permissions you don't have access to.
Comment #15
catchYep, so let's ditch that for core and let contrib handle it.
CNW for the checkbox issue.
Comment #16
dawehnerAlright, this time the admin_role check is just internally.
Comment #18
dawehnerThis is why we have test coverage.
Comment #19
fabianx commentedI think having the role in storage is better, yes contrib could assign multiple admin roles, but I think its fine to do:
- remove isAdmin() from the role when a different role is selected
- add isAdmin() to the new role that is now the admin role.
I dislike having to check config:: everytime a permission is checked.
And I think catch was only opposed to the checkbox anyway ...
Comment #20
tim.plunkettThis conditional will never happen, because it's
->get('admin_role').We need tests for that
continue;statement.Comment #21
catchYes #19 is what I was thinking of. It's slightly messy having to set and unset from the form submit, but that hardly ever happens anyway.
Comment #22
dawehnerComment #23
fabianx commentednit - class variables need to be camelCase.
Love it!
CNW, little nits, but leaving for others to review, too.
Comment #24
berdir1. Not if they are stored in config. We have AFAIK one exception in view mode config entities, but everything else that maps to yaml/config properties uses lowercase. There are some discussions/issues around allowing a mapping, but for now, I think this is correct/consistent.
Comment #25
fabianx commentedWoops, that I did not know, but it makes perfect sense. Of course else how could it be mapped correctly.
Thanks for the clarification berdir.
RTBC! - Test coverage is good.
Comment #26
catchSo is the effect of this that we just don't show the admin role at all on the permissions page?
I think that needs some usability review, will ping Bojhan and yoroy.
Comment #27
berdirI think we can drop this, because the admin role no longer needs that permission? you can't take it away anyway? possibly extend with a description that says that the administrative role will always have access?
We could also ensure that the admin role is not even listed, I don't know.
Comment #28
catchCould use a screenshot to save anyone reviewing having to install and get to that page.
Comment #29
berdirLooking at the screenshots, this needs some work... it looks weird because it is missing the lines, and we should probably at least mention it in the help text above and maybe even remove it completely?
Comment #30
Bojhan commentedI am not sure we should ever display a role, that has no checkboxes. Either we display it somewhere else. Or we display unselectable-enabled checkboxes everywhere.
Comment #31
catchI thought about disabled checkboxes in irc, but I remember some usability testing where these were a problem? Or is that mis-remembering?
Comment #32
Bojhan commentedThey are awful, but I guess better than having an empty column. That you have no clue that the admin does have all the permissions - because its showing nothing.
We can make an alternative design, but I am not sure if that still fits in our beta guidelines.
Comment #33
berdirit's also a pattern that we already use on the site, when you enable a permission for the authenticated user role, all other roles except anon are disabled like that. So it might be awful, but at least it is consistently awful ;)
Not showing at all has a small performance improvement, because generating all those checkboxes takes a lot of time, but yeah, it is probably better to do it like this.
Comment #34
dawehnerThat is a really good point!
Well, maybe someone wants to allow a specific format just for the admin, in which case, they might expected it to find there.
I Agree, its at least less ugly than showing nothing, you really can't disagree here. For a while I was thinking about showing nothing for the admin role,
but this is also kinda confusing, as people might think that this role magically disappeared in the meantime.
Comment #35
fabianx commentedLets add some new screenshots.
Code wise looks RTBC to me.
Comment #36
berdirPermissions page looks fine to me now.
Comment #37
alexpottNot used.
This interaction is extremely problematic. I think we should only display the user admin_role is there is 0 or 1 selected. Plus with this form it is not possible to not have an admin role.
Comment #38
dawehnerFair point.
Comment #39
alexpottAfaics you still have to select a role... There isn't the option to not have an admin role - which is a security issue.
Also this logic needs tests
Comment #40
dawehnerMh, really? You can select '- None -' still.
Here is a test which indeed showed some problems.
Comment #41
berdirAh, this is needed because of the #access check below and shift removed one entry? I guess reset() would work too?
Looks good, the new code now has test coverage.
Comment #42
dawehnerYeah the
array_shiftbroke stuff for 2 roles.Comment #43
jibranBad merge/rebase.
This reverts #2431283: Cron CSRF vulnerability.
This reverts #2395901: Allow the same test-specific overrides in KernelTestBase as in WebTestBase
Comment #44
dawehner@jibran Thanks for seeing it!!
A straight reroll worked.
Comment #45
jibranIMO we need a change notice for this.
Comment #46
alexpottYep we should have a change record for this since this we break the existing administrator role on already installed sites.
I think this is cleaner as:
Less logic. Thanks @Berdir in #41 :)
Comment #47
dawehnerHere is one, with a slightly better match.
https://www.drupal.org/node/2444095
Comment #48
fabianx commentedBack to RTBC then.
Comment #49
alexpottFixed beta evaluation.
Comment #50
dawehnerHa, fair point!
Comment #51
catchLooks good now. Committed/pushed to 8.0.x, thanks!