Index: modules/user/user.admin.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/user/user.admin.inc,v
retrieving revision 1.64
diff -u -p -r1.64 user.admin.inc
--- modules/user/user.admin.inc 12 Jul 2009 08:36:35 -0000 1.64
+++ modules/user/user.admin.inc 16 Jul 2009 19:42:06 -0000
@@ -126,7 +126,6 @@ function user_filter_form_submit($form,
* @see user_admin_account_submit()
*/
function user_admin_account() {
-
$header = array(
array(),
array('data' => t('Username'), 'field' => 'u.name'),
@@ -195,7 +194,7 @@ function user_admin_account() {
}
$form['accounts'] = array(
'#type' => 'checkboxes',
- '#options' => $accounts
+ '#options' => $accounts,
);
$form['pager'] = array('#markup' => theme('pager', NULL));
Index: modules/user/user.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/user/user.module,v
retrieving revision 1.1012
diff -u -p -r1.1012 user.module
--- modules/user/user.module 15 Jul 2009 17:40:18 -0000 1.1012
+++ modules/user/user.module 16 Jul 2009 19:42:07 -0000
@@ -2282,46 +2282,67 @@ function user_multiple_role_edit($accoun
function user_multiple_cancel_confirm(&$form_state) {
$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");
- }
-
- $form['operation'] = array('#type' => 'hidden', '#value' => 'cancel');
-
- module_load_include('inc', 'user', 'user.pages');
- $form['user_cancel_method'] = array(
- '#type' => 'item',
- '#title' => t('When cancelling these accounts'),
- );
- $form['user_cancel_method'] += user_cancel_methods();
- // Remove method descriptions.
- foreach (element_children($form['user_cancel_method']) as $element) {
- unset($form['user_cancel_method'][$element]['#description']);
- }
+ // Remove uid1 from the $accounts array so it can't be accidentally deleted.
+ // This should have been prevented by JS, so this is just a failsafe check.
+ if ($edit['accounts'] && array_key_exists(1, $edit['accounts'])) {
+ $user1 = user_load(1);
+ unset($edit['accounts'][1]);
+ drupal_set_message(t('You attempted to cancel the %user1 account which might have broken your site; that part of the request has been disallowed.', array('%user1' => check_plain($user1->name))), 'warning');
+ }
+
+ if (count($edit['accounts']) > 0) {
+ $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");
+ }
+
+ $form['operation'] = array('#type' => 'hidden', '#value' => 'cancel');
+
+ module_load_include('inc', 'user', 'user.pages');
+ $form['user_cancel_method'] = array(
+ '#type' => 'item',
+ '#title' => t('When cancelling these accounts'),
+ );
+ $form['user_cancel_method'] += user_cancel_methods();
+ // Remove method descriptions.
+ foreach (element_children($form['user_cancel_method']) as $element) {
+ unset($form['user_cancel_method'][$element]['#description']);
+ }
- // Allow to send the account cancellation confirmation mail.
- $form['user_cancel_confirm'] = array(
- '#type' => 'checkbox',
- '#title' => t('Require e-mail confirmation to cancel account.'),
- '#default_value' => FALSE,
- '#description' => t('When enabled, the user must confirm the account cancellation via e-mail.'),
- );
- // Also allow to send account canceled notification mail, if enabled.
- $form['user_cancel_notify'] = array(
- '#type' => 'checkbox',
- '#title' => t('Notify user when account is canceled.'),
- '#default_value' => FALSE,
- '#access' => variable_get('user_mail_status_canceled_notify', FALSE),
- '#description' => t('When enabled, the user will receive an e-mail notification after the account has been cancelled.'),
- );
+ // Allow to send the account cancellation confirmation mail.
+ $form['user_cancel_confirm'] = array(
+ '#type' => 'checkbox',
+ '#title' => t('Require e-mail confirmation to cancel account.'),
+ '#default_value' => FALSE,
+ '#description' => t('When enabled, the user must confirm the account cancellation via e-mail.'),
+ );
+ // Also allow to send account canceled notification mail, if enabled.
+ $form['user_cancel_notify'] = array(
+ '#type' => 'checkbox',
+ '#title' => t('Notify user when account is canceled.'),
+ '#default_value' => FALSE,
+ '#access' => variable_get('user_mail_status_canceled_notify', FALSE),
+ '#description' => t('When enabled, the user will receive an e-mail notification after the account has been cancelled.'),
+ );
- return confirm_form($form,
- t('Are you sure you want to cancel these user accounts?'),
- 'admin/user/user', t('This action cannot be undone.'),
- t('Cancel accounts'), t('Cancel'));
+ return confirm_form($form,
+ t('Are you sure you want to cancel these user accounts?'),
+ 'admin/user/user', t('This action cannot be undone.'),
+ t('Cancel accounts'), t('Cancel'));
+ }
+ else {
+ $form = '';
+ return confirm_form($form,
+ t('No valid user accounts to cancel'),
+ 'admin/user/user',
+ t('You have not selected any valid user accounts to cancel. Go back and try again.'),
+ t('Cancel'),
+ NULL,
+ t('Cancel')
+ );
+ }
}
/**
@@ -2335,6 +2356,10 @@ function user_multiple_cancel_confirm_su
if ($form_state['values']['confirm']) {
foreach ($form_state['values']['accounts'] as $uid => $value) {
+ // Prevent uid 1 from being deleted.
+ if ($uid <= 1) {
+ continue;
+ }
// Prevent user administrators from deleting themselves without confirmation.
if ($uid == $user->uid) {
$admin_form_state = $form_state;
Index: modules/user/user.pages.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/user/user.pages.inc,v
retrieving revision 1.45
diff -u -p -r1.45 user.pages.inc
--- modules/user/user.pages.inc 1 Jul 2009 12:47:30 -0000 1.45
+++ modules/user/user.pages.inc 16 Jul 2009 19:42:07 -0000
@@ -257,7 +257,7 @@ function user_profile_form($form_state,
$form['_category'] = array('#type' => 'value', '#value' => $category);
$form['_account'] = array('#type' => 'value', '#value' => $account);
$form['submit'] = array('#type' => 'submit', '#value' => t('Save'), '#weight' => 30);
- if (($account->uid == $user->uid && user_access('cancel account')) || user_access('administer users')) {
+ if ((($account->uid == $user->uid && user_access('cancel account')) || user_access('administer users')) && $account->uid > 1) {
$form['cancel'] = array(
'#type' => 'submit',
'#value' => t('Cancel account'),
Index: modules/user/user.test
===================================================================
RCS file: /cvs/drupal/drupal/modules/user/user.test,v
retrieving revision 1.46
diff -u -p -r1.46 user.test
--- modules/user/user.test 13 Jul 2009 21:51:42 -0000 1.46
+++ modules/user/user.test 16 Jul 2009 19:42:07 -0000
@@ -169,38 +169,103 @@ class UserCancelTestCase extends DrupalW
}
/**
- * Attempt to cancel account without permission.
+ * Test for protection of user account #1 while logged as uid1.
+ * This should never be possible, for obvious reasons.
*/
- function testUserCancelWithoutPermission() {
+ function testUserCancelUser1() {
+ // Load user #1 from the database.
+ $user1 = user_load(1, TRUE);
+ // Make sure we know what is in user1's object.
+ $account = array();
+ $account['name'] = 'User#1';
+ $account['pass'] = user_password();
+ $user1 = user_save($user1, $account);
+ $user1->pass_raw = $account['pass'];
+ $this->drupalLogin($user1);
+ // Prefill some form fields.
+ $edit = array();
+ $edit['operation'] = 'cancel';
+ $edit['accounts[1]'] = TRUE;
+ $this->drupalPost('admin/user/user', $edit, t('Update'));
+ $this->assertTrue($user1->status == 1, t('User #1 still exists and is enabled.'));
+ $this->assertText(t('You attempted to cancel the '), t('User #1 attempted to cancel user #1 account, but was successfully thwarted.'));
+ }
+
+ /**
+ * Use an administrative user to try to cancel a regular user.
+ */
+ function testUserCancelByAdmin() {
variable_set('user_cancel_method', 'user_cancel_reassign');
- // Create a user.
+ // Create a regular user.
$account = $this->drupalCreateUser(array());
- $this->drupalLogin($account);
- // Load real user object.
- $account = user_load($account->uid, TRUE);
- // Create a node.
- $node = $this->drupalCreateNode(array('uid' => $account->uid));
+ // Create administrative user.
+ $admin_user = $this->drupalCreateUser(array('administer users'));
+ $this->drupalLogin($admin_user);
- // Attempt to cancel account.
+ // Delete regular user.
$this->drupalGet('user/' . $account->uid . '/edit');
- $this->assertNoRaw(t('Cancel account'), t('No cancel account button displayed.'));
+ $this->drupalPost(NULL, NULL, t('Cancel account'));
+ $this->assertRaw(t('Are you sure you want to cancel the account %name?', array('%name' => $account->name)), t('Confirmation form to cancel account displayed.'));
+ $this->assertText(t('Select the method to cancel the account above.'), t('Allows to select account cancellation method.'));
- // Attempt bogus account cancellation request confirmation.
- $timestamp = $account->login;
- $this->drupalGet("user/$account->uid/cancel/confirm/$timestamp/" . user_pass_rehash($account->pass, $timestamp, $account->login));
- $this->assertResponse(403, t('Bogus cancelling request rejected.'));
- $account = user_load($account->uid);
- $this->assertTrue($account->status == 1, t('User account was not canceled.'));
+ // Confirm deletion.
+ $this->drupalPost(NULL, NULL, t('Cancel account'));
+ $this->assertRaw(t('%name has been deleted.', array('%name' => $account->name)), t('User deleted.'));
+ $this->assertFalse(user_load($account->uid), t('User is not found in the database.'));
+ }
- // Confirm user's content has not been altered.
- $test_node = node_load($node->nid, NULL, TRUE);
- $this->assertTrue(($test_node->uid == $account->uid && $test_node->status == 1), t('Node of the user has not been altered.'));
+ /**
+ * Use an administrative user to try and bulk-cancel regular users.
+ */
+ function testBulkUserCancelByAdmin() {
+ variable_set('user_cancel_method', 'user_cancel_reassign');
+ // Enable account cancellation notification.
+ variable_set('user_mail_status_canceled_notify', TRUE);
+
+ // Create administrative user.
+ $admin_user = $this->drupalCreateUser(array('administer users'));
+ $this->drupalLogin($admin_user);
+
+ // Create three regular users.
+ $users = array();
+ for ($i = 0; $i < 3; $i++) {
+ $account = $this->drupalCreateUser(array());
+ $users[$account->uid] = $account;
+ }
+ // Attempt to cancel newly created user accounts.
+ $edit = array();
+ $edit['operation'] = 'cancel';
+ foreach ($users as $uid => $account) {
+ if ($uid > 1) {
+ $edit['accounts[' . $uid . ']'] = TRUE;
+ }
+ }
+ $this->drupalPost('admin/user/user', $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.'));
+ $this->assertText(t('Require e-mail confirmation to cancel account.'), t('Allows to send confirmation mail.'));
+
+ // Confirm deletion.
+ $this->drupalPost(NULL, NULL, t('Cancel accounts'));
+ $status = TRUE;
+ foreach ($users as $account) {
+ $status = $status && (strpos($this->content, t('%name has been deleted.', array('%name' => $account->name))) !== FALSE);
+ $status = $status && !user_load($account->uid, TRUE);
+ }
+ $this->assertTrue($status, t('Users deleted and not found in the database.'));
+ // $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.'));
+
+ // Ensure that neither the administrative account nor uid1 was cancelled.
+ $admin_user = user_load($admin_user->uid);
+ $this->assertTrue($admin_user->status == 1, t('The administrative user still exists and is enabled.'));
+ $uid1 = user_load(1);
+ $this->assertTrue($uid1->status == 1, t('User #1 still exists and is enabled.'));
}
/**
- * Attempt invalid account cancellations.
+ * Attempt some invalid or bogus user account cancellations.
*/
function testUserCancelInvalid() {
variable_set('user_cancel_method', 'user_cancel_reassign');
@@ -241,9 +306,40 @@ class UserCancelTestCase extends DrupalW
}
/**
- * Disable account and keep all content.
+ * Attempt a single user account cancellation without permission.
+ */
+ function testUserCancelWithoutPermission() {
+ variable_set('user_cancel_method', 'user_cancel_reassign');
+
+ // Create a user.
+ $account = $this->drupalCreateUser(array());
+ $this->drupalLogin($account);
+ // Load real user object.
+ $account = user_load($account->uid, TRUE);
+
+ // Create a node.
+ $node = $this->drupalCreateNode(array('uid' => $account->uid));
+
+ // Attempt to cancel account.
+ $this->drupalGet('user/' . $account->uid . '/edit');
+ $this->assertNoRaw(t('Cancel account'), t('Cancel account button is hidden if user does not have sufficient permission.'));
+
+ // Attempt bogus account cancellation request confirmation.
+ $timestamp = $account->login;
+ $this->drupalGet("user/$account->uid/cancel/confirm/$timestamp/" . user_pass_rehash($account->pass, $timestamp, $account->login));
+ $this->assertResponse(403, t('Bogus account cancellation request was rejected.'));
+ $account = user_load($account->uid);
+ $this->assertTrue($account->status == 1, t('User account was not canceled.'));
+
+ // Confirm user's content has not been altered.
+ $test_node = node_load($node->nid, NULL, TRUE);
+ $this->assertTrue(($test_node->uid == $account->uid && $test_node->status == 1), t('Node of the user has not been altered.'));
+ }
+
+ /**
+ * Attempt a single user account cancellation and keep all their content.
*/
- function testUserBlock() {
+ function testUserCancel() {
variable_set('user_cancel_method', 'user_cancel_block');
// Create a user.
@@ -276,9 +372,9 @@ class UserCancelTestCase extends DrupalW
}
/**
- * Disable account and unpublish all content.
+ * Attempt a single user account cancellation and unpublish all their content.
*/
- function testUserBlockUnpublish() {
+ function testUserCancelAndUnpublish() {
variable_set('user_cancel_method', 'user_cancel_block_unpublish');
// Create a user.
@@ -320,9 +416,9 @@ class UserCancelTestCase extends DrupalW
}
/**
- * Delete account and anonymize all content.
+ * Attempt a single user account cancellation and anonymize all their content.
*/
- function testUserAnonymize() {
+ function testUserCancelAndAnonymize() {
variable_set('user_cancel_method', 'user_cancel_reassign');
// Create a user.
@@ -371,9 +467,9 @@ class UserCancelTestCase extends DrupalW
}
/**
- * Delete account and remove all content.
+ * Attempt a single user account cancellation and delete all their content.
*/
- function testUserDelete() {
+ function testUserCancelAndDelete() {
variable_set('user_cancel_method', 'user_cancel_delete');
// Create a user.
@@ -423,78 +519,6 @@ class UserCancelTestCase extends DrupalW
// Confirm that user is logged out.
$this->assertNoText($account->name, t('Logged out.'));
}
-
- /**
- * Create an administrative user and delete another user.
- */
- function testUserCancelByAdmin() {
- variable_set('user_cancel_method', 'user_cancel_reassign');
-
- // Create a regular user.
- $account = $this->drupalCreateUser(array());
-
- // Create administrative user.
- $admin_user = $this->drupalCreateUser(array('administer users'));
- $this->drupalLogin($admin_user);
-
- // Delete regular user.
- $this->drupalGet('user/' . $account->uid . '/edit');
- $this->drupalPost(NULL, NULL, t('Cancel account'));
- $this->assertRaw(t('Are you sure you want to cancel the account %name?', array('%name' => $account->name)), t('Confirmation form to cancel account displayed.'));
- $this->assertText(t('Select the method to cancel the account above.'), t('Allows to select account cancellation method.'));
-
- // Confirm deletion.
- $this->drupalPost(NULL, NULL, t('Cancel account'));
- $this->assertRaw(t('%name has been deleted.', array('%name' => $account->name)), t('User deleted.'));
- $this->assertFalse(user_load($account->uid), t('User is not found in the database.'));
- }
-
- /**
- * Create an administrative user and mass-delete other users.
- */
- function testMassUserCancelByAdmin() {
- variable_set('user_cancel_method', 'user_cancel_reassign');
- // Enable account cancellation notification.
- variable_set('user_mail_status_canceled_notify', TRUE);
-
- // Create administrative user.
- $admin_user = $this->drupalCreateUser(array('administer users'));
- $this->drupalLogin($admin_user);
-
- // Create some users.
- $users = array();
- for ($i = 0; $i < 3; $i++) {
- $account = $this->drupalCreateUser(array());
- $users[$account->uid] = $account;
- }
-
- // Cancel user accounts, including own one.
- $edit = array();
- $edit['operation'] = 'cancel';
- foreach ($users as $uid => $account) {
- $edit['accounts[' . $uid . ']'] = TRUE;
- }
- $edit['accounts[' . $admin_user->uid . ']'] = TRUE;
- $this->drupalPost('admin/user/user', $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.'));
- $this->assertText(t('Require e-mail confirmation to cancel account.'), t('Allows to send confirmation mail.'));
- $this->assertText(t('Notify user when account is canceled.'), t('Allows to send notification mail.'));
-
- // Confirm deletion.
- $this->drupalPost(NULL, NULL, t('Cancel accounts'));
- $status = TRUE;
- foreach ($users as $account) {
- $status = $status && (strpos($this->content, t('%name has been deleted.', array('%name' => $account->name))) !== FALSE);
- $status = $status && !user_load($account->uid, TRUE);
- }
- $this->assertTrue($status, t('Users deleted and not found in the database.'));
-
- // Ensure that admin account was not cancelled.
- $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.'));
- }
}
class UserPictureTestCase extends DrupalWebTestCase {