diff options
-rw-r--r-- | lib/private/AllConfig.php | 4 | ||||
-rw-r--r-- | lib/private/Setup.php | 4 | ||||
-rw-r--r-- | lib/private/Setup/AbstractDatabase.php | 23 | ||||
-rw-r--r-- | lib/private/Setup/MySQL.php | 35 | ||||
-rw-r--r-- | lib/private/Setup/PostgreSQL.php | 185 | ||||
-rw-r--r-- | tests/lib/SetupTest.php | 16 |
6 files changed, 118 insertions, 149 deletions
diff --git a/lib/private/AllConfig.php b/lib/private/AllConfig.php index c8b2009fcc7..b50fc0f42ce 100644 --- a/lib/private/AllConfig.php +++ b/lib/private/AllConfig.php @@ -428,4 +428,8 @@ class AllConfig implements \OCP\IConfig { return $userIDs; } + + public function getSystemConfig() { + return $this->systemConfig; + } } diff --git a/lib/private/Setup.php b/lib/private/Setup.php index f1454805a08..c78654de957 100644 --- a/lib/private/Setup.php +++ b/lib/private/Setup.php @@ -134,8 +134,8 @@ class Setup { 'name' => 'MySQL/MariaDB' ), 'pgsql' => array( - 'type' => 'function', - 'call' => 'pg_connect', + 'type' => 'pdo', + 'call' => 'pgsql', 'name' => 'PostgreSQL' ), 'oci' => array( diff --git a/lib/private/Setup/AbstractDatabase.php b/lib/private/Setup/AbstractDatabase.php index 08ed741f51c..8dee96b1ba5 100644 --- a/lib/private/Setup/AbstractDatabase.php +++ b/lib/private/Setup/AbstractDatabase.php @@ -23,6 +23,8 @@ */ namespace OC\Setup; +use OC\AllConfig; +use OC\DB\ConnectionFactory; use OCP\IConfig; use OCP\ILogger; use OCP\Security\ISecureRandom; @@ -45,7 +47,7 @@ abstract class AbstractDatabase { protected $dbPort; /** @var string */ protected $tablePrefix; - /** @var IConfig */ + /** @var AllConfig */ protected $config; /** @var ILogger */ protected $logger; @@ -99,6 +101,25 @@ abstract class AbstractDatabase { } /** + * @param array $configOverwrite + * @return \OC\DB\Connection + */ + protected function connect(array $configOverwrite = []) { + $systemConfig = $this->config->getSystemConfig(); + $cf = new ConnectionFactory(); + $connectionParams = $cf->createConnectionParams($systemConfig); + // we don't save username/password to the config immediately so this might not be set + if (!$connectionParams['user']) { + $connectionParams['user'] = $this->dbUser; + } + if (!$connectionParams['password']) { + $connectionParams['password'] = $this->dbPassword; + } + $connectionParams = array_merge($connectionParams, $configOverwrite); + return $cf->getConnection($systemConfig->getValue('dbtype', 'sqlite'), $connectionParams); + } + + /** * @param string $userName */ abstract public function setupDatabase($userName); diff --git a/lib/private/Setup/MySQL.php b/lib/private/Setup/MySQL.php index 1ff7b278b86..03a1421f428 100644 --- a/lib/private/Setup/MySQL.php +++ b/lib/private/Setup/MySQL.php @@ -88,41 +88,6 @@ class MySQL extends AbstractDatabase { } /** - * @return \OC\DB\Connection - * @throws \OC\DatabaseSetupException - */ - private function connect() { - - $connectionParams = array( - 'host' => $this->dbHost, - 'user' => $this->dbUser, - 'password' => $this->dbPassword, - 'tablePrefix' => $this->tablePrefix, - ); - - // adding port support through installer - if(!empty($this->dbPort)) { - if (ctype_digit($this->dbPort)) { - $connectionParams['port'] = $this->dbPort; - } else { - $connectionParams['unix_socket'] = $this->dbPort; - } - } else if (strpos($this->dbHost, ':')) { - // Host variable may carry a port or socket. - list($host, $portOrSocket) = explode(':', $this->dbHost, 2); - if (ctype_digit($portOrSocket)) { - $connectionParams['port'] = $portOrSocket; - } else { - $connectionParams['unix_socket'] = $portOrSocket; - } - $connectionParams['host'] = $host; - } - - $cf = new ConnectionFactory(); - return $cf->getConnection('mysql', $connectionParams); - } - - /** * @param $username * @param IDBConnection $connection * @return array diff --git a/lib/private/Setup/PostgreSQL.php b/lib/private/Setup/PostgreSQL.php index 464d1e02e21..30ca88f53aa 100644 --- a/lib/private/Setup/PostgreSQL.php +++ b/lib/private/Setup/PostgreSQL.php @@ -26,42 +26,34 @@ */ namespace OC\Setup; +use OC\DatabaseException; +use OC\DB\QueryBuilder\Literal; +use OCP\IDBConnection; + class PostgreSQL extends AbstractDatabase { public $dbprettyname = 'PostgreSQL'; public function setupDatabase($username) { - $e_host = addslashes($this->dbHost); - $e_user = addslashes($this->dbUser); - $e_password = addslashes($this->dbPassword); - - // adding port support through installer - if(!empty($this->dbPort)) { - // casting to int to avoid malicious input - $port = (int)$this->dbPort; - } else if(strpos($e_host, ':')) { - list($e_host, $port)=explode(':', $e_host, 2); - } else { - $port=false; + $connection = $this->connect([ + 'dbname' => 'postgres' + ]); + //check for roles creation rights in postgresql + $builder = $connection->getQueryBuilder(); + $builder->automaticTablePrefix(false); + $query = $builder + ->select('rolname') + ->from('pg_roles') + ->where($builder->expr()->eq('rolcreaterole', new Literal('TRUE'))) + ->andWhere($builder->expr()->eq('rolname', $builder->createNamedParameter($this->dbUser))); + + try { + $result = $query->execute(); + $canCreateRoles = $result->rowCount() > 0; + } catch (DatabaseException $e) { + $canCreateRoles = false; } - //check if the database user has admin rights - $connection_string = "host='$e_host' dbname=postgres user='$e_user' port='$port' password='$e_password'"; - $connection = @pg_connect($connection_string); - if(!$connection) { - // Try if we can connect to the DB with the specified name - $e_dbname = addslashes($this->dbName); - $connection_string = "host='$e_host' dbname='$e_dbname' user='$e_user' port='$port' password='$e_password'"; - $connection = @pg_connect($connection_string); - - if(!$connection) - throw new \OC\DatabaseSetupException($this->trans->t('PostgreSQL connection failed'), - $this->trans->t('Please check your connection details.')); - } - $e_user = pg_escape_string($this->dbUser); - //check for roles creation rights in postgresql - $query="SELECT 1 FROM pg_roles WHERE rolcreaterole=TRUE AND rolname='$e_user'"; - $result = pg_query($connection, $query); - if($result and pg_num_rows($result) > 0) { + if($canCreateRoles) { //use the admin login data for the new database user //add prefix to the postgresql user name to prevent collisions @@ -72,7 +64,7 @@ class PostgreSQL extends AbstractDatabase { $this->createDBUser($connection); } - $systemConfig = \OC::$server->getSystemConfig(); + $systemConfig = $this->config->getSystemConfig(); $systemConfig->setValues([ 'dbuser' => $this->dbUser, 'dbpassword' => $this->dbPassword, @@ -80,98 +72,85 @@ class PostgreSQL extends AbstractDatabase { //create the database $this->createDatabase($connection); + $query = $connection->prepare("select count(*) FROM pg_class WHERE relname=? limit 1"); + $query->execute([$this->tablePrefix . "users"]); + $tablesSetup = $query->fetchColumn() > 0; // the connection to dbname=postgres is not needed anymore - pg_close($connection); + $connection->close(); // connect to the ownCloud database (dbname=$this->dbname) and check if it needs to be filled $this->dbUser = $systemConfig->getValue('dbuser'); $this->dbPassword = $systemConfig->getValue('dbpassword'); - - $e_host = addslashes($this->dbHost); - $e_dbname = addslashes($this->dbName); - $e_user = addslashes($this->dbUser); - $e_password = addslashes($this->dbPassword); - - // Fix database with port connection - if(strpos($e_host, ':')) { - list($e_host, $port)=explode(':', $e_host, 2); - } else { - $port=false; - } - - $connection_string = "host='$e_host' dbname='$e_dbname' user='$e_user' port='$port' password='$e_password'"; - $connection = @pg_connect($connection_string); - if(!$connection) { + $connection = $this->connect(); + try { + $connection->connect(); + } catch (\Exception $e) { + $this->logger->logException($e); throw new \OC\DatabaseSetupException($this->trans->t('PostgreSQL username and/or password not valid'), - $this->trans->t('You need to enter either an existing account or the administrator.')); + $this->trans->t('You need to enter either an existing account or the administrator.')); } - $query = "select count(*) FROM pg_class WHERE relname='".$this->tablePrefix."users' limit 1"; - $result = pg_query($connection, $query); - if($result) { - $row = pg_fetch_row($result); - } - if(!$result or $row[0]==0) { + + + if(!$tablesSetup) { \OC_DB::createDbFromStructure($this->dbDefinitionFile); } } - private function createDatabase($connection) { - //we can't use OC_BD functions here because we need to connect as the administrative user. - $e_name = pg_escape_string($this->dbName); - $e_user = pg_escape_string($this->dbUser); - $query = "select datname from pg_database where datname = '$e_name'"; - $result = pg_query($connection, $query); - if(!$result) { - $entry = $this->trans->t('DB Error: "%s"', array(pg_last_error($connection))) . '<br />'; - $entry .= $this->trans->t('Offending command was: "%s"', array($query)) . '<br />'; - \OCP\Util::writeLog('setup.pg', $entry, \OCP\Util::WARN); - } - if(! pg_fetch_row($result)) { + private function createDatabase(IDBConnection $connection) { + if(!$this->databaseExists($connection)) { //The database does not exists... let's create it - $query = "CREATE DATABASE \"$e_name\" OWNER \"$e_user\""; - $result = pg_query($connection, $query); - if(!$result) { - $entry = $this->trans->t('DB Error: "%s"', array(pg_last_error($connection))) . '<br />'; - $entry .= $this->trans->t('Offending command was: "%s"', array($query)) . '<br />'; - \OCP\Util::writeLog('setup.pg', $entry, \OCP\Util::WARN); + $query = $connection->prepare("CREATE DATABASE " . addslashes($this->dbName) . " OWNER " . addslashes($this->dbUser)); + try { + $query->execute(); + } catch (DatabaseException $e) { + $this->logger->error('Error while trying to create database'); + $this->logger->logException($e); } - else { - $query = "REVOKE ALL PRIVILEGES ON DATABASE \"$e_name\" FROM PUBLIC"; - pg_query($connection, $query); + } else { + $query = $connection->prepare("REVOKE ALL PRIVILEGES ON DATABASE " . addslashes($this->dbName) . " FROM PUBLIC"); + try { + $query->execute(); + } catch (DatabaseException $e) { + $this->logger->error('Error while trying to restrict database permissions'); + $this->logger->logException($e); } } } - private function createDBUser($connection) { - $e_name = pg_escape_string($this->dbUser); - $e_password = pg_escape_string($this->dbPassword); - $query = "select * from pg_roles where rolname='$e_name';"; - $result = pg_query($connection, $query); - if(!$result) { - $entry = $this->trans->t('DB Error: "%s"', array(pg_last_error($connection))) . '<br />'; - $entry .= $this->trans->t('Offending command was: "%s"', array($query)) . '<br />'; - \OCP\Util::writeLog('setup.pg', $entry, \OCP\Util::WARN); - } + private function userExists(IDBConnection $connection) { + $builder = $connection->getQueryBuilder(); + $builder->automaticTablePrefix(false); + $query = $builder->select('*') + ->from('pg_roles') + ->where($builder->expr()->eq('rolname', $builder->createNamedParameter($this->dbUser))); + $result = $query->execute(); + return $result->rowCount() > 0; + } - if(! pg_fetch_row($result)) { - //user does not exists let's create it :) - $query = "CREATE USER \"$e_name\" CREATEDB PASSWORD '$e_password';"; - $result = pg_query($connection, $query); - if(!$result) { - $entry = $this->trans->t('DB Error: "%s"', array(pg_last_error($connection))) . '<br />'; - $entry .= $this->trans->t('Offending command was: "%s"', array($query)) . '<br />'; - \OCP\Util::writeLog('setup.pg', $entry, \OCP\Util::WARN); - } - } - else { // change password of the existing role - $query = "ALTER ROLE \"$e_name\" WITH PASSWORD '$e_password';"; - $result = pg_query($connection, $query); - if(!$result) { - $entry = $this->trans->t('DB Error: "%s"', array(pg_last_error($connection))) . '<br />'; - $entry .= $this->trans->t('Offending command was: "%s"', array($query)) . '<br />'; - \OCP\Util::writeLog('setup.pg', $entry, \OCP\Util::WARN); + private function databaseExists(IDBConnection $connection) { + $builder = $connection->getQueryBuilder(); + $builder->automaticTablePrefix(false); + $query = $builder->select('datname') + ->from('pg_database') + ->where($builder->expr()->eq('datname', $builder->createNamedParameter($this->dbName))); + $result = $query->execute(); + return $result->rowCount() > 0; + } + + private function createDBUser(IDBConnection $connection) { + try { + if ($this->userExists($connection, $this->dbUser)) { + // change the password + $query = $connection->prepare("ALTER ROLE " . addslashes($this->dbUser) . " CREATEDB WITH PASSWORD " . addslashes($this->dbPassword)); + } else { + // create the user + $query = $connection->prepare("CREATE USER " . addslashes($this->dbUser) . " CREATEDB PASSWORD " . addslashes($this->dbPassword)); } + $query->execute(); + } catch (DatabaseException $e) { + $this->logger->error('Error while trying to create database user'); + $this->logger->logException($e); } } } diff --git a/tests/lib/SetupTest.php b/tests/lib/SetupTest.php index e2723efd76a..c6e219f4029 100644 --- a/tests/lib/SetupTest.php +++ b/tests/lib/SetupTest.php @@ -57,7 +57,7 @@ class SetupTest extends \Test\TestCase { ->method('is_callable') ->will($this->returnValue(false)); $this->setupClass - ->expects($this->once()) + ->expects($this->any()) ->method('getAvailableDbDriversForPdo') ->will($this->returnValue([])); $result = $this->setupClass->getSupportedDatabases(); @@ -76,15 +76,15 @@ class SetupTest extends \Test\TestCase { array('sqlite', 'mysql', 'oci', 'pgsql') )); $this->setupClass - ->expects($this->once()) + ->expects($this->any()) ->method('class_exists') ->will($this->returnValue(false)); $this->setupClass - ->expects($this->exactly(2)) + ->expects($this->any()) ->method('is_callable') ->will($this->returnValue(false)); $this->setupClass - ->expects($this->once()) + ->expects($this->any()) ->method('getAvailableDbDriversForPdo') ->will($this->returnValue([])); $result = $this->setupClass->getSupportedDatabases(); @@ -100,17 +100,17 @@ class SetupTest extends \Test\TestCase { array('sqlite', 'mysql', 'pgsql', 'oci') )); $this->setupClass - ->expects($this->once()) + ->expects($this->any()) ->method('class_exists') ->will($this->returnValue(true)); $this->setupClass - ->expects($this->exactly(2)) + ->expects($this->any()) ->method('is_callable') ->will($this->returnValue(true)); $this->setupClass - ->expects($this->once()) + ->expects($this->any()) ->method('getAvailableDbDriversForPdo') - ->will($this->returnValue(['mysql'])); + ->will($this->returnValue(['mysql', 'pgsql'])); $result = $this->setupClass->getSupportedDatabases(); $expectedResult = array( 'sqlite' => 'SQLite', |