installer: fix MySQL create user permissions check

The previous check for the necessary permissions to create a user were
no longer correct. They did not check for the CREATE USER global
permission, which is also sufficient for user creation. Additionally,
they were checking for the ability to grant permissions by comparing
the IS_GRANTABLE column against a boolean true (integer 1). The column
is a varchar(3) that stores the string 'YES' to indicate the presence
of the permission, so the comparison against the integer boolean is
always false.

MySQL has had both the CREATE USER permission and the use of a
varchar(3) column for IS_GRANTABLE since at least MySQL 5.0, so they
are present for all supported versions of MySQL/MariaDB.

This was likely not a problem until 3f852f7ddc, at which point the
checks were added to the web installer flow.

Now, the CREATE USER permission is also accepted and IS_GRANTABLE is
compared against 'YES', so the web installer will be able to present
the option to create users if the database administrator account has
the correct permissions.

Bug: T391179
Change-Id: I5c2454599d6e91559d9996fe7c274eb8daab44bd
(cherry picked from commit daff6d03638f5c7108339f0a8774d2cfd9fbc0f0)
This commit is contained in:
Zvi "CtrlZvi" Effron 2025-04-06 01:58:54 -07:00 committed by Krinkle
parent c60a5c4206
commit db82379f65

View file

@ -247,18 +247,23 @@ class MysqlInstaller extends DatabaseInstaller {
$quotedUser = $conn->addQuotes( $parts[0] ) .
'@' . $conn->addQuotes( $parts[1] );
// The user needs to have INSERT on mysql.* to be able to CREATE USER
// The user needs to have INSERT on mysql.* or (global) CREATE USER to be able to CREATE USER
// The grantee will be double-quoted in this query, as required
$res = $conn->select( 'INFORMATION_SCHEMA.USER_PRIVILEGES', '*',
[ 'GRANTEE' => $quotedUser ], __METHOD__ );
$insertMysql = false;
$grantOptions = array_fill_keys( $this->webUserPrivs, true );
// The user needs to have permission to GRANT all necessary permissions to the newly created user.
// This starts as a list of all necessary permissions and they are removed as they are found.
// If any are left in the list after checking for permissions, those are the missing permissions.
$missingGrantOptions = array_fill_keys( $this->webUserPrivs, true );
foreach ( $res as $row ) {
if ( $row->PRIVILEGE_TYPE == 'INSERT' ) {
$insertMysql = true;
} elseif ( $row->PRIVILEGE_TYPE == 'CREATE USER' ) {
$insertMysql = true;
}
if ( $row->IS_GRANTABLE ) {
unset( $grantOptions[$row->PRIVILEGE_TYPE] );
if ( $row->IS_GRANTABLE === 'YES' ) {
unset( $missingGrantOptions[$row->PRIVILEGE_TYPE] );
}
}
@ -283,15 +288,15 @@ class MysqlInstaller extends DatabaseInstaller {
$res = $conn->select( 'INFORMATION_SCHEMA.SCHEMA_PRIVILEGES', '*',
[
'GRANTEE' => $quotedUser,
'IS_GRANTABLE' => 1,
'IS_GRANTABLE' => 'YES',
], __METHOD__ );
foreach ( $res as $row ) {
$regex = $this->likeToRegex( $row->TABLE_SCHEMA );
if ( preg_match( $regex, $this->getVar( 'wgDBname' ) ) ) {
unset( $grantOptions[$row->PRIVILEGE_TYPE] );
unset( $missingGrantOptions[$row->PRIVILEGE_TYPE] );
}
}
if ( count( $grantOptions ) ) {
if ( count( $missingGrantOptions ) ) {
// Can't grant everything
return false;
}