Skip to content

Commit ca2f853

Browse files
perf: X-Pack controlled by license
1 parent d687e88 commit ca2f853

File tree

5 files changed

+90
-100
lines changed

5 files changed

+90
-100
lines changed

backend/apps/chat/task/llm.py

Lines changed: 7 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,8 @@
1313
from langchain.chat_models.base import BaseChatModel
1414
from langchain_community.utilities import SQLDatabase
1515
from langchain_core.messages import BaseMessage, SystemMessage, HumanMessage, AIMessage, BaseMessageChunk
16-
from sqlalchemy import and_, cast, or_
1716
from sqlalchemy import select
18-
from sqlalchemy.dialects.postgresql import JSONB
1917
from sqlalchemy.orm import sessionmaker
20-
from sqlbot_xpack.permissions.api.permission import transRecord2DTO
21-
from sqlbot_xpack.permissions.models.ds_permission import DsPermission, PermissionDTO
22-
from sqlbot_xpack.permissions.models.ds_rules import DsRules
2318
from sqlmodel import create_engine, Session
2419

2520
from apps.ai_model.model_factory import LLMConfig, LLMFactory, get_default_config
@@ -30,9 +25,8 @@
3025
get_old_questions, save_analysis_predict_record, list_base_records, rename_chat
3126
from apps.chat.models.chat_model import ChatQuestion, ChatRecord, Chat, RenameChat
3227
from apps.datasource.crud.datasource import get_table_schema
33-
from apps.datasource.crud.datasource import is_normal_user
34-
from apps.datasource.crud.row_permission import transFilterTree
35-
from apps.datasource.models.datasource import CoreDatasource, CoreTable
28+
from apps.datasource.crud.permission import get_row_permission_filters, is_normal_user
29+
from apps.datasource.models.datasource import CoreDatasource
3630
from apps.db.db import exec_sql
3731
from apps.system.crud.assistant import AssistantOutDs, AssistantOutDsFactory, get_assistant_ds
3832
from apps.system.schemas.system_schema import AssistantOutDsSchema
@@ -606,28 +600,9 @@ def build_table_filter(self, sql: str, filters: list):
606600
return full_filter_text
607601

608602
def generate_filter(self, sql: str, tables: List):
609-
table_list = self.session.query(CoreTable).filter(
610-
and_(CoreTable.ds_id == self.ds.id, CoreTable.table_name.in_(tables))
611-
).all()
612-
613-
filters = []
614-
for table in table_list:
615-
row_permissions = self.session.query(DsPermission).filter(
616-
and_(DsPermission.table_id == table.id, DsPermission.type == 'row')).all()
617-
res: List[PermissionDTO] = []
618-
if row_permissions is not None:
619-
for permission in row_permissions:
620-
# check permission and user in same rules
621-
obj = self.session.query(DsRules).filter(
622-
and_(DsRules.permission_list.op('@>')(cast([permission.id], JSONB)),
623-
or_(DsRules.user_list.op('@>')(cast([f'{self.current_user.id}'], JSONB)),
624-
DsRules.user_list.op('@>')(cast([self.current_user.id], JSONB))))
625-
).first()
626-
if obj is not None:
627-
res.append(transRecord2DTO(self.session, permission))
628-
where_str = transFilterTree(self.session, res, self.ds)
629-
filters.append({"table": table.table_name, "filter": where_str})
630-
603+
filters = get_row_permission_filters(session=self.session, current_user=self.current_user, ds=self.ds, tables=tables)
604+
if not filters:
605+
return None
631606
return self.build_table_filter(sql=sql, filters=filters)
632607

633608
def generate_assistant_filter(self, sql, tables: List):
@@ -864,7 +839,8 @@ def run_task(self, in_chat: bool = True):
864839
# todo row permission
865840
if (not self.current_assistant and is_normal_user(self.current_user)) or use_dynamic_ds:
866841
sql, tables = self.check_sql(res=full_sql_text)
867-
842+
sql_result = None
843+
dynamic_sql_result = None
868844
if self.current_assistant:
869845
dynamic_sql_result = self.generate_assistant_dynamic_sql(sql, tables)
870846
if dynamic_sql_result:

