Skip to content

fix(workstation): hide tools the requester lacks view_tool on (IKABQ0) - #2354

Open
yaojin3616 wants to merge 1 commit into
mainfrom
fix/workstation-tool-permission-visibility
Open

fix(workstation): hide tools the requester lacks view_tool on (IKABQ0)#2354
yaojin3616 wants to merge 1 commit into
mainfrom
fix/workstation-tool-permission-visibility

Conversation

@yaojin3616

Copy link
Copy Markdown
Collaborator

Summary

The end-user workspace chat toolbar (GET /api/v1/workstation/config) was
returning every tool the admin had configured for daily chat, with no
intersection against the caller's view_tool / use_tool permission. Any
logged-in end user could therefore see the full set of API and MCP tools
the admin had pinned for the daily-chat agent, regardless of whether
they had been granted access. This was a leak of the admin workbench
surface into the end-user chat surface.

Root cause

WorkStationService.aget_config() /
get_daily_chat_config() / get_linsight_config() returned the
admin-configured WorkstationConfig.tools list verbatim, with no
per-user permission intersection. The platform tool list path
(tool.domain.services.tool.ToolServices.get_tool_list) already does
this via ToolPermissionService.filter_tool_ids_by_permission_async
when listing tools in the admin UI; the workstation path did not.

Fix

  • Add WorkStationService._afilter_tools_by_view_permission, which pipes
    the projected tool list through
    ToolPermissionService.filter_tool_ids_by_permission_async(user_id, ids, "view_tool") before returning.
  • Wire login_user through the public service methods
    (aget_config, get_daily_chat_config,
    get_daily_chat_config_with_meta, get_linsight_config,
    get_linsight_config_with_meta) and their internal projection
    helpers. Update endpoint handlers (/config, /config/daily,
    /config/linsight, /app/recommended) and the chat service to pass
    the caller's login_user.
  • Filter is fail-closed on permission-probe errors (no leak), bypassed
    for admins (config page still echoes every configured tool), and a
    no-op when login_user is None (legacy/test paths keep prior
    behaviour).

Files

  • src/backend/bisheng/workstation/domain/services/workstation_service.py
    — add helper, propagate login_user, apply filter in both inherited
    and non-inherited branches.
  • src/backend/bisheng/workstation/api/endpoints/config.py — pass
    login_user to the three public service methods.
  • src/backend/bisheng/workstation/api/endpoints/apps.py — pass
    login_user to aget_config.
  • src/backend/bisheng/workstation/domain/services/chat_service.py
    pass login_user to the two aget_config callers.
  • src/backend/test/workstation/test_workstation_tool_permission_filter.py
    — regression coverage: non-admin-without-permission, admin bypass,
    no-login-user, fail-closed, empty list, and missing-id cases.

Test plan

  • New regression test in
    test/workstation/test_workstation_tool_permission_filter.py covers
    the helper's six behavioural branches.
  • Existing test/workstation/* suite to confirm no regressions
    (all callers updated, the new login_user parameter is keyword-only
    with a default of None so mocks that don't pass it still work).
  • Manual: log in as a non-admin user without view_tool on a
    configured API/MCP tool, open the chat workspace, confirm the tool
    no longer appears in the agent tool selector. Re-test as an admin
    and confirm the tool is still visible.

Risk

Low. The change is additive (a new login_user keyword argument on
the public methods) and the filter is bypassed when no user is passed
so existing tests / legacy call paths that don't yet pass login_user
keep their prior behaviour.

Closes IKABQ0.

The end-user workspace chat toolbar (GET /api/v1/workstation/config) was
returning every tool the admin had configured for daily chat, with no
intersection against the caller's view_tool / use_tool permission. Any
logged-in end user could therefore see the full set of API and MCP
tools the admin had pinned for the daily-chat agent, regardless of
whether they had been granted access. This was a leak of the admin
workbench surface into the end-user chat surface.

Mirror the platform tool list path (tool.domain.services.tool):
pipe the projected tool list through
ToolPermissionService.filter_tool_ids_by_permission_async(user_id,
ids, "view_tool") before returning. Wire login_user through
aget_config / get_daily_chat_config / get_daily_chat_config_with_meta /
get_linsight_config / get_linsight_config_with_meta and update the
endpoint handlers (config, apps) and the chat service to pass it. The
filter is fail-closed on permission-probe errors (no leak), bypassed
for admins (config page still echoes every configured tool), and a
no-op when login_user is None (legacy/test paths keep prior behaviour).

Regression coverage in
test/workstation/test_workstation_tool_permission_filter.py covers the
non-admin-without-permission, admin-bypass, no-login-user, fail-closed,
empty-list, and missing-id cases.

Closes IKABQ0.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant