Problem/Motivation

This is approved as a public security hardening in #25.

Access to modify a users role is handled inconsistently. The user access control handler incorrectly allows users with administer users to modify roles. The account form correctly requires administer permissions to edit roles of a user since it is implementing its own form elements.

The permission administer permissions is meant to be used to assign roles to users, CRUD roles, and to edit permissions grants for each role. Not administer users.

Drupal 7

In Drupal 7, when you go to a users page, you can not edit the roles of the user. Nor can you use the bulk "[Assign/Remove] a role from the selected users" operation on admin/people. Unless you have administer permissions.

Drupal 8

In Drupal 8, if you do not have administer permissions, you also cannot edit the roles when you are on the /user/%/edit page.

However if you have administer users, you can go to /admin/people and use the 'add the XXXX role to the selected users' actions.

This is a problem, because the user with administer users can assign himself the built-in 'Administrator' role. Which gives him all permissions, including the ability to edit permissions.

This effectively makes administer permissions useless.

Proposed resolution

  1. Disallow users without administer permissions to edit the user roles in the user edit form.
  2. Add a mechanism to determine if the current user is allowed to use a specific action. Note that the actual action plugin ::access() method is only a run-time check, checking if the action can be done by the current user, on a specific object/entity. But we need more generic check to see if a user is able to read/access an action before the action starts to be executed. The method could be used, mainly, on views with bulk operation form, where the list of actions should be limited to the actions that the current user is allowed to access.
  3. Use the above check to filter out the add/remove role operations from the bulk operations form on /admin/people view.

Remaining tasks

None.

User interface changes

Users without administer permissions permission will not see anymore:

  • The roles field on user account edit form.
  • The add/remove role actions, in the bulk operations selector, on user administrative page, at /admin/people

API changes

  • The action entity defines now its own access control handler in the annotation, pointing to:\Drupal\system\ActionAccessControlHandler
  • ActionAccessControlHandler::checkAccess() will grant the access to the action, first, if the current user has administer actions permission. If not, will ask the plugin to make a decision.
  • New method ::userAccess() in the plugin ActionInterface interface.
  • ActionBase::userAccess() will return TRUE. In this way we ensure the Backward Compatibility
  • Implement ChangeUserRoleBase::userAccess() by checking the user permission.
  • In \Drupal\views\Plugin\views\field\BulkForm, limit the bulk operation action options based on action entity ::access().

Data model changes

None.

Original report by kevin.dutra

Access to the roles field on the User entity is not handled consistently. This gap has been made possible due to the sort of non-standard approach that the AccountForm takes by providing form elements for fields directly in the form builder instead of using the field system to handle form display with normal widgets. Since it's providing the form elements, it's also setting the #access property, but not in a manner that is consistent with the actual field access. As a result, there is a disconnect between what can be done via the UI and via the REST API for the same user.

The AccountForm only allows access to the roles field for a user with administer permissions.

    $form['account']['roles'] = array(
      '#type' => 'checkboxes',
      '#title' => $this->t('Roles'),
      '#default_value' => (!$register ? $account->getRoles() : array()),
      '#options' => $roles,
      '#access' => $roles && $user->hasPermission('administer permissions'),
    );

However, the UserAccessControlHandler allows anyone with administer users access to the roles field.

    // Administrative users are allowed to edit and view all fields.
    if (!in_array($field_definition->getName(), $explicit_check_fields) && $account->hasPermission('administer users')) {
      return AccessResult::allowed()->cachePerPermissions();
    }

Left to be done

  1. Decide on who should actually have access to the field
  2. Create a patch
CommentFileSizeAuthor
#90 2846365-90.patch20.95 KBSuresh Prabhu Parkala
#89 diff_83-88.txt18.25 KBMarios Anagnostopoulos
#89 2846365-88.patch21.49 KBMarios Anagnostopoulos
#88 2846365-83.patch21.49 KBMarios Anagnostopoulos
#83 interdiff_82-83.txt4.73 KBranjith_kumar_k_u
#83 2846365-83.patch21.2 KBranjith_kumar_k_u
#82 2846365-82.patch21.42 KBEmberhood
#82 2846365-82.patch21.42 KBEmberhood
#72 2846365-72.patch23.71 KBtrubach
#67 2846365-67.interdiff.txt5.49 KBclaudiu.cristea
#67 2846365-67.patch23.72 KBclaudiu.cristea
#65 2846365-65.patch20.49 KBclaudiu.cristea
#65 2846365-65.interdiff.txt9.71 KBclaudiu.cristea
#63 2846365-63.interdiff.txt10.24 KBclaudiu.cristea
#63 2846365-63.patch15.62 KBclaudiu.cristea
#60 2846365-60.patch14.55 KBJacobSanford
#60 interdiff-2846365-43-60.txt11.55 KBJacobSanford
#43 2846365-43.patch14.47 KBjofitz
#43 interdiff-42-43.txt6.05 KBjofitz
#42 2846365-42.patch12.9 KBjofitz
#17 user-field-access-roles-2846365-17.patch13.05 KBdpi
#17 user-field-access-roles-2846365-17-interdiff.txt1.54 KBdpi
#12 user-field-access-roles-2846365-9-testsonly.patch11.18 KBdpi
#10 user-field-access-roles-2846365-9.patch12.37 KBdpi
#5 user-field-access-roles-2846365-5.patch5.74 KBdpi
#4 user-field-access-roles-2846365.patch31.27 KBdpi

