commit
e6b7b17326
1 changed files with 78 additions and 15 deletions
|
@ -35,6 +35,13 @@ options:
|
||||||
- set the user's password. (Required when adding a user)
|
- set the user's password. (Required when adding a user)
|
||||||
required: false
|
required: false
|
||||||
default: null
|
default: null
|
||||||
|
encrypted:
|
||||||
|
description:
|
||||||
|
- Indicate that the 'password' field is a `mysql_native_password` hash
|
||||||
|
required: false
|
||||||
|
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
|
||||||
|
@ -118,15 +125,19 @@ 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: "Jonathan Mainguy (@Jmainguy)"
|
||||||
'''
|
'''
|
||||||
|
|
||||||
EXAMPLES = """
|
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
|
||||||
|
|
||||||
|
@ -158,6 +169,7 @@ password=n<_665{vS43y
|
||||||
import getpass
|
import getpass
|
||||||
import tempfile
|
import tempfile
|
||||||
import re
|
import re
|
||||||
|
import string
|
||||||
try:
|
try:
|
||||||
import MySQLdb
|
import MySQLdb
|
||||||
except ImportError:
|
except ImportError:
|
||||||
|
@ -206,31 +218,80 @@ 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, new_priv):
|
def user_add(cursor, user, host, password, encrypted, new_priv):
|
||||||
cursor.execute("CREATE USER %s@%s IDENTIFIED BY %s", (user,host,password))
|
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))
|
||||||
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)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def user_mod(cursor, user, host, password, new_priv, append_privs):
|
def is_hash(password):
|
||||||
|
ishash = False
|
||||||
|
if len(password) == 41 and password[0] == '*':
|
||||||
|
if frozenset(password[1:]).issubset(string.hexdigits):
|
||||||
|
ishash = True
|
||||||
|
return ishash
|
||||||
|
|
||||||
|
def user_mod(cursor, user, host, password, encrypted, 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:
|
if bool(password):
|
||||||
cursor.execute("SELECT password FROM user WHERE user = %s AND host = %s", (user,host))
|
# 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()
|
current_pass_hash = cursor.fetchone()
|
||||||
cursor.execute("SELECT PASSWORD(%s)", (password,))
|
|
||||||
new_pass_hash = cursor.fetchone()
|
if encrypted:
|
||||||
if current_pass_hash[0] != new_pass_hash[0]:
|
encrypted_string = (password)
|
||||||
cursor.execute("SET PASSWORD FOR %s@%s = PASSWORD(%s)", (user,host,password))
|
if is_hash(password):
|
||||||
changed = True
|
if current_pass_hash[0] != encrypted_string:
|
||||||
|
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="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:
|
||||||
|
@ -388,6 +449,7 @@ def main():
|
||||||
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, no_log=True),
|
password=dict(default=None, no_log=True),
|
||||||
|
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),
|
||||||
|
@ -401,6 +463,7 @@ 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"]
|
||||||
|
encrypted = module.boolean(module.params["encrypted"])
|
||||||
host = module.params["host"].lower()
|
host = module.params["host"].lower()
|
||||||
state = module.params["state"]
|
state = module.params["state"]
|
||||||
priv = module.params["priv"]
|
priv = module.params["priv"]
|
||||||
|
@ -436,9 +499,9 @@ def main():
|
||||||
if user_exists(cursor, user, host):
|
if user_exists(cursor, user, host):
|
||||||
try:
|
try:
|
||||||
if update_password == 'always':
|
if update_password == 'always':
|
||||||
changed = user_mod(cursor, user, host, password, priv, append_privs)
|
changed = user_mod(cursor, user, host, password, encrypted, priv, append_privs)
|
||||||
else:
|
else:
|
||||||
changed = user_mod(cursor, user, host, None, priv, append_privs)
|
changed = user_mod(cursor, user, host, None, encrypted, priv, append_privs)
|
||||||
|
|
||||||
except (SQLParseError, InvalidPrivsError, MySQLdb.Error), e:
|
except (SQLParseError, InvalidPrivsError, MySQLdb.Error), e:
|
||||||
module.fail_json(msg=str(e))
|
module.fail_json(msg=str(e))
|
||||||
|
@ -446,7 +509,7 @@ def main():
|
||||||
if password is None:
|
if password is None:
|
||||||
module.fail_json(msg="password parameter required when adding a user")
|
module.fail_json(msg="password parameter required when adding a user")
|
||||||
try:
|
try:
|
||||||
changed = user_add(cursor, user, host, password, priv)
|
changed = user_add(cursor, user, host, password, encrypted, priv)
|
||||||
except (SQLParseError, InvalidPrivsError, MySQLdb.Error), e:
|
except (SQLParseError, InvalidPrivsError, MySQLdb.Error), e:
|
||||||
module.fail_json(msg=str(e))
|
module.fail_json(msg=str(e))
|
||||||
elif state == "absent":
|
elif state == "absent":
|
||||||
|
|
Loading…
Reference in a new issue