Skip to content

Commit 3467a0d

Browse files
isrgomeznmariz
authored andcommitted
BUG32162928: Change user command fails on pure python implementation
The cmd_change_user() command failed with pure-python due to the missing connection attributes not being sent along the authentication data.
1 parent 82764da commit 3467a0d

File tree

4 files changed

+128
-6
lines changed

4 files changed

+128
-6
lines changed

CHANGES.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ v8.0.24
1616
- WL#14212: Support connection close notification
1717
- WL#14027: Add support for Python 3.9
1818
- BUG#32435181: Add support for Django 3.2
19+
- BUG#32162928: Change user command fails on pure python implementation
1920
- BUG#32029891: Add context manager support for pooled connections
2021
- BUG#31490101: Fix wrong cast of Python unicode to std::string
2122
- BUG#30416704: Binary columns returned as strings

lib/mysql/connector/connection.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright (c) 2009, 2020, Oracle and/or its affiliates.
1+
# Copyright (c) 2009, 2021, Oracle and/or its affiliates.
22
#
33
# This program is free software; you can redistribute it and/or modify
44
# it under the terms of the GNU General Public License, version 2.0, as
@@ -842,13 +842,13 @@ def cmd_change_user(self, username='', password='', database='',
842842
if self._compress:
843843
raise errors.NotSupportedError("Change user is not supported with "
844844
"compression.")
845-
846845
packet = self._protocol.make_change_user(
847846
handshake=self._handshake,
848847
username=username, password=password, database=database,
849848
charset=charset, client_flags=self._client_flags,
850849
ssl_enabled=self._ssl_active,
851-
auth_plugin=self._auth_plugin)
850+
auth_plugin=self._auth_plugin,
851+
conn_attrs=self._conn_attrs)
852852
self._socket.send(packet, 0, 0)
853853

854854
ok_packet = self._auth_switch_request(username, password)

lib/mysql/connector/protocol.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ def make_stmt_fetch(self, statement_id, rows=1):
156156

157157
def make_change_user(self, handshake, username=None, password=None,
158158
database=None, charset=45, client_flags=0,
159-
ssl_enabled=False, auth_plugin=None):
159+
ssl_enabled=False, auth_plugin=None, conn_attrs=None):
160160
"""Make a MySQL packet with the Change User command"""
161161