backend/apps/datasource/crud/datasource.py

Lines changed: 13 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,11 @@
33
from typing import List, Optional
44

55
from fastapi import HTTPException
6-
from sqlalchemy import and_, text, cast, or_, func
7-
from sqlalchemy.dialects.postgresql import JSONB
8-
from sqlbot_xpack.permissions.api.permission import transRecord2DTO
9-
from sqlbot_xpack.permissions.models.ds_permission import DsPermission, PermissionDTO
10-
from sqlbot_xpack.permissions.models.ds_rules import DsRules
6+
from sqlalchemy import and_, text, func
7+
118
from sqlmodel import select
129

13-
from apps.datasource.crud.row_permission import transFilterTree
10+
1411
from apps.datasource.utils.utils import aes_decrypt
1512
from apps.db.constant import DB
1613
from apps.db.db import get_engine, get_tables, get_fields, exec_sql
@@ -23,7 +20,7 @@
2320
from ..crud.table import delete_table_by_ds_id, update_table
2421
from ..models.datasource import CoreDatasource, CreateDatasource, CoreTable, CoreField, ColumnSchema, TableObj, \
2522
DatasourceConf, TableAndFields
26-
23+
from apps.datasource.crud.permission import get_column_permission_fields, get_row_permission_filters, is_normal_user
2724