Issue fork drupal-2846365

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:

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

kevin.dutra created an issue. See original summary.

kevin.dutra’s picture

I'm happy to provide a patch, but I can't really make the call on which way the access should really work.

On the one hand, administer users is the admin permission for the User entity, so it seems like it should have access to all the fields. But on the other hand, I can understand the desire to restrict this specific field because it governs what that user will have access to, so you may not want someone to be able to grant every role, even though they can edit all the other fields. (You may want to use something like the Role Delegation module so that they can only choose from an allowed set of roles.)

dpi’s picture

Decide on who should actually have access to the field

Anything to do with roles should be controlled by administer permissions. Including CRUDL role entities, editing permissions for roles, or assigning roles to users. The account form isnt the only place that uses the correct permission to edit roles for users.

Seems like a oversight/bug.

dpi’s picture

Status: Active » Needs review
FileSize
31.27 KB

Uploading to see what kind of things this breaks.

dpi’s picture

The last submitted patch, 4: user-field-access-roles-2846365.patch, failed testing.

Status: Needs review » Needs work

The last submitted patch, 5: user-field-access-roles-2846365-5.patch, failed testing.

dpi’s picture

Assigned: Unassigned » dpi

Still working on it.

dpi’s picture

Issue tags: +Security improvements

Security issue Users with 'administer users' permission can gain full access relates to this issue.

The vulnerability was approved to be fixed publicly since it requires a role with the somewhat trusted administer users permission.

dpi’s picture

Status: Needs work » Needs review
FileSize
12.37 KB
dpi’s picture

Title: User roles field access is not consistent » User roles field access is inconsistent, users with 'administer users' permission can gain full access
Issue summary: View changes

Moved majority of security submission to issue summary, original issue summary left intact

dpi’s picture

Status: Needs review » Needs work

The last submitted patch, 12: user-field-access-roles-2846365-9-testsonly.patch, failed testing.

dpi’s picture

Status: Needs work » Needs review

Comment #10 contains the patch for review.

kevin.dutra’s picture

Everything looks good to me, but the one thing I'm wondering is: should we also update the form element on the account form so that it's actually invoking the access control handler? From a DX perspective, if I wanted to tweak the way the access worked for that field, I'd implement the entity field access hook, but then I'd also have to go and alter the form currently, which is pretty dorky.

jibran’s picture

Status: Needs review » Needs work

