1
0
Fork 0
mirror of https://github.com/ansible-collections/community.general.git synced 2024-09-14 20:13:21 +02:00

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 Matt Clay
parent b5a3dd4818
commit b9baed507b

View file

@ -38,9 +38,11 @@ options:
default: null default: null
password_hash: password_hash:
description: 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 required: false
default: null choices: [ "yes", "no" ]
default: "no"
version_added: "2.0"
host: host:
description: description:
- the 'host' part of the MySQL username - the 'host' part of the MySQL username
@ -124,6 +126,7 @@ notes:
without providing any login_user/login_password details. The second must drop a ~/.my.cnf file containing 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 new root credentials. Subsequent runs of the playbook will then succeed by reading the new credentials from
the file." the file."
- Currently, there is only support for the `mysql_native_password` encryted password hash module.
requirements: [ "MySQLdb" ] requirements: [ "MySQLdb" ]
author: "Ansible Core Team" author: "Ansible Core Team"
@ -133,6 +136,9 @@ EXAMPLES = """
# Create database user with name 'bob' and password '12345' with all database privileges # Create database user with name 'bob' and password '12345' with all database privileges
- mysql_user: name=bob password=12345 priv=*.*:ALL state=present - 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' # 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 - mysql_user: name=bob password=12345 priv=*.*:ALL,GRANT state=present
@ -209,16 +215,29 @@ def connect(module, login_user=None, login_password=None, config_file=''):
db_connection = MySQLdb.connect(**config) db_connection = MySQLdb.connect(**config)
return db_connection.cursor() 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): def user_exists(cursor, user, host):
cursor.execute("SELECT count(*) FROM user WHERE user = %s AND host = %s", (user,host)) cursor.execute("SELECT count(*) FROM user WHERE user = %s AND host = %s", (user,host))
count = cursor.fetchone() count = cursor.fetchone()
return count[0] > 0 return count[0] > 0
def user_add(cursor, user, host, password, password_hash, new_priv): def user_add(cursor, user, host, password, encrypted, new_priv):
if password and not password_hash: 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)) 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: if new_priv is not None:
for db_table, priv in new_priv.iteritems(): for db_table, priv in new_priv.iteritems():
privileges_grant(cursor, user,host,db_table,priv) privileges_grant(cursor, user,host,db_table,priv)
@ -226,36 +245,48 @@ def user_add(cursor, user, host, password, password_hash, new_priv):
def is_hash(password): def is_hash(password):
ishash = False 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 ishash = True
for i in password[1:]:
if i not in string.hexdigits:
ishash = False
break
return ishash return ishash
def user_mod(cursor, user, host, password, password_hash, new_priv, append_privs): def user_mod(cursor, user, host, password, password_hash, new_priv, append_privs):
changed = False changed = False
grant_option = False grant_option = False
# Handle passwords. # Handle clear text and hashed passwords.
if password is not None or password_hash is not None: 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)) 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() current_pass_hash = cursor.fetchone()
if password: if encrypted:
cursor.execute("SELECT PASSWORD(%s)", (password,)) if is_hash(password):
new_pass_hash = cursor.fetchone() if current_pass_hash[0] != encrypted:
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)) cursor.execute("SET PASSWORD FOR %s@%s = %s", (user, host, password))
changed = True else:
elif password_hash: cursor.execute("ALTER USER %s@%s IDENTIFIED WITH mysql_native_password AS %s", (user, host, password))
if is_hash(password_hash):
if current_pass_hash[0] != password_hash:
cursor.execute("SET PASSWORD FOR %s@%s = %s", (user, host, password_hash))
changed = True changed = True
else: 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 # Handle privileges
if new_priv is not None: if new_priv is not None:
@ -412,8 +443,8 @@ def main():
login_port=dict(default=3306, type='int'), login_port=dict(default=3306, type='int'),
login_unix_socket=dict(default=None), login_unix_socket=dict(default=None),
user=dict(required=True, aliases=['name']), user=dict(required=True, aliases=['name']),
password=dict(default=None), password=dict(default=None, no_log=True),
password_hash=dict(default=None), encrypted=dict(default=False, type='bool'),
host=dict(default="localhost"), host=dict(default="localhost"),
state=dict(default="present", choices=["absent", "present"]), state=dict(default="present", choices=["absent", "present"]),
priv=dict(default=None), priv=dict(default=None),
@ -427,8 +458,8 @@ def main():
login_password = module.params["login_password"] login_password = module.params["login_password"]
user = module.params["user"] user = module.params["user"]
password = module.params["password"] password = module.params["password"]
password_hash = module.params["password_hash"] encrypted = module.boolean(module.params["encrypted"])
host = module.params["host"] host = module.params["host"].lower()
state = module.params["state"] state = module.params["state"]
priv = module.params["priv"] priv = module.params["priv"]
check_implicit_admin = module.params['check_implicit_admin'] check_implicit_admin = module.params['check_implicit_admin']
@ -462,9 +493,12 @@ def main():
if user_exists(cursor, user, host): if user_exists(cursor, user, host):
changed = user_mod(cursor, user, host, password, password_hash, priv, append_privs) changed = user_mod(cursor, user, host, password, password_hash, priv, append_privs)
else: else:
if password is None and password_hash is None: if password is None:
module.fail_json(msg="password or password_hash parameter required when adding a user") module.fail_json(msg="password parameter required when adding a user")
changed = user_add(cursor, user, host, password, password_hash, priv) 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": elif state == "absent":
if user_exists(cursor, user, host): if user_exists(cursor, user, host):
changed = user_delete(cursor, user, host) changed = user_delete(cursor, user, host)