From 9d0fc0fd67b3b86522b83a71f57d3fa3f13d8b20 Mon Sep 17 00:00:00 2001 From: Stijn Tintel Date: Tue, 23 Jul 2013 13:00:29 +0200 Subject: [PATCH] Only revoke GRANT OPTION when user actually has it When revoking privileges from a user, the GRANT OPTION is always revoked, even if the user doesn't have it. If the user exists, this doesn't give an error, but if the user doesn't exist, it does: mysql> GRANT ALL ON test.* TO 'test'@'localhost'; Query OK, 0 rows affected (0.00 sec) mysql> REVOKE GRANT OPTION ON test.* FROM 'test'@'localhost'; Query OK, 0 rows affected (0.00 sec) mysql> REVOKE GRANT OPTION ON test.* FROM 'test'@'localhost'; Query OK, 0 rows affected (0.00 sec) mysql> REVOKE ALL ON test.* FROM 'test'@'localhost'; Query OK, 0 rows affected (0.00 sec) mysql> REVOKE GRANT OPTION ON test.* FROM 'test'@'localhost'; ERROR 1141 (42000): There is no such grant defined for user 'test' on host 'localhost' Additionally, in MySQL 5.6 this breaks replication because of http://bugs.mysql.com/bug.php?id=68892. Rather than revoking the GRANT OPTION and catching the error, check if the user actually has it and only revoke it when he does. --- library/database/mysql_user | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/library/database/mysql_user b/library/database/mysql_user index bff1e4e74f..85551b58cf 100644 --- a/library/database/mysql_user +++ b/library/database/mysql_user @@ -144,6 +144,7 @@ def user_add(cursor, user, host, password, new_priv): def user_mod(cursor, user, host, password, new_priv): changed = False + grant_option = False # Handle passwords. if password is not None: @@ -162,9 +163,12 @@ def user_mod(cursor, user, host, password, new_priv): # 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 if db_table not in new_priv: if user != "root" and "PROXY" not in priv: - privileges_revoke(cursor, user,host,db_table) + privileges_revoke(cursor, user,host,db_table,grant_option) changed = True # If the user doesn't currently have any privileges on a db.table, then @@ -180,7 +184,7 @@ def user_mod(cursor, user, host, password, new_priv): for db_table in db_table_intersect: priv_diff = set(new_priv[db_table]) ^ set(curr_priv[db_table]) if (len(priv_diff) > 0): - privileges_revoke(cursor, user,host,db_table) + privileges_revoke(cursor, user,host,db_table,grant_option) privileges_grant(cursor, user,host,db_table,new_priv[db_table]) changed = True @@ -243,17 +247,12 @@ def privileges_unpack(priv): return output -def privileges_revoke(cursor, user,host,db_table): +def privileges_revoke(cursor, user,host,db_table,grant_option): + if grant_option: + query = "REVOKE GRANT OPTION ON %s FROM '%s'@'%s'" % (db_table,user,host) + cursor.execute(query) query = "REVOKE ALL PRIVILEGES ON %s FROM '%s'@'%s'" % (db_table,user,host) cursor.execute(query) - query = "REVOKE GRANT OPTION ON %s FROM '%s'@'%s'" % (db_table,user,host) - try: - cursor.execute(query) - except MySQLdb.OperationalError, e: - # 1141 -> There is no such grant defined for user ... on host ... - # If this exception is raised, there is no need to revoke the GRANT privilege - if e.args[0] != 1141 or not e.args[1].startswith("There is no such grant defined for user"): - raise e def privileges_grant(cursor, user,host,db_table,priv):