NW for comment fix other than that this RTBC.

  1. +++ b/core/modules/user/src/UserAccessControlHandler.php
    @@ -78,9 +78,10 @@ protected function checkFieldAccess($operation, FieldDefinitionInterface $field_
    +    // Administrative users are allowed to edit and most fields.
    

    and most fields? Let's rewrite this compelety.

  2. +++ b/core/modules/user/src/UserAccessControlHandler.php
    @@ -126,6 +127,8 @@ protected function checkFieldAccess($operation, FieldDefinitionInterface $field_
           case 'roles':
    +        return AccessResult::allowedIfHasPermission($account, 'administer permissions');
    

    This is the only fix and using admin field totally makes sense here.

dpi’s picture

#15 should we also update the form element on the account form so that it's actually invoking the access control handler?

I think this makes sense. Attached a change which uses the same method the role action plugins use to determine access.

#16 and most fields? Let's rewrite this compelety.

Agreed and done.

jibran’s picture

Status: Needs review » Reviewed & tested by the community

Let's see if testbot agrees.

kevin.dutra’s picture

Nicely done. Thanks for the patch @dpi! :)

Version: 8.2.x-dev » 8.3.x-dev

Drupal 8.2.6 was released on February 1, 2017 and is the final full bugfix release for the Drupal 8.2.x series. Drupal 8.2.x will not receive any further development aside from critical and security fixes. Sites should prepare to update to 8.3.0 on April 5, 2017. (Drupal 8.3.0-alpha1 is available for testing.)

Bug reports should be targeted against the 8.3.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.4.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Status: Reviewed & tested by the community » Needs work

The last submitted patch, 17: user-field-access-roles-2846365-17.patch, failed testing.

dpi’s picture

Status: Needs work » Reviewed & tested by the community

Random CI failure

catch’s picture

Version: 8.3.x-dev » 8.4.x-dev
Issue tags: +Needs subsystem maintainer review

I'm going to move it to 8.4.x, while it's security hardening, it's potentially disruptive to sites relying on the existing behaviour.

moshe weitzman’s picture

I reviewed this patch for code and test coverage and it looks solid. Thanks for untangling these permissions ... 8.4 sounds reasonable to me as well.

xjm’s picture

Yeah, it's clear to me that the permission is wrongly implemented from the summary, but "fixing" it if they are already relying on D8 behavior can lock people out of their sites. So we need to be careful about fixing it.

Also, just wanted to state explicitly that this is okay as a public issue, because the permission is already marked as a restricted admin permission.

I wonder if a PSA would be appropriate to inform people of the current behavior and the upcoming change? Or is that too big a deal for what is already an admin permission? Other than that, at least I think we will need a change record and release notes mention for 8.4.0.

xjm’s picture

Crossposted. Thanks @moshe weitzman!

xjm’s picture

Issue tags: +Needs change record

 

xjm’s picture

Issue summary: View changes

 

xjm’s picture

Issue tags: +8.4.0 release notes

First one. :)

xjm’s picture

Status: Reviewed & tested by the community » Needs work

NW for the CR, at least.

dpi’s picture

dpi’s picture

Status: Needs work » Needs review
Issue tags: -Needs change record
xjm’s picture

Thanks @dpi for the change record draft! I updated its title to be more clear, since this is an important change users need to understand right away: Administer users permission no longer allows assigning roles.

I think the change record could also use some additional improvements for clarity. "Traditionally" doesn't make sense here. We can just say "In Drupal 7" since this is a change record. It also does not make it clear in the text what exactly was changed in the text. Also, adding headers so the user can quickly update the "before" and "after" would be helpful. What does look really good is the very clear explanation of the impact of this change.

I'll try to propose some improvements.

xjm’s picture

Alright, I did an extensive update of the change record: https://www.drupal.org/node/2853612. I removed the screenshot because it was confusing as it focused on the past loophole, when actually the CR should communicate the current state to users. I also rewrote it focusing on the before-and-after and what the site owner would need to know. We can then reference the CR in the release notes.

xjm’s picture

(Updating issue credit.)

dpi’s picture

CR changes look great, thanks!

xjm’s picture

Status: Needs review » Reviewed & tested by the community

Looks like this never got set back to RTBC for the change record review, so doing that now. Thanks @dpi.

Version: 8.4.x-dev » 8.5.x-dev

Drupal 8.4.0-alpha1 will be released the week of July 31, 2017, which means new developments and disruptive changes should now be targeted against the 8.5.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

xjm’s picture

Issue tags: -8.4.0 release notes

It was the first one, but it's not one anymore. :P

xjm’s picture

Status: Reviewed & tested by the community » Needs work
Issue tags: +Novice

