修复普通用户对模型训练的权限操作

This commit is contained in:
wangjiming
2026-08-19 10:32:58 +08:00
parent fed5694796
commit b254fa0985
3 changed files with 60 additions and 8 deletions

View File

@@ -225,12 +225,16 @@ def _require_approval_or_admin(
"""
高风险操作审批旁路:
- admin 用户直接放行(返回 None
- 普通用户创建审批实例返回审批待定响应code=202 None
- 资源创建者Owner直接放行返回 None
- 其他普通用户创建审批实例返回审批待定响应code=202非 None
code=202 使前端响应拦截器走业务错误分支,弹提示并 reject
避免前端误认为删除成功。
"""
if is_admin(current_user):
return None
# 资源创建者直接放行,无需审批
if _check_owner(resource_type, resource_id, current_user.get("id")):
return None
store = get_platform_store()
instance = store.create_approval_instance({
"resource_type": resource_type,
@@ -245,6 +249,28 @@ def _require_approval_or_admin(
}
def _check_owner(resource_type: str, resource_id: str, user_id: str | None) -> bool:
"""直接查数据库判断 user_id 是否为资源的 created_by。"""
from app.core.auth import OWNER_TABLES
table_info = OWNER_TABLES.get(resource_type)
if not table_info:
return False
table, column = table_info
store = get_platform_store()
with store.connect() as conn:
row = conn.execute(f"SELECT {column} FROM {table} WHERE id=?", (resource_id,)).fetchone()
if not row:
return False
owner = row[column]
if column == "payload":
try:
import json
owner = json.loads(owner or "{}").get("created_by")
except (TypeError, ValueError):
owner = None
return owner == user_id
def _node_for_task(task: dict[str, Any]) -> dict[str, Any] | None:
return next((node for node in get_platform_store().compute_nodes() if node["id"] == task.get("compute_node_id")), None)
@@ -1457,8 +1483,11 @@ async def fine_tune_list(current_user: dict = Depends(get_current_user)) -> dict
tasks = get_platform_store().tasks()
if current_user.get("role") == "admin" or current_user.get("protected"):
return ok(tasks)
# 普通用户可见:自己创建的 + ACL 授权的
user_id = current_user.get("id")
accessible = set(filter_accessible_resource_ids("fine-tune", [t["id"] for t in tasks], current_user))
return ok([t for t in tasks if t["id"] in accessible])
result = [t for t in tasks if t.get("created_by") == user_id or t["id"] in accessible]
return ok(result)
@router.post("/fine-tune")
@@ -1740,8 +1769,11 @@ async def model_eval_list(current_user: dict = Depends(get_current_user)) -> dic
tasks = get_platform_store().eval_tasks()
if current_user.get("role") == "admin" or current_user.get("protected"):
return ok(tasks)
# 普通用户可见:自己创建的 + ACL 授权的
user_id = current_user.get("id")
accessible = set(filter_accessible_resource_ids("eval", [t["id"] for t in tasks], current_user))
return ok([t for t in tasks if t["id"] in accessible])
result = [t for t in tasks if t.get("created_by") == user_id or t["id"] in accessible]
return ok(result)
@router.get("/model-eval/{task_id}")
@@ -1777,6 +1809,7 @@ async def model_eval_start(payload: dict[str, Any] = Body(...), current_user: di
"""Start an evaluation task: submit eval job to compute node."""
store = get_platform_store()
# 1. Create eval task record
payload.setdefault("created_by", current_user.get("id"))
task = store.create_eval_task({**payload, "status": "pending"})
# 2. Resolve model path (supports both regular models and trained models)
@@ -1978,8 +2011,11 @@ async def model_compare_list(current_user: dict = Depends(get_current_user)) ->
tasks = get_platform_store().compare_tasks()
if is_admin(current_user):
return ok(tasks)
# 普通用户可见:自己创建的 + ACL 授权的
user_id = current_user.get("id")
accessible = filter_accessible_resource_ids_batch("compare", [item["id"] for item in tasks], current_user)
return ok([item for item in tasks if item["id"] in accessible])
result = [item for item in tasks if item.get("created_by") == user_id or item["id"] in accessible]
return ok(result)
@router.post("/model-compare")

View File

@@ -570,6 +570,16 @@ class PlatformStore:
""")
except Exception:
pass # 列不存在时忽略
# 修复历史数据:为 eval_tasks 表回填 created_by从 payload JSON 中提取)
try:
conn.execute("""
UPDATE eval_tasks SET created_by = payload::json->>'created_by'
WHERE (created_by IS NULL OR created_by = '')
AND payload IS NOT NULL
AND payload::json->>'created_by' IS NOT NULL
""")
except Exception:
pass # 列或语法不支持时忽略
# 清理超过 7 天的操作日志
try:
cutoff = (datetime.now(timezone.utc) - timedelta(days=7)).isoformat()
@@ -2031,6 +2041,7 @@ class PlatformStore:
"process_id": None,
"train_duration": "",
"create_time": now,
"created_by": payload.get("created_by"),
}
with self.connect() as conn:
conn.execute(
@@ -2529,8 +2540,8 @@ class PlatformStore:
if data.get("metric") == "custom":
data["metric"] = data["metric_label"]
conn.execute(
"INSERT INTO eval_tasks (id, name, payload, status, create_time) VALUES (?, ?, ?, ?, ?)",
(task_id, name, json_dumps(data), status, now),
"INSERT INTO eval_tasks (id, name, payload, status, create_time, created_by) VALUES (?, ?, ?, ?, ?, ?)",
(task_id, name, json_dumps(data), status, now, data.get("created_by")),
)
return self.eval_task(task_id)