Browse Source

Merge pull request #11786 from owncloud/MakeSupportedDBsConfigurable

Make supported DBs configurable within config.php
tags/v8.0.0alpha1
Lukas Reschke 9 years ago
parent
commit
d6380a5395
6 changed files with 271 additions and 58 deletions
  1. 17
    0
      config/config.sample.php
  2. 29
    34
      core/setup/controller.php
  3. 1
    1
      lib/base.php
  4. 118
    17
      lib/private/setup.php
  5. 3
    6
      lib/private/util.php
  6. 103
    0
      tests/lib/setup.php

+ 17
- 0
config/config.sample.php View File

), ),
), ),


/**
* Database types that are supported for installation
* Available:
* - sqlite (SQLite3)
* - mysql (MySQL)
* - pgsql (PostgreSQL)
* - oci (Oracle)
* - mssql (Microsoft SQL Server)
*/
'supportedDatabases' => array(
'sqlite',
'mysql',
'pgsql',
'oci',
'mssql'
),

/** /**
* Custom CSP policy, changing this will overwrite the standard policy * Custom CSP policy, changing this will overwrite the standard policy
*/ */

+ 29
- 34
core/setup/controller.php View File

<?php <?php
/** /**
* Copyright (c) 2013 Bart Visscher <bartv@thisnet.nl> * Copyright (c) 2013 Bart Visscher <bartv@thisnet.nl>
* Copyright (c) 2014 Lukas Reschke <lukas@owncloud.com>
* This file is licensed under the Affero General Public License version 3 or * This file is licensed under the Affero General Public License version 3 or
* later. * later.
* See the COPYING-README file. * See the COPYING-README file.


namespace OC\Core\Setup; namespace OC\Core\Setup;


use OCP\IConfig;

class Controller { class Controller {
/** @var \OCP\IConfig */
protected $config;

/**
* @param IConfig $config
*/
function __construct(IConfig $config) {
$this->config = $config;
}