So there are a couple of small things that need work in this patch (I did not do a full review in #37):

  1. +++ b/core/modules/user/tests/src/Unit/UserAccessControlHandlerTest.php
    @@ -40,13 +40,20 @@ class UserAccessControlHandlerTest extends UnitTestCase {
    +   * A user with 'administer users' permission.
        *
        * @var \Drupal\Core\Session\AccountInterface
        */
       protected $admin;
    
    @@ -336,6 +362,60 @@ public function passwordAccessProvider() {
    +        'viewer' => 'admin',
    

    Since we now have two accounts, one that has admin users but not admin roles, and one that has admin roles but not admin user, I think we should probably rename the $admin property and the admin user name to be admin_users instead. Otherwise, the test is difficult to read and confusing.

  2. +++ b/core/modules/user/tests/src/Unit/UserAccessControlHandlerTest.php
    @@ -336,6 +362,60 @@ public function passwordAccessProvider() {
    +  /**
    +   * Provides test data for passwordAccessProvider().
    +   */
    +  public function rolesAccessProvider() {
    

    This docblock is incorrect.

  3. +++ b/core/modules/user/tests/src/Unit/UserAccessControlHandlerTest.php
    @@ -336,6 +362,60 @@ public function passwordAccessProvider() {
    +    $pass_access = array(
    ...
         );
         return $pass_access;
    

    The variable name also is a copy-and-paste name that does not fit this new access provider.

  4. +++ b/core/modules/user/tests/src/Unit/UserAccessControlHandlerTest.php
    @@ -336,6 +362,60 @@ public function passwordAccessProvider() {
    +      array(
    

    All of these old-style arrays should be converted to the [] syntax.

These are all small cleanups that could be done by a novice contributor. Thanks!

Dinesh18’s picture

Issue tags: +Needs reroll

Needs reroll

jofitz’s picture

Assigned: dpi » jofitz
Status: Needs work » Needs review
Issue tags: -Needs reroll
FileSize
12.9 KB

Straight re-roll (before addressing @xjm's comments in #40).

jofitz’s picture

Assigned: jofitz » Unassigned
Issue tags: -Novice
FileSize
6.05 KB
14.47 KB

Addressed @xjm's comments in #40.

  1. Property and username changed from 'admin' to 'admin_users'.
  2. Corrected the docblock (and also the docblock for passwordAccessProvider()).
  3. Updated $pass_access to $role_access.
  4. Short array syntax is now used throughout.

The last submitted patch, 42: 2846365-42.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

Status: Needs review » Needs work

The last submitted patch, 43: 2846365-43.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

AdamPS’s picture

Please could the Core experts consider another potential security aspect - at present there is a way in which this fix could reduce security.

Summary

  • In the patch AccountForm::form() calls $account->roles->access().
  • This leads to hook_entity_field_access being invoked based on the current roles.
  • If that passes, the form allow the current user to assign any role.
  • From reading the hook_entity_field_access docs, I expect the hook to be invoked with the proposed new roles. Otherwise it seems impossible for the hook to control which roles can be assigned.

Example

An example is the module Administer Users by Role (where I am a maintainer). It grants access to "sub-admin" users to

  • edit users with safe roles: hook_entity_access
  • assign/remove selected "safe" roles to users that currently have only safe roles: hook_entity_field_access.

In D8.4, this code is perfectly safe. The roles field is by default not present on /user/XX/edit.

When this fix is applied, this code has a security bug. The roles field is by default present on /user/XX/edit and all roles are available, including admin. The sub-admin can assign admin permission to themselves.

The fix is for the custom code to use hook_form_FORM_ID_alter to filter 'roles' to include only safe values. Fortunately "Administer Users by Role" already does this. However it seems possible that other code exists that doesn't.

Proposal

Ideally AccountForm::Form would automatically reduce the list of roles to only those allowed. However I can't see how.

Fallback is to ensure that when the form verifies, the actual assigned roles are checked again with $account->roles->access.

AdamPS’s picture

For a module to customise access to roles (e.g. to assign specific roles but not other roles) it needs

  • hook_entity_field_access for 'roles'
  • hook _form_user_form_alter for 'roles' to set #access and #options

Unless someone can see a clever way to do both parts of the second bullet automatically then I reckon the safest approach is to leave AccountForm.php unchanged in this patch. Changing #access but not #options is not safe or helpful.

kevin.dutra’s picture

@AdamPS, limiting access to assign individual roles is an entirely different issue and not one that would be addressed here. The functionality you're describing sounds a lot like the Role Delegation module. You might want to check out their approach.

AdamPS’s picture

@kevin.dutra Thanks for the response.

The background to #47 is in #46. In summary, I believe the current patch is insecure. Now I may be wrong and I'm quite happy to have someone explain why (but this is not just theoretical - I did an actual test). However my comment is not "an entirely different issue" - I am talking about the security of this patch. Apologies if I didn't make that clear.

I'll try and explain the scenario more clearly.

  • Create a custom module that implements hook_entity_field_access to allow a user to remove all of their own roles (rough code below, but not tested).
  • Log in as a user with no roles and edit own account.
  • Form displays input for roles (the roles field has no roles hence the access check passes) with all roles available.
  • Assign self administrator role and save.

This custom module is perfectly safe in D8.4, but not safe after the patch.

function XX_entity_field_access($operation, FieldDefinitionInterface $field_definition, AccountInterface $account, FieldItemList $items = NULL) {
  if ($field_definition->getTargetEntityTypeId() != 'user') {
    return AccessResult::neutral();
  }

  if (($field_definition->getName() == 'roles') && ($operation == 'edit') && ($account->id() ==$items->getEntity()->id())) {
   // @todo Or perhaps it needs to be count(==1) because of authenticated user role
    return AccessResult::allowedIf(empty($items->getValue()));
  }
  return AccessResult::neutral();
}

kevin.dutra’s picture

@AdamPS, as far as I can tell, the behavior you are describing is the expected behavior. When you grant access to a field like the roles, then yes, you would have access to all of the roles that exist. This is why the out of the box core behavior is to only allow users with the administer permissions permission because this is a very sensitive field. (As you've described in the exploit where a user could augment their own access.)

If a third party module extends access to that field to additional users, it would be that module's responsibility to do it in a secure manner. Even without this patch, if a third party module is extending access in the manner you describe, it is already allowing the exploit you've described if they can operate via the REST API, a bulk action in a View, etc.

This issue is all about bringing access control into alignment. And yes, this would make it more visible if there was an issue with existing implementations of field access on this field because the user form would finally be subject to the same access as gets used everywhere else. Does that explanation make sense?

AdamPS’s picture

@kevin.dutra Thanks for a clear explanation. The point at which my understanding differs is

Even without this patch, if a third party module is extending access in the manner you describe, it is already allowing the exploit you've described if they can operate via the REST API, a bulk action in a View, etc.

With my hook

  • The user attempts to remove their roles with bulk API, that leads to access('update, 'roles'. []) which succeeds.
  • The user attempts to add admin roles with bulk API, that leads to access('update, 'roles'. ['admin']) which fails.
  • The user accesses user/edit in D8.4, there is no roles field

So it's all safe. However including this patch, if the user (with no roles) accesses user/edit to add admin role, that leads to access('update, 'roles'. []), which succeeds.

The problem is as I said in #46: that the patch is calling $account->roles->access() passing the current value of role then allowing the user to assign an arbitrary new value. The correct usage of access is to pass the new value.

The key to my argument is that access check is not just "can user U access field F" but rather "can user U access field F to set value V" - where V is the $items parameter.

kevin.dutra’s picture

Ah, I see what you're getting at. The problem is, I don't think the API really guarantees when the access check is going to occur, so you would not be able to use the values of the field in that manner. It does provide a FieldItemList as an argument, but field access can be checked at any time, so it might be done on the current entity as-is or it might be done on they entity with changes. (I believe this is one of the pain points that Role Delegation ran into and why they changed their design to create a totally separate form for allowing users to modify roles that was not subject to the same access requirements.)

Anywho, the change record is there to give notice of the change and allow contributed modules the ability to react to it. IMHO, although it does produce different results for that one use case, I don't think it should stop the patch from continuing as-is. It reduces the number of one-offs floating around, and if someone wanted to do something along the lines of the code sample from above, it wouldn't be much more effort to throw in a quick form alter to change the #access property of the role field.

AdamPS’s picture

@kevin.dutra I am rather baffled by your comment. It's great that, as far as I understand, you now accept my argument that this patch introduces a new security vulnerability. However I can't understand why you seem to think it's fine to go ahead anyway (it feels rather dismissive of my point of view:-))

Unless I have missed something, this issue would still be solved even without the change to AccountForm - so why can't we go that way? You suggest "It reduces the number of one-offs floating around" which is a reasonable but low-priority argument - surely way, way less important than security?

I am not just commenting here to make your life difficult! I am the maintainer of Administer Users by Role. The latest D8 version has I hope now successfully achieved the goal of allowing granular assignment of permission to edit users and assign roles fully integrated with Core - using Entity Access API, the existing forms, the existing Actions and so on. It wasn't altogether easy, and has lead to some tidy up of Core #2921112: [META] User access control that is progressing towards getting committed.

I don't think the API really guarantees when the access check is going to occur

The access API has a field $items, which as far as I can see when called from REST API, Views Bulk etc. contains the new value. This gives D8 an access API where the decision can depend on the value. Surely this is a good thing - why try to throw it away? I struggle to see how the $items parameter can be used unless it has this meaning.

if someone wanted to do something along the lines of the code sample from above, it wouldn't be much more effort to throw in a quick form alter

How would someone know they needed to do that? What if another contrib module had copied the the code from Core assuming it was good code? - there would be a missing form alter for the copied code. Why make it more effort for Drupal to be secure as part of a "security hardening" patch:-)?

the change record is there

For the record, the change record does not seem to make any mention that this change makes Drupal less secure in certain scenarios - and quite rightly so. Please don't edit the CR to say that - let's change the patch instead.

kevin.dutra’s picture

It's great that, as far as I understand, you now accept my argument that this patch introduces a new security vulnerability.

Hi @AdamPS, I think you're misinterpreting what I'm saying. In no way do I accept that this patch introduces a security vulnerability. Quite the opposite, actually. By allowing the user form to function differently than the access control set up everywhere else, it is inherently less secure, as a third party module can adjust the field access via a hook in an incorrect manner, but the form would give them the false impression that it was still secure.

This gives D8 an access API where the decision can depend on the value. Surely this is a good thing - why try to throw it away?

I'm not at all suggesting that the API should be changed, but it would be good if it were better documented. It's perfectly fine to make use of the field items, but you have to understand that the contents therein may be current values or proposed new values, just depending on when the access is being checked. As the documentation suggests, it's not even guaranteed to be provided to you, as it's an optional parameter. (For example, when Views is deciding whether to render a column, it does an access check without any $items to see if the user has access to the field. If they don't, it omits the column altogether.)

How would someone know they needed to do that? What if another contrib module had copied the the code from Core assuming it was good code?

There's nothing wrong with the code. I'm not sure what core code they would be copying from core that you think would be problematic. Suffice it to say that if someone is creating an access module and they don't have a fully understanding of the access API and the ramifications of the changes they are introducing, or have sufficient test coverage to catch discrepancies with their assumptions of how things should work, then they have no business releasing that code into the wild.

I understand that you're the maintainer of Administer Users by Role, and I appreciate your point of view. My aim is not to be antagonistic, but I disagree with the notion that the patch makes things less secure. It merely doesn't work the way you want it to with your current implementation. Like I said, I don't think the current documentation around the $items parameter is sufficient and I think that's the main pain point here. If that was better communicated, then I think people will better understand the risk/limitations of trying to make use of field values to check access. If we create a followup to improve that documentation, would you agree that the user form change becomes a non-issue?

AdamPS’s picture

Thanks for the follow up.

I don't think the current documentation around the $items parameter is sufficient

Certainly we can agree on that.

In no way do I accept that this patch introduces a security vulnerability.

Wait a mo. I thought I explained in detail a specific scenario in which this patch changes a site from being secure to not being secure, and in #52 you said "Ah, I see what you're getting at." Please can you clarify? Aha I guess that in your view the code in the specific scenario was wrong in the first place, because of the next point. Now "I see what you are getting at":-).

It's perfectly fine to make use of the field items, but you have to understand that the contents therein may be current values or proposed new values, just depending on when the access is being checked.

So this seems the key to it all. Please can you explain what evidence you have to support this? So far you have merely asserted that my interpretation is wrong and yours is right:-)

I realise you may have an overwhelming reason that I don't know about. But otherwise I don't favour that interpretation as it seems confusing and hard to use. I can't see how to control access to allow one value but not another. I can't see how to write secure code that references $items (other than to call ->getEntity() etc.). I can't see how to grant field access to 'roles' (except to a full unlimited administrator - who presumably has 'administer permissions' anyway so there is no point). Because in each case, the attacker can arrange for their old value to pass your access test, then supply an arbitrary new value.

It's a shame as it seems to prevent modules like "Administer Users by Role" from granting selective field access on 'roles' (and same for any other field where access needs to depend on value).

BTW I am already aware that $items an optional parameter, and I don't see any difficulty with that case - I'd say it's the non-NULL that we should focus on.

If we create a followup to improve that documentation, would you agree that the user form change becomes a non-issue?

Not yet - but if you convince me of the previous point then yes.

AdamPS’s picture

@kevin.dutra I have raised #2925042: Unclear documentation for field access - please can you read it and add your comments?

idebr’s picture

AdamPS’s picture

@kevin.dutra With some help from @Berdir on #2925042: Unclear documentation for field access I have found the evidence that backs up your claim about how the access functions should be used. I no longer have any concerns from a security perspective and I withdraw my request from #46. Thanks for your patience, and I hope you will agree it's worth taking time to remove security doubts. In this case the debate has lead me to discover there was a security flaw in the alpha release of the Administer Users by Role module, so thank you.

I have one minor item of feedback on the patch

-      '#access' => $roles && $user->hasPermission('administer permissions'),
+      '#access' => $roles && $account->roles->access('edit', $user, TRUE),

Why not just $account->roles->access('edit')?

Version: 8.5.x-dev » 8.6.x-dev

Drupal 8.5.0-alpha1 will be released the week of January 17, 2018, which means new developments and disruptive changes should now be targeted against the 8.6.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

JacobSanford’s picture

@Jo Fitzgerald's patch in #43 no longer applied to 8.6.x. A reroll with no further modifications is attached. @AdamPS's suggestion to simplify the access check has not been implemented.

zahord’s picture

Status: Needs work » Needs review

changing status to allow tests last patch

Status: Needs review » Needs work

The last submitted patch, 60: 2846365-60.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

claudiu.cristea’s picture

  • Fixed the test failure.
  • Using non-deprecated code on the new test.
  • Renamed the new account from the unit test to a more meaningful names.
  • Fixed the coding standards failures

Also, this, being a bug is eligible for 8.5.x.

AdamPS’s picture

Status: Needs review » Needs work

@claudiu.cristea Nice work, good to get this issue moving again. I think the review comment from #58 is still valid.

  '#access' => $roles && $user->hasPermission('administer permissions'),
+      '#access' => $roles && $account->roles->access('edit', $user, TRUE),

Why not just $account->roles->access('edit')?

claudiu.cristea’s picture

Status: Needs work » Needs review
FileSize
9.71 KB
20.49 KB

However, we are facing a UX problem here. We are showing the option (eg "Add the XXXX role to the selected user(s)") to the unprivileged user but in reality he's not able to execute that operation, receiving an error message. What a flawed user experience for the 3rd millennium AD!!! Instead, additionally to what has been done so far, we should also hide the options. But this is hard to do because the action plugin access is checking the access on a per-object (entity) basis. So, it's a runtime check. We need a check that doesn't go that far, we have to check if the current user is able to access that action. For this reason, this patch is expanding a little bit the scope of this issue:

  • The action config entity defines now an access handler.
  • The access handler delegates the check to a new plugin method ActionInterface::userAccess().
  • The ActionBase abstract implements ::userAccess() by returning TRUE. In this way, we ensure the BC.
  • We implement ::userAccess() also in ChangeUserRoleBase and we check there for the administer permissions permission.
  • In BulkForm Views field plugin, we check also the access when we build the list of actions.

The patch contains also the #58 fix.

Status: Needs review » Needs work

The last submitted patch, 65: 2846365-65.patch, failed testing. View results

claudiu.cristea’s picture

Status: Needs work » Needs review
FileSize
23.72 KB
5.49 KB

Fixed some docs and test failures. If the user has 'administer actions', permission, that should prevail over plugin check.

claudiu.cristea’s picture

Title: User roles field access is inconsistent, users with 'administer users' permission can gain full access » [regression] User roles field access is inconsistent, users with 'administer users' permission can gain full access
Version: 8.5.x-dev » 8.6.x-dev
Issue summary: View changes

Updated the IS to reflect the patch. Updated the CR and, yes, because this introduces kind of disruption, it should go in 8.6.x. Changed the title to reflect that this is a regression from D7.

idimopoulos’s picture

+++ b/core/modules/system/src/ActionAccessControlHandler.php
@@ -0,0 +1,26 @@
+    return AccessResult::allowedIfHasPermission($account, $admin_permission)->orIf($plugin_result);

I haven't done a full review on this, just some local tests. This line troubles me a bit though.
What this does is that it allows the user to view the action if he has the entity type's admin permission OR if the plugin allows it.
However, if the user has the 'administer actions' permission, and not the 'administer permissions' permission, in a bulk form, he will be able to view the Add/Remove roles actions but if he attempts to execute them, he will get an access denied error.

While this fixes the security issue of the ticket, it is a bad UX as - since there are not per-action permissions - we give the user the impression that he can mangle with the roles of the user until he actually tries to execute the action.

Initially I was thinking that an andIf would be a solution to that but in reality, this would mean that it would not suffice that the user would have the administer permission of the entity type.

For the record, I do not think that this makes the current implementation a bad approach, I just think that we still lack a per-action permissions system to take care of that.

Version: 8.6.x-dev » 8.7.x-dev

Drupal 8.6.0-alpha1 will be released the week of July 16, 2018, which means new developments and disruptive changes should now be targeted against the 8.7.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.7.x-dev » 8.8.x-dev

Drupal 8.7.0-alpha1 will be released the week of March 11, 2019, which means new developments and disruptive changes should now be targeted against the 8.8.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

trubach’s picture

Reroll of #67 that applies to 8.7.x and 8.8.x

Status: Needs review » Needs work

The last submitted patch, 72: 2846365-72.patch, failed testing. View results

Version: 8.8.x-dev » 8.9.x-dev

Drupal 8.8.0-alpha1 will be released the week of October 14th, 2019, which means new developments and disruptive changes should now be targeted against the 8.9.x-dev branch. (Any changes to 8.9.x will also be committed to 9.0.x in preparation for Drupal 9’s release, but some changes like significant feature additions will be deferred to 9.1.x.). For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

Version: 8.9.x-dev » 9.1.x-dev

Drupal 8.9.0-beta1 was released on March 20, 2020. 8.9.x is the final, long-term support (LTS) minor release of Drupal 8, which means new developments and disruptive changes should now be targeted against the 9.1.x-dev branch. For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

Version: 9.1.x-dev » 9.2.x-dev

Drupal 9.1.0-alpha1 will be released the week of October 19, 2020, which means new developments and disruptive changes should now be targeted for the 9.2.x-dev branch. For more information see the Drupal 9 minor version schedule and the Allowed changes during the Drupal 9 release cycle.

Warped made their first commit to this issue’s fork.

Warped’s picture

This issue seems to have been lost in the shuffle for 2 years? I'm experiencing it on 8.9.13

The manager who has the right to create accounts and grant elevated roles to users is not allowed to change role permissions as they could change the way the site is configured. Changes that require more knowledge about how Drupal works than someone who is just managing users would not normally need to know. Changes that would have been based on decisions when the site was created. The fact that "Roles" are not displayed on the user maintenance screen, but are available on the bulk operations is inconsistent and may not be noticed because the bulk operations must be viewed as a drop down selection. Granted, the https://www.drupal.org/project/role_delegation and https://www.drupal.org/project/administerusersbyrole are options, but another user might be the one delegated to control permissions. Then an additional module would not be required.

Changes should be made one way or the other to avoid inconsistencies. Either allow both bulk operations AND the edit profile form to change the roles (by removing the authority check from the edit profile form), or remove the options from the bulk operations list (by applying the patch above). Or maybe a 3rd way of adding "Change user roles" and "Change own roles" to core. Then all that would be needed additionally, if wanted, would be an option to limit changing roles to those below your own role, or a selection of available roles allowed.

Version: 9.2.x-dev » 9.3.x-dev

Drupal 9.2.0-alpha1 will be released the week of May 3, 2021, which means new developments and disruptive changes should now be targeted for the 9.3.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.3.x-dev » 9.4.x-dev

Drupal 9.3.0-rc1 was released on November 26, 2021, which means new developments and disruptive changes should now be targeted for the 9.4.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

s.messaris’s picture

Still relevant in 9.3.

Patch doesn't apply.

Emberhood’s picture

Status: Needs work » Needs review
FileSize
21.42 KB
21.42 KB

Reroll of #67 that applies to 9.3.x and 9.4.x

ranjith_kumar_k_u’s picture

Status: Needs review » Needs work

The last submitted patch, 83: 2846365-83.patch, failed testing. View results

Version: 9.4.x-dev » 9.5.x-dev

Drupal 9.4.0-alpha1 was released on May 6, 2022, which means new developments and disruptive changes should now be targeted for the 9.5.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.5.x-dev » 10.1.x-dev

Drupal 9.5.0-beta2 and Drupal 10.0.0-beta2 were released on September 29, 2022, which means new developments and disruptive changes should now be targeted for the 10.1.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 10.1.x-dev » 11.x-dev

Drupal core is moving towards using a “main” branch. As an interim step, a new 11.x branch has been opened, as Drupal.org infrastructure cannot currently fully support a branch named main. New developments and disruptive changes should now be targeted for the 11.x branch, which currently accepts only minor-version allowed changes. For more information, see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Marios Anagnostopoulos’s picture

FileSize
21.49 KB

Here is a reroll for 9.5.10 for anyone who might need it.

Leaving is as needs work until someone posts a patch for at least 10.x

Marios Anagnostopoulos’s picture

FileSize
21.49 KB
18.25 KB

Re-uploading the same patch with the correct name.
Also adding an interdiff.

Suresh Prabhu Parkala’s picture

Status: Needs work » Needs review
FileSize
20.95 KB

Patch for the latest 11.x. Please review.

smustgrave’s picture

Status: Needs review » Needs work

Please include interdiffs with patches or a comment of what you changed.

The last patch doesn't pass commit checks, could you make sure to run ./core/scripts/dev/commit-code-check.sh before uploading a patch to make sure there are no issues with code formatting. see https://www.drupal.org/docs/develop/development-tools/running-core-devel...