From ab97b6fdf0a58ea78cd20b8580a9ca6c8de59dc6 Mon Sep 17 00:00:00 2001 From: sukhwinder33445 <54990055+sukhwinder33445@users.noreply.github.com> Date: Thu, 18 Feb 2021 12:31:21 +0100 Subject: [PATCH] Enforce database as configuration backend (#4135) --- application/controllers/AccountController.php | 4 +- .../Config/General/ApplicationConfigForm.php | 52 ++++++++++------ .../forms/Config/GeneralConfigForm.php | 9 +++ library/Icinga/Authentication/Auth.php | 4 +- library/Icinga/Common/Database.php | 29 +++++++++ .../User/Preferences/PreferencesStore.php | 4 +- .../Setup/Requirement/SetRequirement.php | 34 +++++++++++ modules/setup/library/Setup/WebWizard.php | 60 ++++++++++++------- 8 files changed, 151 insertions(+), 45 deletions(-) create mode 100644 library/Icinga/Common/Database.php create mode 100644 modules/setup/library/Setup/Requirement/SetRequirement.php diff --git a/application/controllers/AccountController.php b/application/controllers/AccountController.php index 795935fb5..0ddd69e3b 100644 --- a/application/controllers/AccountController.php +++ b/application/controllers/AccountController.php @@ -61,9 +61,9 @@ class AccountController extends Controller $form = new PreferenceForm(); $form->setPreferences($user->getPreferences()); - if ($config->get('config_backend', 'ini') !== 'none') { + if ($config->get('config_backend', 'db') !== 'none') { $form->setStore(PreferencesStore::create(new ConfigObject(array( - 'store' => $config->get('config_backend', 'ini'), + 'store' => $config->get('config_backend', 'db'), 'resource' => $config->config_resource )), $user)); } diff --git a/application/forms/Config/General/ApplicationConfigForm.php b/application/forms/Config/General/ApplicationConfigForm.php index 00eed236b..63a32fa9b 100644 --- a/application/forms/Config/General/ApplicationConfigForm.php +++ b/application/forms/Config/General/ApplicationConfigForm.php @@ -70,22 +70,35 @@ class ApplicationConfigForm extends Form ) ); - $this->addElement( - 'select', - 'global_config_backend', - array( - 'required' => true, - 'autosubmit' => true, - 'label' => $this->translate('User Preference Storage Type'), - 'multiOptions' => array( - 'ini' => $this->translate('File System (INI Files)'), - 'db' => $this->translate('Database'), - 'none' => $this->translate('Don\'t Store Preferences') - ) - ) - ); + // we do not need this form for setup because we set the database there as default. + // this form is only displayed in configuration -> application if preferences backend type of ini is recognized + if (isset($formData['global_config_backend']) && $formData['global_config_backend'] === 'ini') { + $this->addElement( + 'select', + 'global_config_backend', + [ + 'required' => true, + 'autosubmit' => true, + 'label' => $this->translate('User Preference Storage Type'), + 'multiOptions' => [ + 'ini' => $this->translate('File System (INI Files)'), + 'db' => $this->translate('Database') + ] + ] + ); + } else { + $this->addElement( + 'hidden', + 'global_config_backend', + [ + 'required' => true, + 'value' => 'db', + 'disabled' => true + ] + ); + } - if (isset($formData['global_config_backend']) && $formData['global_config_backend'] === 'db') { + if (! isset($formData['global_config_backend']) || $formData['global_config_backend'] === 'db') { $backends = array(); foreach (ResourceFactory::getResourceConfigs()->toArray() as $name => $resource) { if ($resource['type'] === 'db') { @@ -98,8 +111,13 @@ class ApplicationConfigForm extends Form 'global_config_resource', array( 'required' => true, - 'multiOptions' => $backends, - 'label' => $this->translate('Database Connection') + 'multiOptions' => array_merge( + ['' => sprintf(' - %s - ', $this->translate('Please choose'))], + $backends + ), + 'disable' => [''], + 'value' => '', + 'label' => $this->translate('Configuration Database') ) ); } diff --git a/application/forms/Config/GeneralConfigForm.php b/application/forms/Config/GeneralConfigForm.php index 5f15512a5..cab750085 100644 --- a/application/forms/Config/GeneralConfigForm.php +++ b/application/forms/Config/GeneralConfigForm.php @@ -37,4 +37,13 @@ class GeneralConfigForm extends ConfigForm $this->addSubForm($themingConfigForm->create($formData)); $this->addSubForm($domainConfigForm->create($formData)); } + + public function onRequest() + { + parent::onRequest(); + + if ($this->config->getConfigObject()->global->config_backend === 'ini') { + $this->warning('The preferences backend of type INI is deprecated and will be removed with version 2.10'); + } + } } diff --git a/library/Icinga/Authentication/Auth.php b/library/Icinga/Authentication/Auth.php index 431a7777c..28d2e0ba1 100644 --- a/library/Icinga/Authentication/Auth.php +++ b/library/Icinga/Authentication/Auth.php @@ -111,9 +111,9 @@ class Auth ); $config = new Config(); } - if ($config->get('global', 'config_backend', 'ini') !== 'none') { + if ($config->get('global', 'config_backend', 'db') !== 'none') { $preferencesConfig = new ConfigObject(array( - 'store' => $config->get('global', 'config_backend', 'ini'), + 'store' => $config->get('global', 'config_backend', 'db'), 'resource' => $config->get('global', 'config_resource') )); try { diff --git a/library/Icinga/Common/Database.php b/library/Icinga/Common/Database.php new file mode 100644 index 000000000..34300c4ed --- /dev/null +++ b/library/Icinga/Common/Database.php @@ -0,0 +1,29 @@ +get('global', 'config_resource') + )); + $config->options = [ + PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_OBJ, + PDO::MYSQL_ATTR_INIT_COMMAND => "SET SESSION SQL_MODE='STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE" + . ",ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION'" + ]; + + $conn = new Connection($config); + + return $conn; + } +} diff --git a/library/Icinga/User/Preferences/PreferencesStore.php b/library/Icinga/User/Preferences/PreferencesStore.php index ae953ace9..ad1c51608 100644 --- a/library/Icinga/User/Preferences/PreferencesStore.php +++ b/library/Icinga/User/Preferences/PreferencesStore.php @@ -4,6 +4,7 @@ namespace Icinga\User\Preferences; use Icinga\Application\Config; +use Icinga\Application\Logger; use Icinga\User; use Icinga\User\Preferences; use Icinga\Data\ConfigObject; @@ -116,7 +117,7 @@ abstract class PreferencesStore */ public static function create(ConfigObject $config, User $user) { - $type = ucfirst(strtolower($config->get('store', 'ini'))); + $type = ucfirst(strtolower($config->get('store', 'db'))); $storeClass = 'Icinga\\User\\Preferences\\Store\\' . $type . 'Store'; if (!class_exists($storeClass)) { throw new ConfigurationError( @@ -126,6 +127,7 @@ abstract class PreferencesStore } if ($type === 'Ini') { + Logger::warning('The preferences backend of type INI is deprecated and will be removed with version 2.10'); $config->location = Config::resolvePath('preferences'); } elseif ($type === 'Db') { $config->connection = new DbConnection(ResourceFactory::getResourceConfig($config->resource)); diff --git a/modules/setup/library/Setup/Requirement/SetRequirement.php b/modules/setup/library/Setup/Requirement/SetRequirement.php new file mode 100644 index 000000000..77cbaf018 --- /dev/null +++ b/modules/setup/library/Setup/Requirement/SetRequirement.php @@ -0,0 +1,34 @@ +getCondition(); + + if ($condition->getState()) { + $this->setStateText(sprintf( + mt('setup', '%s is available.'), + $this->getAlias() ?: $this->getTitle() + )); + return true; + } + + $this->setStateText(sprintf( + mt('setup', '%s is missing.'), + $this->getAlias() ?: $this->getTitle() + )); + + return false; + } +} diff --git a/modules/setup/library/Setup/WebWizard.php b/modules/setup/library/Setup/WebWizard.php index 511d73791..7a7e77d45 100644 --- a/modules/setup/library/Setup/WebWizard.php +++ b/modules/setup/library/Setup/WebWizard.php @@ -3,6 +3,7 @@ namespace Icinga\Module\Setup; +use Icinga\Module\Setup\Requirement\SetRequirement; use PDOException; use Icinga\Web\Form; use Icinga\Web\Wizard; @@ -227,28 +228,13 @@ class WebWizard extends Wizard implements SetupWizard unset($pageData['setup_usergroup_backend']); } } - } elseif ($page->getName() === 'setup_general_config') { - $authData = $this->getPageData('setup_authentication_type'); - if ($authData['type'] === 'db') { - $page - ->create($this->getRequestData($page, $request)) - ->getElement('global_config_backend') - ->setValue('db'); - $page->info( - mt( - 'setup', - 'Note that choosing "Database" as preference storage causes' - . ' Icinga Web 2 to use the same database as for authentication.' - ), - false - ); - } - } elseif ($page->getName() === 'setup_authentication_type' && $this->getDirection() === static::FORWARD) { + } elseif ($page->getName() === 'setup_authentication_type') { $authData = $this->getPageData($page->getName()); + $pageData = & $this->getPageData(); + if ($authData !== null && $request->getPost('type') !== $authData['type']) { // Drop any existing page data in case the authentication type has changed, // otherwise it will conflict with other forms that depend on this one - $pageData = & $this->getPageData(); unset($pageData['setup_admin_account']); unset($pageData['setup_authentication_backend']); @@ -259,6 +245,13 @@ class WebWizard extends Wizard implements SetupWizard unset($pageData['setup_config_db_resource']); unset($pageData['setup_config_db_creation']); } + } elseif (isset($authData['type']) && $authData['type'] == 'external') { + // If you choose the authentication type external and validate the database and then come + // back to change the authentication type but do not change it, you will get an database configuration + // related error message on the next page. To avoid this error, the 'setup_config_db_resource' + // page must be unset. + + unset($pageData['setup_config_db_resource']); } } } @@ -295,18 +288,20 @@ class WebWizard extends Wizard implements SetupWizard $skip = $backendConfig['backend'] !== 'ldap'; } elseif ($newPage->getName() === 'setup_config_db_resource') { $authData = $this->getPageData('setup_authentication_type'); - $configData = $this->getPageData('setup_general_config'); - $skip = $authData['type'] === 'db' || $configData['global_config_backend'] !== 'db'; + $skip = $authData['type'] === 'db'; } elseif (in_array($newPage->getName(), array('setup_auth_db_creation', 'setup_config_db_creation'))) { if (($newPage->getName() === 'setup_auth_db_creation' || $this->hasPageData('setup_config_db_resource')) && (($config = $this->getPageData('setup_auth_db_resource')) !== null || ($config = $this->getPageData('setup_config_db_resource')) !== null) - && !$config['skip_validation'] + && !$config['skip_validation'] && $this->getDirection() == static::FORWARD ) { + // Execute this code only if the direction is forward. + // Otherwise, an error will be output when you go back. $db = new DbTool($config); try { $db->connectToDb(); // Are we able to login on the database? + if (array_search(reset($this->databaseTables), $db->listTables(), true) === false) { // In case the database schema does not yet exist the // user needs the privileges to setup the database @@ -651,6 +646,8 @@ class WebWizard extends Wizard implements SetupWizard ) ))); + $dbSet = new RequirementSet(false, RequirementSet::MODE_OR); + $mysqlSet = new RequirementSet(true); $mysqlSet->add(new PhpModuleRequirement(array( 'optional' => true, @@ -680,7 +677,8 @@ class WebWizard extends Wizard implements SetupWizard 'setup.requirement.class' ) ))); - $set->merge($mysqlSet); + + $dbSet->merge($mysqlSet); $pgsqlSet = new RequirementSet(true); $pgsqlSet->add(new PhpModuleRequirement(array( @@ -711,7 +709,23 @@ class WebWizard extends Wizard implements SetupWizard 'setup.requirement.class' ) ))); - $set->merge($pgsqlSet); + $dbSet->merge($pgsqlSet); + $set->merge($dbSet); + + $dbRequire = (new SetRequirement(array( + 'optional' => false, + 'condition' => $dbSet, + 'title' =>'Database', + 'alias' => 'PDO-MySQL OR PDO-PostgreSQL', + 'description' => mt( + 'setup', + 'A database is mandatory, therefore at least one module PDO-MySQL OR PDO-PostgreSQL for PHP + is required.' + ) + ))); + + $set->add($dbRequire); + $set->add(new ConfigDirectoryRequirement(array( 'condition' => Icinga::app()->getConfigDir(),