From 3f68b618effbcac13f5bba43455debf68064db12 Mon Sep 17 00:00:00 2001 From: wangjiahao <1522128093@qq.com> Date: Wed, 9 Sep 2026 16:36:19 +0800 Subject: [PATCH] fix: Authenticated IDOR in Dashboard update endpoints allows cross-user Dashboard modification --- backend/apps/dashboard/api/dashboard_api.py | 14 +++++------ .../apps/dashboard/crud/dashboard_service.py | 24 ++----------------- backend/apps/system/schemas/permission.py | 6 ++++- 3 files changed, 13 insertions(+), 31 deletions(-) diff --git a/backend/apps/dashboard/api/dashboard_api.py b/backend/apps/dashboard/api/dashboard_api.py index 8ccc0c98..85823932 100644 --- a/backend/apps/dashboard/api/dashboard_api.py +++ b/backend/apps/dashboard/api/dashboard_api.py @@ -6,6 +6,7 @@ from apps.dashboard.crud.dashboard_service import list_resource, load_resource, create_resource, create_canvas, validate_name, delete_resource, update_resource, update_canvas from apps.dashboard.models.dashboard_model import CreateDashboard, BaseDashboard, QueryDashboard from apps.swagger.i18n import PLACEHOLDER_PREFIX +from apps.system.schemas.permission import SqlbotPermission, require_permissions from common.audit.models.log_model import OperationType, OperationModules from common.audit.schemas.logger_decorator import LogConfig, system_log from common.core.deps import SessionDep, CurrentUser @@ -19,15 +20,9 @@ async def list_resource_api(session: SessionDep, dashboard: QueryDashboard, curr @router.post("/load_resource", summary=f"{PLACEHOLDER_PREFIX}load_resource_api") +@require_permissions(permission=SqlbotPermission(type='dashboard', keyExpression="dashboard.id")) async def load_resource_api(session: SessionDep, current_user: CurrentUser, dashboard: QueryDashboard): - resource_dict = load_resource(session=session, dashboard=dashboard) - if resource_dict and resource_dict.get("create_by") != str(current_user.id): - raise HTTPException( - status_code=403, - detail="You do not have permission to access this resource" - ) - - return resource_dict + return load_resource(session=session, dashboard=dashboard) @router.post("/create_resource", response_model=BaseDashboard, summary=f"{PLACEHOLDER_PREFIX}create_resource_api") @@ -36,6 +31,7 @@ async def create_resource_api(session: SessionDep, user: CurrentUser, dashboard: @router.post("/update_resource", response_model=BaseDashboard, summary=f"{PLACEHOLDER_PREFIX}update_resource") +@require_permissions(permission=SqlbotPermission(type='dashboard', keyExpression="dashboard.id")) @system_log(LogConfig( operation_type=OperationType.UPDATE, module=OperationModules.DASHBOARD, @@ -46,6 +42,7 @@ async def update_resource_api(session: SessionDep, user: CurrentUser, dashboard: @router.delete("/delete_resource/{resource_id}/{name}", summary=f"{PLACEHOLDER_PREFIX}delete_resource_api") +@require_permissions(permission=SqlbotPermission(type='dashboard', keyExpression="resource_id")) @system_log(LogConfig( operation_type=OperationType.DELETE, module=OperationModules.DASHBOARD, @@ -67,6 +64,7 @@ async def create_canvas_api(session: SessionDep, user: CurrentUser, dashboard: C @router.post("/update_canvas", response_model=BaseDashboard, summary=f"{PLACEHOLDER_PREFIX}update_canvas_api") +@require_permissions(permission=SqlbotPermission(type='dashboard', keyExpression="dashboard.id")) @system_log(LogConfig( operation_type=OperationType.UPDATE, module=OperationModules.DASHBOARD, diff --git a/backend/apps/dashboard/crud/dashboard_service.py b/backend/apps/dashboard/crud/dashboard_service.py index 9d76c143..e07bf010 100644 --- a/backend/apps/dashboard/crud/dashboard_service.py +++ b/backend/apps/dashboard/crud/dashboard_service.py @@ -79,16 +79,7 @@ def create_resource(session: SessionDep, user: CurrentUser, dashboard: CreateDas def update_resource(session: SessionDep, user: CurrentUser, dashboard: QueryDashboard): - # 添加权限检查:只能更新自己创建的、且在自己工作空间的 Dashboard - record = session.query(CoreDashboard).filter( - CoreDashboard.id == dashboard.id, - CoreDashboard.create_by == str(user.id), - CoreDashboard.workspace_id == str(user.oid if user.oid is not None else 1) - ).first() - - if not record: - raise ValueError(f"Dashboard with id {dashboard.id} does not exist or you do not have permission to update it") - + record = session.query(CoreDashboard).filter(CoreDashboard.id == dashboard.id).first() record.name = dashboard.name record.update_by = user.id record.update_time = int(time.time()) @@ -111,16 +102,7 @@ def create_canvas(session: SessionDep, user: CurrentUser, dashboard: CreateDashb def update_canvas(session: SessionDep, user: CurrentUser, dashboard: CreateDashboard): - # 添加权限检查:只能更新自己创建的、且在自己工作空间的 Dashboard - record = session.query(CoreDashboard).filter( - CoreDashboard.id == dashboard.id, - CoreDashboard.create_by == str(user.id), - CoreDashboard.workspace_id == str(user.oid if user.oid is not None else 1) - ).first() - - if not record: - raise ValueError(f"Dashboard with id {dashboard.id} does not exist or you do not have permission to update it") - + record = session.query(CoreDashboard).filter(CoreDashboard.id == dashboard.id).first() record.name = dashboard.name record.update_by = user.id record.update_time = int(time.time()) @@ -167,8 +149,6 @@ def delete_resource(session: SessionDep, current_user: CurrentUser, resource_id: coreDashboard = session.get(CoreDashboard, resource_id) if not coreDashboard: raise ValueError(f"Resource with id {resource_id} does not exist") - if coreDashboard.create_by != str(current_user.id): - raise ValueError(f"Resource with id {resource_id} not owned by the current user") sql = text("DELETE FROM core_dashboard WHERE id = :resource_id") result = session.execute(sql, {"resource_id": resource_id}) session.commit() diff --git a/backend/apps/system/schemas/permission.py b/backend/apps/system/schemas/permission.py index 45c78f81..0f686ab1 100644 --- a/backend/apps/system/schemas/permission.py +++ b/backend/apps/system/schemas/permission.py @@ -8,6 +8,7 @@ import re from starlette.middleware.base import BaseHTTPMiddleware from sqlmodel import Session, select from apps.chat.models.chat_model import Chat +from apps.dashboard.models.dashboard_model import CoreDashboard from apps.datasource.crud.datasource import get_ws_ds from apps.datasource.models.datasource import CoreDatasource from common.core.db import engine @@ -27,7 +28,10 @@ async def get_ws_resource(oid, type) -> list: if type == 'ds' or type == 'datasource': return await get_ws_ds(session, oid) if type == 'chat': - stmt = select(Chat.id).where(Chat.oid == oid) + stmt = select(Chat.id).where(Chat.oid == oid) + if type == 'dashboard': + # workspace_id 是 VARCHAR 类型,需要转换为字符串 + stmt = select(CoreDashboard.id).where(CoreDashboard.workspace_id == str(oid)) if stmt is not None: db_list = session.exec(stmt).all() return db_list