diff --git a/core/modules/node/node.module b/core/modules/node/node.module index 466f510001..c7ec68f82d 100644 --- a/core/modules/node/node.module +++ b/core/modules/node/node.module @@ -873,8 +873,9 @@ function node_form_system_themes_admin_form_submit($form, FormStateInterface $fo * * In determining access rights for a node, \Drupal\node\NodeAccessControlHandler * first checks whether the user has the "bypass node access" permission. Such - * users have unrestricted access to all nodes. user 1 will always pass this - * check. + * users have unrestricted access to all nodes. Any user with the currently + * designated "administrator" role will always pass this check. + * * * Next, all implementations of hook_node_access() will be called. Each * implementation may explicitly allow, explicitly forbid, or ignore the access diff --git a/core/modules/user/src/AccountSettingsForm.php b/core/modules/user/src/AccountSettingsForm.php index e2e9441ddd..c3809d906c 100644 --- a/core/modules/user/src/AccountSettingsForm.php +++ b/core/modules/user/src/AccountSettingsForm.php @@ -120,7 +120,7 @@ public function buildForm(array $form, FormStateInterface $form_state) { '#empty_value' => '', '#default_value' => $default_value, '#options' => $roles, - '#description' => $this->t('This role will be automatically assigned new permissions whenever a module is enabled. Changing this setting will not affect existing permissions.'), + '#description' => $this->t('Users with this role will bypass all permission access checks. Changing this setting will not affect existing permissions.'), // Don't allow to select a single admin role in case multiple roles got // marked as admin role already. '#access' => count($admin_roles) <= 1, diff --git a/core/modules/user/src/Form/UserMultipleCancelConfirm.php b/core/modules/user/src/Form/UserMultipleCancelConfirm.php index 62a64e5d2b..af761fbbd1 100644 --- a/core/modules/user/src/Form/UserMultipleCancelConfirm.php +++ b/core/modules/user/src/Form/UserMultipleCancelConfirm.php @@ -2,6 +2,7 @@ namespace Drupal\user\Form; +use Drupal\User\RoleStorageInterface; use Drupal\Core\Entity\EntityManagerInterface; use Drupal\Core\Form\ConfirmFormBase; use Drupal\Core\Form\FormStateInterface; @@ -37,6 +38,13 @@ class UserMultipleCancelConfirm extends ConfirmFormBase { protected $entityManager; /** + * The role storage used when changing the admin role. + * + * @var \Drupal\user\RoleStorageInterface + */ + protected $roleStorage; + + /** * Constructs a new UserMultipleCancelConfirm. * * @param \Drupal\user\PrivateTempStoreFactory $temp_store_factory @@ -45,11 +53,14 @@ class UserMultipleCancelConfirm extends ConfirmFormBase { * The user storage. * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager * The entity manager. + * @param \Drupal\user\RoleStorageInterface $role_storage + * The role storage. */ - public function __construct(PrivateTempStoreFactory $temp_store_factory, UserStorageInterface $user_storage, EntityManagerInterface $entity_manager) { + public function __construct(PrivateTempStoreFactory $temp_store_factory, UserStorageInterface $user_storage, EntityManagerInterface $entity_manager, RoleStorageInterface $role_storage) { $this->tempStoreFactory = $temp_store_factory; $this->userStorage = $user_storage; $this->entityManager = $entity_manager; + $this->roleStorage = $role_storage; } /** @@ -59,7 +70,8 @@ public static function create(ContainerInterface $container) { return new static( $container->get('user.private_tempstore'), $container->get('entity.manager')->getStorage('user'), - $container->get('entity.manager') + $container->get('entity.manager'), + $container->get('entity.manager')->getStorage('user_role') ); } @@ -104,37 +116,32 @@ public function buildForm(array $form, FormStateInterface $form_state) { return $this->redirect('entity.user.collection'); } - $root = NULL; - $names = []; - $form['accounts'] = ['#tree' => TRUE]; + // Get the admin role. + $admin_roles = $this->roleStorage->getQuery() + ->condition('is_admin', TRUE) + ->execute(); + $admin_role = reset($admin_roles); + + // Count the admin users in this batch. + $deleted_admin_user_count = 0; foreach ($accounts as $account) { - $uid = $account->id(); - $names[$uid] = $account->label(); - // Prevent user 1 from being canceled. - if ($uid <= 1) { - $root = intval($uid) === 1 ? $account : $root; - continue; + if ($account->hasRole($admin_role)) { + $deleted_admin_user_count++; } - $form['accounts'][$uid] = [ - '#type' => 'hidden', - '#value' => $uid, - ]; } - $form['account']['names'] = [ - '#theme' => 'item_list', - '#items' => $names, - ]; + // Get count of all users with admin role. + $ids = $this->userStorage->getQuery() + ->condition('roles', $admin_role) + ->execute(); + $admin_user_count = count($ids); - // Output a notice that user 1 cannot be canceled. - if (isset($root)) { - $redirect = (count($accounts) == 1); - $message = $this->t('The user account %name cannot be canceled.', ['%name' => $root->label()]); - drupal_set_message($message, $redirect ? 'error' : 'warning'); + // Prevent all/last user(s) with administrator role from being deleted. + if ($admin_user_count == $deleted_admin_user_count) { + $message = $this->t('This action cannot be completed, because it would cancel all administrator accounts.'); + drupal_set_message($message, 'error'); // If only user 1 was selected, redirect to the overview. - if ($redirect) { - return $this->redirect('entity.user.collection'); - } + return $this->redirect('entity.user.collection'); } $form['operation'] = ['#type' => 'hidden', '#value' => 'cancel']; diff --git a/core/modules/user/src/ProfileForm.php b/core/modules/user/src/ProfileForm.php index 343d4d0e06..982a487fa3 100644 --- a/core/modules/user/src/ProfileForm.php +++ b/core/modules/user/src/ProfileForm.php @@ -23,7 +23,7 @@ protected function actions(array $form, FormStateInterface $form_state) { $element['delete']['#type'] = 'submit'; $element['delete']['#value'] = $this->t('Cancel account'); $element['delete']['#submit'] = ['::editCancelSubmit']; - $element['delete']['#access'] = $account->id() > 1 && (($account->id() == $user->id() && $user->hasPermission('cancel account')) || $user->hasPermission('administer users')); + $element['delete']['#access'] = (($account->id() == $user->id() && $user->hasPermission('cancel account')) || $user->hasPermission('administer users')); return $element; } diff --git a/core/modules/user/src/RoleAccessControlHandler.php b/core/modules/user/src/RoleAccessControlHandler.php index e979f2d2c8..4dd5ee17c2 100644 --- a/core/modules/user/src/RoleAccessControlHandler.php +++ b/core/modules/user/src/RoleAccessControlHandler.php @@ -20,7 +20,9 @@ class RoleAccessControlHandler extends EntityAccessControlHandler { protected function checkAccess(EntityInterface $entity, $operation, AccountInterface $account) { switch ($operation) { case 'delete': - if ($entity->id() == RoleInterface::ANONYMOUS_ID || $entity->id() == RoleInterface::AUTHENTICATED_ID) { + // Prevent deletion of special roles. + $perm_roles = array(RoleInterface::ANONYMOUS_ID, RoleInterface::AUTHENTICATED_ID, RoleInterface::ADMINISTRATOR_ID); + if (in_array($entity->id(), $perm_roles)) { return AccessResult::forbidden(); }