feat: data permission

This commit is contained in:
junjun
2025-07-15 17:32:03 +08:00
parent a817cfcdc9
commit 080d4e08a8
10 changed files with 366 additions and 8 deletions
+48 -4
View File
@@ -2,7 +2,10 @@ import datetime
import json
from typing import List
from sqlalchemy import and_, text
from sqlalchemy import and_, text, cast
from sqlalchemy.dialects.postgresql import JSONB
from sqlbot_xpack.permissions.models.ds_permission import DsPermission
from sqlbot_xpack.permissions.models.ds_rules import DsRules
from sqlmodel import select
from apps.datasource.utils.utils import aes_decrypt
@@ -12,6 +15,7 @@ from apps.db.engine import get_engine_conn
from apps.db.type import db_type_relation
from common.core.deps import SessionDep, CurrentUser
from common.utils.utils import deepcopy_ignore_extra
from .table import get_tables_by_ds_id
from ..crud.field import delete_field_by_ds_id, update_field
from ..crud.table import delete_table_by_ds_id, update_table
from ..models.datasource import CoreDatasource, CreateDatasource, CoreTable, CoreField, ColumnSchema, TableObj, \
@@ -71,12 +75,14 @@ def create_ds(session: SessionDep, user: CurrentUser, create_ds: CreateDatasourc
# save tables and fields
sync_table(session, ds, create_ds.tables)
updateNum(session, ds)
return ds
def chooseTables(session: SessionDep, id: int, tables: List[CoreTable]):
ds = session.query(CoreDatasource).filter(CoreDatasource.id == id).first()
sync_table(session, ds, tables)
updateNum(session, ds)
def update_ds(session: SessionDep, ds: CoreDatasource):
@@ -239,20 +245,49 @@ def preview(session: SessionDep, id: int, data: TableObj):
return exec_sql(ds, sql)
def get_table_obj_by_ds(session: SessionDep, ds: CoreDatasource) -> List[TableAndFields]:
def updateNum(session: SessionDep, ds: CoreDatasource):
all_tables = get_tables(ds)
selected_tables = get_tables_by_ds_id(session, ds.id)
num = f'{len(selected_tables)}/{len(all_tables)}'
record = session.exec(select(CoreDatasource).where(CoreDatasource.id == ds.id)).first()
update_data = ds.model_dump(exclude_unset=True)
for field, value in update_data.items():
setattr(record, field, value)
record.num = num
session.add(record)
session.commit()
def get_table_obj_by_ds(session: SessionDep, current_user: CurrentUser, ds: CoreDatasource) -> List[TableAndFields]:
_list: List = []
tables = session.query(CoreTable).filter(CoreTable.ds_id == ds.id).all()
conf = DatasourceConf(**json.loads(aes_decrypt(ds.configuration))) if ds.type != "excel" else get_engine_config()
schema = conf.dbSchema if conf.dbSchema is not None and conf.dbSchema != "" else conf.database
for table in tables:
fields = session.query(CoreField).filter(and_(CoreField.table_id == table.id, CoreField.checked == True)).all()
# do column permissions, filter fields
column_permissions = session.query(DsPermission).filter(
and_(DsPermission.table_id == table.id, DsPermission.type == 'column')).all()
if column_permissions is not None:
for permission in column_permissions:
# check permission and user in same rules
obj = session.query(DsRules).filter(
and_(DsRules.permission_list.op('@>')(cast([permission.id], JSONB)),
DsRules.user_list.op('@>')(cast([current_user.id], JSONB)))
).first()
if obj is not None:
permission_list = json.loads(permission.permissions)
fields = filter_list(fields, permission_list)
_list.append(TableAndFields(schema=schema, table=table, fields=fields))
return _list
def get_table_schema(session: SessionDep, ds: CoreDatasource) -> str:
def get_table_schema(session: SessionDep, current_user: CurrentUser, ds: CoreDatasource) -> str:
schema_str = ""
table_objs = get_table_obj_by_ds(session=session, ds=ds)
table_objs = get_table_obj_by_ds(session=session, current_user=current_user, ds=ds)
if len(table_objs) == 0:
return schema_str
db_name = table_objs[0].schema
@@ -279,3 +314,12 @@ def get_table_schema(session: SessionDep, ds: CoreDatasource) -> str:
schema_str += ",\n".join(field_list)
schema_str += '\n]\n'
return schema_str
def filter_list(list_a, list_b):
id_to_invalid = {}
for b in list_b:
if not b['enable']:
id_to_invalid[b['field_id']] = True
return [a for a in list_a if not id_to_invalid.get(a.id, False)]
@@ -0,0 +1,126 @@
# Author: Junjun
# Date: 2025/6/25
from typing import List, Dict
from apps.datasource.models.datasource import CoreField, CoreDatasource
from sqlbot_xpack.permissions.models.ds_permission import PermissionTree, PermissionDTO
from common.core.deps import SessionDep
def transFilterTree(session: SessionDep, tree_list: List[PermissionDTO], ds: CoreDatasource) -> str | None:
if tree_list is None:
return None
res: List[str] = []
for dto in tree_list:
tree = dto.tree
if tree is None:
continue
tree_exp = transTreeToWhere(session, tree, ds)
if tree_exp is not None:
res.append(tree_exp)
return " AND ".join(res)
def transTreeToWhere(session: SessionDep, tree: PermissionTree, ds: CoreDatasource) -> str | None:
if tree is None:
return None
logic = tree['logic']
items = tree['items']
list: List[str] = []
if items is not None:
for item in items:
exp: str = None
if item['type'] == 'item':
exp = transTreeItem(session, item, ds)
elif item['type'] == 'tree':
exp = transTreeToWhere(session, item['sub_tree'], ds)
if exp is not None:
list.append(exp)
return '(' + f' {logic} '.join(list) + ')' if len(list) > 0 else None
def transTreeItem(session: SessionDep, item: Dict, ds: CoreDatasource) -> str | None:
res: str = None
field = session.query(CoreField).filter(CoreField.id == int(item['field_id'])).first()
if field is None:
return None
whereName = field.field_name
if item['filter_type'] == 'enum':
if len(item['enum_value']) > 0:
if ds['type'] == 'sqlServer' and (
field.field_type == 'nchar' or field.field_type == 'NCHAR' or field.field_type == 'nvarchar' or field.field_type == 'NVARCHAR'):
res = "(" + whereName + " IN (N'" + "',N'".join(item['enum_value']) + "'))"
else:
res = "(" + whereName + " IN ('" + "','".join(item['enum_value']) + "'))"
else:
value = item['value']
whereTerm = transFilterTerm(item['term'])
whereValue = ''
if item['term'] == 'null':
whereValue = ''
elif item['term'] == 'not_null':
whereValue = ''
elif item['term'] == 'empty':
whereValue = "''"
elif item['term'] == 'not_empty':
whereValue = "''"
elif item['term'] == 'in' or item['term'] == 'not in':
if ds.type == 'sqlServer' and (
field.field_type == 'nchar' or field.field_type == 'NCHAR' or field.field_type == 'nvarchar' or field.field_type == 'NVARCHAR'):
whereValue = "(N'" + "', N'".join(value.split(",")) + "')"
else:
whereValue = "('" + "', '".join(value.split(",")) + "')"
elif item['term'] == 'like' or item['term'] == 'not like':
if ds.type == 'sqlServer' and (
field.field_type == 'nchar' or field.field_type == 'NCHAR' or field.field_type == 'nvarchar' or field.field_type == 'NVARCHAR'):
whereValue = f"N'%{value}%'"
else:
whereValue = f"'%{value}%'"
else:
if ds.type == 'sqlServer' and (
field.field_type == 'nchar' or field.field_type == 'NCHAR' or field.field_type == 'nvarchar' or field.field_type == 'NVARCHAR'):
whereValue = f"N'{value}'"
else:
whereValue = f"'{value}'"
res = whereName + whereTerm + whereValue
return res
def transFilterTerm(term: str) -> str:
if term == "eq":
return " = "
if term == "not_eq":
return " <> "
if term == "lt":
return " < "
if term == "le":
return " <= "
if term == "gt":
return " > "
if term == "ge":
return " >= "
if term == "in":
return " IN "
if term == "not in":
return " NOT IN "
if term == "like":
return " LIKE "
if term == "not like":
return " NOT LIKE "
if term == "null":
return " IS NULL "
if term == "not_null":
return " IS NOT NULL "
if term == "empty":
return " = "
if term == "not_empty":
return " <> "
if term == "between":
return " BETWEEN "
return ""
@@ -17,6 +17,7 @@ class CoreDatasource(SQLModel, table=True):
create_time: datetime = Field(sa_column=Column(DateTime(timezone=False), nullable=True))
create_by: int = Field(sa_column=Column(BigInteger()))
status: str = Field(max_length=64, nullable=True)
num: str = Field(max_length=256, nullable=True)
class CoreTable(SQLModel, table=True):
@@ -51,6 +52,7 @@ class CreateDatasource(BaseModel):
create_time: Optional[datetime] = None
create_by: int = 0
status: str = ''
num: str = ''
tables: List[CoreTable] = []