fix: MCP更新系ツールにロール権限チェックが無い #40

Closed
opened 2026-09-18 03:52:00 +00:00 by joe · 1 comment
Owner

概要

MCP(JSON-RPC /mcp)経由の更新系ツールにロール権限チェックがありません。POST /mcp はルーターレベルで require_read のみであり、dispatch_tool はユーザー/ロールを受け取らず、ツールごとの必要権限を検証していません。

問題

  • src/o2/api/__init__.py:207 で mcp_router に _auth_deps(require_read)が付与
  • src/o2/mcp/tools.py の dispatch_tool は tenant_id のみ受け取り、_get_current_user やロールを見ない
  • 結果、Role.VIEWER のトークンでも create_journal / create_product / create_delivery_note / complete_delivery_note などの更新系 MCP ツールを実行できる(各ツールは tenant scope のみ検証)

影響

  • 閲覧者ロールが API 直叩きで MCP 更新ツールを通してデータを作成・変更できる
  • #39 で REST の変更系は write 必須にしたが、MCP が抜け道になっている

対応方針

  1. MCP ハンドラに認証コンテキスト(_get_current_user)を渡し、ツール定義に必要権限(read/write、場合により manage)を持たせる
  2. tools/call 実行時に、呼び出したユーザーのロールが必要権限を満たすか検証(不足時は JSON-RPC エラー)
  3. tools/list などの参照系は read のまま
  4. 監査: MCP 経由の書き込みを監査ログに残す(任意)

受け入れ条件

  • Role.VIEWER が更新系 MCP ツールを呼ぶと権限エラー
  • Role.ACCOUNTANT/ADMIN は従来どおり実行可能
  • 参照系ツールは Role.VIEWER でも成功
  • tests/test_mcp*.py が緑(権限テストを追加)

証拠

  • src/o2/api/__init__.py:207
  • src/o2/mcp/tools.py(dispatch_tool)
  • #39 のレビュー所見

関連

  • #38(masters/write/accounts)、#39(全REST変更系)
## 概要 MCP(JSON-RPC `/mcp`)経由の更新系ツールにロール権限チェックがありません。`POST /mcp` はルーターレベルで `require_read` のみであり、`dispatch_tool` はユーザー/ロールを受け取らず、ツールごとの必要権限を検証していません。 ## 問題 - `src/o2/api/__init__.py:207` で `mcp_router` に `_auth_deps`(`require_read`)が付与 - `src/o2/mcp/tools.py` の `dispatch_tool` は `tenant_id` のみ受け取り、`_get_current_user` やロールを見ない - 結果、`Role.VIEWER` のトークンでも `create_journal` / `create_product` / `create_delivery_note` / `complete_delivery_note` などの更新系 MCP ツールを実行できる(各ツールは tenant scope のみ検証) ## 影響 - 閲覧者ロールが API 直叩きで MCP 更新ツールを通してデータを作成・変更できる - #39 で REST の変更系は write 必須にしたが、MCP が抜け道になっている ## 対応方針 1. MCP ハンドラに認証コンテキスト(`_get_current_user`)を渡し、ツール定義に必要権限(read/write、場合により manage)を持たせる 2. `tools/call` 実行時に、呼び出したユーザーのロールが必要権限を満たすか検証(不足時は JSON-RPC エラー) 3. `tools/list` などの参照系は read のまま 4. 監査: MCP 経由の書き込みを監査ログに残す(任意) ## 受け入れ条件 - [ ] `Role.VIEWER` が更新系 MCP ツールを呼ぶと権限エラー - [ ] `Role.ACCOUNTANT`/`ADMIN` は従来どおり実行可能 - [ ] 参照系ツールは `Role.VIEWER` でも成功 - [ ] `tests/test_mcp*.py` が緑(権限テストを追加) ## 証拠 - `src/o2/api/__init__.py:207` - `src/o2/mcp/tools.py`(`dispatch_tool`) - #39 のレビュー所見 ## 関連 - #38(masters/write/accounts)、#39(全REST変更系)
Author
Owner

