Merge pull request #51152 from garethgreenaway/50433_handle_grants_better

[2018.3] Fixes to mysql module to handle ALL PRIVILEGES grant
This commit is contained in:
Daniel Wozniak 2019-01-11 20:17:14 -07:00 committed by GitHub
commit 8bb589dbc1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 191 additions and 31 deletions

View file

@ -81,7 +81,11 @@ __grants__ = [
'ALL PRIVILEGES',
'ALTER',
'ALTER ROUTINE',
'BACKUP_ADMIN',
'BINLOG_ADMIN',
'CONNECTION_ADMIN',
'CREATE',
'CREATE ROLE',
'CREATE ROUTINE',
'CREATE TABLESPACE',
'CREATE TEMPORARY TABLES',
@ -89,26 +93,37 @@ __grants__ = [
'CREATE VIEW',
'DELETE',
'DROP',
'DROP ROLE',
'ENCRYPTION_KEY_ADMIN',
'EVENT',
'EXECUTE',
'FILE',
'GRANT OPTION',
'GROUP_REPLICATION_ADMIN',
'INDEX',
'INSERT',
'LOCK TABLES',
'PERSIST_RO_VARIABLES_ADMIN',
'PROCESS',
'REFERENCES',
'RELOAD',
'REPLICATION CLIENT',
'REPLICATION SLAVE',
'REPLICATION_SLAVE_ADMIN',
'RESOURCE_GROUP_ADMIN',
'RESOURCE_GROUP_USER',
'ROLE_ADMIN',
'SELECT',
'SET_USER_ID',
'SHOW DATABASES',
'SHOW VIEW',
'SHUTDOWN',
'SUPER',
'SYSTEM_VARIABLES_ADMIN',
'TRIGGER',
'UPDATE',
'USAGE'
'USAGE',
'XA_RECOVER_ADMIN'
]
__ssl_options_parameterized__ = [
@ -121,6 +136,52 @@ __ssl_options__ = __ssl_options_parameterized__ + [
'X509'
]
__all_privileges__ = [
'ALTER',
'ALTER ROUTINE',
'BACKUP_ADMIN',
'BINLOG_ADMIN',
'CONNECTION_ADMIN',
'CREATE',
'CREATE ROLE',
'CREATE ROUTINE',
'CREATE TABLESPACE',
'CREATE TEMPORARY TABLES',
'CREATE USER',
'CREATE VIEW',
'DELETE',
'DROP',
'DROP ROLE',
'ENCRYPTION_KEY_ADMIN',
'EVENT',
'EXECUTE',
'FILE',
'GROUP_REPLICATION_ADMIN',
'INDEX',
'INSERT',
'LOCK TABLES',
'PERSIST_RO_VARIABLES_ADMIN',
'PROCESS',
'REFERENCES',
'RELOAD',
'REPLICATION CLIENT',
'REPLICATION SLAVE',
'REPLICATION_SLAVE_ADMIN',
'RESOURCE_GROUP_ADMIN',
'RESOURCE_GROUP_USER',
'ROLE_ADMIN',
'SELECT',
'SET_USER_ID',
'SHOW DATABASES',
'SHOW VIEW',
'SHUTDOWN',
'SUPER',
'SYSTEM_VARIABLES_ADMIN',
'TRIGGER',
'UPDATE',
'XA_RECOVER_ADMIN'
]
r'''
DEVELOPER NOTE: ABOUT arguments management, escapes, formats, arguments and
security of SQL.
@ -1789,12 +1850,12 @@ def user_grants(user,
def grant_exists(grant,
database,
user,
host='localhost',
grant_option=False,
escape=True,
**connection_args):
database,
user,
host='localhost',
grant_option=False,
escape=True,
**connection_args):
'''
Checks to see if a grant exists in the database
@ -1805,6 +1866,14 @@ def grant_exists(grant,
salt '*' mysql.grant_exists \
'SELECT,INSERT,UPDATE,...' 'database.*' 'frank' 'localhost'
'''
server_version = version(**connection_args)
if 'ALL' in grant:
if salt.utils.versions.version_cmp(server_version, '8.0') >= 0:
grant = ','.join([i for i in __all_privileges__])
else:
grant = 'ALL PRIVILEGES'
try:
target = __grant_generate(
grant, database, user, host, grant_option, escape
@ -1820,15 +1889,27 @@ def grant_exists(grant,
'this could also indicate a connection error. Check your configuration.')
return False
target_tokens = None
# Combine grants that match the same database
_grants = {}
for grant in grants:
try:
if not target_tokens: # Avoid the overhead of re-calc in loop
target_tokens = _grant_to_tokens(target)
grant_tokens = _grant_to_tokens(grant)
grant_token = _grant_to_tokens(grant)
if grant_token['database'] not in _grants:
_grants[grant_token['database']] = {'user': grant_token['user'],
'database': grant_token['database'],
'host': grant_token['host'],
'grant': grant_token['grant']}
else:
_grants[grant_token['database']]['grant'].extend(grant_token['grant'])
target_tokens = _grant_to_tokens(target)
for database, grant_tokens in _grants.items():
try:
_grant_tokens = {}
_target_tokens = {}
_grant_matches = [True if i in grant_tokens['grant']
else False for i in target_tokens['grant']]
for item in ['user', 'database', 'host']:
_grant_tokens[item] = grant_tokens[item].replace('"', '').replace('\\', '').replace('`', '')
_target_tokens[item] = target_tokens[item].replace('"', '').replace('\\', '').replace('`', '')
@ -1836,7 +1917,7 @@ def grant_exists(grant,
if _grant_tokens['user'] == _target_tokens['user'] and \
_grant_tokens['database'] == _target_tokens['database'] and \
_grant_tokens['host'] == _target_tokens['host'] and \
set(grant_tokens['grant']) >= set(target_tokens['grant']):
all(_grant_matches):
return True
else:
log.debug('grants mismatch \'%s\'<>\'%s\'', grant_tokens, target_tokens)

View file

@ -24,6 +24,52 @@ try:
except Exception:
NO_MYSQL = True
__all_privileges__ = [
'ALTER',
'ALTER ROUTINE',
'BACKUP_ADMIN',
'BINLOG_ADMIN',
'CONNECTION_ADMIN',
'CREATE',
'CREATE ROLE',
'CREATE ROUTINE',
'CREATE TABLESPACE',
'CREATE TEMPORARY TABLES',
'CREATE USER',
'CREATE VIEW',
'DELETE',
'DROP',
'DROP ROLE',
'ENCRYPTION_KEY_ADMIN',
'EVENT',
'EXECUTE',
'FILE',
'GROUP_REPLICATION_ADMIN',
'INDEX',
'INSERT',
'LOCK TABLES',
'PERSIST_RO_VARIABLES_ADMIN',
'PROCESS',
'REFERENCES',
'RELOAD',
'REPLICATION CLIENT',
'REPLICATION SLAVE',
'REPLICATION_SLAVE_ADMIN',
'RESOURCE_GROUP_ADMIN',
'RESOURCE_GROUP_USER',
'ROLE_ADMIN',
'SELECT',
'SET_USER_ID',
'SHOW DATABASES',
'SHOW VIEW',
'SHUTDOWN',
'SUPER',
'SYSTEM_VARIABLES_ADMIN',
'TRIGGER',
'UPDATE',
'XA_RECOVER_ADMIN'
]
@skipIf(NO_MOCK, NO_MOCK_REASON)
@skipIf(NO_MYSQL, 'Install MySQL bindings before running MySQL unit tests.')
@ -256,15 +302,16 @@ class MySQLTestCase(TestCase, LoaderModuleMockMixin):
"GRANT SELECT ON `testdb`.`testtabletwo` TO 'testuer'@'%'",
"GRANT SELECT ON `testdb`.`testtablethree` TO 'testuser'@'%'",
]
mock = MagicMock(return_value=mock_grants)
with patch.object(mysql, 'user_grants', return_value=mock_grants) as mock_user_grants:
ret = mysql.grant_exists(
'SELECT, INSERT, UPDATE',
'testdb.testtableone',
'testuser',
'%'
)
self.assertEqual(ret, True)
with patch.object(mysql, 'version', return_value='5.6.41'):
mock = MagicMock(return_value=mock_grants)
with patch.object(mysql, 'user_grants', return_value=mock_grants) as mock_user_grants:
ret = mysql.grant_exists(
'SELECT, INSERT, UPDATE',
'testdb.testtableone',
'testuser',
'%'
)
self.assertEqual(ret, True)
def test_grant_exists_false(self):
'''
@ -275,15 +322,47 @@ class MySQLTestCase(TestCase, LoaderModuleMockMixin):
"GRANT SELECT, INSERT, UPDATE ON `testdb`.`testtableone` TO 'testuser'@'%'",
"GRANT SELECT ON `testdb`.`testtablethree` TO 'testuser'@'%'",
]
mock = MagicMock(return_value=mock_grants)
with patch.object(mysql, 'user_grants', return_value=mock_grants) as mock_user_grants:
ret = mysql.grant_exists(
'SELECT',
'testdb.testtabletwo',
'testuser',
'%'
)
self.assertEqual(ret, False)
with patch.object(mysql, 'version', return_value='5.6.41'):
mock = MagicMock(return_value=mock_grants)
with patch.object(mysql, 'user_grants', return_value=mock_grants) as mock_user_grants:
ret = mysql.grant_exists(
'SELECT',
'testdb.testtabletwo',
'testuser',
'%'
)
self.assertEqual(ret, False)
def test_grant_exists_all(self):
'''
Test to ensure that we can find a grant that exists
'''
mock_grants = [
"GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, RELOAD, SHUTDOWN, PROCESS, FILE, REFERENCES, INDEX, ALTER, SHOW DATABASES, SUPER, CREATE TEMPORARY TABLES, LOCK TABLES, EXECUTE, REPLICATION SLAVE, REPLICATION CLIENT, CREATE VIEW, SHOW VIEW, CREATE ROUTINE, ALTER ROUTINE, CREATE USER, EVENT, TRIGGER, CREATE TABLESPACE, CREATE ROLE, DROP ROLE ON testdb.testtableone TO `testuser`@`%`",
"GRANT BACKUP_ADMIN,BINLOG_ADMIN,CONNECTION_ADMIN,ENCRYPTION_KEY_ADMIN,GROUP_REPLICATION_ADMIN,PERSIST_RO_VARIABLES_ADMIN,REPLICATION_SLAVE_ADMIN,RESOURCE_GROUP_ADMIN,RESOURCE_GROUP_USER,ROLE_ADMIN,SET_USER_ID,SYSTEM_VARIABLES_ADMIN,XA_RECOVER_ADMIN ON testdb.testtableone TO `testuser`@`%`"
]
with patch.object(mysql, 'version', return_value='8.0.10'):
mock = MagicMock(return_value=mock_grants)
with patch.object(mysql, 'user_grants', return_value=mock_grants) as mock_user_grants:
ret = mysql.grant_exists(
'ALL',
'testdb.testtableone',
'testuser',
'%'
)
self.assertEqual(ret, True)
mock_grants = ["GRANT ALL PRIVILEGES ON testdb.testtableone TO `testuser`@`%`"]
with patch.object(mysql, 'version', return_value='5.6.41'):
mock = MagicMock(return_value=mock_grants)
with patch.object(mysql, 'user_grants', return_value=mock_grants) as mock_user_grants:
ret = mysql.grant_exists(
'ALL PRIVILEGES',
'testdb.testtableone',
'testuser',
'%'
)
self.assertEqual(ret, True)
@skipIf(True, 'TODO: Mock up user_grants()')
def test_grant_add(self):