From 31eae6a575c1dea8767ad91181c21db542ab1ced Mon Sep 17 00:00:00 2001 From: Tim Starling Date: Thu, 9 Dec 2010 08:24:54 +0000 Subject: [PATCH] * Made the web upgrade process more friendly. Instead of saying "access denied, go away" when the user has a normal LocalSettings.php file, generate a random $wgUpgradeKey and instruct the user to insert it into their LocalSettings.php. The subsequent file modification then authenticates the session and allows the upgrade. * When an upgrade key is entered, or a supplied upgrade key is edited into LocalSettings.php by the upgrader, fetch database settings from LocalSettings.php and AdminSettings.php for use during the upgrade. This allows the DBConnect page to be skipped, making web upgrade almost as easy to use as CLI upgrade. * Made LocalSettingsGenerator add $wgUpgradeKey in non-commented form, for easier subsequent upgrades. * Converted the $wgUpgradeKey check to a normal in-sequence page, called ExistingWiki. This allows the removal of related special cases from WebInstaller. The code for WebInstaller_ExistingWiki is loosely based on WebInstaller_Locked. * Added Status::replaceMessage(), to support informative DB connection error messages from the ExistingWiki page. * In WebInstaller::getInfoBox(), call parse() with $lineStart=true, so that line-start syntax like bullet points can work. * Reduced the length of the generated $wgUpgradeKey from 64 to 16. This is ample for what it does, and makes it fit on the screen and not overlap with the right sidebar when when displayed by WebInstaller_ExistingWiki. * Added $wgUpgradeKey to DefaultSettings.php. --- includes/DefaultSettings.php | 4 + includes/Status.php | 17 ++ includes/installer/CoreInstaller.php | 18 +- includes/installer/Installer.i18n.php | 24 ++- includes/installer/Installer.php | 31 ++- includes/installer/LocalSettingsGenerator.php | 2 +- includes/installer/WebInstaller.php | 13 +- includes/installer/WebInstallerPage.php | 194 +++++++++++++----- 8 files changed, 209 insertions(+), 94 deletions(-) diff --git a/includes/DefaultSettings.php b/includes/DefaultSettings.php index ccd5261712..a2ee8e52c4 100644 --- a/includes/DefaultSettings.php +++ b/includes/DefaultSettings.php @@ -5233,6 +5233,10 @@ $wgEnableSelenium = false; $wgSeleniumTestConfigs = array(); $wgSeleniumConfigFile = null; +/** + * Set this to a random string to allow web-based upgrades + */ +$wgUpgradeKey = false; /** diff --git a/includes/Status.php b/includes/Status.php index ffb396cdb7..f049980f64 100644 --- a/includes/Status.php +++ b/includes/Status.php @@ -300,6 +300,23 @@ class Status { return false; } + /** + * If the specified source message exists, replace it with the specified + * destination message, but keep the same parameters as in the original error. + * + * Return true if the replacement was done, false otherwise. + */ + function replaceMessage( $source, $dest ) { + $replaced = false; + foreach ( $this->errors as $index => $error ) { + if ( $error['message'] === $source ) { + $this->errors[$index]['message'] = $dest; + $replaced = true; + } + } + return $replaced; + } + /** * Backward compatibility function for WikiError -> Status migration * diff --git a/includes/installer/CoreInstaller.php b/includes/installer/CoreInstaller.php index 32ab950902..e86ccc57fc 100644 --- a/includes/installer/CoreInstaller.php +++ b/includes/installer/CoreInstaller.php @@ -84,8 +84,8 @@ abstract class CoreInstaller extends Installer { '_CCDone' => false, '_Extensions' => array(), '_MemCachedServers' => '', - '_LocalSettingsLocked' => true, - '_UpgradeKey' => '', + '_UpgradeKeySupplied' => false, + '_ExistingDBSettings' => false, ); /** @@ -381,7 +381,7 @@ abstract class CoreInstaller extends Installer { * * @return Status */ - protected function generateSecret( $secretName ) { + protected function generateSecret( $secretName, $length = 64 ) { if ( wfIsWindows() ) { $file = null; } else { @@ -393,12 +393,12 @@ abstract class CoreInstaller extends Installer { $status = Status::newGood(); if ( $file ) { - $secretKey = bin2hex( fread( $file, 32 ) ); + $secretKey = bin2hex( fread( $file, $length / 2 ) ); fclose( $file ); } else { $secretKey = ''; - for ( $i=0; $i<8; $i++ ) { + for ( $i = 0; $i < $length / 8; $i++ ) { $secretKey .= dechex( mt_rand( 0, 0x7fffffff ) ); } @@ -411,13 +411,15 @@ abstract class CoreInstaller extends Installer { } /** - * Generate a default $wgUpradeKey, Will warn if we had to use + * Generate a default $wgUpgradeKey. Will warn if we had to use * mt_rand() instead of /dev/urandom * * @return Status */ - protected function generateUpgradeKey() { - return $this->generateSecret( 'wgUpgradeKey' ); + public function generateUpgradeKey() { + if ( strval( $this->getVar( 'wgUpgradeKey' ) ) === '' ) { + return $this->generateSecret( 'wgUpgradeKey', 16 ); + } } /** diff --git a/includes/installer/Installer.i18n.php b/includes/installer/Installer.i18n.php index eeaa84c4c2..c492c1be97 100644 --- a/includes/installer/Installer.i18n.php +++ b/includes/installer/Installer.i18n.php @@ -15,14 +15,22 @@ $messages['en'] = array( 'config-desc' => 'The installer for MediaWiki', 'config-title' => 'MediaWiki $1 installation', 'config-information' => 'Information', - 'config-localsettings-upgrade' => "'''Warning''': A LocalSettings.php file has been detected. -Your software is able to upgrade. -Please fill in the value of \$wgUpgradeKey in the box.", + 'config-localsettings-upgrade' => "A LocalSettings.php file has been detected. +To upgrade this installation, please enter the value of \$wgUpgradeKey in the box below. +You will find it in LocalSettings.php.", 'config-localsettings-key' => 'Upgrade key:', - 'config-localsettings-badkey' => 'The key you provided is incorrect', - 'config-localsettings-noupgrade' => "'''Error''': A LocalSettings.php file has been detected. -Your software is not able to upgrade at this time. -The installer has been disabled for security reasons.", + 'config-localsettings-badkey' => 'The key you provided is incorrect.', + 'config-upgrade-key-missing' => 'An existing installation of MediaWiki has been detected. +To upgrade this installation, please put the following line at the bottom of your LocalSettings.php: + +$1 +', + 'config-localsettings-incomplete' => 'The existing LocalSettings.php appears to be incomplete. +The $1 variable is not set. +Please change LocalSettings.php so that this variable is set, and click "Continue".', + 'config-localsettings-connection-error' => 'An error was encountered when connecting to the database using the settings specified in LocalSettings.php or AdminSettings.php. Please fix these settings and try again. + +$1', 'config-session-error' => 'Error starting session: $1', 'config-session-expired' => 'Your session data seems to have expired. Sessions are configured for a lifetime of $1. @@ -51,7 +59,7 @@ Check your php.ini and make sure session.save_path is set to an app 'config-page-releasenotes' => 'Release notes', 'config-page-copying' => 'Copying', 'config-page-upgradedoc' => 'Upgrading', - 'config-page-locked' => 'Permission denied', + 'config-page-existingwiki' => 'Existing wiki', 'config-help-restart' => 'Do you want to clear all saved data that you have entered and restart the installation process?', 'config-restart' => 'Yes, restart it', 'config-welcome' => "=== Environmental checks === diff --git a/includes/installer/Installer.php b/includes/installer/Installer.php index 42828069e6..5f91ceeb10 100644 --- a/includes/installer/Installer.php +++ b/includes/installer/Installer.php @@ -213,33 +213,30 @@ abstract class Installer { } /** - * Determine if LocalSettings exists. If it does, return an appropriate - * status for whether upgrading is enabled or not. + * Determine if LocalSettings.php exists. If it does, return its variables, + * merged with those from AdminSettings.php, as an array. * - * @return Status + * @return Array */ - public function getLocalSettingsStatus() { + public function getExistingLocalSettings() { global $IP; - $status = Status::newGood(); - wfSuppressWarnings(); - $ls = file_exists( "$IP/LocalSettings.php" ); + $_lsExists = file_exists( "$IP/LocalSettings.php" ); wfRestoreWarnings(); - if( $ls ) { + if( $_lsExists ) { require( "$IP/includes/DefaultSettings.php" ); - require_once( "$IP/LocalSettings.php" ); - $vars = get_defined_vars(); - if( isset( $vars['wgUpgradeKey'] ) && $vars['wgUpgradeKey'] ) { - $status->warning( 'config-localsettings-upgrade' ); - $this->setVar( '_UpgradeKey', $vars['wgUpgradeKey' ] ); - } else { - $status->fatal( 'config-localsettings-noupgrade' ); + require( "$IP/LocalSettings.php" ); + if ( file_exists( "$IP/AdminSettings.php" ) ) { + require( "$IP/AdminSettings.php" ); } + $vars = get_defined_vars(); + unset( $vars['_lsExists'] ); + return $vars; + } else { + return false; } - - return $status; } /** diff --git a/includes/installer/LocalSettingsGenerator.php b/includes/installer/LocalSettingsGenerator.php index 4342a176c2..5cc0e3c270 100644 --- a/includes/installer/LocalSettingsGenerator.php +++ b/includes/installer/LocalSettingsGenerator.php @@ -282,7 +282,7 @@ if ( !defined( 'MEDIAWIKI' ) ) { # Site upgrade key. Must be set to a string (default provided) to turn on the # web installer while LocalSettings.php is in place -#\$wgUpgradeKey = \"{$this->values['wgUpgradeKey']}\"; +\$wgUpgradeKey = \"{$this->values['wgUpgradeKey']}\"; ## Default skin: you can change the default skin. Use the internal symbolic ## names, ie 'standard', 'nostalgia', 'cologneblue', 'monobook', 'vector': diff --git a/includes/installer/WebInstaller.php b/includes/installer/WebInstaller.php index fc250dcfc2..e4d115497b 100644 --- a/includes/installer/WebInstaller.php +++ b/includes/installer/WebInstaller.php @@ -47,6 +47,7 @@ class WebInstaller extends CoreInstaller { */ public $pageSequence = array( 'Language', + 'ExistingWiki', 'Welcome', 'DBConnect', 'Upgrade', @@ -175,15 +176,7 @@ class WebInstaller extends CoreInstaller { # Get the page name. $pageName = $this->request->getVal( 'page' ); - # Check LocalSettings status - $localSettings = $this->getLocalSettingsStatus(); - - if( !$localSettings->isGood() && $this->getVar( '_LocalSettingsLocked' ) ) { - $pageName = 'Locked'; - $pageId = false; - $page = $this->getPageByName( $pageName ); - $page->setLocalSettingsStatus( $localSettings ); - } elseif ( in_array( $pageName, $this->otherPages ) ) { + if ( in_array( $pageName, $this->otherPages ) ) { # Out of sequence $pageId = false; $page = $this->getPageByName( $pageName ); @@ -620,7 +613,7 @@ class WebInstaller extends CoreInstaller { ) . "\n" . "\n" . "
\n" . - $this->parse( $text ) . "\n" . + $this->parse( $text, true ) . "\n" . "
\n" . "
\n" . "\n"; diff --git a/includes/installer/WebInstallerPage.php b/includes/installer/WebInstallerPage.php index 26dea3da64..ff7c681f76 100644 --- a/includes/installer/WebInstallerPage.php +++ b/includes/installer/WebInstallerPage.php @@ -116,56 +116,6 @@ abstract class WebInstallerPage { } } -class WebInstaller_Locked extends WebInstallerPage { - // The status of Installer::getLocalSettingsStatus() - private $status; - - public function setLocalSettingsStatus( Status $s ) { - $this->status = $s; - } - - protected function getId() { - return 0; - } - - public function execute() { - $r = $this->parent->request; - if( !$r->wasPosted() || !$this->status->isOK() ) { - $this->display(); - return 'output'; - } else { - $key = $r->getText( 'config_wpUpgradeKey' ); - if( !$key || $key !== $this->getVar( '_UpgradeKey' ) ) { - $this->parent->showError( 'config-localsettings-badkey' ); - $this->display(); - return 'output'; - } else { - $this->setVar( '_LocalSettingsLocked', false ); - return 'continue'; - } - } - } - - /** - * Display stuff to the end user - */ - private function display() { - $this->startForm(); - $this->parent->showStatusBox( $this->status ); - $continue = false; - if( $this->status->isOK() && !$this->status->isGood() ) { - $this->addHTML( "
" . - $this->parent->getTextBox( array( - 'var' => 'wpUpgradeKey', - 'label' => 'config-localsettings-key', - ) ) - ); - $continue = 'continue'; - } - $this->endForm( $continue ); - } -} - class WebInstaller_Language extends WebInstallerPage { public function execute() { @@ -245,6 +195,146 @@ class WebInstaller_Language extends WebInstallerPage { } +class WebInstaller_ExistingWiki extends WebInstallerPage { + public function execute() { + // If there is no LocalSettings.php, continue to the installer welcome page + $vars = $this->parent->getExistingLocalSettings(); + if ( !$vars ) { + return 'skip'; + } + + // Check if the upgrade key supplied to the user has appeared in LocalSettings.php + if ( $vars['wgUpgradeKey'] !== false + && $this->getVar( '_UpgradeKeySupplied' ) + && $this->getVar( 'wgUpgradeKey' ) === $vars['wgUpgradeKey'] ) + { + // It's there, so the user is authorized + $status = $this->handleExistingUpgrade( $vars ); + if ( $status->isOK() ) { + return 'skip'; + } else { + $this->startForm(); + $this->parent->showStatusBox( $status ); + $this->endForm( 'continue' ); + return 'output'; + } + return $this->handleExistingUpgrade( $vars ); + } + + // If there is no $wgUpgradeKey, tell the user to add one to LocalSettings.php + if ( $vars['wgUpgradeKey'] === false ) { + if ( $this->getVar( 'wgUpgradeKey', false ) === false ) { + $this->parent->generateUpgradeKey(); + $this->setVar( '_UpgradeKeySupplied', true ); + } + $this->startForm(); + $this->addHTML( $this->parent->getInfoBox( + wfMsgNoTrans( 'config-upgrade-key-missing', + "
\$wgUpgradeKey = '" . $this->getVar( 'wgUpgradeKey' ) . "';
" ) + ) ); + $this->endForm( 'continue' ); + return 'output'; + } + + // If there is an upgrade key, but it wasn't supplied, prompt the user to enter it + + $r = $this->parent->request; + if ( $r->wasPosted() ) { + $key = $r->getText( 'config_wgUpgradeKey' ); + if( !$key || $key !== $vars['wgUpgradeKey'] ) { + $this->parent->showError( 'config-localsettings-badkey' ); + $this->showKeyForm(); + return 'output'; + } + // Key was OK + $status = $this->handleExistingUpgrade( $vars ); + if ( $status->isOK() ) { + return 'continue'; + } else { + $this->parent->showStatusBox( $status ); + $this->showKeyForm(); + return 'output'; + } + } else { + $this->showKeyForm(); + return 'output'; + } + } + + /** + * Show the "enter key" form + */ + protected function showKeyForm() { + $this->startForm(); + $this->addHTML( + $this->parent->getInfoBox( wfMsgNoTrans( 'config-localsettings-upgrade' ) ). + '
' . + $this->parent->getTextBox( array( + 'var' => 'wgUpgradeKey', + 'label' => 'config-localsettings-key', + 'attribs' => array( 'autocomplete' => 'off' ), + ) ) + ); + $this->endForm( 'continue' ); + } + + protected function importVariables( $names, $vars ) { + $status = Status::newGood(); + foreach ( $names as $name ) { + if ( !isset( $vars[$name] ) ) { + $status->fatal( 'config-localsettings-incomplete', $name ); + } + $this->setVar( $name, $vars[$name] ); + } + return $status; + } + + /** + * Initiate an upgrade of the existing database + * @param $vars Variables from LocalSettings.php and AdminSettings.php + * @return Status + */ + protected function handleExistingUpgrade( $vars ) { + // Check $wgDBtype + if ( !isset( $vars['wgDBtype'] ) || !in_array( $vars['wgDBtype'], Installer::getDBTypes() ) ) { + return Status::newFatal( 'config-localsettings-connection-error', '' ); + } + + // Set the relevant variables from LocalSettings.php + $requiredVars = array( 'wgDBtype', 'wgDBuser', 'wgDBpassword' ); + $status = $this->importVariables( $requiredVars , $vars ); + $installer = $this->parent->getDBInstaller(); + $status->merge( $this->importVariables( $installer->getGlobalNames(), $vars ) ); + if ( !$status->isOK() ) { + return $status; + } + + if ( isset( $vars['wgDBadminuser'] ) ) { + $this->setVar( '_InstallUser', $vars['wgDBadminuser'] ); + } else { + $this->setVar( '_InstallUser', $vars['wgDBuser'] ); + } + if ( isset( $vars['wgDBadminpassword'] ) ) { + $this->setVar( '_InstallPassword', $vars['wgDBadminpassword'] ); + } else { + $this->setVar( '_InstallPassword', $vars['wgDBpassword'] ); + } + + // Test the database connection + $status = $installer->getConnection(); + if ( !$status->isOK() ) { + // Adjust the error message to explain things correctly + $status->replaceMessage( 'config-connection-error', + 'config-localsettings-connection-error' ); + return $status; + } + + // All good + $this->setVar( '_ExistingDBSettings', true ); + return $status; + } +} + class WebInstaller_Welcome extends WebInstallerPage { public function execute() { @@ -268,6 +358,10 @@ class WebInstaller_Welcome extends WebInstallerPage { class WebInstaller_DBConnect extends WebInstallerPage { public function execute() { + if ( $this->getVar( '_ExistingDBSettings' ) ) { + return 'skip'; + } + $r = $this->parent->request; if ( $r->wasPosted() ) { $status = $this->submit(); -- 2.20.1