[ Index ]

PHP Cross Reference of Joomla 4.2.2 documentation

title

Body

[close]

/installation/src/Helper/ -> DatabaseHelper.php (source)

   1  <?php
   2  
   3  /**
   4   * @package    Joomla.Installation
   5   *
   6   * @copyright  (C) 2009 Open Source Matters, Inc. <https://www.joomla.org>
   7   * @license    GNU General Public License version 2 or later; see LICENSE.txt
   8   */
   9  
  10  namespace Joomla\CMS\Installation\Helper;
  11  
  12  use Joomla\CMS\Factory;
  13  use Joomla\CMS\Filesystem\File;
  14  use Joomla\CMS\Filesystem\Path;
  15  use Joomla\CMS\Language\Text;
  16  use Joomla\CMS\User\UserHelper;
  17  use Joomla\Database\DatabaseDriver;
  18  use Joomla\Database\DatabaseInterface;
  19  use Joomla\Utilities\ArrayHelper;
  20  
  21  // phpcs:disable PSR1.Files.SideEffects
  22  \defined('_JEXEC') or die;
  23  // phpcs:enable PSR1.Files.SideEffects
  24  
  25  /**
  26   * Joomla Installation Database Helper Class.
  27   *
  28   * @since  1.6
  29   */
  30  abstract class DatabaseHelper
  31  {
  32      /**
  33       * The minimum database server version for MariaDB databases as required by the CMS.
  34       * This is not necessarily equal to what the database driver requires.
  35       *
  36       * @var    string
  37       * @since  4.0.0
  38       */
  39      protected static $dbMinimumMariaDb = '10.1';
  40  
  41      /**
  42       * The minimum database server version for MySQL databases as required by the CMS.
  43       * This is not necessarily equal to what the database driver requires.
  44       *
  45       * @var    string
  46       * @since  4.0.0
  47       */
  48      protected static $dbMinimumMySql = '5.6';
  49  
  50      /**
  51       * The minimum database server version for PostgreSQL databases as required by the CMS.
  52       * This is not necessarily equal to what the database driver requires.
  53       *
  54       * @var    string
  55       * @since  4.0.0
  56       */
  57      protected static $dbMinimumPostgreSql = '11.0';
  58  
  59      /**
  60       * Method to get a database driver.
  61       *
  62       * @param   string   $driver    The database driver to use.
  63       * @param   string   $host      The hostname to connect on.
  64       * @param   string   $user      The user name to connect with.
  65       * @param   string   $password  The password to use for connection authentication.
  66       * @param   string   $database  The database to use.
  67       * @param   string   $prefix    The table prefix to use.
  68       * @param   boolean  $select    True if the database should be selected.
  69       * @param   array    $ssl       Database TLS connection options.
  70       *
  71       * @return  DatabaseInterface
  72       *
  73       * @since   1.6
  74       */
  75      public static function getDbo($driver, $host, $user, $password, $database, $prefix, $select = true, array $ssl = [])
  76      {
  77          static $db;
  78  
  79          if (!$db) {
  80              // Build the connection options array.
  81              $options = [
  82                  'driver'   => $driver,
  83                  'host'     => $host,
  84                  'user'     => $user,
  85                  'password' => $password,
  86                  'database' => $database,
  87                  'prefix'   => $prefix,
  88                  'select'   => $select,
  89              ];
  90  
  91              if (!empty($ssl['dbencryption'])) {
  92                  $options['ssl'] = [
  93                      'enable'             => true,
  94                      'verify_server_cert' => (bool) $ssl['dbsslverifyservercert'],
  95                  ];
  96  
  97                  foreach (['cipher', 'ca', 'key', 'cert'] as $value) {
  98                      $confVal = trim($ssl['dbssl' . $value]);
  99  
 100                      if ($confVal !== '') {
 101                          $options['ssl'][$value] = $confVal;
 102                      }
 103                  }
 104              }
 105  
 106              // Enable utf8mb4 connections for mysql adapters
 107              if (strtolower($driver) === 'mysqli') {
 108                  $options['utf8mb4'] = true;
 109              }
 110  
 111              if (strtolower($driver) === 'mysql') {
 112                  $options['charset'] = 'utf8mb4';
 113              }
 114  
 115              // Get a database object.
 116              $db = DatabaseDriver::getInstance($options);
 117          }
 118  
 119          return $db;
 120      }
 121  
 122      /**
 123       * Convert encryption options to array.
 124       *
 125       * @param   \stdClass  $options  The session options
 126       *
 127       * @return  array  The encryption settings
 128       *
 129       * @since   4.0.0
 130       */
 131      public static function getEncryptionSettings($options)
 132      {
 133          return [
 134              'dbencryption'          => $options->db_encryption,
 135              'dbsslverifyservercert' => $options->db_sslverifyservercert,
 136              'dbsslkey'              => $options->db_sslkey,
 137              'dbsslcert'             => $options->db_sslcert,
 138              'dbsslca'               => $options->db_sslca,
 139              'dbsslcipher'           => $options->db_sslcipher,
 140          ];
 141      }
 142  
 143      /**
 144       * Get the minimum required database server version.
 145       *
 146       * @param   DatabaseDriver  $db       Database object
 147       * @param   \stdClass       $options  The session options
 148       *
 149       * @return  string  The minimum required database server version.
 150       *
 151       * @since   4.0.0
 152       */
 153      public static function getMinimumServerVersion($db, $options)
 154      {
 155          // Get minimum database version required by the database driver
 156          $minDbVersionRequired = $db->getMinimum();
 157  
 158          // Get minimum database version required by the CMS
 159          if (in_array($options->db_type, ['mysql', 'mysqli'])) {
 160              if ($db->isMariaDb()) {
 161                  $minDbVersionCms = self::$dbMinimumMariaDb;
 162              } else {
 163                  $minDbVersionCms = self::$dbMinimumMySql;
 164              }
 165          } else {
 166              $minDbVersionCms = self::$dbMinimumPostgreSql;
 167          }
 168  
 169          // Use most restrictive, i.e. largest minimum database version requirement
 170          if (version_compare($minDbVersionCms, $minDbVersionRequired) > 0) {
 171              $minDbVersionRequired = $minDbVersionCms;
 172          }
 173  
 174          return $minDbVersionRequired;
 175      }
 176  
 177      /**
 178       * Validate and clean up database connection parameters.
 179       *
 180       * @param   \stdClass       $options  The session options
 181       *
 182       * @return  string|boolean  A string with the translated error message if
 183       *                          validation error, otherwise false.
 184       *
 185       * @since   4.0.0
 186       */
 187      public static function validateConnectionParameters($options)
 188      {
 189          // Ensure a database type was selected.
 190          if (empty($options->db_type)) {
 191              return Text::_('INSTL_DATABASE_INVALID_TYPE');
 192          }
 193  
 194          // Ensure that a hostname and user name were input.
 195          if (empty($options->db_host) || empty($options->db_user)) {
 196              return Text::_('INSTL_DATABASE_INVALID_DB_DETAILS');
 197          }
 198  
 199          // Ensure that a database name is given.
 200          if (empty($options->db_name)) {
 201              return Text::_('INSTL_DATABASE_EMPTY_NAME');
 202          }
 203  
 204          // Validate length of database name.
 205          if (strlen($options->db_name) > 64) {
 206              return Text::_('INSTL_DATABASE_NAME_TOO_LONG');
 207          }
 208  
 209          // Validate database table prefix.
 210          if (empty($options->db_prefix) || !preg_match('#^[a-zA-Z]+[a-zA-Z0-9_]*$#', $options->db_prefix)) {
 211              return Text::_('INSTL_DATABASE_PREFIX_MSG');
 212          }
 213  
 214          // Validate length of database table prefix.
 215          if (strlen($options->db_prefix) > 15) {
 216              return Text::_('INSTL_DATABASE_FIX_TOO_LONG');
 217          }
 218  
 219          // Validate database name.
 220          if (in_array($options->db_type, ['pgsql', 'postgresql']) && !preg_match('#^[a-zA-Z_][0-9a-zA-Z_$]*$#', $options->db_name)) {
 221              return Text::_('INSTL_DATABASE_NAME_MSG_POSTGRES');
 222          }
 223  
 224          if (in_array($options->db_type, ['mysql', 'mysqli']) && preg_match('#[\\\\\/]#', $options->db_name)) {
 225              return Text::_('INSTL_DATABASE_NAME_MSG_MYSQL');
 226          }
 227  
 228          // Workaround for UPPERCASE table prefix for postgresql
 229          if (in_array($options->db_type, ['pgsql', 'postgresql'])) {
 230              if (strtolower($options->db_prefix) != $options->db_prefix) {
 231                  return Text::_('INSTL_DATABASE_FIX_LOWERCASE');
 232              }
 233          }
 234  
 235          // Validate and clean up database connection encryption options
 236          $optionsChanged = false;
 237  
 238          if ($options->db_encryption === 0) {
 239              // Reset unused options
 240              if (!empty($options->db_sslkey)) {
 241                  $options->db_sslkey = '';
 242                  $optionsChanged     = true;
 243              }
 244  
 245              if (!empty($options->db_sslcert)) {
 246                  $options->db_sslcert = '';
 247                  $optionsChanged      = true;
 248              }
 249  
 250              if ($options->db_sslverifyservercert) {
 251                  $options->db_sslverifyservercert = false;
 252                  $optionsChanged                  = true;
 253              }
 254  
 255              if (!empty($options->db_sslca)) {
 256                  $options->db_sslca = '';
 257                  $optionsChanged    = true;
 258              }
 259  
 260              if (!empty($options->db_sslcipher)) {
 261                  $options->db_sslcipher = '';
 262                  $optionsChanged        = true;
 263              }
 264          } else {
 265              // Check localhost
 266              if (strtolower($options->db_host) === 'localhost') {
 267                  return Text::_('INSTL_DATABASE_ENCRYPTION_MSG_LOCALHOST');
 268              }
 269  
 270              // Check CA file and folder depending on database type if server certificate verification
 271              if ($options->db_sslverifyservercert) {
 272                  if (empty($options->db_sslca)) {
 273                      return Text::sprintf('INSTL_DATABASE_ENCRYPTION_MSG_FILE_FIELD_EMPTY', Text::_('INSTL_DATABASE_ENCRYPTION_CA_LABEL'));
 274                  }
 275  
 276                  if (!File::exists(Path::clean($options->db_sslca))) {
 277                      return Text::sprintf('INSTL_DATABASE_ENCRYPTION_MSG_FILE_FIELD_BAD', Text::_('INSTL_DATABASE_ENCRYPTION_CA_LABEL'));
 278                  }
 279              } else {
 280                  // Reset unused option
 281                  if (!empty($options->db_sslca)) {
 282                      $options->db_sslca = '';
 283                      $optionsChanged    = true;
 284                  }
 285              }
 286  
 287              // Check key and certificate if two-way encryption
 288              if ($options->db_encryption === 2) {
 289                  if (empty($options->db_sslkey)) {
 290                      return Text::sprintf('INSTL_DATABASE_ENCRYPTION_MSG_FILE_FIELD_EMPTY', Text::_('INSTL_DATABASE_ENCRYPTION_KEY_LABEL'));
 291                  }
 292  
 293                  if (!File::exists(Path::clean($options->db_sslkey))) {
 294                      return Text::sprintf('INSTL_DATABASE_ENCRYPTION_MSG_FILE_FIELD_BAD', Text::_('INSTL_DATABASE_ENCRYPTION_KEY_LABEL'));
 295                  }
 296  
 297                  if (empty($options->db_sslcert)) {
 298                      return Text::sprintf('INSTL_DATABASE_ENCRYPTION_MSG_FILE_FIELD_EMPTY', Text::_('INSTL_DATABASE_ENCRYPTION_CERT_LABEL'));
 299                  }
 300  
 301                  if (!File::exists(Path::clean($options->db_sslcert))) {
 302                      return Text::sprintf('INSTL_DATABASE_ENCRYPTION_MSG_FILE_FIELD_BAD', Text::_('INSTL_DATABASE_ENCRYPTION_CERT_LABEL'));
 303                  }
 304              } else {
 305                  // Reset unused options
 306                  if (!empty($options->db_sslkey)) {
 307                      $options->db_sslkey = '';
 308                      $optionsChanged     = true;
 309                  }
 310  
 311                  if (!empty($options->db_sslcert)) {
 312                      $options->db_sslcert = '';
 313                      $optionsChanged      = true;
 314                  }
 315              }
 316          }
 317  
 318          // Save options to session data if changed
 319          if ($optionsChanged) {
 320              $optsArr = ArrayHelper::fromObject($options);
 321              Factory::getSession()->set('setup.options', $optsArr);
 322          }
 323  
 324          return false;
 325      }
 326  
 327      /**
 328       * Security check for remote db hosts
 329       *
 330       * @param   \stdClass       $options  The session options
 331       *
 332       * @return  boolean         True if passed, otherwise false.
 333       *
 334       * @since   4.0.0
 335       */
 336      public static function checkRemoteDbHost($options)
 337      {
 338          // Security check for remote db hosts: Check env var if disabled
 339          $shouldCheckLocalhost = getenv('JOOMLA_INSTALLATION_DISABLE_LOCALHOST_CHECK') !== '1';
 340  
 341          // Per default allowed DB hosts: localhost / 127.0.0.1 / ::1 (optionally with port)
 342          $localhost = '/^(((localhost|127\.0\.0\.1|\[\:\:1\])(\:[1-9]{1}[0-9]{0,4})?)|(\:\:1))$/';
 343  
 344          // Check the security file if the db_host is not localhost / 127.0.0.1 / ::1
 345          if ($shouldCheckLocalhost && preg_match($localhost, $options->db_host) !== 1) {
 346              $remoteDbFileTestsPassed = Factory::getSession()->get('remoteDbFileTestsPassed', false);
 347  
 348              // When all checks have been passed we don't need to do this here again.
 349              if ($remoteDbFileTestsPassed === false) {
 350                  $generalRemoteDatabaseMessage = Text::sprintf(
 351                      'INSTL_DATABASE_HOST_IS_NOT_LOCALHOST_GENERAL_MESSAGE',
 352                      'https://docs.joomla.org/Special:MyLanguage/J3.x:Secured_procedure_for_installing_Joomla_with_a_remote_database'
 353                  );
 354  
 355                  $remoteDbFile = Factory::getSession()->get('remoteDbFile', false);
 356  
 357                  if ($remoteDbFile === false) {
 358                      // Add the general message
 359                      Factory::getApplication()->enqueueMessage($generalRemoteDatabaseMessage, 'warning');
 360  
 361                      // This is the file you need to remove if you want to use a remote database
 362                      $remoteDbFile = '_Joomla' . UserHelper::genRandomPassword(21) . '.txt';
 363                      Factory::getSession()->set('remoteDbFile', $remoteDbFile);
 364  
 365                      // Get the path
 366                      $remoteDbPath = JPATH_INSTALLATION . '/' . $remoteDbFile;
 367  
 368                      // When the path is not writable the user needs to create the file manually
 369                      if (!File::write($remoteDbPath, '')) {
 370                          // Request to create the file manually
 371                          Factory::getApplication()->enqueueMessage(
 372                              Text::sprintf(
 373                                  'INSTL_DATABASE_HOST_IS_NOT_LOCALHOST_CREATE_FILE',
 374                                  $remoteDbFile,
 375                                  'installation',
 376                                  Text::_('INSTL_INSTALL_JOOMLA')
 377                              ),
 378                              'notice'
 379                          );
 380  
 381                          Factory::getSession()->set('remoteDbFileUnwritable', true);
 382  
 383                          return false;
 384                      }
 385  
 386                      // Save the file name to the session
 387                      Factory::getSession()->set('remoteDbFileWrittenByJoomla', true);
 388  
 389                      // Request to delete that file
 390                      Factory::getApplication()->enqueueMessage(
 391                          Text::sprintf(
 392                              'INSTL_DATABASE_HOST_IS_NOT_LOCALHOST_DELETE_FILE',
 393                              $remoteDbFile,
 394                              'installation',
 395                              Text::_('INSTL_INSTALL_JOOMLA')
 396                          ),
 397                          'notice'
 398                      );
 399  
 400                      return false;
 401                  }
 402  
 403                  if (
 404                      Factory::getSession()->get('remoteDbFileWrittenByJoomla', false) === true
 405                      && File::exists(JPATH_INSTALLATION . '/' . $remoteDbFile)
 406                  ) {
 407                      // Add the general message
 408                      Factory::getApplication()->enqueueMessage($generalRemoteDatabaseMessage, 'warning');
 409  
 410                      // Request to delete the file
 411                      Factory::getApplication()->enqueueMessage(
 412                          Text::sprintf(
 413                              'INSTL_DATABASE_HOST_IS_NOT_LOCALHOST_DELETE_FILE',
 414                              $remoteDbFile,
 415                              'installation',
 416                              Text::_('INSTL_INSTALL_JOOMLA')
 417                          ),
 418                          'notice'
 419                      );
 420  
 421                      return false;
 422                  }
 423  
 424                  if (Factory::getSession()->get('remoteDbFileUnwritable', false) === true && !File::exists(JPATH_INSTALLATION . '/' . $remoteDbFile)) {
 425                      // Add the general message
 426                      Factory::getApplication()->enqueueMessage($generalRemoteDatabaseMessage, 'warning');
 427  
 428                      // Request to create the file manually
 429                      Factory::getApplication()->enqueueMessage(
 430                          Text::sprintf(
 431                              'INSTL_DATABASE_HOST_IS_NOT_LOCALHOST_CREATE_FILE',
 432                              $remoteDbFile,
 433                              'installation',
 434                              Text::_('INSTL_INSTALL_JOOMLA')
 435                          ),
 436                          'notice'
 437                      );
 438  
 439                      return false;
 440                  }
 441  
 442                  // All tests for this session passed set it to the session
 443                  Factory::getSession()->set('remoteDbFileTestsPassed', true);
 444              }
 445          }
 446  
 447          return true;
 448      }
 449  
 450      /**
 451       * Check database server parameters after connection
 452       *
 453       * @param   DatabaseDriver  $db       Database object
 454       * @param   \stdClass       $options  The session options
 455       *
 456       * @return  string|boolean  A string with the translated error message if
 457       *                          some server parameter is not ok, otherwise false.
 458       *
 459       * @since   4.0.0
 460       */
 461      public static function checkDbServerParameters($db, $options)
 462      {
 463          $dbVersion = $db->getVersion();
 464  
 465          // Get required database version
 466          $minDbVersionRequired = self::getMinimumServerVersion($db, $options);
 467  
 468          // Check minimum database version
 469          if (version_compare($dbVersion, $minDbVersionRequired) < 0) {
 470              if (in_array($options->db_type, ['mysql', 'mysqli']) && $db->isMariaDb()) {
 471                  $errorMessage = Text::sprintf(
 472                      'INSTL_DATABASE_INVALID_MARIADB_VERSION',
 473                      $minDbVersionRequired,
 474                      $dbVersion
 475                  );
 476              } else {
 477                  $errorMessage = Text::sprintf(
 478                      'INSTL_DATABASE_INVALID_' . strtoupper($options->db_type) . '_VERSION',
 479                      $minDbVersionRequired,
 480                      $dbVersion
 481                  );
 482              }
 483  
 484              return $errorMessage;
 485          }
 486  
 487          // Check database connection encryption
 488          if ($options->db_encryption !== 0 && empty($db->getConnectionEncryption())) {
 489              if ($db->isConnectionEncryptionSupported()) {
 490                  $errorMessage = Text::_('INSTL_DATABASE_ENCRYPTION_MSG_CONN_NOT_ENCRYPT');
 491              } else {
 492                  $errorMessage = Text::_('INSTL_DATABASE_ENCRYPTION_MSG_SRV_NOT_SUPPORTS');
 493              }
 494  
 495              return $errorMessage;
 496          }
 497  
 498          return false;
 499      }
 500  }


Generated: Wed Sep 7 05:41:13 2022 Chilli.vc Blog - For Webmaster,Blog-Writer,System Admin and Domainer