2825
def get_datasource_list(session: SessionDep, user: CurrentUser, oid: Optional[int] = None) -> List[CoreDatasource]:
2926
current_oid = user.oid if user.oid is not None else 1
@@ -252,35 +249,14 @@ def preview(session: SessionDep, current_user: CurrentUser, id: int, data: Table
252249
f_list = [f for f in data.fields if f.checked]
253250
if is_normal_user(current_user):
254251
# column is checked, and, column permission for data.fields
255-
column_permissions = session.query(DsPermission).filter(
256-
and_(DsPermission.table_id == data.table.id, DsPermission.type == 'column')).all()
257-
if column_permissions is not None:
258-
for permission in column_permissions:
259-
# check permission and user in same rules
260-
obj = session.query(DsRules).filter(
261-
and_(DsRules.permission_list.op('@>')(cast([permission.id], JSONB)),
262-
or_(DsRules.user_list.op('@>')(cast([f'{current_user.id}'], JSONB)),
263-
DsRules.user_list.op('@>')(cast([current_user.id], JSONB))))
264-
).first()
265-
if obj is not None:
266-
permission_list = json.loads(permission.permissions)
267-
f_list = filter_list(f_list, permission_list)
252+
f_list = get_column_permission_fields(session=session, current_user=current_user, table=data.table, fields=f_list) or f_list
268253

269254
# row permission tree
270-
row_permissions = session.query(DsPermission).filter(
271-
and_(DsPermission.table_id == data.table.id, DsPermission.type == 'row')).all()
272-
res: List[PermissionDTO] = []
273-
if row_permissions is not None:
274-
for permission in row_permissions:
275-
# check permission and user in same rules
276-
obj = session.query(DsRules).filter(
277-
and_(DsRules.permission_list.op('@>')(cast([permission.id], JSONB)),
278-
or_(DsRules.user_list.op('@>')(cast([f'{current_user.id}'], JSONB)),
279-
DsRules.user_list.op('@>')(cast([current_user.id], JSONB))))
280-
).first()
281-
if obj is not None:
282-
res.append(transRecord2DTO(session, permission))
283-
where_str = transFilterTree(session, res, ds)
255+
where_str = ''
256+
filter_mapping = get_row_permission_filters(session=session, current_user=current_user, ds=ds, tables=None, single_table=data.table)
257+
if filter_mapping:
258+
mapping_dict = filter_mapping[0]
259+
where_str = mapping_dict.get('filter')
284260
where = (' where ' + where_str) if where_str is not None and where_str != '' else ''
285261

286262
fields = [f.field_name for f in f_list]
@@ -349,25 +325,13 @@ def get_table_obj_by_ds(session: SessionDep, current_user: CurrentUser, ds: Core
349325
fields = session.query(CoreField).filter(and_(CoreField.table_id == table.id, CoreField.checked == True)).all()
350326

351327
# do column permissions, filter fields
352-
if is_normal_user(current_user):
353-
column_permissions = session.query(DsPermission).filter(
354-
and_(DsPermission.table_id == table.id, DsPermission.type == 'column')).all()
355-
if column_permissions is not None:
356-
for permission in column_permissions:
357-
# check permission and user in same rules
358-
obj = session.query(DsRules).filter(
359-
and_(DsRules.permission_list.op('@>')(cast([permission.id], JSONB)),
360-
or_(DsRules.user_list.op('@>')(cast([f'{current_user.id}'], JSONB)),
361-
DsRules.user_list.op('@>')(cast([current_user.id], JSONB))))
362-
).first()
363-
if obj is not None:
364-
permission_list = json.loads(permission.permissions)
365-
fields = filter_list(fields, permission_list)
366-
328+
fields = get_column_permission_fields(session=session, current_user=current_user, table=table, fields=fields) or fields
367329
_list.append(TableAndFields(schema=schema, table=table, fields=fields))
368330
return _list
369331

370332

333+
334+
371335
def get_table_schema(session: SessionDep, current_user: CurrentUser, ds: CoreDatasource) -> str:
372336
schema_str = ""
373337
table_objs = get_table_obj_by_ds(session=session, current_user=current_user, ds=ds)
@@ -397,16 +361,3 @@ def get_table_schema(session: SessionDep, current_user: CurrentUser, ds: CoreDat
397361
schema_str += ",\n".join(field_list)
398362
schema_str += '\n]\n'
399363
return schema_str
400-
401-
402-
def filter_list(list_a, list_b):
403-
id_to_invalid = {}
404-
for b in list_b:
405-
if not b['enable']:
406-
id_to_invalid[b['field_id']] = True
407-
408-
return [a for a in list_a if not id_to_invalid.get(a.id, False)]
409-
410-
411-
def is_normal_user(current_user: CurrentUser):
412-
return current_user.id != 1
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
2+
import json
3+
from typing import List, Optional
4+
5+
from sqlalchemy import and_, cast, or_
6+
from apps.datasource.crud.row_permission import transFilterTree
7+
from apps.datasource.models.datasource import CoreDatasource, CoreField, CoreTable
8+
from common.core.deps import CurrentUser, SessionDep
9+
from sqlbot_xpack.permissions.api.permission import transRecord2DTO
10+
from sqlbot_xpack.permissions.models.ds_permission import DsPermission, PermissionDTO
11+
from sqlbot_xpack.permissions.models.ds_rules import DsRules
12+
from sqlalchemy.dialects.postgresql import JSONB
13+
14+
def get_row_permission_filters(session: SessionDep, current_user: CurrentUser, ds: CoreDatasource, tables: Optional[list] = None, single_table: Optional[CoreTable] = None):
15+
if single_table:
16+
table_list = [session.get(CoreTable, single_table.id)]
17+
else:
18+
table_list =session.query(CoreTable).filter(
19+
and_(CoreTable.ds_id == ds.id, CoreTable.table_name.in_(tables))
20+
).all()
21+
22+
filters = []
23+
for table in table_list:
24+
row_permissions = session.query(DsPermission).filter(
25+
and_(DsPermission.table_id == table.id, DsPermission.type == 'row')).all()
26+
res: List[PermissionDTO] = []
27+
if row_permissions is not None:
28+
for permission in row_permissions:
29+
# check permission and user in same rules
30+
obj = session.query(DsRules).filter(
31+
and_(DsRules.permission_list.op('@>')(cast([permission.id], JSONB)),
32+
or_(DsRules.user_list.op('@>')(cast([f'{current_user.id}'], JSONB)),
33+
DsRules.user_list.op('@>')(cast([current_user.id], JSONB))))
34+
).first()
35+
if obj is not None:
36+
res.append(transRecord2DTO(session, permission))
37+
where_str = transFilterTree(session, res, ds)
38+
filters.append({"table": table.table_name, "filter": where_str})
39+
return filters
40+
41+
def get_column_permission_fields(session: SessionDep, current_user: CurrentUser, table: CoreTable, fields: list[CoreField]):
42+
if is_normal_user(current_user):
43+
column_permissions = session.query(DsPermission).filter(
44+
and_(DsPermission.table_id == table.id, DsPermission.type == 'column')).all()
45+
if column_permissions is not None:
46+
for permission in column_permissions:
47+
# check permission and user in same rules
48+
obj = session.query(DsRules).filter(
49+
and_(DsRules.permission_list.op('@>')(cast([permission.id], JSONB)),
50+
or_(DsRules.user_list.op('@>')(cast([f'{current_user.id}'], JSONB)),
51+
DsRules.user_list.op('@>')(cast([current_user.id], JSONB))))
52+
).first()
53+
if obj is not None:
54+
permission_list = json.loads(permission.permissions)
55+
fields = filter_list(fields, permission_list)
56+
return fields
57+
def is_normal_user(current_user: CurrentUser):
58+
return current_user.id != 1
59+
60+
def filter_list(list_a, list_b):
61+
id_to_invalid = {}
62+
for b in list_b:
63+
if not b['enable']:
64+
id_to_invalid[b['field_id']] = True
65+
66+
return [a for a in list_a if not id_to_invalid.get(a.id, False)]

backend/apps/datasource/crud/row_permission.py

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,12 @@
22
# Date: 2025/6/25
33

44
from typing import List, Dict
5-
6-
from sqlbot_xpack.permissions.models.ds_permission import PermissionTree, PermissionDTO
7-
85
from apps.datasource.models.datasource import CoreField, CoreDatasource
96
from apps.db.constant import DB
107
from common.core.deps import SessionDep
118

129

13-
def transFilterTree(session: SessionDep, tree_list: List[PermissionDTO], ds: CoreDatasource) -> str | None:
10+
def transFilterTree(session: SessionDep, tree_list: List[any], ds: CoreDatasource) -> str | None:
1411
if tree_list is None:
1512
return None
1613
res: List[str] = []
@@ -24,7 +21,7 @@ def transFilterTree(session: SessionDep, tree_list: List[PermissionDTO], ds: Cor
2421
return " AND ".join(res)
2522

2623

27-
def transTreeToWhere(session: SessionDep, tree: PermissionTree, ds: CoreDatasource) -> str | None:
24+
def transTreeToWhere(session: SessionDep, tree: any, ds: CoreDatasource) -> str | None:
2825
if tree is None:
2926
return None
3027
logic = tree['logic']

backend/main.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from fastapi.routing import APIRoute
44
from starlette.middleware.cors import CORSMiddleware
55
from starlette.exceptions import HTTPException as StarletteHTTPException
6-
from apps.api import api_router
6+
77
from apps.system.crud.assistant import init_dynamic_cors
88
from apps.system.middleware.auth import TokenMiddleware
99
from common.core.config import settings
@@ -13,9 +13,9 @@
1313
from fastapi_mcp import FastApiMCP
1414
from fastapi.staticfiles import StaticFiles
1515
import sqlbot_xpack
16-
1716
from common.utils.utils import SQLBotLogUtil
1817
from common.core.sqlbot_cache import init_sqlbot_cache
18+
from apps.api import api_router
1919

2020
def run_migrations():
2121
alembic_cfg = Config("alembic.ini")

0 commit comments

Comments
 (0)