diff --git a/lib/private/Setup/MySQL.php b/lib/private/Setup/MySQL.php index 24093ae97a40c..e656fe4b3a3fc 100644 --- a/lib/private/Setup/MySQL.php +++ b/lib/private/Setup/MySQL.php @@ -18,6 +18,9 @@ class MySQL extends AbstractDatabase { public string $dbprettyname = 'MySQL/MariaDB'; + /** @var int MySQL username length limit */ + private const MAX_USERNAME_LENGTH = 16; + public function setupDatabase(): void { //check if the database user has admin right $connection = $this->connect(['dbname' => null]); @@ -58,6 +61,36 @@ public function setupDatabase(): void { } } + /** + * Check whether a MySQL user account already exists. + */ + private function userExists(IDBConnection $connection, string $username): bool { + $result = $connection->executeQuery( + 'SELECT user FROM mysql.user WHERE user=?', + [$username] + ); + $exists = count($result->fetchAll()) > 0; + $result->closeCursor(); + return $exists; + } + + /** + * Find a username starting from $base that doesn't already exist, + * respecting MySQL's 16-character username limit. + */ + private function findAvailableUsername(IDBConnection $connection, string $base): string { + $candidate = substr($base, 0, self::MAX_USERNAME_LENGTH); + + $i = 1; + while ($this->userExists($connection, $candidate)) { + $suffix = (string)$i; + $candidate = substr($base, 0, self::MAX_USERNAME_LENGTH - strlen($suffix)) . $suffix; + $i++; + } + + return $candidate; + } + private function createDatabase(\OC\DB\Connection $connection): void { try { $name = $this->dbName; @@ -143,35 +176,12 @@ private function createSpecificUser(string $username, IDBConnection $connection) //we don't have a dbuser specified in config if ($this->dbUser !== $oldUser) { //add prefix to the admin username to prevent collisions - $adminUser = substr('oc_' . $username, 0, 16); - - $i = 1; - while (true) { - //this should be enough to check for admin rights in mysql - $query = 'SELECT user FROM mysql.user WHERE user=?'; - $result = $connection->executeQuery($query, [$adminUser]); - - //current dbuser has admin rights - $data = $result->fetchAll(); - $result->closeCursor(); - //new dbuser does not exist - if (count($data) === 0) { - //use the admin login data for the new database user - $this->dbUser = $adminUser; - $this->createDBUser($connection); - // if sharding is used we need to manually call this for every shard as those also need the user setup! - /** @var ConnectionAdapter $connection */ - foreach ($connection->getInner()->getShardConnections() as $shard) { - $this->createDBUser($shard); - } - - break; - } else { - //repeat with different username - $length = strlen((string)$i); - $adminUser = substr('oc_' . $username, 0, 16 - $length) . $i; - $i++; - } + $this->dbUser = $this->findAvailableUsername($connection, 'oc_' . $username); + $this->createDBUser($connection); + // if sharding is used we need to manually call this for every shard as those also need the user setup! + /** @var ConnectionAdapter $connection */ + foreach ($connection->getInner()->getShardConnections() as $shard) { + $this->createDBUser($shard); } } else { // Reuse existing password if a database config is already present diff --git a/lib/private/Setup/PostgreSQL.php b/lib/private/Setup/PostgreSQL.php index 5ace7f6bcbee3..e781ab3424853 100644 --- a/lib/private/Setup/PostgreSQL.php +++ b/lib/private/Setup/PostgreSQL.php @@ -103,6 +103,21 @@ public function setupDatabase(): void { } } + /** + * Find a role name starting from $base that doesn't already exist. + */ + private function findAvailableUsername(Connection $connection, string $base): string { + $candidate = $base; + + $i = 1; + while ($this->userExists($connection, $candidate)) { + $i++; + $candidate = $base . $i; + } + + return $candidate; + } + private function createDatabase(Connection $connection): void { if (!$this->databaseExists($connection)) { //The database does not exists... let's create it @@ -126,12 +141,15 @@ private function createDatabase(Connection $connection): void { } } - private function userExists(Connection $connection): bool { + /** + * Check whether a PostgreSQL role already exists. + */ + private function userExists(Connection $connection, string $username): bool { $builder = $connection->getQueryBuilder(); $builder->automaticTablePrefix(false); - $query = $builder->select('*') + $query = $builder->select('rolname') ->from('pg_roles') - ->where($builder->expr()->eq('rolname', $builder->createNamedParameter($this->dbUser))); + ->where($builder->expr()->eq('rolname', $builder->createNamedParameter($username))); $result = $query->executeQuery(); return $result->rowCount() > 0; } @@ -147,14 +165,8 @@ private function databaseExists(Connection $connection): bool { } private function createDBUser(Connection $connection): void { - $dbUser = $this->dbUser; + $this->dbUser = $this->findAvailableUsername($connection, $this->dbUser); try { - $i = 1; - while ($this->userExists($connection)) { - $i++; - $this->dbUser = $dbUser . $i; - } - // create the user $query = $connection->prepare('CREATE USER "' . addslashes($this->dbUser) . "\" CREATEDB PASSWORD '" . addslashes($this->dbPassword) . "'"); $query->executeStatement();