Index: modules/user/user.module =================================================================== RCS file: /cvs/drupal/drupal/modules/user/user.module,v retrieving revision 1.1089 diff -u -p -r1.1089 user.module --- modules/user/user.module 7 Dec 2009 03:45:16 -0000 1.1089 +++ modules/user/user.module 10 Dec 2009 22:54:27 -0000 @@ -2703,10 +2703,29 @@ function user_multiple_cancel_confirm($f $edit = $form_state['input']; $form['accounts'] = array('#prefix' => '', '#tree' => TRUE); - // array_filter() returns only elements with TRUE values. - foreach (array_filter($edit['accounts']) as $uid => $value) { - $user = db_query('SELECT name FROM {users} WHERE uid = :uid', array(':uid' => $uid))->fetchField(); - $form['accounts'][$uid] = array('#type' => 'hidden', '#value' => $uid, '#prefix' => '
  • ', '#suffix' => check_plain($user) . "
  • \n"); + $accounts = user_load_multiple(array_keys(array_filter($edit['accounts']))); + foreach ($accounts as $uid => $account) { + // Prevent user 1 from being canceled. + if ($uid <= 1) { + continue; + } + $form['accounts'][$uid] = array( + '#type' => 'hidden', + '#value' => $uid, + '#prefix' => '
  • ', + '#suffix' => check_plain($account->name) . "
  • \n", + ); + } + + // Output a notice that user 1 cannot be canceled. + if (isset($accounts[1])) { + $redirect = (count($accounts) == 1); + $message = t('The user account %name cannot be cancelled.', array('%name' => $accounts[1]->name)); + drupal_set_message($message, $redirect ? 'error' : 'warning'); + // If only user 1 was selected, redirect to the overview. + if ($redirect) { + drupal_goto('admin/people'); + } } $form['operation'] = array('#type' => 'hidden', '#value' => 'cancel'); @@ -2755,6 +2774,10 @@ function user_multiple_cancel_confirm_su if ($form_state['values']['confirm']) { foreach ($form_state['values']['accounts'] as $uid => $value) { + // Prevent programmatic form submissions from cancelling user 1. + if ($uid <= 1) { + continue; + } // Prevent user administrators from deleting themselves without confirmation. if ($uid == $user->uid) { $admin_form_state = $form_state; @@ -2768,7 +2791,6 @@ function user_multiple_cancel_confirm_su } } $form_state['redirect'] = 'admin/people'; - return; } /** Index: modules/user/user.pages.inc =================================================================== RCS file: /cvs/drupal/drupal/modules/user/user.pages.inc,v retrieving revision 1.62 diff -u -p -r1.62 user.pages.inc --- modules/user/user.pages.inc 1 Dec 2009 16:03:35 -0000 1.62 +++ modules/user/user.pages.inc 10 Dec 2009 22:33:12 -0000 @@ -244,7 +244,7 @@ function user_profile_form($form, &$form '#value' => t('Cancel account'), '#weight' => 31, '#submit' => array('user_edit_cancel_submit'), - '#access' => ($account->uid == $user->uid && user_access('cancel account')) || user_access('administer users'), + '#access' => $account->uid > 1 && (($account->uid == $user->uid && user_access('cancel account')) || user_access('administer users')), ); } Index: modules/user/user.test =================================================================== RCS file: /cvs/drupal/drupal/modules/user/user.test,v retrieving revision 1.71 diff -u -p -r1.71 user.test --- modules/user/user.test 10 Dec 2009 15:39:43 -0000 1.71 +++ modules/user/user.test 10 Dec 2009 22:33:12 -0000 @@ -343,6 +343,44 @@ class UserCancelTestCase extends DrupalW } /** + * Tests that user account for uid 1 cannot be cancelled. + * + * This should never be possible, or the site owner would become unable to + * administer the site. + */ + function testUserCancelUid1() { + // Update uid 1's name and password to we know it. + $password = user_password(); + require_once DRUPAL_ROOT . '/' . variable_get('password_inc', 'includes/password.inc'); + $account = array( + 'name' => 'user1', + 'pass' => user_hash_password(trim($password)), + ); + // We cannot use user_save() here or the password would be hashed again. + db_update('users') + ->fields($account) + ->condition('uid', 1) + ->execute(); + + // Reload and log in uid 1. + $user1 = user_load(1, TRUE); + $user1->pass_raw = $password; + + // Try to cancel uid 1's account with a different user. + $this->admin_user = $this->drupalCreateUser(array('administer users')); + $this->drupalLogin($this->admin_user); + $edit = array( + 'operation' => 'cancel', + 'accounts[1]' => TRUE, + ); + $this->drupalPost('admin/people', $edit, t('Update')); + + // Verify that uid 1's account was not cancelled. + $user1 = user_load(1, TRUE); + $this->assertEqual($user1->status, 1, t('User #1 still exists and is not blocked.')); + } + + /** * Attempt invalid account cancellations. */ function testUserCancelInvalid() { @@ -619,6 +657,8 @@ class UserCancelTestCase extends DrupalW $edit['accounts[' . $uid . ']'] = TRUE; } $edit['accounts[' . $admin_user->uid . ']'] = TRUE; + // Also try to cancel uid 1. + $edit['accounts[1]'] = TRUE; $this->drupalPost('admin/people', $edit, t('Update')); $this->assertText(t('Are you sure you want to cancel these user accounts?'), t('Confirmation form to cancel accounts displayed.')); $this->assertText(t('When cancelling these accounts'), t('Allows to select account cancellation method.')); @@ -638,6 +678,10 @@ class UserCancelTestCase extends DrupalW $this->assertText(t('A confirmation request to cancel your account has been sent to your e-mail address.'), t('Account cancellation request mailed message displayed.')); $admin_user = user_load($admin_user->uid); $this->assertTrue($admin_user->status == 1, t('Administrative user is found in the database and enabled.')); + + // Verify that uid 1's account was not cancelled. + $user1 = user_load(1, TRUE); + $this->assertEqual($user1->status, 1, t('User #1 still exists and is not blocked.')); } }