mirror of
https://github.com/ansible-collections/community.mysql.git
synced 2025-04-18 16:31:26 -07:00
Fix mysql_user on_new_username IndexError (#642)
* fix tuple indexerror when no accounts are found * Fix tests for update_password not executed * Add test for case where existing user have different password * lint to prevent warning about jinja templating in when clause * Refactor get_existing_authentication to return a list of all row found Previously we were returning only the first row found. We need to be able to see if there is a difference in the existing passwords. * Refactor host option to be optional This make it possible to use the same method from mysql_user to help update_password retrieve existing password for all account with the same username independently of their hostname. And from mysql_info to get the password of a specif user using WHERE user = '' AND host = '' * Add change log fragment * Add link to the PR in the change log * lint for ansible devel * Fix templating type error could not cconvert to bool with ansible devel * Revert changes made for ansible-devel that broke tests for Ansible 2.15 * Revert changes made for ansible-devel that broke tests * Cut unnecessary set, uniqueness is ensured by the group_by in the query * Cut auth plugin from returned values when multiple existing auths exists Discussed here: https://github.com/ansible-collections/community.mysql/pull/642/files#r1649720519 * fix convertion of list(dict) to list(tuple) * Fix test for empty password on MySQL 8+
This commit is contained in:
parent
1922e7154e
commit
33e8754c4e
8 changed files with 141 additions and 74 deletions
|
@ -95,8 +95,12 @@ def get_grants(cursor, user, host):
|
|||
return grants.split(", ")
|
||||
|
||||
|
||||
def get_existing_authentication(cursor, user, host):
|
||||
# Return the plugin and auth_string if there is exactly one distinct existing plugin and auth_string.
|
||||
def get_existing_authentication(cursor, user, host=None):
|
||||
""" Return a list of dict containing the plugin and auth_string for the
|
||||
specified username.
|
||||
If hostname is provided, return only the information about this particular
|
||||
account.
|
||||
"""
|
||||
cursor.execute("SELECT VERSION()")
|
||||
srv_type = cursor.fetchone()
|
||||
# Mysql_info use a DictCursor so we must convert back to a list
|
||||
|
@ -107,37 +111,50 @@ def get_existing_authentication(cursor, user, host):
|
|||
if 'mariadb' in srv_type[0].lower():
|
||||
# before MariaDB 10.2.19 and 10.3.11, "password" and "authentication_string" can differ
|
||||
# when using mysql_native_password
|
||||
cursor.execute("""select plugin, auth from (
|
||||
select plugin, password as auth from mysql.user where user=%(user)s
|
||||
and host=%(host)s
|
||||
union select plugin, authentication_string as auth from mysql.user where user=%(user)s
|
||||
and host=%(host)s) x group by plugin, auth limit 2
|
||||
""", {'user': user, 'host': host})
|
||||
if host:
|
||||
cursor.execute("""select plugin, auth from (
|
||||
select plugin, password as auth from mysql.user where user=%(user)s
|
||||
and host=%(host)s
|
||||
union select plugin, authentication_string as auth from mysql.user where user=%(user)s
|
||||
and host=%(host)s) x group by plugin, auth
|
||||
""", {'user': user, 'host': host})
|
||||
else:
|
||||
cursor.execute("""select plugin, auth from (
|
||||
select plugin, password as auth from mysql.user where user=%(user)s
|
||||
union select plugin, authentication_string as auth from mysql.user where user=%(user)s
|
||||
) x group by plugin, auth
|
||||
""", {'user': user})
|
||||
else:
|
||||
cursor.execute("""select plugin, authentication_string as auth
|
||||
from mysql.user where user=%(user)s and host=%(host)s
|
||||
group by plugin, authentication_string limit 2""", {'user': user, 'host': host})
|
||||
if host:
|
||||
cursor.execute("""select plugin, authentication_string as auth
|
||||
from mysql.user where user=%(user)s and host=%(host)s
|
||||
group by plugin, authentication_string""", {'user': user, 'host': host})
|
||||
else:
|
||||
cursor.execute("""select plugin, authentication_string as auth
|
||||
from mysql.user where user=%(user)s
|
||||
group by plugin, authentication_string""", {'user': user})
|
||||
|
||||
rows = cursor.fetchall()
|
||||
|
||||
# Mysql_info use a DictCursor so we must convert back to a list
|
||||
# otherwise we get KeyError 0
|
||||
if isinstance(rows, dict):
|
||||
rows = list(rows.values())
|
||||
if len(rows) == 0:
|
||||
return []
|
||||
|
||||
# 'plugin_auth_string' contains the hash string. Must be removed in c.mysql 4.0
|
||||
# See https://github.com/ansible-collections/community.mysql/pull/629
|
||||
if isinstance(rows[0], tuple):
|
||||
return {'plugin': rows[0][0],
|
||||
'plugin_auth_string': rows[0][1],
|
||||
'plugin_hash_string': rows[0][1]}
|
||||
|
||||
# 'plugin_auth_string' contains the hash string. Must be removed in c.mysql 4.0
|
||||
# See https://github.com/ansible-collections/community.mysql/pull/629
|
||||
# Mysql_info use a DictCursor so we must convert list(dict)
|
||||
# to list(tuple) otherwise we get KeyError 0
|
||||
if isinstance(rows[0], dict):
|
||||
return {'plugin': rows[0].get('plugin'),
|
||||
'plugin_auth_string': rows[0].get('auth'),
|
||||
'plugin_hash_string': rows[0].get('auth')}
|
||||
return None
|
||||
rows = [tuple(row.values()) for row in rows]
|
||||
|
||||
existing_auth_list = []
|
||||
|
||||
# 'plugin_auth_string' contains the hash string. Must be removed in c.mysql 4.0
|
||||
# See https://github.com/ansible-collections/community.mysql/pull/629
|
||||
for r in rows:
|
||||
existing_auth_list.append({
|
||||
'plugin': r[0],
|
||||
'plugin_auth_string': r[1],
|
||||
'plugin_hash_string': r[1]})
|
||||
|
||||
return existing_auth_list
|
||||
|
||||
|
||||
def user_add(cursor, user, host, host_all, password, encrypted,
|
||||
|
@ -161,14 +178,24 @@ def user_add(cursor, user, host, host_all, password, encrypted,
|
|||
|
||||
mogrify = do_not_mogrify_requires if old_user_mgmt else mogrify_requires
|
||||
|
||||
# This is for update_password: on_new_username
|
||||
used_existing_password = False
|
||||
if reuse_existing_password:
|
||||
existing_auth = get_existing_authentication(cursor, user, host)
|
||||
existing_auth = get_existing_authentication(cursor, user)
|
||||
if existing_auth:
|
||||
plugin = existing_auth['plugin']
|
||||
plugin_hash_string = existing_auth['plugin_hash_string']
|
||||
password = None
|
||||
used_existing_password = True
|
||||
if len(existing_auth) != 1:
|
||||
module.warn("An account with the username %s has a different "
|
||||
"password than the others existing accounts. Thus "
|
||||
"on_new_username can't decide which password to "
|
||||
"reuse so it will use your provided password "
|
||||
"instead. If no password is provided, the account "
|
||||
"will have an empty password!" % user)
|
||||
used_existing_password = False
|
||||
else:
|
||||
plugin_hash_string = existing_auth[0]['plugin_hash_string']
|
||||
password = None
|
||||
used_existing_password = True
|
||||
plugin = existing_auth[0]['plugin'] # What if plugin differ?
|
||||
if password and encrypted:
|
||||
if impl.supports_identified_by_password(cursor):
|
||||
query_with_args = "CREATE USER %s@%s IDENTIFIED BY PASSWORD %s", (user, host, password)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue