Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 39 additions & 29 deletions lib/private/Setup/MySQL.php
Original file line number Diff line number Diff line change
Expand Up @@ -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]);
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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
Expand Down
32 changes: 22 additions & 10 deletions lib/private/Setup/PostgreSQL.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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;
}
Expand All @@ -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();
Expand Down
Loading