Index: UPGRADE.txt =================================================================== RCS file: /cvs/drupal/drupal/UPGRADE.txt,v retrieving revision 1.16 diff -u -p -r1.16 UPGRADE.txt --- UPGRADE.txt 14 Sep 2009 07:33:55 -0000 1.16 +++ UPGRADE.txt 7 Oct 2009 03:22:34 -0000 @@ -35,11 +35,12 @@ Let's begin! More information on multisite configuration is located in INSTALL.txt. -2. If possible, log on as the user with user ID 1, which is the first account - created - also known as the site maintenance account. Only this account will - be able to automatically access update.php in step #10. There are special - instructions in step #10 if you are unable to log on as user ID 1. Do not - close your browser until the final step is complete. +2. If possible, log on either as a user with the "Administer software updates" + permission or as the user with user ID 1, which is the first account + created (also known as the site maintenance account). Only these accounts + will be able to automatically access update.php in step #10. There are + special instructions in step #10 if you are unable to log on as one of + these users. Do not close your browser until the final step is complete. 3. Place the site in "Offline" mode, to let the database updates run without interruption and avoid displaying errors to end users of the site. This Index: update.php =================================================================== RCS file: /cvs/drupal/drupal/update.php,v retrieving revision 1.305 diff -u -p -r1.305 update.php --- update.php 28 Sep 2009 22:16:32 -0000 1.305 +++ update.php 7 Oct 2009 03:22:34 -0000 @@ -13,10 +13,11 @@ define('DRUPAL_ROOT', getcwd()); * Point your browser to "http://www.example.com/update.php" and follow the * instructions. * - * If you are not logged in using the site maintenance account, you will need - * to modify the access check statement inside your settings.php file. After - * finishing the upgrade, be sure to open settings.php again, and change it back - * to its original state! + * If you are not logged in using either the site maintenance account or an + * account with the "Administer software updates" permission, you will need to + * modify the access check statement inside your settings.php file. After + * finishing the upgrade, be sure to open settings.php again, and change it + * back to its original state! */ /** @@ -201,17 +202,42 @@ function update_info_page() { } function update_access_denied_page() { + drupal_add_http_header('403 Forbidden'); + watchdog('access denied', 'update.php', NULL, WATCHDOG_WARNING); drupal_set_title('Access denied'); - return '

Access denied. You are not authorized to access this page. Please log in using the site maintenance account (the account you created during installation). If you cannot log in, you will have to edit settings.php to bypass this access check. To do this:

+ return '

Access denied. You are not authorized to access this page. Please log in using either an account with the administer software updates permission or the site maintenance account (the account you created during installation). If you cannot log in, you will have to edit settings.php to bypass this access check. To do this:

  1. With a text editor find the settings.php file on your system. From the main Drupal directory that you installed all the files into, go to sites/your_site_name if such directory exists, or else to sites/default which applies otherwise.
  2. There is a line inside your settings.php file that says $update_free_access = FALSE;. Change it to $update_free_access = TRUE;.
  3. As soon as the update.php script is done, you must change the settings.php file back to its original form with $update_free_access = FALSE;.
  4. -
  5. To avoid having this problem in the future, remember to log in to your website using the site maintenance account (the account you created during installation) before you backup your database at the beginning of the update process.
  6. +
  7. To avoid having this problem in the future, remember to log in to your website using either an account with the administer software updates permission or the site maintenance account (the account you created during installation) before you backup your database at the beginning of the update process.
