From dfebdecfa7b37822e2fcdeb53064a9f60f277bc3 Mon Sep 17 00:00:00 2001
From: Dries Buytaert <dries@buytaert.net>
Date: Thu, 14 Jan 2010 19:21:55 +0000
Subject: [PATCH] - Patch #46149 by Senpai, sun, alexanderpas, hunmonk,
 ChrisKennedy, tstoeckler, cwgordon7: prevent account cancellation for uid 1.

---
 modules/user/user.module    | 32 ++++++++++++++++++++++-----
 modules/user/user.pages.inc |  2 +-
 modules/user/user.test      | 44 +++++++++++++++++++++++++++++++++++++
 3 files changed, 72 insertions(+), 6 deletions(-)

diff --git a/modules/user/user.module b/modules/user/user.module
index 993b13f58919..c906c5cdcab3 100644
--- a/modules/user/user.module
+++ b/modules/user/user.module
@@ -2747,10 +2747,29 @@ function user_multiple_cancel_confirm($form, &$form_state) {
   $edit = $form_state['input'];
 
   $form['accounts'] = array('#prefix' => '<ul>', '#suffix' => '</ul>', '#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' => '<li>', '#suffix' => check_plain($user) . "</li>\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' => '<li>',
+      '#suffix' => check_plain($account->name) . "</li>\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');
@@ -2799,6 +2818,10 @@ function user_multiple_cancel_confirm_submit($form, &$form_state) {
 
   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;
@@ -2812,7 +2835,6 @@ function user_multiple_cancel_confirm_submit($form, &$form_state) {
     }
   }
   $form_state['redirect'] = 'admin/people';
-  return;
 }
 
 /**
diff --git a/modules/user/user.pages.inc b/modules/user/user.pages.inc
index 7eff7de4360e..063ea100e19f 100644
--- a/modules/user/user.pages.inc
+++ b/modules/user/user.pages.inc
@@ -248,7 +248,7 @@ function user_profile_form($form, &$form_state, $account, $category = 'account')
       '#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')),
     );
   }
 
diff --git a/modules/user/user.test b/modules/user/user.test
index 323580eb3897..cc1c075b3e9a 100644
--- a/modules/user/user.test
+++ b/modules/user/user.test
@@ -362,6 +362,44 @@ class UserCancelTestCase extends DrupalWebTestCase {
     $this->assertTrue(($test_node->uid == $account->uid && $test_node->status == 1), t('Node of the user has not been altered.'));
   }
 
+  /**
+   * 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.
    */
@@ -647,6 +685,8 @@ class UserCancelTestCase extends DrupalWebTestCase {
       $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.'));
@@ -666,6 +706,10 @@ class UserCancelTestCase extends DrupalWebTestCase {
     $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.'));
   }
 }
 
-- 
GitLab