Skip to content

Commit

Permalink
调整查询权限校验细节,放行explain和show create语句
Browse files Browse the repository at this point in the history
检测到语法错误时提前返回,不再去校验库表权限
修复测试用例中mock Exception的错误写法
  • Loading branch information
hhyo committed Jun 16, 2019
1 parent afee887 commit ed0caf2
Show file tree
Hide file tree
Showing 4 changed files with 48 additions and 13 deletions.
4 changes: 2 additions & 2 deletions sql/engines/inception.py
Original file line number Diff line number Diff line change
Expand Up @@ -162,8 +162,8 @@ def query_print(self, instance, db_name=None, sql=''):
# 兼容语法错误时errlevel=0的场景
if print_info['errlevel'] == 0 and print_info['errmsg'] == 'None':
return json.loads(_repair_json_str(print_info['query_tree']))
elif print_info['errlevel'] == 0 and print_info['errmsg']:
raise RuntimeError(f"Inception Error: {print_info['query_tree']}")
elif print_info['errlevel'] == 0 and print_info['errmsg'] == 'Global environment':
raise SyntaxError(f"Inception Error: {print_info['query_tree']}")
else:
raise RuntimeError(f"Inception Error: {print_info['errmsg']}")

Expand Down
11 changes: 10 additions & 1 deletion sql/query_privileges.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"""
import logging
import datetime
import re
import traceback

import simplejson as json
Expand All @@ -33,6 +34,7 @@
__author__ = 'hhyo'


# TODO 权限校验内的语法解析和判断独立到每个engine内
def query_priv_check(user, instance, db_name, sql_content, limit_num):
"""
查询权限校验
Expand All @@ -50,7 +52,10 @@ def query_priv_check(user, instance, db_name, sql_content, limit_num):
priv_limit = int(SysConfig().get('admin_query_limit', 5000))
result['data']['limit_num'] = min(priv_limit, limit_num) if limit_num else priv_limit
return result

# explain和show create跳过权限校验
if re.match(r"^explain|^show\s+create", sql_content, re.I):
return result
# 其他尝试使用inception解析
try:
# 尝试使用Inception校验表权限
table_ref = _table_ref(f"{sql_content.rstrip(';')};", instance, db_name)
Expand All @@ -67,6 +72,10 @@ def query_priv_check(user, instance, db_name, sql_content, limit_num):
priv_limit = _priv_limit(user, instance, db_name=table['db'], tb_name=table['table'])
limit_num = min(priv_limit, limit_num) if limit_num else priv_limit
result['data']['limit_num'] = limit_num
except SyntaxError as msg:
result['status'] = 1
result['msg'] = f"SQL语法错误,{msg}"
return result
except Exception as msg:
# 表权限校验失败再次校验库权限
# 先获取查询语句涉及的库
Expand Down
44 changes: 35 additions & 9 deletions sql/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,14 @@ def test_query_priv_check_super(self):
limit_num=100)
self.assertDictEqual(r, {'status': 0, 'msg': 'ok', 'data': {'priv_check': True, 'limit_num': 100}})

def test_query_priv_check_explain_or_show_create(self):
"""测试用户权限校验,explain和show create不做校验"""
r = sql.query_privileges.query_priv_check(user=self.user,
instance=self.slave, db_name=self.db_name,
sql_content="show create table archery.sql_users;",
limit_num=100)
self.assertTrue(r)

@patch('sql.query_privileges._table_ref', return_value=[{'db': 'archery', 'table': 'sql_users'}])
@patch('sql.query_privileges._tb_priv', return_value=False)
@patch('sql.query_privileges._db_priv', return_value=False)
Expand Down Expand Up @@ -327,50 +335,68 @@ def test_query_priv_check_tb_priv_exist(self, __db_priv, __tb_priv, __table_ref)
limit_num=100)
self.assertDictEqual(r, {'data': {'limit_num': 10, 'priv_check': True}, 'msg': 'ok', 'status': 0})

@patch('sql.query_privileges._table_ref', return_value=RuntimeError())
@patch('sql.query_privileges._table_ref', return_value=SyntaxError())
def test_query_priv_check_table_ref_SyntaxError(self, __table_ref):
"""
测试用户权限校验,mysql实例、普通用户 ,inception语法树抛出异常,query_check开启,无库权限
:return:
"""
self.sys_config.get_all_config()
r = sql.query_privileges.query_priv_check(user=self.user,
instance=self.slave, db_name=self.db_name,
sql_content="select * from archery.sql_users;",
limit_num=100)
self.assertDictEqual(r, {'status': 1,
'msg': "你无archery数据库的查询权限!请先到查询权限管理进行申请",
'data': {'priv_check': True, 'limit_num': 0}})