public function run($post) { public function run($post) {
// Check for autosetup: // Check for autosetup:
$post = $this->loadAutoConfig($post); $post = $this->loadAutoConfig($post);
* in case of errors/warnings * in case of errors/warnings
*/ */
public function getSystemInfo() { public function getSystemInfo() {
$hasSQLite = class_exists('SQLite3');
$hasMySQL = is_callable('mysql_connect');
$hasPostgreSQL = is_callable('pg_connect');
$hasOracle = is_callable('oci_connect');
$hasMSSQL = is_callable('sqlsrv_connect');
$databases = array();
if ($hasSQLite) {
$databases['sqlite'] = 'SQLite';
}
if ($hasMySQL) {
$databases['mysql'] = 'MySQL/MariaDB';
}
if ($hasPostgreSQL) {
$databases['pgsql'] = 'PostgreSQL';
}
if ($hasOracle) {
$databases['oci'] = 'Oracle';
}
if ($hasMSSQL) {
$databases['mssql'] = 'MS SQL';
}
$datadir = \OC_Config::getValue('datadirectory', \OC::$SERVERROOT.'/data');
$setup = new \OC_Setup($this->config);
$databases = $setup->getSupportedDatabases();

$dataDir = $this->config->getSystemValue('datadirectory', \OC::$SERVERROOT.'/data');
$vulnerableToNullByte = false; $vulnerableToNullByte = false;
if(@file_exists(__FILE__."\0Nullbyte")) { // Check if the used PHP version is vulnerable to the NULL Byte attack (CVE-2006-7243) if(@file_exists(__FILE__."\0Nullbyte")) { // Check if the used PHP version is vulnerable to the NULL Byte attack (CVE-2006-7243)
$vulnerableToNullByte = true; $vulnerableToNullByte = true;
// Create data directory to test whether the .htaccess works // Create data directory to test whether the .htaccess works
// Notice that this is not necessarily the same data directory as the one // Notice that this is not necessarily the same data directory as the one
// that will effectively be used. // that will effectively be used.
@mkdir($datadir);
if (is_dir($datadir) && is_writable($datadir)) {
@mkdir($dataDir);
$htAccessWorking = true;
if (is_dir($dataDir) && is_writable($dataDir)) {
// Protect data directory here, so we can test if the protection is working // Protect data directory here, so we can test if the protection is working
\OC_Setup::protectDataDirectory(); \OC_Setup::protectDataDirectory();


try { try {
$htaccessWorking = \OC_Util::isHtaccessWorking();
$htAccessWorking = \OC_Util::isHtaccessWorking();
} catch (\OC\HintException $e) { } catch (\OC\HintException $e) {
$errors[] = array( $errors[] = array(
'error' => $e->getMessage(), 'error' => $e->getMessage(),
'hint' => $e->getHint() 'hint' => $e->getHint()
); );
$htaccessWorking = false;
$htAccessWorking = false;
} }
} }


if (\OC_Util::runningOnMac()) { if (\OC_Util::runningOnMac()) {
$l10n = \OC::$server->getL10N('core'); $l10n = \OC::$server->getL10N('core');
$themeName = \OC_Util::getTheme();
$theme = new \OC_Defaults(); $theme = new \OC_Defaults();
$errors[] = array( $errors[] = array(
'error' => $l10n->t( 'error' => $l10n->t(
} }


return array( return array(
'hasSQLite' => $hasSQLite,
'hasMySQL' => $hasMySQL,
'hasPostgreSQL' => $hasPostgreSQL,
'hasOracle' => $hasOracle,
'hasMSSQL' => $hasMSSQL,
'hasSQLite' => isset($databases['sqlite']),
'hasMySQL' => isset($databases['mysql']),
'hasPostgreSQL' => isset($databases['postgre']),
'hasOracle' => isset($databases['oci']),
'hasMSSQL' => isset($databases['mssql']),
'databases' => $databases, 'databases' => $databases,
'directory' => $datadir,
'htaccessWorking' => $htaccessWorking,
'directory' => $dataDir,
'htaccessWorking' => $htAccessWorking,
'vulnerableToNullByte' => $vulnerableToNullByte, 'vulnerableToNullByte' => $vulnerableToNullByte,
'errors' => $errors, 'errors' => $errors,
); );

+ 1
- 1
lib/base.php View File



// Check if ownCloud is installed or in maintenance (update) mode // Check if ownCloud is installed or in maintenance (update) mode
if (!OC_Config::getValue('installed', false)) { if (!OC_Config::getValue('installed', false)) {
$controller = new OC\Core\Setup\Controller();
$controller = new OC\Core\Setup\Controller(\OC::$server->getConfig());
$controller->run($_POST); $controller->run($_POST);
exit(); exit();
} }

+ 118
- 17
lib/private/setup.php View File

<?php <?php
/**
* Copyright (c) 2014 Lukas Reschke <lukas@owncloud.com>
* This file is licensed under the Affero General Public License version 3 or
* later.
* See the COPYING-README file.
*/

use OCP\IConfig;


class DatabaseSetupException extends \OC\HintException { class DatabaseSetupException extends \OC\HintException {
} }


class OC_Setup { class OC_Setup {
/** @var IConfig */
protected $config;

/**
* @param IConfig $config
*/
function __construct(IConfig $config) {
$this->config = $config;
}

static $dbSetupClasses = array( static $dbSetupClasses = array(
'mysql' => '\OC\Setup\MySQL', 'mysql' => '\OC\Setup\MySQL',
'pgsql' => '\OC\Setup\PostgreSQL', 'pgsql' => '\OC\Setup\PostgreSQL',
'sqlite3' => '\OC\Setup\Sqlite', 'sqlite3' => '\OC\Setup\Sqlite',
); );


/**
* @return OC_L10N
*/
public static function getTrans(){ public static function getTrans(){
return \OC::$server->getL10N('lib'); return \OC::$server->getL10N('lib');
} }


/**
* Wrapper around the "class_exists" PHP function to be able to mock it
* @param string $name
* @return bool
*/
public function class_exists($name) {
return class_exists($name);
}

/**
* Wrapper around the "is_callable" PHP function to be able to mock it
* @param string $name
* @return bool
*/
public function is_callable($name) {
return is_callable($name);
}

/**
* Get the available and supported databases of this instance
*
* @throws Exception
* @return array
*/
public function getSupportedDatabases() {
$availableDatabases = array(
'sqlite' => array(
'type' => 'class',
'call' => 'SQLite3',
'name' => 'SQLite'
),
'mysql' => array(
'type' => 'function',
'call' => 'mysql_connect',
'name' => 'MySQL/MariaDB'
),
'pgsql' => array(
'type' => 'function',
'call' => 'oci_connect',
'name' => 'PostgreSQL'
),
'oci' => array(
'type' => 'function',
'call' => 'oci_connect',
'name' => 'Oracle'
),
'mssql' => array(
'type' => 'function',
'call' => 'sqlsrv_connect',
'name' => 'MS SQL'
)
);
$configuredDatabases = $this->config->getSystemValue('supportedDatabases',
array('sqlite', 'mysql', 'pgsql', 'oci', 'mssql'));
if(!is_array($configuredDatabases)) {
throw new Exception('Supported databases are not properly configured.');
}

$supportedDatabases = array();

foreach($configuredDatabases as $database) {
if(array_key_exists($database, $availableDatabases)) {
$working = false;
if($availableDatabases[$database]['type'] === 'class') {
$working = $this->class_exists($availableDatabases[$database]['call']);
} elseif ($availableDatabases[$database]['type'] === 'function') {
$working = $this->is_callable($availableDatabases[$database]['call']);
}
if($working) {
$supportedDatabases[$database] = $availableDatabases[$database]['name'];
}
}
}

return $supportedDatabases;
}

/**
* @param $options
* @return array
*/
public static function install($options) { public static function install($options) {
$l = self::getTrans(); $l = self::getTrans();


$error = array(); $error = array();
$dbtype = $options['dbtype'];
$dbType = $options['dbtype'];


if(empty($options['adminlogin'])) { if(empty($options['adminlogin'])) {
$error[] = $l->t('Set an admin username.'); $error[] = $l->t('Set an admin username.');
$options['directory'] = OC::$SERVERROOT."/data"; $options['directory'] = OC::$SERVERROOT."/data";
} }


if (!isset(self::$dbSetupClasses[$dbtype])) {
$dbtype = 'sqlite';
if (!isset(self::$dbSetupClasses[$dbType])) {
$dbType = 'sqlite';
} }


$username = htmlspecialchars_decode($options['adminlogin']); $username = htmlspecialchars_decode($options['adminlogin']);
$password = htmlspecialchars_decode($options['adminpass']); $password = htmlspecialchars_decode($options['adminpass']);
$datadir = htmlspecialchars_decode($options['directory']);
$dataDir = htmlspecialchars_decode($options['directory']);


$class = self::$dbSetupClasses[$dbtype];
$class = self::$dbSetupClasses[$dbType];
/** @var \OC\Setup\AbstractDatabase $dbSetup */ /** @var \OC\Setup\AbstractDatabase $dbSetup */
$dbSetup = new $class(self::getTrans(), 'db_structure.xml'); $dbSetup = new $class(self::getTrans(), 'db_structure.xml');
$error = array_merge($error, $dbSetup->validate($options)); $error = array_merge($error, $dbSetup->validate($options));


// validate the data directory // validate the data directory
if ( if (
(!is_dir($datadir) and !mkdir($datadir)) or
!is_writable($datadir)
(!is_dir($dataDir) and !mkdir($dataDir)) or
!is_writable($dataDir)
) { ) {
$error[] = $l->t("Can't create or write into the data directory %s", array($datadir));
$error[] = $l->t("Can't create or write into the data directory %s", array($dataDir));
} }


if(count($error) != 0) { if(count($error) != 0) {
} }


//no errors, good //no errors, good
if( isset($options['trusted_domains'])
if(isset($options['trusted_domains'])
&& is_array($options['trusted_domains'])) { && is_array($options['trusted_domains'])) {
$trustedDomains = $options['trusted_domains']; $trustedDomains = $options['trusted_domains'];
} else { } else {
} }


if (OC_Util::runningOnWindows()) { if (OC_Util::runningOnWindows()) {
$datadir = rtrim(realpath($datadir), '\\');
$dataDir = rtrim(realpath($dataDir), '\\');
} }


//use sqlite3 when available, otherise sqlite2 will be used.
if($dbtype=='sqlite' and class_exists('SQLite3')) {
$dbtype='sqlite3';
//use sqlite3 when available, otherwise sqlite2 will be used.
if($dbType=='sqlite' and class_exists('SQLite3')) {
$dbType='sqlite3';
} }


//generate a random salt that is used to salt the local user passwords //generate a random salt that is used to salt the local user passwords


//write the config file //write the config file
\OC::$server->getConfig()->setSystemValue('trusted_domains', $trustedDomains); \OC::$server->getConfig()->setSystemValue('trusted_domains', $trustedDomains);
\OC::$server->getConfig()->setSystemValue('datadirectory', $datadir);
\OC::$server->getConfig()->setSystemValue('datadirectory', $dataDir);
\OC::$server->getConfig()->setSystemValue('overwrite.cli.url', \OC_Request::serverProtocol() . '://' . \OC_Request::serverHost() . OC::$WEBROOT); \OC::$server->getConfig()->setSystemValue('overwrite.cli.url', \OC_Request::serverProtocol() . '://' . \OC_Request::serverHost() . OC::$WEBROOT);
\OC::$server->getConfig()->setSystemValue('dbtype', $dbtype);
\OC::$server->getConfig()->setSystemValue('dbtype', $dbType);
\OC::$server->getConfig()->setSystemValue('version', implode('.', OC_Util::getVersion())); \OC::$server->getConfig()->setSystemValue('version', implode('.', OC_Util::getVersion()));


try { try {
//create the user and group //create the user and group
try { try {
OC_User::createUser($username, $password); OC_User::createUser($username, $password);
}
catch(Exception $exception) {
} catch(Exception $exception) {
$error[] = $exception->getMessage(); $error[] = $exception->getMessage();
} }



+ 3
- 6
lib/private/util.php View File

} }


$webServerRestart = false; $webServerRestart = false;
//check for database drivers
if (!(is_callable('sqlite_open') or class_exists('SQLite3'))
and !is_callable('mysql_connect')
and !is_callable('pg_connect')
and !is_callable('oci_connect')
) {
$setup = new OC_Setup($config);
$availableDatabases = $setup->getSupportedDatabases();
if (empty($availableDatabases)) {
$errors[] = array( $errors[] = array(
'error' => $l->t('No database drivers (sqlite, mysql, or postgresql) installed.'), 'error' => $l->t('No database drivers (sqlite, mysql, or postgresql) installed.'),
'hint' => '' //TODO: sane hint 'hint' => '' //TODO: sane hint

+ 103
- 0
tests/lib/setup.php View File

<?php
/**
* Copyright (c) 2014 Lukas Reschke <lukas@owncloud.com>
* This file is licensed under the Affero General Public License version 3 or
* later.
* See the COPYING-README file.
*/

use OCP\IConfig;

class Test_OC_Setup extends PHPUnit_Framework_TestCase {

/** @var IConfig */
protected $config;
/** @var \OC_Setup */
protected $setupClass;

public function setUp() {
$this->config = $this->getMock('\OCP\IConfig');
$this->setupClass = $this->getMock('\OC_Setup', array('class_exists', 'is_callable'), array($this->config));
}

public function testGetSupportedDatabasesWithOneWorking() {
$this->config
->expects($this->once())
->method('getSystemValue')
->will($this->returnValue(
array('sqlite', 'mysql', 'oci')
));
$this->setupClass
->expects($this->once())
->method('class_exists')
->will($this->returnValue(true));
$this->setupClass
->expects($this->exactly(2))
->method('is_callable')
->will($this->returnValue(false));
$result = $this->setupClass->getSupportedDatabases();
$expectedResult = array(
'sqlite' => 'SQLite'
);

$this->assertSame($expectedResult, $result);
}

public function testGetSupportedDatabasesWithNoWorking() {
$this->config
->expects($this->once())
->method('getSystemValue')
->will($this->returnValue(
array('sqlite', 'mysql', 'oci', 'pgsql')
));
$this->setupClass
->expects($this->once())
->method('class_exists')
->will($this->returnValue(false));
$this->setupClass
->expects($this->exactly(3))
->method('is_callable')
->will($this->returnValue(false));
$result = $this->setupClass->getSupportedDatabases();

$this->assertSame(array(), $result);
}

public function testGetSupportedDatabasesWitAllWorking() {
$this->config
->expects($this->once())
->method('getSystemValue')
->will($this->returnValue(
array('sqlite', 'mysql', 'pgsql', 'oci', 'mssql')
));
$this->setupClass
->expects($this->once())
->method('class_exists')
->will($this->returnValue(true));
$this->setupClass
->expects($this->exactly(4))
->method('is_callable')
->will($this->returnValue(true));
$result = $this->setupClass->getSupportedDatabases();
$expectedResult = array(
'sqlite' => 'SQLite',
'mysql' => 'MySQL/MariaDB',
'pgsql' => 'PostgreSQL',
'oci' => 'Oracle',
'mssql' => 'MS SQL'
);
$this->assertSame($expectedResult, $result);
}

/**
* @expectedException \Exception
* @expectedExceptionMessage Supported databases are not properly configured.
*/
public function testGetSupportedDatabaseException() {
$this->config
->expects($this->once())
->method('getSystemValue')
->will($this->returnValue('NotAnArray'));
$this->setupClass->getSupportedDatabases();
}
}

Loading…
Cancel
Save