対応完了

MCP tools/call にロール権限検証を追加しました。main ccb5f59 にマージ・push 済み。

実装

  • src/o2/mcp/tools.py:
    • TOOL_PERMISSIONS でツール別必要権限を定義(参照系4=read / 更新系4=write)。未定義ツールは安全側で write
    • MCPPermissionError を追加
    • dispatch_tool(name, arguments, tenant_id, user=None) に user を追加し、PermissionChecker.check で検証
  • src/o2/mcp/server.py:
    • mcp_handler に user: User = Depends(_get_current_user) を追加し dispatch_tool(..., user=user) へ配線
    • 権限不足は JSON-RPC error(日本語メッセージ)で返却

権限マップ

read write
list_accounts / get_trial_balance / get_income_statement / get_balance_sheet create_journal / create_product / create_delivery_note / complete_delivery_note

テスト

  • 新規 tests/test_mcp_permissions.py: VIEWER の更新系ツール → error、VIEWER の参照系 → 成功、ACCOUNTANT の更新系 → 成功、tools/list/initialize は VIEWER でも成功、未認証 → 401
  • MCP スイート 31 passed / コア 5349 passed, 0 failed
  • 検証: TOOL_MAP と TOOL_PERMISSIONS のキー一致を確認

成果物

  • コミット 075342c → main ccb5f59

補足・残課題

  • stdio モード(src/o2/mcp/__main__.py)は認証コンテキストを持たないローカル実行のため user なしで dispatch_tool を呼びます(権限チェックはスキップ)。HTTP /mcp は常に user を渡すため検証されます。stdio を外部公開する場合は別途対応が必要
  • これで #38/#39/#40 の権限強化が完了し、REST・MCP の変更系はいずれも write 権限を要求します

本 Issue は完了につきクローズします。

## 対応完了 MCP `tools/call` にロール権限検証を追加しました。main `ccb5f59` にマージ・push 済み。 ### 実装 - `src/o2/mcp/tools.py`: - `TOOL_PERMISSIONS` でツール別必要権限を定義(参照系4=read / 更新系4=write)。未定義ツールは安全側で `write` - `MCPPermissionError` を追加 - `dispatch_tool(name, arguments, tenant_id, user=None)` に `user` を追加し、`PermissionChecker.check` で検証 - `src/o2/mcp/server.py`: - `mcp_handler` に `user: User = Depends(_get_current_user)` を追加し `dispatch_tool(..., user=user)` へ配線 - 権限不足は JSON-RPC `error`(日本語メッセージ)で返却 ### 権限マップ | read | write | |---|---| | `list_accounts` / `get_trial_balance` / `get_income_statement` / `get_balance_sheet` | `create_journal` / `create_product` / `create_delivery_note` / `complete_delivery_note` | ### テスト - 新規 `tests/test_mcp_permissions.py`: VIEWER の更新系ツール → error、VIEWER の参照系 → 成功、ACCOUNTANT の更新系 → 成功、`tools/list`/`initialize` は VIEWER でも成功、未認証 → 401 - MCP スイート 31 passed / コア **5349 passed, 0 failed** - 検証: `TOOL_MAP` と `TOOL_PERMISSIONS` のキー一致を確認 ### 成果物 - コミット `075342c` → main `ccb5f59` ### 補足・残課題 - stdio モード(`src/o2/mcp/__main__.py`)は認証コンテキストを持たないローカル実行のため `user` なしで `dispatch_tool` を呼びます(権限チェックはスキップ)。HTTP `/mcp` は常に `user` を渡すため検証されます。stdio を外部公開する場合は別途対応が必要 - これで #38/#39/#40 の権限強化が完了し、REST・MCP の変更系はいずれも `write` 権限を要求します 本 Issue は完了につきクローズします。
joe closed this issue 2026-09-18 04:11:21 +00:00
Sign in to join this conversation.
No labels
h1-request
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
joe/o2#40
No description provided.