Skip to content

Commit

Permalink
cut option maria_role as it as nothing to do with roles
Browse files Browse the repository at this point in the history
This was introduced in #189. To my knowledge, there is no difference
between MySQL and MariaDB regarding roles or when you call a user by
its name alone. Both works if the host it '%'. Same for roles.
  • Loading branch information
laurent-indermuehle committed Oct 11, 2023
1 parent 033b4c7 commit a7ea113
Show file tree
Hide file tree
Showing 2 changed files with 30 additions and 45 deletions.
70 changes: 28 additions & 42 deletions plugins/module_utils/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ def is_hash(password):

def user_mod(cursor, user, host, host_all, password, encrypted,
plugin, plugin_hash_string, plugin_auth_string, new_priv,
append_privs, subtract_privs, tls_requires, module, role=False, maria_role=False):
append_privs, subtract_privs, tls_requires, module, role=False):
changed = False
msg = "User unchanged"
grant_option = False
Expand Down Expand Up @@ -323,7 +323,7 @@ def user_mod(cursor, user, host, host_all, password, encrypted,

# Handle privileges
if new_priv is not None:
curr_priv = privileges_get(cursor, user, host, maria_role)
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.
Expand All @@ -337,7 +337,7 @@ def user_mod(cursor, user, host, host_all, password, encrypted,
msg = "Privileges updated"
if module.check_mode:
return {'changed': True, 'msg': msg, 'password_changed': password_changed}
privileges_revoke(cursor, user, host, db_table, priv, grant_option, maria_role)
privileges_revoke(cursor, user, host, db_table, priv, grant_option)
changed = True

# If the user doesn't currently have any privileges on a db.table, then
Expand All @@ -348,7 +348,7 @@ def user_mod(cursor, user, host, host_all, password, encrypted,
msg = "New privileges granted"
if module.check_mode:
return {'changed': True, 'msg': msg, 'password_changed': password_changed}
privileges_grant(cursor, user, host, db_table, priv, tls_requires, maria_role)
privileges_grant(cursor, user, host, db_table, priv, tls_requires)
changed = True

# If the db.table specification exists in both the user's current privileges
Expand Down Expand Up @@ -390,12 +390,12 @@ def user_mod(cursor, user, host, host_all, password, encrypted,
if module.check_mode:
return {'changed': True, 'msg': msg, 'password_changed': password_changed}
if len(revoke_privs) > 0:
privileges_revoke(cursor, user, host, db_table, revoke_privs, grant_option, maria_role)
privileges_revoke(cursor, user, host, db_table, revoke_privs, grant_option)
if len(grant_privs) > 0:
privileges_grant(cursor, user, host, db_table, grant_privs, tls_requires, maria_role)
privileges_grant(cursor, user, host, db_table, grant_privs, tls_requires)

# after privilege manipulation, compare privileges from before and now
after_priv = privileges_get(cursor, user, host, maria_role)
after_priv = privileges_get(cursor, user, host)
changed = changed or (curr_priv != after_priv)

if role:
Expand Down Expand Up @@ -454,7 +454,7 @@ def user_get_hostnames(cursor, user):
return hostnames


def privileges_get(cursor, user, host, maria_role=False):
def privileges_get(cursor, user, host):
""" MySQL doesn't have a better method of getting privileges aside from the
SHOW GRANTS query syntax, which requires us to then parse the returned string.
Here's an example of the string that is returned from MySQL:
Expand All @@ -465,10 +465,8 @@ def privileges_get(cursor, user, host, maria_role=False):
The dictionary format is the same as that returned by privileges_unpack() below.
"""
output = {}
if not maria_role:
cursor.execute("SHOW GRANTS FOR %s@%s", (user, host))
else:
cursor.execute("SHOW GRANTS FOR %s", (user,))
query = "SHOW GRANTS FOR '%s'@'%s'" % (user, host)
cursor.execute(query)
grants = cursor.fetchall()

def pick(x):
Expand All @@ -478,10 +476,10 @@ def pick(x):
return x

for grant in grants:
if not maria_role:
if get_server_type(cursor) == 'mariadb':
res = re.match("""GRANT (.+) ON (.+) TO (['`"]).*\\3@(['`"]).*\\4( IDENTIFIED BY PASSWORD (['`"]).+\\6)? ?(.*)""", grant[0])
else:
res = re.match("""GRANT (.+) ON (.+) TO (['`"]).*\\3""", grant[0])
res = re.match("""GRANT (.+) ON (.+) TO (['`"]).*\\3 ?(.*)""", grant[0])

if res is None:
# If a user has roles assigned, we'll have one of priv tuples looking like
Expand All @@ -490,7 +488,7 @@ def pick(x):
# As we use the mysql_role module to manipulate roles
# we just ignore such privs below:
res = re.match("""GRANT (.+) TO (['`"]).*""", grant[0])
if not maria_role and res:
if res:
continue

raise InvalidPrivsError('unable to parse the MySQL grant string: %s' % grant[0])
Expand All @@ -505,10 +503,14 @@ def pick(x):
# Determine if there's a case similar to the above:
privileges = normalize_col_grants(privileges)

if not maria_role:
if get_server_type(cursor) == 'mariadb':
if "WITH GRANT OPTION" in res.group(7):
privileges.append('GRANT')
else:
if "WITH GRANT OPTION" in res.group(4):
privileges.append('GRANT')
db = res.group(2)

output.setdefault(db, []).extend(privileges)
return output

Expand Down Expand Up @@ -684,48 +686,33 @@ def privileges_unpack(priv, mode, column_case_sensitive, ensure_usage=True):
return output


def privileges_revoke(cursor, user, host, db_table, priv, grant_option, maria_role=False):
def privileges_revoke(cursor, user, host, db_table, priv, grant_option):
# Escape '%' since mysql db.execute() uses a format string
db_table = db_table.replace('%', '%%')
if grant_option:
query = ["REVOKE GRANT OPTION ON %s" % db_table]
if not maria_role:
query.append("FROM %s@%s")
else:
query.append("FROM %s")

query.append("FROM %s@%s")
query = ' '.join(query)
cursor.execute(query, (user, host))
priv_string = ",".join([p for p in priv if p not in ('GRANT', )])

if priv_string != "":
query = ["REVOKE %s ON %s" % (priv_string, db_table)]

if not maria_role:
query.append("FROM %s@%s")
params = (user, host)
else:
query.append("FROM %s")
params = (user,)

query.append("FROM %s@%s")
query = ' '.join(query)
cursor.execute(query, params)
cursor.execute(query, (user, host))
cursor.execute("FLUSH PRIVILEGES")


def privileges_grant(cursor, user, host, db_table, priv, tls_requires, maria_role=False):
def privileges_grant(cursor, user, host, db_table, priv, tls_requires):
# Escape '%' since mysql db.execute uses a format string and the
# specification of db and table often use a % (SQL wildcard)
db_table = db_table.replace('%', '%%')
priv_string = ",".join([p for p in priv if p not in ('GRANT', )])
query = ["GRANT %s ON %s" % (priv_string, db_table)]

if not maria_role:
query.append("TO %s@%s")
params = (user, host)
else:
query.append("TO %s")
params = (user)
query.append("TO %s@%s")
params = (user, host)

if tls_requires and impl.use_old_user_mgmt(cursor):
query, params = mogrify_requires(" ".join(query), params, tls_requires)
Expand Down Expand Up @@ -851,8 +838,7 @@ def limit_resources(module, cursor, user, host, resource_limits, check_mode):
module.fail_json(msg="The server version does not match the requirements "
"for resource_limits parameter. See module's documentation.")

cursor.execute("SELECT VERSION()")
if 'mariadb' not in cursor.fetchone()[0].lower():
if get_server_type(cursor) != 'mariadb':
if 'MAX_STATEMENT_TIME' in resource_limits:
module.fail_json(msg="MAX_STATEMENT_TIME resource limit is only supported by MariaDB.")

Expand All @@ -879,8 +865,8 @@ def limit_resources(module, cursor, user, host, resource_limits, check_mode):

def get_impl(cursor):
global impl
cursor.execute("SELECT VERSION()")
if 'mariadb' in cursor.fetchone()[0].lower():

if get_server_type(cursor) == 'mariadb':
from ansible_collections.community.mysql.plugins.module_utils.implementations.mariadb import user as mariauser
impl = mariauser
else:
Expand Down
5 changes: 2 additions & 3 deletions plugins/modules/mysql_role.py
Original file line number Diff line number Diff line change
Expand Up @@ -789,8 +789,7 @@ def add(self, users, privs, check_mode=False, admin=False,
if privs:
for db_table, priv in iteritems(privs):
privileges_grant(self.cursor, self.name, self.host,
db_table, priv, tls_requires=None,
maria_role=self.is_mariadb)
db_table, priv, tls_requires=None)

return True

Expand Down Expand Up @@ -932,7 +931,7 @@ def update(self, users, privs, check_mode=False,
result = user_mod(self.cursor, self.name, self.host,
None, None, None, None, None, None,
privs, append_privs, subtract_privs, None,
self.module, role=True, maria_role=self.is_mariadb)
self.module, role=True)
changed = result['changed']

if admin:
Expand Down

0 comments on commit a7ea113

Please sign in to comment.