'; } /** + * Determines if the current user is allowed to run update.php. + * + * @return + * TRUE if the current user should be granted access, or FALSE otherwise. + */ +function update_access_allowed() { + global $update_free_access, $user; + // Allow the global variable in settings.php to override the access check. + if (!empty($update_free_access)) { + return TRUE; + } + // Calls to user_access() might fail during the Drupal 6 to 7 update process, + // so we fall back on requiring that the user be logged in as user #1. + try { + require_once drupal_get_path('module', 'user') . '/user.module'; + return user_access('administer software updates'); + } + catch (Exception $e) { + return $user->uid == 1; + } +} + +/** * Add the update task list to the current page. */ function update_task_list($active = NULL) { @@ -273,13 +299,12 @@ update_prepare_d7_bootstrap(); // Determine if the current user has access to run update.php. drupal_bootstrap(DRUPAL_BOOTSTRAP_SESSION); -$update_access_allowed = !empty($update_free_access) || $user->uid == 1; // Only allow the requirements check to proceed if the current user has access // to run updates (since it may expose sensitive information about the site's // configuration). $op = isset($_REQUEST['op']) ? $_REQUEST['op'] : ''; -if (empty($op) && $update_access_allowed) { +if (empty($op) && update_access_allowed()) { require_once DRUPAL_ROOT . '/includes/install.inc'; require_once DRUPAL_ROOT . '/includes/file.inc'; require_once DRUPAL_ROOT . '/modules/system/system.install'; @@ -317,7 +342,7 @@ drupal_maintenance_theme(); ini_set('display_errors', TRUE); // Only proceed with updates if the user is allowed to run them. -if ($update_access_allowed) { +if (update_access_allowed()) { include_once DRUPAL_ROOT . '/includes/install.inc'; include_once DRUPAL_ROOT . '/includes/batch.inc'; Index: modules/system/system.module =================================================================== RCS file: /cvs/drupal/drupal/modules/system/system.module,v retrieving revision 1.801 diff -u -p -r1.801 system.module --- modules/system/system.module 5 Oct 2009 02:43:01 -0000 1.801 +++ modules/system/system.module 7 Oct 2009 03:22:45 -0000 @@ -216,6 +216,10 @@ function system_permission() { 'title' => t('Administer site configuration'), 'description' => t('Configure site-wide settings such as module or theme administration settings.'), ), + 'administer software updates' => array( + 'title' => t('Administer software updates'), + 'description' => t('Run the update.php script.'), + ), 'administer actions' => array( 'title' => t('Administer actions'), 'description' => t('Manage the actions defined for your site.'), Index: modules/system/system.test =================================================================== RCS file: /cvs/drupal/drupal/modules/system/system.test,v retrieving revision 1.81 diff -u -p -r1.81 system.test --- modules/system/system.test 3 Oct 2009 19:16:04 -0000 1.81 +++ modules/system/system.test 7 Oct 2009 03:22:45 -0000 @@ -1210,7 +1210,7 @@ class TokenReplaceTestCase extends Drupa // passed properly through the call stack and being handled correctly by a 'known' // token, [node:title]. $this->assertFalse(strcmp($target, $result), t('Basic placeholder tokens replaced.')); - + $raw_tokens = array('title' => '[node:title]'); $generated = token_generate('node', $raw_tokens, array('node' => $node)); $this->assertFalse(strcmp($generated['[node:title]'], check_plain($node->title)), t('Token sanitized.')); @@ -1290,3 +1290,58 @@ array_space[a b] = Value'; $this->assertEqual($parsed, $expected, t('Entire parsed .info string and expected array are identical.')); } } + +/** + * Tests for the update system functionality. + */ +class UpdateScriptFunctionalTest extends DrupalWebTestCase { + public static function getInfo() { + return array( + 'name' => t('Update.php functionality'), + 'description' => t('Tests the update script access and functionality.'), + 'group' => t('System'), + ); + } + + /** + * Tests access to the update script. + */ + function testUpdateAccess() { + $update_url = url('update.php', array('absolute' => TRUE)); + + // Try accessing update.php without the proper permission. + $regular_user = $this->drupalCreateUser(); + $this->drupalLogin($regular_user); + $this->drupalGet($update_url); + $this->assertResponse(403); + + // Try accessing update.php as an anonymous user. + $this->drupalLogout(); + $this->drupalGet($update_url); + $this->assertResponse(403); + + // Access the update page as user 1. + $user1 = user_load(1); + $user1->pass_raw = user_password(); + require_once DRUPAL_ROOT . '/' . variable_get('password_inc', 'includes/password.inc'); + $user1->pass = user_hash_password(trim($user1->pass_raw)); + db_query("UPDATE {users} SET pass = :pass WHERE uid = :uid", array(':pass' => $user1->pass, ':uid' => $user1->uid)); + $this->drupalLogin($user1); + $this->drupalGet($update_url); + $this->assertResponse(200); + + // Access the update page with the proper permission. + $update_user = $this->drupalCreateUser(array('administer software updates', 'access administration pages')); + $this->drupalLogin($update_user); + $this->drupalGet($update_url); + $this->assertResponse(200); + + // Proceed through the update process without any pending updates. + $this->drupalPost(NULL, array(), t('Continue')); + $this->assertText(t('No pending updates.')); + + // Click through back to the administration page. + $this->clickLink('Administration pages'); + $this->assertResponse(200); + } +} Index: sites/default/default.settings.php =================================================================== RCS file: /cvs/drupal/drupal/sites/default/default.settings.php,v retrieving revision 1.31 diff -u -p -r1.31 default.settings.php --- sites/default/default.settings.php 14 Sep 2009 19:03:04 -0000 1.31 +++ sites/default/default.settings.php 7 Oct 2009 03:22:46 -0000 @@ -157,7 +157,8 @@ $db_prefix = ''; * Access control for update.php script * * If you are updating your Drupal installation using the update.php script but - * are not logged in using the site maintenance account (the account that was + * are not logged in using either an account with the "Administer software + * updates" permission or the site maintenance account (the account that was * created during installation), you will need to modify the access check * statement below. Change the FALSE to a TRUE to disable the access check. * After finishing the upgrade, be sure to open this file again and change the