N°9571 - Do not allow DB change or install when upgrading iTop (#958)

* N°9571 - Do not allow DB change or install when upgrading iTop

* N°9571 - Fix unit tests

* N°9711 - Removed step Install or Upgrade

* N°9711 - After revue
This commit is contained in:
Eric Espié
2026-07-06 10:31:43 +02:00
committed by GitHub
parent c448f8411c
commit 6b396adeea
10 changed files with 86 additions and 229 deletions

View File

@@ -444,7 +444,7 @@ EOF;
$sPwd = $oConfig->Get('db_pwd');
$sSource = $oConfig->Get('db_name');
$sTlsEnabled = $oConfig->Get('db_tls.enabled');
$sTlsCA = $oConfig->Get('db_tls.ca');
$sTlsCA = $oConfig->Get('db_tls.ca') ?? '';
try {
$oMysqli = CMDBSource::GetMysqliInstance(

View File

@@ -1437,7 +1437,7 @@ EOF
// Unsupported Password, warn the user
$oPage->add_ready_script(
<<<JS
$("#db_info").html('<div class="message message-error"><span class="message-title">Error:</span>On Windows, the backup won\'t work because database password contains %, ! or &quot; character</div>');
$("#db_info").html('<div class="message message-error ibo-is-html-content"><span class="message-title">Error:</span>On Windows, the backup won\'t work because database password contains %, ! or &quot; character</div>');
JS
);
} else {

View File

@@ -132,7 +132,7 @@ if ($bUseItopConfig && file_exists($sConfigFile)) {
$aDBXmlSettings ['prefix'] = $oConfig->Get('db_subname');
$aDBXmlSettings ['db_tls_enabled'] = $oConfig->Get('db_tls.enabled');
//cannot be null or infinite loop triggered!
$aDBXmlSettings ['db_tls_ca'] = $oConfig->Get('db_tls.ca') ?? "";
$aDBXmlSettings ['db_tls_ca'] = $oConfig->Get('db_tls.ca') ?? '';
$oParams->Set('database', $aDBXmlSettings);
$aFields = [

View File

@@ -17,6 +17,7 @@
*
* You should have received a copy of the GNU Affero General Public License
*/
use Combodo\iTop\Application\WebPage\WebPage;
/**
@@ -205,7 +206,7 @@ EOF
$sUpgradeDMVersionToDisplay = utils::HtmlEntities($sUpgradeDMVersion);
$oPage->add(
<<<HTML
<div class="message message-valid">The datamodel will be upgraded from version $sInstalledDataModelVersion to version $sUpgradeDMVersion.</div>
<div class="message message-valid ibo-is-html-content">The datamodel will be upgraded from version $sInstalledDataModelVersion to version $sUpgradeDMVersion.</div>
<input type="hidden" name="upgrade_type" value="use-compatible">
<input type="hidden" name="datamodel_path" value="$sCompatibleDMDirToDisplay">
<input type="hidden" name="datamodel_version" value="$sUpgradeDMVersionToDisplay">
@@ -214,15 +215,59 @@ HTML
}
$oPage->add('<div id="db_info"></div>');
$sDBServer = json_encode($this->oWizard->GetParameter('db_server', ''));
$sDBUser = json_encode($this->oWizard->GetParameter('db_user', ''));
$sDBPwd = json_encode($this->oWizard->GetParameter('db_pwd', ''));
$sDBName = json_encode($this->oWizard->GetParameter('db_name', ''));
$sTlsCA = json_encode($this->oWizard->GetParameter('db_tls_ca', ''));
$sTlsEnabled = $this->oWizard->GetParameter('db_tls_enabled', false) ? 1 : 0;
$oPage->add_ready_script(
<<<EOF
$("#changes_summary .title").on('click', function() { $(this).parent().toggleClass('closed'); } );
$('input[name=upgrade_type]').on('click change', function() { WizardUpdateButtons(); });
var iCheckDBTimer = null;
var oXHRCheckDB = null;
function CheckDBConnection()
{
// Don't call the server too often...
if (iCheckDBTimer !== null)
{
clearTimeout(iCheckDBTimer);
iCheckDBTimer = null;
}
iCheckDBTimer = setTimeout(DoCheckDBConnection, 500);
}
function DoCheckDBConnection()
{
iCheckDBTimer = null;
var oParams = {
'db_server': $sDBServer,
'db_user': $sDBUser,
'db_pwd': $sDBPwd,
'db_name': $sDBName,
'db_tls_enabled': $sTlsEnabled,
'db_tls_ca': $sTlsCA,
}
if ((oXHRCheckDB != null) && (oXHRCheckDB != undefined))
{
oXHRCheckDB.abort();
oXHRCheckDB = null;
}
oXHRCheckDB = WizardAsyncAction("check_db", oParams);
}
DoCheckDBConnection(); // Validate the initial values immediately
EOF
);
$oMutex = new iTopMutex(
'cron'.$this->oWizard->GetParameter('db_name', '').$this->oWizard->GetParameter('db_prefix', ''),
'cron'.$sDBName.$this->oWizard->GetParameter('db_prefix', ''),
$this->oWizard->GetParameter('db_server', ''),
$this->oWizard->GetParameter('db_user', ''),
$this->oWizard->GetParameter('db_pwd', ''),
@@ -235,6 +280,15 @@ EOF
}
}
public function AsyncAction(WebPage $oPage, $sCode, $aParameters)
{
switch ($sCode) {
case 'check_db':
SetupUtils::AsyncCheckDB($oPage, $aParameters);
break;
}
}
public function CanMoveForward()
{
return $this->bCanMoveForward;

View File

@@ -1,216 +0,0 @@
<?php
/**
* Copyright (C) 2013-2026 Combodo SAS
*
* This file is part of iTop.
*
* iTop is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* iTop is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
*/
use Combodo\iTop\Application\WebPage\WebPage;
/**
* Second step of the iTop Installation Wizard: Install or Upgrade
*/
class WizStepInstallOrUpgrade extends WizardStep
{
public function GetTitle()
{
return 'Install or Upgrade choice';
}
public function GetPossibleSteps()
{
return [WizStepDetectedInfo::class, WizStepLicense::class];
}
public function UpdateWizardStateAndGetNextStep($bMoveForward = true): WizardState
{
$sNextStep = '';
$sInstallMode = utils::ReadParam('install_mode');
$this->oWizard->SaveParameter('previous_version_dir', '');
$this->oWizard->SaveParameter('db_server', '');
$this->oWizard->SaveParameter('db_user', '');
$this->oWizard->SaveParameter('db_pwd', '');
$this->oWizard->SaveParameter('db_name', '');
$this->oWizard->SaveParameter('db_prefix', '');
$this->oWizard->SaveParameter('db_tls_enabled', false);
$this->oWizard->SaveParameter('db_tls_ca', '');
if ($sInstallMode == 'install') {
$this->oWizard->SetParameter('install_mode', 'install');
$sFullSourceDir = SetupUtils::GetLatestDataModelDir();
$this->oWizard->SetParameter('source_dir', $sFullSourceDir);
$this->oWizard->SetParameter('datamodel_version', SetupUtils::GetDataModelVersion($sFullSourceDir));
$sNextStep = WizStepLicense::class;
} else {
$this->oWizard->SetParameter('install_mode', 'upgrade');
$sNextStep = WizStepDetectedInfo::class;
}
return new WizardState($sNextStep);
}
public function Display(SetupPage $oPage): void
{
$sInstallMode = $this->oWizard->GetParameter('install_mode', '');
$sDBServer = $this->oWizard->GetParameter('db_server', '');
$sDBUser = $this->oWizard->GetParameter('db_user', '');
$sDBPwd = $this->oWizard->GetParameter('db_pwd', '');
$sDBName = $this->oWizard->GetParameter('db_name', '');
$sDBPrefix = $this->oWizard->GetParameter('db_prefix', '');
$sTlsEnabled = $this->oWizard->GetParameter('db_tls_enabled', false);
$sTlsCA = $this->oWizard->GetParameter('db_tls_ca', '');
$sPreviousVersionDir = '';
if ($sInstallMode == '') {
$aPreviousInstance = SetupUtils::GetPreviousInstance(APPROOT);
if ($aPreviousInstance['found']) {
$sInstallMode = 'upgrade';
$sDBServer = $aPreviousInstance['db_server'];
$sDBUser = $aPreviousInstance['db_user'];
$sDBPwd = $aPreviousInstance['db_pwd'];
$sDBName = $aPreviousInstance['db_name'];
$sDBPrefix = $aPreviousInstance['db_prefix'];
$sTlsEnabled = $aPreviousInstance['db_tls_enabled'];
$sTlsCA = $aPreviousInstance['db_tls_ca'];
$this->oWizard->SaveParameter('graphviz_path', $aPreviousInstance['graphviz_path']);
$sPreviousVersionDir = APPROOT;
} else {
$sInstallMode = 'install';
}
}
$sPreviousVersionDir = $this->oWizard->GetParameter('previous_version_dir', $sPreviousVersionDir);
$sUpgradeInfoStyle = '';
if ($sInstallMode == 'install') {
$sUpgradeInfoStyle = ' style="display: none;" ';
}
$oPage->add('<div class="setup-content-title">What do you want to do?</div>');
$sChecked = ($sInstallMode == 'install') ? ' checked ' : '';
$oPage->p('<input id="radio_install" type="radio" name="install_mode" value="install" '.$sChecked.'/><label for="radio_install">&nbsp;Install a new '.ITOP_APPLICATION.'</label>');
$sChecked = ($sInstallMode == 'upgrade') ? ' checked ' : '';
$sDisabled = (($sInstallMode == 'install') && (empty($sPreviousVersionDir))) ? ' disabled' : '';
$oPage->p('<input id="radio_update" type="radio" name="install_mode" value="upgrade" '.$sChecked.$sDisabled.'/><label for="radio_update">&nbsp;Upgrade an existing '.ITOP_APPLICATION.' instance</label>');
$sUpgradeDir = utils::HtmlEntities($sPreviousVersionDir);
$oPage->add(
<<<HTML
<div id="upgrade_info"'.$sUpgradeInfoStyle.'>
<div class="setup-disk-location--input--container">Location on the disk:<input id="previous_version_dir_display" type="text" value="$sUpgradeDir" class="ibo-input" disabled>
<input type="hidden" name="previous_version_dir" value="$sUpgradeDir"></div>
HTML
);
SetupUtils::DisplayDBParameters(
$oPage,
false,
$sDBServer,
$sDBUser,
$sDBPwd,
$sDBName,
$sDBPrefix,
$sTlsEnabled,
$sTlsCA
);
$oPage->add('</div>');
//$oPage->add('</fieldset>');
$oPage->add_ready_script(
<<<JS
$("#radio_update").on('change', function() { if (this.checked ) { $('#upgrade_info').show(); WizardUpdateButtons(); } else { $('#upgrade_info').hide(); } });
$("#radio_install").on('change', function() { if (this.checked ) { $('#upgrade_info').hide(); WizardUpdateButtons(); } else { $('#upgrade_info').show(); } });
JS
);
}
public function AsyncAction(WebPage $oPage, $sCode, $aParameters)
{
switch ($sCode) {
case 'check_path':
$sPreviousVersionDir = $aParameters['previous_version_dir'];
$aPreviousInstance = SetupUtils::GetPreviousInstance($sPreviousVersionDir);
if ($aPreviousInstance['found']) {
$sDBServer = utils::EscapeHtml($aPreviousInstance['db_server']);
$sDBUser = utils::EscapeHtml($aPreviousInstance['db_user']);
$sDBPwd = utils::EscapeHtml($aPreviousInstance['db_pwd']);
$sDBName = utils::EscapeHtml($aPreviousInstance['db_name']);
$sDBPrefix = utils::EscapeHtml($aPreviousInstance['db_prefix']);
$oPage->add_ready_script(
<<<EOF
$("#db_server").val('$sDBServer');
$("#db_user").val('$sDBUser');
$("#db_pwd").val('$sDBPwd');
$("#db_name").val('$sDBName');
$("#db_prefix").val('$sDBPrefix');
$("#db_pwd").trigger('change'); // Forces check of the DB connection
EOF
);
}
break;
case 'check_db':
SetupUtils::AsyncCheckDB($oPage, $aParameters);
break;
case 'check_backup':
$sDBBackupPath = $aParameters['db_backup_path'];
$fFreeSpace = SetupUtils::CheckDiskSpace($sDBBackupPath);
if ($fFreeSpace !== false) {
$sMessage = utils::EscapeHtml(SetupUtils::HumanReadableSize($fFreeSpace).' free in '.dirname($sDBBackupPath));
$oPage->add_ready_script(
<<<EOF
$("#backup_info").html('$sMessage');
EOF
);
} else {
$oPage->add_ready_script(
<<<EOF
$("#backup_info").html('');
EOF
);
}
break;
}
}
/**
* Tells whether the "Next" button should be enabled interactively
* @return string A piece of javascript code returning either true or false
*/
public function JSCanMoveForward()
{
return
<<<EOF
if ($("#radio_install").prop("checked"))
{
ValidateField("db_name", false);
ValidateField("db_new_name", false);
ValidateField("db_prefix", false);
return true;
}
else
{
var bRet = ($("#wiz_form").data("db_connection") !== "error");
bRet = ValidateField("db_name", true) && bRet;
bRet = ValidateField("db_new_name", true) && bRet;
bRet = ValidateField("db_prefix", true) && bRet;
return bRet;
}
EOF
;
}
}

View File

@@ -171,7 +171,6 @@ class WizStepModulesChoice extends AbstractWizStepInstall
{
$aSteps = [
["class" => "WizStepWelcome","state" => ""],
["class" => "WizStepInstallOrUpgrade","state" => ""],
["class" => "WizStepDetectedInfo","state" => ""],
["class" => "WizStepUpgradeMiscParams","state" => ""],
];

View File

@@ -40,7 +40,7 @@ class WizStepWelcome extends WizardStep
}
/**
* Returns the label for the " Next >> " button
* Returns the label for the "Next >>" button
* @return string The label for the button
*/
public function GetNextButtonLabel()
@@ -50,12 +50,17 @@ class WizStepWelcome extends WizardStep
public function GetPossibleSteps()
{
return [WizStepInstallOrUpgrade::class];
return [WizStepDetectedInfo::class, WizStepLicense::class];
}
public function UpdateWizardStateAndGetNextStep($bMoveForward = true): WizardState
{
return new WizardState(WizStepInstallOrUpgrade::class);
if ($this->oWizard->GetParameter('install_mode', 'install') === 'install') {
return new WizardState(WizStepLicense::class);
}
return new WizardState(WizStepDetectedInfo::class);
}
public function Display(SetupPage $oPage): void
@@ -63,6 +68,25 @@ class WizStepWelcome extends WizardStep
$this->oWizard->EraseParameters();
$this->oWizard->SetWizardSteps([]);
$aPreviousInstance = SetupUtils::GetPreviousInstance(APPROOT);
if ($aPreviousInstance['found']) {
$this->oWizard->SetParameter('install_mode', 'upgrade');
$this->oWizard->SetParameter('db_server', $aPreviousInstance['db_server']);
$this->oWizard->SetParameter('db_user', $aPreviousInstance['db_user']);
$this->oWizard->SetParameter('db_pwd', $aPreviousInstance['db_pwd']);
$this->oWizard->SetParameter('db_name', $aPreviousInstance['db_name']);
$this->oWizard->SetParameter('db_prefix', $aPreviousInstance['db_prefix']);
$this->oWizard->SetParameter('db_tls_enabled', $aPreviousInstance['db_tls_enabled']);
$this->oWizard->SetParameter('db_tls_ca', $aPreviousInstance['db_tls_ca'] ?? '');
$this->oWizard->SetParameter('graphviz_path', $aPreviousInstance['graphviz_path']);
} else {
$this->oWizard->SetParameter('install_mode', 'install');
$sFullSourceDir = SetupUtils::GetLatestDataModelDir();
$this->oWizard->SetParameter('source_dir', $sFullSourceDir);
$this->oWizard->SetParameter('datamodel_version', SetupUtils::GetDataModelVersion($sFullSourceDir));
}
$this->oWizard->SetParameter('previous_version_dir', APPROOT);
// Store the misc_options for the future...
$aMiscOptions = utils::ReadParam('option', [], false, 'raw_data');
$sMiscOptions = $this->oWizard->GetParameter('misc_options', json_encode($aMiscOptions));

View File

@@ -24,7 +24,6 @@
* Steps order (can be retrieved using \WizardController::DumpStructure) :
*
* WizStepWelcome
* WizStepInstallOrUpgrade
* + +
* | |
* v +----->

View File

@@ -4,7 +4,6 @@ require_once(APPROOT.'setup/wizardsteps/WizardState.php');
require_once(APPROOT.'setup/wizardsteps/WizardStep.php');
require_once(APPROOT.'setup/wizardsteps/AbstractWizStepInstall.php');
require_once(APPROOT.'setup/wizardsteps/WizStepWelcome.php');
require_once(APPROOT.'setup/wizardsteps/WizStepInstallOrUpgrade.php');
require_once(APPROOT.'setup/wizardsteps/WizStepDetectedInfo.php');
require_once(APPROOT.'setup/wizardsteps/WizStepLicense.php');
require_once(APPROOT.'setup/wizardsteps/WizStepLicense2.php');

View File

@@ -1420,7 +1420,6 @@ HTML,
$expected = [
["class" => "WizStepWelcome","state" => ""],
["class" => "WizStepInstallOrUpgrade","state" => ""],
["class" => "WizStepDetectedInfo","state" => ""],
["class" => "WizStepUpgradeMiscParams","state" => ""],
];
@@ -1441,7 +1440,6 @@ HTML,
$expected = [
["class" => "WizStepWelcome","state" => ""],
["class" => "WizStepInstallOrUpgrade","state" => ""],
["class" => "WizStepDetectedInfo","state" => ""],
["class" => "WizStepUpgradeMiscParams","state" => ""],
];