updated examples

added mysql 5.7 user password modification support with backwards compatibility

resolved mysql server version check and differences in user authentication management

explicitly state support for mysql_native_password type and no others. fixed some failing logic and updated samples

updated comment to actually match logic.

simplified conditionals and a little refactor
This commit is contained in:
Derek Smith 2015-06-23 15:57:18 -05:00 committed by Jonathan Mainguy
parent c007cd7f9a
commit aba519868f

View file

@ -37,9 +37,11 @@ options:
default: null
password_hash:
description:
- set the user's password hash (used in place of plain text password)
- Indicate that the 'password' field is a `mysql_native_password` hash
required: false
default: null
choices: [ "yes", "no" ]
default: "no"
version_added: "2.0"
host:
description:
- the 'host' part of the MySQL username
@ -123,6 +125,7 @@ notes:
without providing any login_user/login_password details. The second must drop a ~/.my.cnf file containing
the new root credentials. Subsequent runs of the playbook will then succeed by reading the new credentials from
the file."
- Currently, there is only support for the `mysql_native_password` encryted password hash module.
requirements: [ "MySQLdb" ]
author: "Ansible Core Team"
@ -132,6 +135,9 @@ EXAMPLES = """
# Create database user with name 'bob' and password '12345' with all database privileges
- mysql_user: name=bob password=12345 priv=*.*:ALL state=present
# Create database user with name 'bob' and previously hashed mysql native password '*EE0D72C1085C46C5278932678FBE2C6A782821B4' with all database privileges
- mysql_user: name=bob password='*EE0D72C1085C46C5278932678FBE2C6A782821B4' encrypted=yes priv=*.*:ALL state=present
# Creates database user 'bob' and password '12345' with all database privileges and 'WITH GRANT OPTION'
- mysql_user: name=bob password=12345 priv=*.*:ALL,GRANT state=present
@ -212,16 +218,29 @@ def connect(module, login_user=None, login_password=None, config_file=''):
db_connection = MySQLdb.connect(**config)
return db_connection.cursor()
# User Authentication Management was change in MySQL 5.7
# This is a generic check for if the server version is less than version 5.7
def server_version_check(cursor):
cursor.execute("SELECT VERSION()");
result = cursor.fetchone()
version_str = result[0]
version = version_str.split('.')
if (int(version[0]) <= 5 and int(version[1]) < 7):
return True
else:
return False
def user_exists(cursor, user, host):
cursor.execute("SELECT count(*) FROM user WHERE user = %s AND host = %s", (user,host))
count = cursor.fetchone()
return count[0] > 0
def user_add(cursor, user, host, password, password_hash, new_priv):
if password and not password_hash:
def user_add(cursor, user, host, password, encrypted, new_priv):
if password and encrypted:
cursor.execute("CREATE USER %s@%s IDENTIFIED BY PASSWORD %s", (user,host,password))
elif password and not encrypted:
cursor.execute("CREATE USER %s@%s IDENTIFIED BY %s", (user,host,password))
elif password_hash:
cursor.execute("CREATE USER %s@%s IDENTIFIED BY PASSWORD %s", (user,host,password_hash))
if new_priv is not None:
for db_table, priv in new_priv.iteritems():
privileges_grant(cursor, user,host,db_table,priv)
@ -229,36 +248,48 @@ def user_add(cursor, user, host, password, password_hash, new_priv):
def is_hash(password):
ishash = False
if len(password) is 41 and password[0] is '*':
if len(password) == 41 and password[0] == '*':
if frozenset(password[1:]).issubset(string.hexdigits):
ishash = True
for i in password[1:]:
if i not in string.hexdigits:
ishash = False
break
return ishash
def user_mod(cursor, user, host, password, password_hash, new_priv, append_privs):
changed = False
grant_option = False
# Handle passwords.
if password is not None or password_hash is not None:
# Handle clear text and hashed passwords.
if bool(password):
# Determine what user management method server uses
old_user_mgmt = server_version_check(cursor)
if old_user_mgmt:
cursor.execute("SELECT password FROM user WHERE user = %s AND host = %s", (user,host))
else:
cursor.execute("SELECT authentication_string FROM user WHERE user = %s AND host = %s", (user,host))
current_pass_hash = cursor.fetchone()
if password:
cursor.execute("SELECT PASSWORD(%s)", (password,))
new_pass_hash = cursor.fetchone()
if current_pass_hash[0] != new_pass_hash[0]:
cursor.execute("SET PASSWORD FOR %s@%s = PASSWORD(%s)", (user,host,password))
changed = True
elif password_hash:
if is_hash(password_hash):
if current_pass_hash[0] != password_hash:
cursor.execute("SET PASSWORD FOR %s@%s = %s", (user, host, password_hash))
if encrypted:
if is_hash(password):
if current_pass_hash[0] != encrypted:
if old_user_mgmt:
cursor.execute("SET PASSWORD FOR %s@%s = %s", (user, host, password))
else:
cursor.execute("ALTER USER %s@%s IDENTIFIED WITH mysql_native_password AS %s", (user, host, password))
changed = True
else:
module.fail_json(msg="password_hash was specified however it does not appear to be a valid hash expecting: *SHA1(SHA1(your_password))")
module.fail_json(msg="encrypted was specified however it does not appear to be a valid hash expecting: *SHA1(SHA1(your_password))")
else:
if old_user_mgmt:
cursor.execute("SELECT PASSWORD(%s)", (password,))
else:
cursor.execute("SELECT CONCAT('*', UCASE(SHA1(UNHEX(SHA1(%s)))))", (password,))
new_pass_hash = cursor.fetchone()
if current_pass_hash[0] != new_pass_hash[0]:
if old_user_mgmt:
cursor.execute("SET PASSWORD FOR %s@%s = PASSWORD(%s)", (user, host, password))
else:
cursor.execute("ALTER USER %s@%s IDENTIFIED BY %s", (user, host, password))
changed = True
# Handle privileges
if new_priv is not None:
@ -415,8 +446,8 @@ def main():
login_port=dict(default=3306, type='int'),
login_unix_socket=dict(default=None),
user=dict(required=True, aliases=['name']),
password=dict(default=None),
password_hash=dict(default=None),
password=dict(default=None, no_log=True),
encrypted=dict(default=False, type='bool'),
host=dict(default="localhost"),
state=dict(default="present", choices=["absent", "present"]),
priv=dict(default=None),
@ -430,8 +461,8 @@ def main():
login_password = module.params["login_password"]
user = module.params["user"]
password = module.params["password"]
password_hash = module.params["password_hash"]
host = module.params["host"]
encrypted = module.boolean(module.params["encrypted"])
host = module.params["host"].lower()
state = module.params["state"]
priv = module.params["priv"]
check_implicit_admin = module.params['check_implicit_admin']
@ -466,9 +497,12 @@ def main():
if user_exists(cursor, user, host):
changed = user_mod(cursor, user, host, password, password_hash, priv, append_privs)
else:
if password is None and password_hash is None:
module.fail_json(msg="password or password_hash parameter required when adding a user")
changed = user_add(cursor, user, host, password, password_hash, priv)
if password is None:
module.fail_json(msg="password parameter required when adding a user")
try:
changed = user_add(cursor, user, host, password, encrypted, priv)
except (SQLParseError, InvalidPrivsError, MySQLdb.Error), e:
module.fail_json(msg=str(e))
elif state == "absent":
if user_exists(cursor, user, host):
changed = user_delete(cursor, user, host)