fix: 書き込み系APIが require_read のみ(VIEWERでもマスタ変更可能) #38

Closed
opened 2026-09-17 15:56:18 +00:00 by joe · 1 comment
Owner

概要

書き込み系(POST/PUT/DELETE)の一部 API が require_read しか要求しておらず、Role.VIEWER でもマスタの作成・更新・無効化が可能です。P2 マスタ画面の実装でこの操作導線が UI に露出したため、別タスクとして記録します。

根拠

  • src/o2/api/__init__.py:158 付近で _auth_deps = [Depends(require_read)] を accounts_router / write_router / masters_router 等に付与している
  • 各書き込みルートには require_write が個別指定されていない
  • 例: src/o2/api/masters.py の POST /customers、PUT /customers/{id}、POST /customers/{id}/deactivate、POST /products(write.py)、POST /accounts(accounts.py)
  • Web UI は getToken() の有無しか見ず、ロールでボタンを出し分けていない

影響

  • 閲覧者ロールのユーザーが API 直叩き/UI 経由でマスタを変更・無効化できる
  • 監査上も「誰が変更したか」以前に「変更できてしまう」状態

対応方針(要検討)

  1. 書き込みルートに require_write を付与する(ルーター単位 or ルート単位)。get_current_user のロール判定は PermissionChecker を使用
  2. テナント境界(verify_tenant_ownership)は既存のまま維持
  3. Web UI 側でロールに応じて作成/編集/無効化ボタンを出し分け(任意)

受け入れ条件

  • Role.VIEWER のトークンで書き込み API が 403
  • Role.ACCOUNTANT/ADMIN は従来どおり操作可能
  • 既存テストが緑(必要なら VIEWER の 403 テストを追加)

証拠

  • src/o2/api/__init__.py:158 / src/o2/api/masters.py / src/o2/api/write.py / src/o2/api/accounts.py
  • P2 レビュー(#29 のコメント)で検出
## 概要 書き込み系(POST/PUT/DELETE)の一部 API が `require_read` しか要求しておらず、`Role.VIEWER` でもマスタの作成・更新・無効化が可能です。P2 マスタ画面の実装でこの操作導線が UI に露出したため、別タスクとして記録します。 ## 根拠 - `src/o2/api/__init__.py:158` 付近で `_auth_deps = [Depends(require_read)]` を `accounts_router` / `write_router` / `masters_router` 等に付与している - 各書き込みルートには `require_write` が個別指定されていない - 例: `src/o2/api/masters.py` の `POST /customers`、`PUT /customers/{id}`、`POST /customers/{id}/deactivate`、`POST /products`(`write.py`)、`POST /accounts`(`accounts.py`) - Web UI は `getToken()` の有無しか見ず、ロールでボタンを出し分けていない ## 影響 - 閲覧者ロールのユーザーが API 直叩き/UI 経由でマスタを変更・無効化できる - 監査上も「誰が変更したか」以前に「変更できてしまう」状態 ## 対応方針(要検討) 1. 書き込みルートに `require_write` を付与する(ルーター単位 or ルート単位)。`get_current_user` のロール判定は `PermissionChecker` を使用 2. テナント境界(`verify_tenant_ownership`)は既存のまま維持 3. Web UI 側でロールに応じて作成/編集/無効化ボタンを出し分け(任意) ## 受け入れ条件 - [ ] `Role.VIEWER` のトークンで書き込み API が 403 - [ ] `Role.ACCOUNTANT`/`ADMIN` は従来どおり操作可能 - [ ] 既存テストが緑(必要なら VIEWER の 403 テストを追加) ## 証拠 - `src/o2/api/__init__.py:158` / `src/o2/api/masters.py` / `src/o2/api/write.py` / `src/o2/api/accounts.py` - P2 レビュー(#29 のコメント)で検出
Author
Owner

部分対応完了(UI露出モジュール)

書き込み系 API の権限不足について、Web UI から露出している3モジュールの変更系ルートに require_write を付与しました。main ebcfc14 にマージ・push 済み。

対応

モジュール 付与ルート数
src/o2/api/masters.py 20
src/o2/api/write.py 3
src/o2/api/accounts.py 2
合計 25
  • 方式: 各変更系 decorator に dependencies=[Depends(require_write)](GET には付与しない)
  • 回帰テスト tests/test_write_permissions.py(15件): VIEWER で変更系が 403、ACCOUNTANT で 200/201、VIEWER の GET は 200 のまま、未認証は 401
  • コアスイート: 5282 passed, 0 failed

未対応(フォローアップ #39)

_auth_deps = [Depends(require_read)] は他多数のルーターにも付与されており、変更系に require_write が無いモジュールが残っています(sales_orders / purchase_orders / inventory / automation / payroll / tax / bank_reconciliation / ec / fixed_assets 等)。全体監査は #39 として起票しました。

本 Issue は UI露出分の是正が完了したためクローズします。

## 部分対応完了(UI露出モジュール) 書き込み系 API の権限不足について、Web UI から露出している3モジュールの変更系ルートに `require_write` を付与しました。main `ebcfc14` にマージ・push 済み。 ### 対応 | モジュール | 付与ルート数 | |---|---| | `src/o2/api/masters.py` | 20 | | `src/o2/api/write.py` | 3 | | `src/o2/api/accounts.py` | 2 | | 合計 | **25** | - 方式: 各変更系 decorator に `dependencies=[Depends(require_write)]`(GET には付与しない) - 回帰テスト `tests/test_write_permissions.py`(15件): VIEWER で変更系が **403**、ACCOUNTANT で 200/201、VIEWER の GET は 200 のまま、未認証は 401 - コアスイート: **5282 passed, 0 failed** ### 未対応(フォローアップ #39) `_auth_deps = [Depends(require_read)]` は他多数のルーターにも付与されており、変更系に `require_write` が無いモジュールが残っています(`sales_orders` / `purchase_orders` / `inventory` / `automation` / `payroll` / `tax` / `bank_reconciliation` / `ec` / `fixed_assets` 等)。全体監査は **#39** として起票しました。 本 Issue は UI露出分の是正が完了したためクローズします。
joe closed this issue 2026-09-17 16:29:31 +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#38
No description provided.