summaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authorGuilhem Moulin <guilhem@fripost.org>2013-11-25 23:59:15 +0100
committerGuilhem Moulin <guilhem@fripost.org>2015-06-07 02:50:49 +0200
commit98f094ac5f59ad223f2dd930fc70c36059d631c8 (patch)
treea9642a4cc85604dfa6810dd4cee38d498683c89d /lib
parent652b443f210abef9cd1e6d01799533761bd4d484 (diff)
Add support for MySQL's Authentication Plugins.
A.k.a "IDENTIFIED WITH ...". The plugin is automatically loaded on first use. References: - https://dev.mysql.com/doc/refman/5.5/en/pluggable-authentication.html - https://dev.mysql.com/doc/refman/5.5/en/socket-authentication-plugin.html Sadly as of MySQL 5.5, the "ALTER USER" command does not allow changing the Authentication Plugin, so we have to manually manipulate `mysql.user` (and FLUSH PRIVILEGES) instead. See also http://bugs.mysql.com/bug.php?id=67449
Diffstat (limited to 'lib')
-rw-r--r--lib/mysql_user39
1 files changed, 32 insertions, 7 deletions
diff --git a/lib/mysql_user b/lib/mysql_user
index 9758939..602235a 100644
--- a/lib/mysql_user
+++ b/lib/mysql_user
@@ -134,51 +134,74 @@ password=n<_665{vS43y
import ConfigParser
import getpass
import tempfile
try:
import MySQLdb
except ImportError:
mysqldb_found = False
else:
mysqldb_found = True
# ===========================================
# MySQL module specific support methods.
#
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, new_priv):
- cursor.execute("CREATE USER %s@%s IDENTIFIED BY %s", (user,host,password))
+def load_plugin(cursor, plugin):
+ cursor.execute("SELECT count(*) FROM plugin WHERE name = %s", plugin)
+ count = cursor.fetchone()
+ if count[0] == 0:
+ so = "%s.so" % plugin
+ cursor.execute("INSTALL PLUGIN %s SONAME %s", (plugin, so))
+
+def user_add(cursor, user, host, password, new_priv, auth_plugin):
+ if password is None:
+ # Automatically loaded on first first use.
+ load_plugin(cursor, auth_plugin)
+ cursor.execute("CREATE USER %s@%s IDENTIFIED WITH %s", (user,host,auth_plugin))
+ else:
+ cursor.execute("CREATE USER %s@%s IDENTIFIED BY %s", (user,host,password))
if new_priv is not None:
for db_table, priv in new_priv.iteritems():
privileges_grant(cursor, user,host,db_table,priv)
return True
-def user_mod(cursor, user, host, password, new_priv, append_privs):
+def user_mod(cursor, user, host, password, new_priv, append_privs, auth_plugin):
changed = False
grant_option = False
+ # Handle plugin.
+ if auth_plugin is not None:
+ cursor.execute("SELECT plugin FROM user WHERE user = %s AND host = %s", (user,host))
+ if cursor.fetchone()[0] != auth_plugin:
+ # Sadly there is no proper way to updade the authentication plugin:
+ # http://bugs.mysql.com/bug.php?id=67449
+ cursor.execute( "UPDATE user SET plugin = %s, password = '' WHERE user = %s AND host = %s"
+ , (auth_plugin,user,host))
+ cursor.execute("FLUSH PRIVILEGES")
+ changed = True
+
# Handle passwords.
if password is not None:
cursor.execute("SELECT password FROM user WHERE user = %s AND host = %s", (user,host))
current_pass_hash = cursor.fetchone()
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
# Handle privileges.
if new_priv is not None:
curr_priv = privileges_get(cursor, user,host)
# If the user has privileges on a db.table that doesn't appear at all in
# the new specification, then revoke all privileges on it.
for db_table, priv in curr_priv.iteritems():
# If the user has the GRANT OPTION on a db.table, revoke it first.
if "GRANT" in priv:
grant_option = True
@@ -382,49 +405,51 @@ def connect(module, login_user, login_password):
# ===========================================
# Module execution.
#
def main():
module = AnsibleModule(
argument_spec = dict(
login_user=dict(default=None),
login_password=dict(default=None),
login_host=dict(default="localhost"),
login_port=dict(default="3306"),
login_unix_socket=dict(default=None),
user=dict(required=True, aliases=['name']),
password=dict(default=None),
host=dict(default="localhost"),
state=dict(default="present", choices=["absent", "present"]),
priv=dict(default=None),
append_privs=dict(type="bool", default="no"),
check_implicit_admin=dict(default=False),
+ auth_plugin=dict(default=None)
)
)
user = module.params["user"]
password = module.params["password"]
host = module.params["host"]
state = module.params["state"]
priv = module.params["priv"]
check_implicit_admin = module.params['check_implicit_admin']
append_privs = module.boolean(module.params["append_privs"])
+ auth_plugin = module.params['auth_plugin']
if not mysqldb_found:
module.fail_json(msg="the python mysqldb module is required")
if priv is not None:
try:
priv = privileges_unpack(priv)
except:
module.fail_json(msg="invalid privileges string")
# Either the caller passes both a username and password with which to connect to
# mysql, or they pass neither and allow this module to read the credentials from
# ~/.my.cnf.
login_password = module.params["login_password"]
login_user = module.params["login_user"]
if login_user is None and login_password is None:
mycnf_creds = load_mycnf()
if mycnf_creds is False:
login_user = "root"
login_password = ""
@@ -432,35 +457,35 @@ def main():
login_user = mycnf_creds["user"]
login_password = mycnf_creds["passwd"]
elif login_password is None or login_user is None:
module.fail_json(msg="when supplying login arguments, both login_user and login_password must be provided")
cursor = None
try:
if check_implicit_admin:
try:
cursor = connect(module, 'root', '')
except:
pass
if not cursor:
cursor = connect(module, login_user, login_password)
except Exception, e:
module.fail_json(msg="unable to connect to database, check login_user and login_password are correct or ~/.my.cnf has the credentials")
if state == "present":
if user_exists(cursor, user, host):
- changed = user_mod(cursor, user, host, password, priv, append_privs)
+ changed = user_mod(cursor, user, host, password, priv, append_privs, auth_plugin)
else:
- if password is None:
- module.fail_json(msg="password parameter required when adding a user")
- changed = user_add(cursor, user, host, password, priv)
+ if (password is None and auth_plugin is None) or (password is not None and auth_plugin is not None):
+ module.fail_json(msg="password xor auth_plugin is required when adding a user")
+ changed = user_add(cursor, user, host, password, priv, auth_plugin)
elif state == "absent":
if user_exists(cursor, user, host):
changed = user_delete(cursor, user, host)
else:
changed = False
module.exit_json(changed=changed, user=user)
# this is magic, see lib/ansible/module_common.py
#<<INCLUDE_ANSIBLE_MODULE_COMMON>>
main()