@patch('sql.query_privileges._table_ref')
@patch('sql.query_privileges._tb_priv', return_value=False)
@patch('sql.query_privileges._db_priv', return_value=False)
def test_query_priv_check_table_ref_Exception_and_no_db_priv(self, __db_priv, __tb_priv, __table_ref):
"""
测试用户权限校验,mysql实例、普通用户 ,inception语法树抛出异常,query_check开启,无库权限
:return:
"""
__table_ref.side_effect = SyntaxError('语法错误')
self.sys_config.set('query_check', 'true')
self.sys_config.get_all_config()
r = sql.query_privileges.query_priv_check(user=self.user,
instance=self.slave, db_name=self.db_name,
sql_content="select * from archery.sql_users;",
limit_num=100)
self.assertDictEqual(r, {'status': 1,
'msg': "你无archery数据库的查询权限!请先到查询权限管理进行申请",
'msg': "SQL语法错误,语法错误",
'data': {'priv_check': True, 'limit_num': 0}})

@patch('sql.query_privileges._table_ref', return_value=RuntimeError())
@patch('sql.query_privileges._table_ref')
@patch('sql.query_privileges._tb_priv', return_value=False)
@patch('sql.query_privileges._db_priv', return_value=1000)
def test_query_priv_check_table_ref_Exception_and_open_query_check(self, __db_priv, __tb_priv, __table_ref):
"""
测试用户权限校验,mysql实例、普通用户 ,有表权限,inception语法树抛出异常,query_check开启,有库权限
:return:
"""
__table_ref.side_effect = RuntimeError('RuntimeError')
self.sys_config.set('query_check', 'true')
self.sys_config.get_all_config()
r = sql.query_privileges.query_priv_check(user=self.user,
instance=self.slave, db_name=self.db_name,
sql_content="select * from archery.sql_users;",
limit_num=100)
self.assertDictEqual(r, {'status': 1,
'msg': "无法校验查询语句权限,请检查语法是否正确或联系管理员,错误信息:'RuntimeError' object is not iterable",
'msg': "无法校验查询语句权限,请检查语法是否正确或联系管理员,错误信息:RuntimeError",
'data': {'priv_check': True, 'limit_num': 100}})

@patch('sql.query_privileges._table_ref', return_value=RuntimeError())
@patch('sql.query_privileges._table_ref')
@patch('sql.query_privileges._tb_priv', return_value=False)
@patch('sql.query_privileges._db_priv', return_value=1000)
def test_query_priv_check_table_ref_Exception_and_close_query_check(self, __db_priv, __tb_priv, __table_ref):
"""
测试用户权限校验,mysql实例、普通用户 ,有表权限,inception语法树抛出异常,query_check关闭,有库权限
:return:
"""
__table_ref.side_effect = RuntimeError()
self.sys_config.set('query_check', 'false')
self.sys_config.get_all_config()
r = sql.query_privileges.query_priv_check(user=self.user,
Expand Down Expand Up @@ -856,11 +882,11 @@ def test_check_inception_Exception(self, _get_engine):
"instance_name": self.master1.instance_name,
"db_name": "archery",
}
_get_engine.return_value = RuntimeError
_get_engine.side_effect = RuntimeError('RuntimeError')
r = c.post('/simplecheck/', data=data)
self.assertDictEqual(json.loads(r.content),
{'status': 1,
'msg': "type object 'RuntimeError' has no attribute 'execute_check'",
'msg': "RuntimeError",
'data': {}})

@patch('sql.sql_workflow.get_engine')
Expand Down Expand Up @@ -1208,10 +1234,10 @@ def test_osc_control_exception(self, _get_engine):
'sqlsha1': 'sqlsha1',
'command': 'get',
}
_get_engine.return_value.osc_control.return_value = RuntimeError
_get_engine.return_value.osc_control.side_effect = RuntimeError('RuntimeError')
r = c.post('/inception/osc_control/', data=request_data, follow=False)
self.assertDictEqual(json.loads(r.content),
{"total": 0, "rows": [], "msg": "type object 'RuntimeError' has no attribute 'to_dict'"})
{"total": 0, "rows": [], "msg": "RuntimeError"})


class TestOptimize(TestCase):
Expand Down
2 changes: 1 addition & 1 deletion sql/utils/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -986,7 +986,7 @@ def test_can_review_no_prem(self, _detail_by_workflow_id, _auth_group_users):
def test_can_review_no_prem(self, _detail_by_workflow_id, _auth_group_users):
"""测试判断用户当前是否是可审核,权限组不存在"""
aug = Group.objects.create(name='auth_group')
_detail_by_workflow_id.return_value = RuntimeError
_detail_by_workflow_id.side_effect = RuntimeError()
_auth_group_users.return_value.filter.exists = True
self.audit.workflow_type = WorkflowDict.workflow_type['sqlreview']
self.audit.workflow_id = self.wf.id
Expand Down

0 comments on commit ed0caf2

Please sign in to comment.