162162
try:
@@ -188,6 +188,9 @@ def make_change_user(self, handshake, username=None, password=None,
188188
if client_flags & ClientFlag.PLUGIN_AUTH:
189189
packet += auth_plugin.encode('utf8') + b'\x00'
190190

191+
if (client_flags & ClientFlag.CONNECT_ARGS) and conn_attrs is not None:
192+
packet += self.make_conn_attrs(conn_attrs)
193+
191194
return packet
192195

193196
def parse_handshake(self, packet):

tests/test_bugs.py

Lines changed: 120 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,8 @@
6060

6161
from tests import foreach_cnx, cnx_config
6262
from . import check_tls_versions_support
63-
from mysql.connector import (connection, cursor, conversion, protocol,
64-
errors, constants, pooling)
63+
from mysql.connector import (connection, constants, conversion, cursor,
64+
errors, MySQLConnection, pooling, protocol,)
6565
from mysql.connector.optionfiles import read_option_files
6666
from mysql.connector.pooling import PooledMySQLConnection
6767
import mysql.connector
@@ -5943,3 +5943,121 @@ def test_without_use_unicode_cursor_prepared(self):
59435943
cursor=cursor.MySQLCursorPrepared,
59445944
use_binary_charset=True,
59455945
)
5946+
5947+
5948+
class Bug32162928(tests.MySQLConnectorTests):
5949+
"""BUG#32162928: change user command fails with pure python implementation.
5950+
5951+
The cmd_change_user() command fails with pure-python.
5952+
"""
5953+
def setUp(self):
5954+
self.connect_kwargs = tests.get_mysql_config()
5955+
cnx = MySQLConnection(**self.connect_kwargs)
5956+
self.users = [
5957+
{'user': 'user_native1',
5958+
'host': self.connect_kwargs['host'],
5959+
'port': self.connect_kwargs['port'],
5960+
'database': self.connect_kwargs['database'],
5961+
'password': 'native1_pass',
5962+
'auth_plugin': 'mysql_native_password'},
5963+
{'user': 'user_native2',
5964+
'host': self.connect_kwargs['host'],
5965+
'port': self.connect_kwargs['port'],
5966+
'database': self.connect_kwargs['database'],
5967+
'password': 'native2_pass',
5968+
'auth_plugin': 'mysql_native_password'},
5969+
{'user': 'user_sha2561',
5970+
'host': self.connect_kwargs['host'],
5971+
'port': self.connect_kwargs['port'],
5972+
'database': self.connect_kwargs['database'],
5973+
'password': 'sha2561_pass',
5974+
'auth_plugin': 'sha256_password'},
5975+
{'user': 'user_sha2562',
5976+
'host': self.connect_kwargs['host'],
5977+
'port': self.connect_kwargs['port'],
5978+
'database': self.connect_kwargs['database'],
5979+
'password': 'sha2562_pass',
5980+
'auth_plugin': 'sha256_password'},
5981+
{'user': 'user_caching1',
5982+
'host': self.connect_kwargs['host'],
5983+
'port': self.connect_kwargs['port'],
5984+
'database': self.connect_kwargs['database'],
5985+
'password': 'caching1_pass',
5986+
'auth_plugin': 'caching_sha2_password'},
5987+
{'user': 'user_caching2',
5988+
'host': self.connect_kwargs['host'],
5989+
'port': self.connect_kwargs['port'],
5990+
'database': self.connect_kwargs['database'],
5991+
'password': 'caching2_pass',
5992+
'auth_plugin': 'caching_sha2_password'}]
5993+
5994+
# create users
5995+
if tests.MYSQL_VERSION < (8, 0, 0):
5996+
self.new_users = self.users[0:4]
5997+
else:
5998+
self.new_users = self.users
5999+
6000+
for new_user in self.new_users:
6001+
cnx.cmd_query("DROP USER IF EXISTS '{user}'@'{host}'".format(**new_user))
6002+
6003+
stmt = ("CREATE USER IF NOT EXISTS '{user}'@'{host}' IDENTIFIED "
6004+
"WITH {auth_plugin} BY '{password}'").format(**new_user)
6005+
cnx.cmd_query(stmt)
6006+
6007+
cnx.cmd_query("GRANT ALL PRIVILEGES ON {database}.* TO "
6008+
"'{user}'@'{host}'".format(**new_user))
6009+
6010+
@foreach_cnx()
6011+
def test_change_user(self):
6012+
# test users can connect
6013+
for user in self.new_users:
6014+
conn_args = user.copy()
6015+
try:
6016+
self.connect_kwargs.pop('auth_plugin')
6017+
except:
6018+
pass
6019+
cnx_test = self.cnx.__class__(**conn_args)
6020+
cnx_test.cmd_query("SELECT USER()")
6021+
logged_user = cnx_test.get_rows()[0][0][0]
6022+
self.assertEqual(u"{user}@{host}".format(**user), logged_user)
6023+
cnx_test.close()
6024+
6025+
# tests change user
6026+
if tests.MYSQL_VERSION < (8, 0, 0):
6027+
# 5.6 does not support caching_sha2_password auth plugin
6028+
test_cases = [(0, 1), (1, 2), (2,3), (3, 0)]
6029+
else:
6030+
test_cases = [(0, 1), (1, 2), (2,3), (3, 0), (3, 4), (4, 5),
6031+
(5, 3), (5, 0)]
6032+
for user1, user2 in test_cases:
6033+
conn_args_user1 = self.users[user1].copy()
6034+
try:
6035+
conn_args_user1.pop('auth_plugin')
6036+
except:
6037+
pass
6038+
if tests.MYSQL_VERSION < (8, 0, 0):
6039+
# change user does not work in 5.x with charset utf8mb4
6040+
conn_args_user1['charset'] = 'utf8'
6041+
6042+
cnx_test = self.cnx.__class__(**conn_args_user1)
6043+
cnx_test.cmd_query("SELECT USER()")
6044+
first_user = cnx_test.get_rows()[0][0][0]
6045+
self.assertEqual(
6046+
u"{user}@{host}".format(**self.users[user1]), first_user)
6047+
6048+
cnx_test.cmd_change_user(self.users[user2]['user'],
6049+
self.users[user2]['password'],
6050+
self.users[user2]['database'])
6051+
cnx_test.cmd_query("SELECT USER()")
6052+
rows = cnx_test.get_rows()
6053+
current_user = rows[0][0][0]
6054+
self.assertNotEqual(first_user, current_user)
6055+
self.assertEqual(
6056+
u"{user}@{host}".format(**self.users[user2]), current_user)
6057+
cnx_test.close()
6058+
6059+
def tearDown(self):
6060+
cnx = MySQLConnection(**self.connect_kwargs)
6061+
# cleanup users
6062+
for new_user in self.users:
6063+
cnx.cmd_query("DROP USER IF EXISTS '{user}'@'{host}'".format(**new_user))

0 commit comments

Comments
 (0)