Fix: missing authentication on agent file upload and download endpoints (#14854)

### What problem does this PR solve?

Closes #14853

The `/agents/download` and `/agents/<agent_id>/upload` endpoints in the
agent API are missing `@login_required` and `@add_tenant_id_to_kwargs`
decorators, allowing unauthenticated access. This is a security issue —
any user can upload files to or download files from an agent without
being logged in. Additionally, the upload endpoint bypasses canvas
access control (`@_require_canvas_access_async`).
This PR adds the missing authentication and authorization decorators to
both endpoints and replaces the manual `user_id` / `created_by` lookups
with the `tenant_id` provided by the auth middleware, making these
endpoints consistent with the rest of the agent API.
### Type of change
- [x] Bug Fix (non-breaking change which fixes an issue)
This commit is contained in:
dale053
2026-05-13 22:48:41 -07:00
committed by GitHub
parent f0122179dd
commit 714f777fa0
2 changed files with 143 additions and 14 deletions

View File

@@ -245,10 +245,12 @@ def delete_agent_session_item(agent_id, session_id, tenant_id):
@manager.route("/agents/download", methods=["GET"]) # noqa: F821
async def download_agent_file():
@login_required
@add_tenant_id_to_kwargs
async def download_agent_file(tenant_id):
id = request.args.get("id")
created_by = request.args.get("created_by")
blob = FileService.get_blob(created_by, id)
logging.info("Agent file download requested: tenant_id=%s file_id=%s", tenant_id, id)
blob = await thread_pool_exec(FileService.get_blob, tenant_id, id)
return Response(blob)
@@ -482,22 +484,34 @@ async def create_agent(tenant_id):
@manager.route("/agents/<agent_id>/upload", methods=["POST"]) # noqa: F821
async def upload_agent_file(agent_id):
exists, canvas = UserCanvasService.get_by_canvas_id(agent_id)
if not exists:
return get_data_error_result(message="canvas not found.")
user_id = canvas["user_id"]
@login_required
@add_tenant_id_to_kwargs
@_require_canvas_access_async
async def upload_agent_file(agent_id, tenant_id):
files = await request.files
file_objs = files.getlist("file") if files and files.get("file") else []
logging.info(
"Agent file upload requested: tenant_id=%s agent_id=%s file_count=%s",
tenant_id,
agent_id,
len(file_objs),
)
try:
if len(file_objs) == 1:
return get_json_result(
data=FileService.upload_info(user_id, file_objs[0], request.args.get("url"))
uploaded = await thread_pool_exec(
FileService.upload_info, tenant_id, file_objs[0], request.args.get("url")
)
results = [FileService.upload_info(user_id, file_obj) for file_obj in file_objs]
return get_json_result(data=uploaded)
results = await asyncio.gather(
*(thread_pool_exec(FileService.upload_info, tenant_id, file_obj) for file_obj in file_objs)
)
return get_json_result(data=results)
except Exception as exc:
logging.exception(
"Agent file upload failed: tenant_id=%s agent_id=%s",
tenant_id,
agent_id,
)
return server_error_response(exc)