Skip to content

Commit 49a50e3

Browse files
fix: Cross-Workspace Assistant Modification and Deletion (IDOR)
1 parent 5613065 commit 49a50e3

2 files changed

Lines changed: 27 additions & 16 deletions

File tree

‎backend/apps/system/api/assistant.py‎

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
from apps.db.constant import DB
1414
from apps.swagger.i18n import PLACEHOLDER_PREFIX
1515
from apps.system.crud.assistant import AssistantOutDs, AssistantOutDsFactory, get_assistant_info
16-
from apps.system.crud.assistant_manage import dynamic_upgrade_cors, save
16+
from apps.system.crud.assistant_manage import dynamic_upgrade_cors, get_ws_assistant, save
1717
from apps.system.models.system_model import AssistantModel
1818
from apps.system.schemas.auth import CacheName, CacheNamespace
1919
from apps.system.schemas.permission import SqlbotPermission, require_permissions
@@ -177,13 +177,11 @@ def iterfile():
177177
@router.patch('/ui', summary=f"{PLACEHOLDER_PREFIX}assistant_ui_api", description=f"{PLACEHOLDER_PREFIX}assistant_ui_api")
178178
@require_permissions(permission=SqlbotPermission(role=['ws_admin']))
179179
@system_log(LogConfig(operation_type=OperationType.UPDATE, module=OperationModules.APPLICATION, result_id_expr="id"))
180-
async def ui(session: SessionDep, data: str = Form(), files: List[UploadFile] = []):
180+
async def ui(session: SessionDep, current_user: CurrentUser, trans: Trans, data: str = Form(), files: List[UploadFile] = []):
181181
json_data = json.loads(data)
182182
uiSchema = AssistantUiSchema(**json_data)
183183
id = uiSchema.id
184-
db_model = session.get(AssistantModel, id)
185-
if not db_model:
186-
raise ValueError(f"AssistantModel with id {id} not found")
184+
db_model = get_ws_assistant(session, id, current_user, trans)
187185
configuration = db_model.configuration
188186
config_obj = json.loads(configuration) if configuration else {}
189187

@@ -332,12 +330,15 @@ async def add(request: Request, session: SessionDep, current_user: CurrentUser,
332330
@require_permissions(permission=SqlbotPermission(role=['ws_admin']))
333331
@clear_cache(namespace=CacheNamespace.EMBEDDED_INFO, cacheName=CacheName.ASSISTANT_INFO, keyExpression="editor.id")
334332
@system_log(LogConfig(operation_type=OperationType.UPDATE, module=OperationModules.APPLICATION, resource_id_expr="editor.id"))
335-
async def update(request: Request, session: SessionDep, editor: AssistantDTO):
333+
async def update(request: Request, session: SessionDep, current_user: CurrentUser, trans: Trans, editor: AssistantDTO):
336334
id = editor.id
337-
db_model = session.get(AssistantModel, id)
338-
if not db_model:
339-
raise ValueError(f"AssistantModel with id {id} not found")
335+
db_model = get_ws_assistant(session, id, current_user, trans)
340336
update_data = AssistantModel.model_validate(editor)
337+
if not current_user.isAdmin:
338+
# 忽略请求体 oid(含默认值 1):非 admin 不允许跨工作空间移动/接管
339+
update_data.oid = db_model.oid
340+
# create_time 不在请求体中,model_validate 会填默认值 0,更新时保留原值
341+
update_data.create_time = db_model.create_time
341342
db_model.sqlmodel_update(update_data)
342343
session.add(db_model)
343344
session.commit()
@@ -357,10 +358,8 @@ async def get_one(session: SessionDep, id: int = Path(description="ID")):
357358
@require_permissions(permission=SqlbotPermission(role=['ws_admin']))
358359
@clear_cache(namespace=CacheNamespace.EMBEDDED_INFO, cacheName=CacheName.ASSISTANT_INFO, keyExpression="id")
359360
@system_log(LogConfig(operation_type=OperationType.DELETE, module=OperationModules.APPLICATION, resource_id_expr="id"))
360-
async def delete(request: Request, session: SessionDep, id: int = Path(description="ID")):
361-
db_model = session.get(AssistantModel, id)
362-
if not db_model:
363-
raise ValueError(f"AssistantModel with id {id} not found")
361+
async def delete(request: Request, session: SessionDep, current_user: CurrentUser, trans: Trans, id: int = Path(description="ID")):
362+
db_model = get_ws_assistant(session, id, current_user, trans)
364363
session.delete(db_model)
365364
session.commit()
366365
dynamic_upgrade_cors(request=request, session=session)

‎backend/apps/system/crud/assistant_manage.py‎

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11

22

33
from typing import Optional
4-
from fastapi import FastAPI, Request
4+
from fastapi import FastAPI, HTTPException, Request
55
from sqlmodel import Session, select
66
from starlette.middleware.cors import CORSMiddleware
7-
from apps.system.schemas.system_schema import AssistantBase
7+
from apps.system.schemas.system_schema import AssistantBase, UserInfoDTO
88
from common.core.config import settings
99
from apps.system.models.system_model import AssistantModel
1010
from common.utils.time import get_timestamp
@@ -60,4 +60,16 @@ async def save(request: Request, session: Session, creator: AssistantBase, oid:
6060
session.add(db_model)
6161
session.commit()
6262
dynamic_upgrade_cors(request=request, session=session)
63-
return db_model
63+
return db_model
64+
65+
66+
def get_ws_assistant(session: Session, id: int, current_user: UserInfoDTO, trans=None) -> AssistantModel:
67+
"""按 id 加载小助手并校验工作空间归属。资源不存在与越权访问统一返回 404,不泄露资源存在性。
68+
69+
系统管理员(isAdmin=True)保留跨工作空间管理能力。
70+
"""
71+
db_model = session.get(AssistantModel, id)
72+
if not db_model or (not current_user.isAdmin and db_model.oid != current_user.oid):
73+
raise HTTPException(status_code=404,
74+
detail=trans('i18n_permission.permission_resource_limit') if trans else 'Assistant not found')
75+
return db_model

0 commit comments

Comments
 (0)