Fix team member cannot edit agent (#14612)

### What problem does this PR solve?

Follow on PR: https://github.com/infiniflow/ragflow/pull/14602
to fix: team member cannot edit agent.
new behavior: beside delete, everything is allowed for team member.

### Type of change

- [x] Bug Fix (non-breaking change which fixes an issue)
This commit is contained in:
Wang Qi
2026-05-07 15:09:13 +08:00
committed by GitHub
parent 1d114f034b
commit c50028b1f3
4 changed files with 46 additions and 46 deletions

View File

@@ -72,7 +72,27 @@ def _require_canvas_access_sync(func):
@wraps(func)
def wrapper(*args, **kwargs):
if not UserCanvasService.accessible(kwargs.get('agent_id'), kwargs.get('tenant_id')):
return get_json_result(data=False, message="Only owner of canvas authorized for this operation.", code=RetCode.OPERATING_ERROR)
return get_json_result(data=False, message="Make sure you have permission to access the agent.", code=RetCode.OPERATING_ERROR)
return func(*args, **kwargs)
return wrapper
def _require_canvas_access_async(func):
@wraps(func)
async def wrapper(*args, **kwargs):
agent_id = kwargs.get('agent_id')
tenant_id = kwargs.get('tenant_id')
if not await thread_pool_exec(UserCanvasService.accessible, agent_id, tenant_id):
return get_json_result(data=False, message="Make sure you have permission to access the agent.", code=RetCode.OPERATING_ERROR)
return await func(*args, **kwargs)
return wrapper
def _require_canvas_owner_sync(func):
@wraps(func)
def wrapper(*args, **kwargs):
if not UserCanvasService.query(user_id=kwargs.get('tenant_id'), id=kwargs.get('agent_id')):
return get_json_result(data=False, message="Only the owner of the agent is authorized for this operation.", code=RetCode.OPERATING_ERROR)
return func(*args, **kwargs)
return wrapper
@@ -172,6 +192,7 @@ def list_agent_sessions(agent_id, tenant_id):
@manager.route("/agents/<agent_id>/sessions", methods=["POST"]) # noqa: F821
@login_required
@add_tenant_id_to_kwargs
@_require_canvas_access_async
async def create_agent_session(agent_id, tenant_id):
req = await get_request_json()
user_id = req.get("user_id") or request.args.get("user_id", tenant_id)
@@ -422,18 +443,12 @@ async def upload_agent_file(agent_id):
@manager.route("/agents/<agent_id>/components/<component_id>/input-form", methods=["GET"]) # noqa: F821
@login_required
@add_tenant_id_to_kwargs
@_require_canvas_access_sync
def get_agent_component_input_form(agent_id, component_id, tenant_id):
try:
exists, user_canvas = UserCanvasService.get_by_id(agent_id)
if not exists:
return get_data_error_result(message="canvas not found.")
if not UserCanvasService.query(user_id=tenant_id, id=agent_id):
return get_json_result(
data=False,
message="Only owner of canvas authorized for this operation.",
code=RetCode.OPERATING_ERROR,
)
canvas = Canvas(json.dumps(user_canvas.dsl), tenant_id, canvas_id=user_canvas.id)
return get_json_result(data=canvas.get_component_input_form(component_id))
except Exception as exc:
@@ -444,14 +459,9 @@ def get_agent_component_input_form(agent_id, component_id, tenant_id):
@validate_request("params")
@login_required
@add_tenant_id_to_kwargs
@_require_canvas_access_async
async def debug_agent_component(agent_id, component_id, tenant_id):
req = await get_request_json()
if not UserCanvasService.accessible(agent_id, tenant_id):
return get_json_result(
data=False,
message="Only owner of canvas authorized for this operation.",
code=RetCode.OPERATING_ERROR,
)
try:
_, user_canvas = UserCanvasService.get_by_id(agent_id)
canvas = Canvas(json.dumps(user_canvas.dsl), tenant_id, canvas_id=user_canvas.id)
@@ -569,14 +579,8 @@ def get_agent_logs(agent_id, message_id, tenant_id):
@manager.route("/agents/<agent_id>", methods=["DELETE"]) # noqa: F821
@login_required
@add_tenant_id_to_kwargs
@_require_canvas_owner_sync
def delete_agent(agent_id, tenant_id):
if not UserCanvasService.query(user_id=tenant_id, id=agent_id):
return get_json_result(
data=False,
message="Only owner of canvas authorized for this operation.",
code=RetCode.OPERATING_ERROR,
)
UserCanvasService.delete_by_id(agent_id)
return get_json_result(data=True)
@@ -584,9 +588,9 @@ def delete_agent(agent_id, tenant_id):
@manager.route("/agents/<agent_id>", methods=["PUT"]) # noqa: F821
@login_required
@add_tenant_id_to_kwargs
@_require_canvas_access_async
async def update_agent(agent_id, tenant_id):
req = {k: v for k, v in (await get_request_json()).items() if v is not None}
req["user_id"] = tenant_id
req["release"] = bool(req.get("release", ""))
if req.get("dsl") is not None:
@@ -602,13 +606,6 @@ async def update_agent(agent_id, tenant_id):
if req.get("title") is not None:
req["title"] = req["title"].strip()
if not UserCanvasService.query(user_id=tenant_id, id=agent_id):
return get_json_result(
data=False,
message="Only owner of canvas authorized for this operation.",
code=RetCode.OPERATING_ERROR,
)
_, current_agent = UserCanvasService.get_by_id(agent_id)
agent_title_for_version = req.get("title") or (current_agent.title if current_agent else "")
canvas_category = (
@@ -642,14 +639,8 @@ async def update_agent(agent_id, tenant_id):
@manager.route("/agents/<agent_id>/reset", methods=["POST"]) # noqa: F821
@login_required
@add_tenant_id_to_kwargs
@_require_canvas_access_async
async def reset_agent(agent_id, tenant_id):
if not UserCanvasService.accessible(agent_id, tenant_id):
return get_json_result(
data=False,
message="Only owner of canvas authorized for this operation.",
code=RetCode.OPERATING_ERROR,
)
try:
exists, user_canvas = UserCanvasService.get_by_id(agent_id)
if not exists:
@@ -911,10 +902,11 @@ async def agent_chat_completion(tenant_id, agent_id=None):
runtime_user_id = req.get("user_id") or tenant_id
user_id = str(runtime_user_id)
custom_header = req.get("custom_header", "")
if not await thread_pool_exec(UserCanvasService.accessible, agent_id, tenant_id):
if not UserCanvasService.accessible(agent_id, tenant_id):
return get_json_result(
data=False,
message="Only owner of canvas authorized for this operation.",
message="Make sure you have permission to access the agent.",
code=RetCode.OPERATING_ERROR,
)

View File

@@ -221,8 +221,6 @@ class UserCanvasService(CommonService):
e, cvs = cls.get_by_id(agent_id)
if not e:
raise LookupError("Agent not found.")
if tenant_id and cvs.user_id != tenant_id:
raise PermissionError("You do not own the agent.")
if release_mode:
released_version = UserCanvasVersionService.get_latest_released(agent_id)

View File

@@ -108,8 +108,8 @@ class TestAgentSessions:
update_url = f"{HOST_ADDRESS}/api/{VERSION}/agents/invalid-agent-id"
res = requests.put(update_url, auth=HttpApiAuth, json={"title": "updated", "dsl": MINIMAL_DSL}).json()
assert res["code"] == 103, res
assert "Only owner of canvas authorized" in res["message"], res
assert "Make sure you have permission to access the agent." in res["message"], res
res = delete_agent(HttpApiAuth, "invalid-agent-id")
assert res["code"] == 103, res
assert "Only owner of canvas authorized" in res["message"], res
assert "Only the owner of the agent is authorized for this operation." in res["message"], res

View File

@@ -568,12 +568,17 @@ def test_agents_crud_unit_branches(monkeypatch):
return {"dsl": {"nodes": []}, "title": " webhook-agent ", "unused": None}
monkeypatch.setattr(module, "get_request_json", req_update)
monkeypatch.setattr(module.UserCanvasService, "query", lambda **_kwargs: False)
res = _run(module.update_agent.__wrapped__("agent-1", "tenant-1"))
monkeypatch.setattr(module.UserCanvasService, "accessible", lambda *_a, **_kw: False)
@module._require_canvas_access_async
async def _dummy_update(agent_id, tenant_id):
return module.get_json_result(data=True)
res = _run(_dummy_update(agent_id="agent-1", tenant_id="tenant-1"))
assert res["code"] == module.RetCode.OPERATING_ERROR
calls = {"update": 0, "save_or_replace_latest": 0, "replace_for_set": 0}
monkeypatch.setattr(module.UserCanvasService, "query", lambda **_kwargs: True)
monkeypatch.setattr(module.UserCanvasService, "accessible", lambda *_a, **_kw: True)
monkeypatch.setattr(
module.UserCanvasService,
"get_by_id",
@@ -599,7 +604,12 @@ def test_agents_crud_unit_branches(monkeypatch):
assert calls == {"update": 1, "save_or_replace_latest": 1, "replace_for_set": 1}
monkeypatch.setattr(module.UserCanvasService, "query", lambda **_kwargs: False)
res = module.delete_agent.__wrapped__("agent-1", "tenant-1")
@module._require_canvas_owner_sync
def _dummy_delete(agent_id, tenant_id):
return module.get_json_result(data=True)
res = _dummy_delete(agent_id="agent-1", tenant_id="tenant-1")
assert res["code"] == module.RetCode.OPERATING_ERROR