mirror of
https://github.com/ansible-collections/community.mysql.git
synced 2025-08-29 01:11:46 -07:00
add option subtract_privs to mysql_role and mysql_user
see https://github.com/ansible-collections/community.mysql/issues/331
This commit is contained in:
parent
641894e6e8
commit
3dc21216cb
3 changed files with 83 additions and 36 deletions
|
@ -169,7 +169,7 @@ def is_hash(password):
|
||||||
|
|
||||||
def user_mod(cursor, user, host, host_all, password, encrypted,
|
def user_mod(cursor, user, host, host_all, password, encrypted,
|
||||||
plugin, plugin_hash_string, plugin_auth_string, new_priv,
|
plugin, plugin_hash_string, plugin_auth_string, new_priv,
|
||||||
append_privs, tls_requires, module, role=False, maria_role=False):
|
append_privs, subtract_privs, tls_requires, module, role=False, maria_role=False):
|
||||||
changed = False
|
changed = False
|
||||||
msg = "User unchanged"
|
msg = "User unchanged"
|
||||||
grant_option = False
|
grant_option = False
|
||||||
|
@ -288,12 +288,13 @@ def user_mod(cursor, user, host, host_all, password, encrypted,
|
||||||
|
|
||||||
# If the user has privileges on a db.table that doesn't appear at all in
|
# 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.
|
# the new specification, then revoke all privileges on it.
|
||||||
|
if not append_privs and not subtract_privs:
|
||||||
for db_table, priv in iteritems(curr_priv):
|
for db_table, priv in iteritems(curr_priv):
|
||||||
# If the user has the GRANT OPTION on a db.table, revoke it first.
|
# If the user has the GRANT OPTION on a db.table, revoke it first.
|
||||||
if "GRANT" in priv:
|
if "GRANT" in priv:
|
||||||
grant_option = True
|
grant_option = True
|
||||||
if db_table not in new_priv:
|
if db_table not in new_priv:
|
||||||
if user != "root" and "PROXY" not in priv and not append_privs:
|
if user != "root" and "PROXY" not in priv:
|
||||||
msg = "Privileges updated"
|
msg = "Privileges updated"
|
||||||
if module.check_mode:
|
if module.check_mode:
|
||||||
return (True, msg)
|
return (True, msg)
|
||||||
|
@ -302,6 +303,7 @@ def user_mod(cursor, user, host, host_all, password, encrypted,
|
||||||
|
|
||||||
# If the user doesn't currently have any privileges on a db.table, then
|
# If the user doesn't currently have any privileges on a db.table, then
|
||||||
# we can perform a straight grant operation.
|
# we can perform a straight grant operation.
|
||||||
|
if not subtract_privs:
|
||||||
for db_table, priv in iteritems(new_priv):
|
for db_table, priv in iteritems(new_priv):
|
||||||
if db_table not in curr_priv:
|
if db_table not in curr_priv:
|
||||||
msg = "New privileges granted"
|
msg = "New privileges granted"
|
||||||
|
@ -315,20 +317,29 @@ def user_mod(cursor, user, host, host_all, password, encrypted,
|
||||||
db_table_intersect = set(new_priv.keys()) & set(curr_priv.keys())
|
db_table_intersect = set(new_priv.keys()) & set(curr_priv.keys())
|
||||||
for db_table in db_table_intersect:
|
for db_table in db_table_intersect:
|
||||||
|
|
||||||
# If appending privileges, only the set difference between new privileges and current privileges matter.
|
grant_privs = []
|
||||||
# The symmetric difference isn't relevant for append because existing privileges will not be revoked.
|
revoke_privs = []
|
||||||
if append_privs:
|
if append_privs:
|
||||||
priv_diff = set(new_priv[db_table]) - set(curr_priv[db_table])
|
# When appending privileges, only missing privileges need to be granted. Nothing is revoked.
|
||||||
|
grant_privs = list(set(new_priv[db_table]) - set(curr_priv[db_table]))
|
||||||
|
elif subtract_privs:
|
||||||
|
# When subtracting privileges, revoke only the intersection of requested and current privileges.
|
||||||
|
# No privileges are granted.
|
||||||
|
revoke_privs = list(set(new_priv[db_table]) & set(curr_priv[db_table]))
|
||||||
else:
|
else:
|
||||||
priv_diff = set(new_priv[db_table]) ^ set(curr_priv[db_table])
|
# When replacing (neither append_privs nor subtract_privs), grant all missing privileges
|
||||||
|
# and revoke existing privileges that were not requested.
|
||||||
|
grant_privs = list(set(new_priv[db_table]) - set(curr_priv[db_table]))
|
||||||
|
revoke_privs = list(set(curr_priv[db_table]) - set(new_priv[db_table]))
|
||||||
|
|
||||||
if len(priv_diff) > 0:
|
if len(grant_privs) + len(revoke_privs) > 0:
|
||||||
msg = "Privileges updated"
|
msg = "Privileges updated"
|
||||||
if module.check_mode:
|
if module.check_mode:
|
||||||
return (True, msg)
|
return (True, msg)
|
||||||
if not append_privs:
|
if len(revoke_privs) > 0:
|
||||||
privileges_revoke(cursor, user, host, db_table, curr_priv[db_table], grant_option, maria_role)
|
privileges_revoke(cursor, user, host, db_table, revoke_privs, grant_option, maria_role)
|
||||||
privileges_grant(cursor, user, host, db_table, new_priv[db_table], tls_requires, maria_role)
|
if len(grant_privs) > 0:
|
||||||
|
privileges_grant(cursor, user, host, db_table, grant_privs, tls_requires, maria_role)
|
||||||
changed = True
|
changed = True
|
||||||
|
|
||||||
if role:
|
if role:
|
||||||
|
|
|
@ -51,7 +51,14 @@ options:
|
||||||
append_privs:
|
append_privs:
|
||||||
description:
|
description:
|
||||||
- Append the privileges defined by the I(priv) option to the existing ones
|
- Append the privileges defined by the I(priv) option to the existing ones
|
||||||
for this role instead of overwriting them.
|
for this role instead of overwriting them. Mutually exclusive with I(subtract_privs).
|
||||||
|
type: bool
|
||||||
|
default: no
|
||||||
|
|
||||||
|
subtract_privs:
|
||||||
|
description:
|
||||||
|
- Revoke the privileges defined by the I(priv) option and keep other existing privileges.
|
||||||
|
Mutually exclusive with I(append_privs).
|
||||||
type: bool
|
type: bool
|
||||||
default: no
|
default: no
|
||||||
|
|
||||||
|
@ -233,6 +240,14 @@ EXAMPLES = r'''
|
||||||
name: business
|
name: business
|
||||||
members:
|
members:
|
||||||
- marketing
|
- marketing
|
||||||
|
|
||||||
|
- name: Ensure the role foo does not have the DELETE privilege
|
||||||
|
community.mysql.mysql_role:
|
||||||
|
state: present
|
||||||
|
name: foo
|
||||||
|
subtract_privs: yes
|
||||||
|
priv:
|
||||||
|
'db1.*': DELETE
|
||||||
'''
|
'''
|
||||||
|
|
||||||
RETURN = '''#'''
|
RETURN = '''#'''
|
||||||
|
@ -821,9 +836,9 @@ class Role():
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def update(self, users, privs, check_mode=False,
|
def update(self, users, privs, check_mode=False,
|
||||||
append_privs=False, append_members=False,
|
append_privs=False, subtract_privs=False,
|
||||||
detach_members=False, admin=False,
|
append_members=False, detach_members=False,
|
||||||
set_default_role_all=True):
|
admin=False, set_default_role_all=True):
|
||||||
"""Update a role.
|
"""Update a role.
|
||||||
|
|
||||||
Update a role if needed.
|
Update a role if needed.
|
||||||
|
@ -837,6 +852,8 @@ class Role():
|
||||||
check_mode (bool): If True, just checks and does nothing.
|
check_mode (bool): If True, just checks and does nothing.
|
||||||
append_privs (bool): If True, adds new privileges passed through privs
|
append_privs (bool): If True, adds new privileges passed through privs
|
||||||
not touching current privileges.
|
not touching current privileges.
|
||||||
|
subtract_privs (bool): If True, revoke the privileges passed through privs
|
||||||
|
not touching other existing privileges.
|
||||||
append_members (bool): If True, adds new members passed through users
|
append_members (bool): If True, adds new members passed through users
|
||||||
not touching current members.
|
not touching current members.
|
||||||
detach_members (bool): If True, removes members passed through users from a role.
|
detach_members (bool): If True, removes members passed through users from a role.
|
||||||
|
@ -861,7 +878,7 @@ class Role():
|
||||||
if privs:
|
if privs:
|
||||||
changed, msg = user_mod(self.cursor, self.name, self.host,
|
changed, msg = user_mod(self.cursor, self.name, self.host,
|
||||||
None, None, None, None, None, None,
|
None, None, None, None, None, None,
|
||||||
privs, append_privs, None,
|
privs, append_privs, subtract_privs, None,
|
||||||
self.module, role=True, maria_role=self.is_mariadb)
|
self.module, role=True, maria_role=self.is_mariadb)
|
||||||
|
|
||||||
if admin:
|
if admin:
|
||||||
|
@ -931,6 +948,7 @@ def main():
|
||||||
admin=dict(type='str'),
|
admin=dict(type='str'),
|
||||||
priv=dict(type='raw'),
|
priv=dict(type='raw'),
|
||||||
append_privs=dict(type='bool', default=False),
|
append_privs=dict(type='bool', default=False),
|
||||||
|
subtract_privs=dict(type='bool', default=False),
|
||||||
members=dict(type='list', elements='str'),
|
members=dict(type='list', elements='str'),
|
||||||
append_members=dict(type='bool', default=False),
|
append_members=dict(type='bool', default=False),
|
||||||
detach_members=dict(type='bool', default=False),
|
detach_members=dict(type='bool', default=False),
|
||||||
|
@ -945,6 +963,7 @@ def main():
|
||||||
('admin', 'members'),
|
('admin', 'members'),
|
||||||
('admin', 'append_members'),
|
('admin', 'append_members'),
|
||||||
('admin', 'detach_members'),
|
('admin', 'detach_members'),
|
||||||
|
('append_privs', 'subtract_privs'),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -958,6 +977,7 @@ def main():
|
||||||
connect_timeout = module.params['connect_timeout']
|
connect_timeout = module.params['connect_timeout']
|
||||||
config_file = module.params['config_file']
|
config_file = module.params['config_file']
|
||||||
append_privs = module.params['append_privs']
|
append_privs = module.params['append_privs']
|
||||||
|
subtract_privs = module.boolean(module.params['subtract_privs'])
|
||||||
members = module.params['members']
|
members = module.params['members']
|
||||||
append_members = module.params['append_members']
|
append_members = module.params['append_members']
|
||||||
detach_members = module.params['detach_members']
|
detach_members = module.params['detach_members']
|
||||||
|
@ -1047,7 +1067,7 @@ def main():
|
||||||
set_default_role_all)
|
set_default_role_all)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
changed = role.update(members, priv, module.check_mode, append_privs,
|
changed = role.update(members, priv, module.check_mode, append_privs, subtract_privs,
|
||||||
append_members, detach_members, admin,
|
append_members, detach_members, admin,
|
||||||
set_default_role_all)
|
set_default_role_all)
|
||||||
|
|
||||||
|
|
|
@ -63,7 +63,13 @@ options:
|
||||||
append_privs:
|
append_privs:
|
||||||
description:
|
description:
|
||||||
- Append the privileges defined by priv to the existing ones for this
|
- Append the privileges defined by priv to the existing ones for this
|
||||||
user instead of overwriting existing ones.
|
user instead of overwriting existing ones. Mutually exclusive with I(subtract_privs).
|
||||||
|
type: bool
|
||||||
|
default: no
|
||||||
|
subtract_privs:
|
||||||
|
description:
|
||||||
|
- Revoke the privileges defined by the I(priv) option and keep other existing privileges.
|
||||||
|
Mutually exclusive with I(append_privs).
|
||||||
type: bool
|
type: bool
|
||||||
default: no
|
default: no
|
||||||
tls_requires:
|
tls_requires:
|
||||||
|
@ -306,6 +312,13 @@ EXAMPLES = r'''
|
||||||
MAX_QUERIES_PER_HOUR: 10
|
MAX_QUERIES_PER_HOUR: 10
|
||||||
MAX_CONNECTIONS_PER_HOUR: 5
|
MAX_CONNECTIONS_PER_HOUR: 5
|
||||||
|
|
||||||
|
- name: Ensure bob does not have the DELETE privilege
|
||||||
|
community.mysql.mysql_user:
|
||||||
|
name: bob
|
||||||
|
subtract_privs: yes
|
||||||
|
priv:
|
||||||
|
'db1.*': DELETE
|
||||||
|
|
||||||
# Example .my.cnf file for setting the root password
|
# Example .my.cnf file for setting the root password
|
||||||
# [client]
|
# [client]
|
||||||
# user=root
|
# user=root
|
||||||
|
@ -352,6 +365,7 @@ def main():
|
||||||
priv=dict(type='raw'),
|
priv=dict(type='raw'),
|
||||||
tls_requires=dict(type='dict'),
|
tls_requires=dict(type='dict'),
|
||||||
append_privs=dict(type='bool', default=False),
|
append_privs=dict(type='bool', default=False),
|
||||||
|
subtract_privs=dict(type='bool', default=False),
|
||||||
check_implicit_admin=dict(type='bool', default=False),
|
check_implicit_admin=dict(type='bool', default=False),
|
||||||
update_password=dict(type='str', default='always', choices=['always', 'on_create'], no_log=False),
|
update_password=dict(type='str', default='always', choices=['always', 'on_create'], no_log=False),
|
||||||
sql_log_bin=dict(type='bool', default=True),
|
sql_log_bin=dict(type='bool', default=True),
|
||||||
|
@ -364,6 +378,7 @@ def main():
|
||||||
module = AnsibleModule(
|
module = AnsibleModule(
|
||||||
argument_spec=argument_spec,
|
argument_spec=argument_spec,
|
||||||
supports_check_mode=True,
|
supports_check_mode=True,
|
||||||
|
mutually_exclusive=(('append_privs', 'subtract_privs'),)
|
||||||
)
|
)
|
||||||
login_user = module.params["login_user"]
|
login_user = module.params["login_user"]
|
||||||
login_password = module.params["login_password"]
|
login_password = module.params["login_password"]
|
||||||
|
@ -379,6 +394,7 @@ def main():
|
||||||
connect_timeout = module.params["connect_timeout"]
|
connect_timeout = module.params["connect_timeout"]
|
||||||
config_file = module.params["config_file"]
|
config_file = module.params["config_file"]
|
||||||
append_privs = module.boolean(module.params["append_privs"])
|
append_privs = module.boolean(module.params["append_privs"])
|
||||||
|
subtract_privs = module.boolean(module.params['subtract_privs'])
|
||||||
update_password = module.params['update_password']
|
update_password = module.params['update_password']
|
||||||
ssl_cert = module.params["client_cert"]
|
ssl_cert = module.params["client_cert"]
|
||||||
ssl_key = module.params["client_key"]
|
ssl_key = module.params["client_key"]
|
||||||
|
@ -435,11 +451,11 @@ def main():
|
||||||
if update_password == "always":
|
if update_password == "always":
|
||||||
changed, msg = user_mod(cursor, user, host, host_all, password, encrypted,
|
changed, msg = user_mod(cursor, user, host, host_all, password, encrypted,
|
||||||
plugin, plugin_hash_string, plugin_auth_string,
|
plugin, plugin_hash_string, plugin_auth_string,
|
||||||
priv, append_privs, tls_requires, module)
|
priv, append_privs, subtract_privs, tls_requires, module)
|
||||||
else:
|
else:
|
||||||
changed, msg = user_mod(cursor, user, host, host_all, None, encrypted,
|
changed, msg = user_mod(cursor, user, host, host_all, None, encrypted,
|
||||||
plugin, plugin_hash_string, plugin_auth_string,
|
plugin, plugin_hash_string, plugin_auth_string,
|
||||||
priv, append_privs, tls_requires, module)
|
priv, append_privs, subtract_privs, tls_requires, module)
|
||||||
|
|
||||||
except (SQLParseError, InvalidPrivsError, mysql_driver.Error) as e:
|
except (SQLParseError, InvalidPrivsError, mysql_driver.Error) as e:
|
||||||
module.fail_json(msg=to_native(e))
|
module.fail_json(msg=to_native(e))
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue