From 300db4602b425a74507a1e41d088a7ec9f0c069a Mon Sep 17 00:00:00 2001 From: Michael Kaufmann Date: Thu, 12 May 2022 13:38:34 +0200 Subject: [PATCH] fixes to installation and sequence of events in there Signed-off-by: Michael Kaufmann --- lib/Froxlor/Install/Install.php | 30 ++++++- lib/Froxlor/Install/Install/Core.php | 88 ++++++++++---------- lib/formfields/install/formfield.install.php | 8 +- lng/en.lng.php | 4 +- 4 files changed, 79 insertions(+), 51 deletions(-) diff --git a/lib/Froxlor/Install/Install.php b/lib/Froxlor/Install/Install.php index eaf33c48..07e76a88 100644 --- a/lib/Froxlor/Install/Install.php +++ b/lib/Froxlor/Install/Install.php @@ -26,6 +26,7 @@ namespace Froxlor\Install; use Exception; +use PDO; use Froxlor\Install\Install\Core; use Froxlor\UI\Panel\UI; use Froxlor\UI\Request; @@ -164,14 +165,37 @@ class Install // redirect user to home if the installation is done if ($this->currentStep == $this->maxSteps) { - header('Location: ../'); - return; + // check setting for "panel.is_configured" whether user has + // run the config-services script (or checked the manual mode) + if ($this->checkInstallStateFinished()) { + header('Location: ../'); + return; + } + throw new Exception(lng('install.errors.notyetconfigured')); } // redirect to next step header('Location: ?step=' . ($this->currentStep + 1)); } + private function checkInstallStateFinished(): bool + { + $core = new Core($_SESSION['installation']); + if (isset($_SESSION['installation']['manual_config']) && (int) $_SESSION['installation']['manual_config'] == 1) { + $core->createUserdataConf(); + return true; + } + $pdo = $core->getUnprivilegedPdo(); + $stmt = $pdo->prepare("SELECT `value` FROM `panel_settings` WHERE `settinggroup` = 'panel' AND `varname` = 'is_configured'"); + $stmt->execute(); + $result = $stmt->fetch(PDO::FETCH_ASSOC); + if ($result && (int) $result['value'] == 1) { + $core->createUserdataConf(); + return true; + } + return false; + } + /** * @return array */ @@ -349,8 +373,6 @@ class Install if ($pdo->prepare('FLUSH PRIVILEGES')->execute() === false) { throw new Exception(lng('install.errors.unabletoflushprivs')); } - - // @todo build and set $validatedData['mysql_access_host'] } private function guessWebserver(): ?string diff --git a/lib/Froxlor/Install/Install/Core.php b/lib/Froxlor/Install/Install/Core.php index 222b3718..9384e334 100644 --- a/lib/Froxlor/Install/Install/Core.php +++ b/lib/Froxlor/Install/Install/Core.php @@ -108,6 +108,27 @@ class Core $options[PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT] = (bool)$this->validatedData['mysql_ssl_verify_server_certificate']; } + $pdo = $this->getUnprivilegedPdo(); + + // change settings accordingly + $this->doSettings($pdo); + // create entries + $this->doDataEntries($pdo); + // create JSON array for config-services + $this->createJsonArray(); + } + + public function getUnprivilegedPdo(): PDO + { + $options = [ + 'PDO::MYSQL_ATTR_INIT_COMMAND' => 'SET names utf8' + ]; + + if (!empty($this->validatedData['mysql_ssl_ca_file'])) { + $options[PDO::MYSQL_ATTR_SSL_CA] = $this->validatedData['mysql_ssl_ca_file']; + $options[PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT] = (bool)$this->validatedData['mysql_ssl_verify_server_certificate']; + } + $dsn = "mysql:host=" . $this->validatedData['mysql_host'] . ";dbname=" . $this->validatedData['mysql_database'] . ";"; try { $pdo = new PDO($dsn, $this->validatedData['mysql_unprivileged_user'], $this->validatedData['mysql_unprivileged_pass'], $options); @@ -117,18 +138,10 @@ class Core $sql_mode .= ',NO_AUTO_CREATE_USER'; } $pdo->exec('SET sql_mode = "' . $sql_mode . '"'); + return $pdo; } catch (PDOException $e) { throw new Exception('Unexpected exception occured', 0, $e); } - - // change settings accordingly - $this->doSettings($pdo); - // create entries - $this->doDataEntries($pdo); - // create config-file - $this->createUserdataConf(); - // create JSON array for config-services - $this->createJsonArray(); } /** @@ -193,6 +206,8 @@ class Core */ private function createDatabaseAndUser(object &$db_root) { + $this->validatedData['mysql_access_host'] = $this->validatedData['mysql_host']; + // so first we have to delete the database and // the user given for the unpriv-user if they exit $del_stmt = $db_root->prepare("DELETE FROM `mysql`.`user` WHERE `User` = :user AND `Host` = :accesshost"); @@ -228,21 +243,21 @@ class Core $ins_stmt = $db_root->prepare("CREATE DATABASE `" . str_replace('`', '', $this->validatedData['mysql_database']) . "` CHARACTER SET=utf8 COLLATE=utf8_general_ci"); $ins_stmt->execute(); + $mysql_access_host_array = array_map('trim', explode(',', $this->validatedData['mysql_access_host'])); - // @todo localhost/127.0.0.1/serverip checks and addition is only required if mysql_access_host is not a separate machine - /* if (in_array('127.0.0.1', $mysql_access_host_array) && !in_array('localhost', $mysql_access_host_array)) { $mysql_access_host_array[] = 'localhost'; } if (!in_array('127.0.0.1', $mysql_access_host_array) && in_array('localhost', $mysql_access_host_array)) { $mysql_access_host_array[] = '127.0.0.1'; } - if (!in_array($this->validatedData['serverip'], $mysql_access_host_array)) { - $mysql_access_host_array[] = $this->validatedData['serverip']; + if (!empty($this->validatedData['serveripv4']) && !in_array($this->validatedData['serveripv4'], $mysql_access_host_array)) { + $mysql_access_host_array[] = $this->validatedData['serveripv4']; + } + if (!empty($this->validatedData['serveripv6']) && !in_array($this->validatedData['serveripv6'], $mysql_access_host_array)) { + $mysql_access_host_array[] = $this->validatedData['serveripv6']; } - */ - $mysql_access_host_array = array_unique($mysql_access_host_array); foreach ($mysql_access_host_array as $mysql_access_host) { @@ -268,11 +283,17 @@ class Core private function grantDbPrivilegesTo(&$db_root, $database, $username, $password, $access_host) { if ($this->validatedData['mysql_force_create']) { - $drop_stmt = $db_root->prepare("DROP USER :username@:host"); - $drop_stmt->execute([ - "username" => $username, - "host" => $access_host - ]); + try { + // try to drop the user, but ignore exceptions as the mysql-access-hosts might + // have changed and we would try to drop a non-existing user + $drop_stmt = $db_root->prepare("DROP USER :username@:host"); + $drop_stmt->execute([ + "username" => $username, + "host" => $access_host + ]); + } catch (PDOException $e) { + /* continue */ + } } if (version_compare($db_root->getAttribute(PDO::ATTR_SERVER_VERSION), '10.0.0', '>=')) { // mariadb compatibility @@ -319,31 +340,8 @@ class Core */ private function importDatabaseData() { - $options = [ - 'PDO::MYSQL_ATTR_INIT_COMMAND' => 'SET names utf8' - ]; - - if (!empty($this->validatedData['mysql_ssl_ca_file'])) { - $options[PDO::MYSQL_ATTR_SSL_CA] = $this->validatedData['mysql_ssl_ca_file']; - $options[PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT] = (bool)$this->validatedData['mysql_ssl_verify_server_certificate']; - } - - $dsn = "mysql:host=" . $this->validatedData['mysql_host'] . ";dbname=" . $this->validatedData['mysql_database'] . ";"; try { - $pdo = new PDO($dsn, $this->validatedData['mysql_unprivileged_user'], $this->validatedData['mysql_unprivileged_pass'], $options); - $attributes = [ - 'ATTR_ERRMODE' => 'ERRMODE_EXCEPTION' - ]; - // set attributes - foreach ($attributes as $k => $v) { - $pdo->setAttribute(constant("PDO::" . $k), constant("PDO::" . $v)); - } - $version_server = $pdo->getAttribute(PDO::ATTR_SERVER_VERSION); - $sql_mode = 'NO_ENGINE_SUBSTITUTION'; - if (version_compare($version_server, '8.0.11', '<')) { - $sql_mode .= ',NO_AUTO_CREATE_USER'; - } - $pdo->exec('SET sql_mode = "' . $sql_mode . '"'); + $pdo = $this->getUnprivilegedPdo(); } catch (PDOException $e) { throw new Exception('failed to initialize froxlor user connection!'); } @@ -603,7 +601,7 @@ class Core * @return void * @throws Exception */ - private function createUserdataConf() + public function createUserdataConf() { $userdata = [ 'sql' => [ diff --git a/lib/formfields/install/formfield.install.php b/lib/formfields/install/formfield.install.php index 5e650aa0..f45fd6de 100644 --- a/lib/formfields/install/formfield.install.php +++ b/lib/formfields/install/formfield.install.php @@ -222,8 +222,14 @@ return [ 'type' => 'textarea', 'value' => !empty($_SESSION['installation']['json_params']) ? Froxlor::getInstallDir() . "bin/froxlor-cli froxlor:config-services -a '" . $_SESSION['installation']['json_params'] . "' --yes-to-all" : "something went wrong...", 'readonly' => true, - 'rows' => 1 + 'rows' => 3 ], + 'manual_config' => [ + 'label' => lng('install.install.manual_config'), + 'type' => 'checkbox', + 'value' => '1', + 'checked' => old('manual_config', '0', 'installation'), + ] ] ] ] diff --git a/lng/en.lng.php b/lng/en.lng.php index 5e66e913..3fd3e136 100644 --- a/lng/en.lng.php +++ b/lng/en.lng.php @@ -2498,8 +2498,9 @@ Yours sincerely, your administrator', 'install' => [ 'top' => 'Finish setup', 'title' => 'One last step...', - 'description' => 'The command below will download, install and configure your system according to the data you have given in this installation process.', + 'description' => 'The command below will download, install and configure required services on your system according to the data you have given in this installation process.', 'runcmd' => 'Run the following command as root-user in your shell on this server:', + 'manual_config' => 'I will manually configure the services, just take me to the login', ], 'errors' => [ 'databaseexists' => 'Database already exist, please set override option to rebuild!', @@ -2513,6 +2514,7 @@ Yours sincerely, your administrator', 'servernameneedstobevalid' => 'Given servername does not seem to be a FQDN or hostname', 'websrvuserdoesnotexist' => 'Given webserver-user does not seem to exist on the system', 'websrvgrpdoesnotexist' => 'Given webserver-group does not seem to exist on the system', + 'notyetconfigured' => 'It seems that the services were not yet configured (successfully). Please either run the command shown below or check the box to do it later.' ] ], ];