Drupal 8: Prevent User Role Elevation

25th October 2019

Drupal has a little flaw in its user permission system that allows users to give themselves, or other users, roles that they shouldn't be able to. If the user has the 'administer users' permission this essentially gives them access to alter roles for any user on the system, meaning that they can grand administrator access to any user on the system.

The fix to this involves a couple of actions.

The first to set up user roles in a very specific order from least amount of permission (ie. anonymous) to most amount of permissions. This is actually the default way that Drupal is set up out of the box, but be sure that when adding more roles that you keep to this order. Here is a typical Drupal role administration screen with some other permissions set up.

Drupal user roles

The second is to create a hook that will alter the user edit screen and hide roles from the user if they don't have the 'administer permissions' permission. With this code in place the user will still be able to visit the user edit screen and alter user roles, but they don't be able to give any user a role that is higher than their own role.

  1. /**
  2.  * Implements hook_form_FORM_ID_alter().
  3.  */
  4. function my_module_form_user_form_alter(&$form, FormStateInterface $form_state) {
  5. // Get the current user and their roles (minus 'authenticated').
  6. $user = \Drupal::currentUser();
  7. $userRoles = $user->getRoles(TRUE);
  8.  
  9. if ($user->hasPermission('administer permissions')) {
  10. // Don't do anything here as the user has the correct permissions.
  11. return;
  12. }
  13.  
  14. // Extract the role options from the form.
  15. $options = $form['account']['roles']['#options'];
  16.  
  17. $removeRole = FALSE;
  18.  
  19. // Remove any roles that are above the user.
  20. foreach ($options as $rid => $option) {
  21. if ($removeRole == TRUE) {
  22. unset($options[$rid]);
  23. }
  24.  
  25. if (in_array($rid, $userRoles)) {
  26. $removeRole = TRUE;
  27. }
  28. }
  29.  
  30. // Put the options back into the form.
  31. $form['account']['roles']['#options'] = $options;
  32. $form['account']['roles']['#access'] = $user->hasPermission('administer user roles');
  33. }

The final step is to remove the user role add and remove actions from the People administration view. To do this edit the view at the path /admin/structure/views/view/user_admin_people and edit the field called "User: Bulk update (Bulk update)". In the dialog that appears select the "Available actions" option to "Only selected actions" and then ensure that no role update action is selected. The form in my site looked like this. 

 

Hide user role action in the people view

 

With these elements in place you'll have a much more locked down user administration area without too much code to maintain. The only thing you need to be careful of is making sure your roles and permissions are carefully stacked so that users can't give themselves a role that would give them the 'administer permissions' permission. This isn't a massive security flaw as you'll need to allow users 

Add new comment

The content of this field is kept private and will not be shown publicly.