Commit 7df3f372 authored by webchick's avatar webchick

#67234 by Ralf, Dave Reid, David_Rothstein, Rob Loach, dww, et al: Added a...

#67234 by Ralf, Dave Reid, David_Rothstein, Rob Loach, dww, et al: Added a permission to update.php.
parent bd48d8be
......@@ -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
......
......@@ -1789,42 +1789,28 @@ protected function clickLink($label, $index = 0) {
* Takes a path and returns an absolute path.
*
* @param $path
* The path, can be a Drupal path or a site-relative path. It might have a
* query, too. Can even be an absolute path which is just passed through.
* A path from the internal browser content.
* @return
* An absolute path.
*
* @todo What is the intention of this function? It is only invoked from
* locations, where URLs from the *output* are turned into absolute URLs,
* so why do we pass that through url() again?
* The $path with $base_url prepended, if necessary.
*/
protected function getAbsoluteUrl($path) {
global $base_url, $base_path;
$parts = parse_url($path);
// This is more crude than menu_path_is_external() but enough here.
if (empty($parts['host'])) {
$options = array('absolute' => TRUE);
$path = $parts['path'];
$base_path = base_path();
$n = strlen($base_path);
if (substr($path, 0, $n) == $base_path) {
$path = substr($path, $n);
// Ensure that we have a string (and no xpath object).
$path = (string) $path;
// Strip $base_path, if existent.
$length = strlen($base_path);
if (substr($path, 0, $length) === $base_path) {
$path = substr($path, $length);
}
if (isset($parts['query'])) {
parse_str($parts['query'], $options['query']);
// Let's make it a bit more crude. It's not clear why we invoke url() on
// a URL contained in the returned page output again, but since $path is
// FALSE (see $path = $parts['path'] above) with Clean URLs disabled,
// and url() now encodes the passed in query parameter array, we get a
// double-encoded 'q' query string in the resulting absolute URL
// generated by url(). This cannot be avoided in url(). But it could be
// completely avoided if this function wouldn't be calling url().
// @see SimpleTestURLTestCase->testGetAbsoluteUrl()
if (isset($options['query']['q'])) {
$path = $options['query']['q'];
unset($options['query']['q']);
}
// Ensure that we have an absolute path.
if ($path[0] !== '/') {
$path = '/' . $path;
}
$path = url($path, $options);
// Finally, prepend the $base_url.
$path = $base_url . $path;
}
return $path;
}
......
......@@ -293,17 +293,17 @@ class SimpleTestURLTestCase extends DrupalWebTestCase {
$this->drupalGet($url);
$absolute = url($url, array('absolute' => TRUE));
$this->assertEqual($absolute, $this->url, t('Passed and requested URL are equal.'));
$this->assertEqual($this->url, $this->getAbsoluteUrl($url), t('Requested and returned absolute URL are equal.'));
$this->assertEqual($this->url, $this->getAbsoluteUrl($this->url), t('Requested and returned absolute URL are equal.'));
$this->drupalPost(NULL, array(), t('Log in'));
$this->assertEqual($absolute, $this->url, t('Passed and requested URL are equal.'));
$this->assertEqual($this->url, $this->getAbsoluteUrl($url), t('Requested and returned absolute URL are equal.'));
$this->assertEqual($this->url, $this->getAbsoluteUrl($this->url), t('Requested and returned absolute URL are equal.'));
$this->clickLink('Create new account');
$url = 'user/register';
$absolute = url($url, array('absolute' => TRUE));
$this->assertEqual($absolute, $this->url, t('Passed and requested URL are equal.'));
$this->assertEqual($this->url, $this->getAbsoluteUrl($url), t('Requested and returned absolute URL are equal.'));
$this->assertEqual($this->url, $this->getAbsoluteUrl($this->url), t('Requested and returned absolute URL are equal.'));
}
}
......
......@@ -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.'),
......
......@@ -1210,7 +1210,7 @@ class TokenReplaceTestCase extends DrupalWebTestCase {
// 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,56 @@ 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 {
private $update_url;
private $update_user;
public static function getInfo() {
return array(
'name' => 'Update functionality',
'description' => 'Tests the update script access and functionality.',
'group' => 'System',
);
}
function setUp() {
parent::setUp();
$this->update_url = $GLOBALS['base_url'] . '/update.php';
$this->update_user = $this->drupalCreateUser(array('administer software updates'));
}
/**
* Tests access to the update script.
*/
function testUpdateAccess() {
// Try accessing update.php without the proper permission.
$regular_user = $this->drupalCreateUser();
$this->drupalLogin($regular_user);
$this->drupalGet($this->update_url, array('external' => TRUE));
$this->assertResponse(403);
// Try accessing update.php as an anonymous user.
$this->drupalLogout();
$this->drupalGet($this->update_url, array('external' => TRUE));
$this->assertResponse(403);
// Access the update page with the proper permission.
$this->drupalLogin($this->update_user);
$this->drupalGet($this->update_url, array('external' => TRUE));
$this->assertResponse(200);
// 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($this->update_url, array('external' => TRUE));
$this->assertResponse(200);
}
}
......@@ -157,7 +157,8 @@
* 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
......
......@@ -13,10 +13,11 @@
* 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!
*/
/**
......@@ -99,7 +100,6 @@ function update_script_selection_form() {
return $form;
}
function update_helpful_links() {
// NOTE: we can't use l() here because the URL would point to 'update.php?q=admin'.
$links[] = '<a href="' . base_path() . '">Front page</a>';
......@@ -201,16 +201,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 '<p>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 <code>settings.php</code> to bypass this access check. To do this:</p>
return '<p>Access denied. You are not authorized to access this page. Please log in using either an account with the <em>administer software updates</em> permission or the site maintenance account (the account you created during installation). If you cannot log in, you will have to edit <code>settings.php</code> to bypass this access check. To do this:</p>
<ol>
<li>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 <code>sites/your_site_name</code> if such directory exists, or else to <code>sites/default</code> which applies otherwise.</li>
<li>There is a line inside your settings.php file that says <code>$update_free_access = FALSE;</code>. Change it to <code>$update_free_access = TRUE;</code>.</li>
<li>As soon as the update.php script is done, you must change the settings.php file back to its original form with <code>$update_free_access = FALSE;</code>.</li>
<li>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.</li>
<li>To avoid having this problem in the future, remember to log in to your website using either an account with the <em>administer software updates</em> permission or the site maintenance account (the account you created during installation) before you backup your database at the beginning of the update process.</li>
</ol>';
}
/**
* 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.
*/
......@@ -273,13 +299,12 @@ function update_check_requirements() {
// 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 @@ function update_check_requirements() {
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';
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment