Skip to content
Open
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
4 changes: 4 additions & 0 deletions changelog/62169.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Fix ``mysql_user.present`` corrupting the ``mysql.user`` view on MariaDB
>= 10.4 when switching a user to unix-socket authentication. Use
``ALTER USER ... IDENTIFIED VIA unix_socket`` instead of updating
``mysql.user`` directly, which is now a view into ``mysql.global_priv``.
22 changes: 14 additions & 8 deletions salt/modules/mysql.py
Original file line number Diff line number Diff line change
Expand Up @@ -2033,14 +2033,20 @@ def _mariadb_user_chpass(
qry = False
else:
args["unix_socket"] = "unix_socket"
qry = (
"UPDATE mysql.user SET "
+ password_column
+ "="
+ password_sql
+ ", plugin=%(unix_socket)s"
+ " WHERE User=%(user)s AND Host = %(host)s;"
)
if (
salt.utils.versions.version_cmp(server_version, compare_version)
>= 0
):
qry = "ALTER USER %(user)s@%(host)s IDENTIFIED VIA %(unix_socket)s;"
else:
qry = (
"UPDATE mysql.user SET "
+ password_column
+ "="
+ password_sql
+ ", plugin=%(unix_socket)s"
+ " WHERE User=%(user)s AND Host = %(host)s;"
)
else:
log.error("Auth via unix_socket can be set only for host=localhost")

Expand Down
101 changes: 101 additions & 0 deletions tests/pytests/unit/modules/test_mysql.py
Original file line number Diff line number Diff line change
Expand Up @@ -424,6 +424,107 @@ def test_user_chpass():
connect_mock.assert_has_calls(calls, any_order=True)


def test_user_chpass_mariadb_unix_socket():
"""
Regression test for #62169.

On MariaDB >= 10.4, ``mysql.user`` is a view into ``mysql.global_priv``
and cannot be updated directly. ``user_chpass`` with
``allow_passwordless=True`` / ``unix_socket=True`` must issue an
``ALTER USER ... IDENTIFIED VIA unix_socket`` statement on MariaDB
>= 10.4 and the legacy ``UPDATE mysql.user`` only on older releases.
"""
# MariaDB 10.4+ should use ALTER USER, not UPDATE mysql.user
connect_mock = MagicMock()
with patch.object(mysql, "_connect", connect_mock):
with patch.object(mysql, "version", return_value="10.4.21-MariaDB"):
with patch.object(mysql, "user_exists", MagicMock(return_value=True)):
with patch.object(
mysql, "plugin_status", MagicMock(return_value="ACTIVE")
):
with patch.object(
mysql,
"__get_auth_plugin",
MagicMock(return_value="mysql_native_password"),
):
with patch.dict(mysql.__salt__, {"config.option": MagicMock()}):
mysql.user_chpass(
"root",
host="localhost",
allow_passwordless=True,
unix_socket=True,
)
calls = (
call()
.cursor()
.execute(
"ALTER USER %(user)s@%(host)s IDENTIFIED VIA %(unix_socket)s;",
{
"user": "root",
"host": "localhost",
"auth_plugin": "mysql_native_password",
"unix_socket": "unix_socket",
},
),
call().cursor().execute("FLUSH PRIVILEGES;"),
)
connect_mock.assert_has_calls(calls, any_order=True)
executed_sql = [
str(c)
for c in connect_mock.mock_calls
if ".cursor().execute(" in str(c)
]
assert not any(
"UPDATE mysql.user" in s for s in executed_sql
), (
"MariaDB >= 10.4 must not UPDATE mysql.user "
"(it is a view into mysql.global_priv); "
"executed: {}".format(executed_sql)
)

# MariaDB < 10.4 must keep using the legacy UPDATE mysql.user path
connect_mock = MagicMock()
with patch.object(mysql, "_connect", connect_mock):
with patch.object(mysql, "version", return_value="10.2.21-MariaDB"):
with patch.object(mysql, "user_exists", MagicMock(return_value=True)):
with patch.object(
mysql, "plugin_status", MagicMock(return_value="ACTIVE")
):
with patch.object(
mysql,
"__get_auth_plugin",
MagicMock(return_value="mysql_native_password"),
):
with patch.object(
mysql,
"__password_column",
MagicMock(return_value="Password"),
):
with patch.dict(
mysql.__salt__, {"config.option": MagicMock()}
):
mysql.user_chpass(
"root",
host="localhost",
allow_passwordless=True,
unix_socket=True,
)
executed_sql = [
str(c)
for c in connect_mock.mock_calls
if ".cursor().execute(" in str(c)
]
assert any(
"UPDATE mysql.user" in s
and "plugin=%(unix_socket)s" in s
for s in executed_sql
), (
"MariaDB < 10.4 must keep the legacy "
"UPDATE mysql.user path; executed: "
"{}".format(executed_sql)
)


def test_user_remove():
"""
Test the removal of a MySQL user in mysql exec module
Expand Down
Loading