From 609e41793ffa8d7917e1350b1af2a41ddc05d8fd Mon Sep 17 00:00:00 2001 From: Vincent Jancso Date: Wed, 15 Jun 2022 15:28:29 +0200 Subject: [PATCH 1/2] use ALTER USER on mariadb >= 10.4 when switching to unix socket authentication --- salt/modules/mysql.py | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/salt/modules/mysql.py b/salt/modules/mysql.py index 2558edd01113..cbcf2552a65c 100644 --- a/salt/modules/mysql.py +++ b/salt/modules/mysql.py @@ -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") From 74fd0f4c4aa9421762d3d7b03f4747ef4c9319ff Mon Sep 17 00:00:00 2001 From: "Daniel A. Wozniak" Date: Wed, 10 Jun 2026 21:18:49 -0700 Subject: [PATCH 2/2] Add changelog and regression test for PR #62169 Adds the missing changelog fragment and a unit test pinning the new MariaDB >= 10.4 ``ALTER USER ... IDENTIFIED VIA unix_socket`` code path in ``_mariadb_user_chpass``. The test also verifies the legacy ``UPDATE mysql.user`` path is still used on MariaDB < 10.4. --- changelog/62169.fixed.md | 4 + tests/pytests/unit/modules/test_mysql.py | 101 +++++++++++++++++++++++ 2 files changed, 105 insertions(+) create mode 100644 changelog/62169.fixed.md diff --git a/changelog/62169.fixed.md b/changelog/62169.fixed.md new file mode 100644 index 000000000000..bb9e15131c78 --- /dev/null +++ b/changelog/62169.fixed.md @@ -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``. diff --git a/tests/pytests/unit/modules/test_mysql.py b/tests/pytests/unit/modules/test_mysql.py index 1fa701702958..616f39250b36 100644 --- a/tests/pytests/unit/modules/test_mysql.py +++ b/tests/pytests/unit/modules/test_mysql.py @@ -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