# FR-048 Phase 1 交接 prompt（建 common/authz/ guard library，純搬家）

> 下面整段是給執行 session 的 prompt，複製貼上即可。

---

【接手主題】FR-048 統一授權守門 Phase 1 — 建 `common/authz/` package（純搬家＋一支新 helper，**零行為變更**）

你接手的是已拍板設計的第一波實作。設計、拍板紀錄、各 phase 注意事項全在
`docs/analysis/2026-07-07-unified-auth-guard-design.md`（**必讀，尤其 §0 拍板紀錄、§2 軸模型、§3.2 收斂形狀、§7 Phase 1 注意事項**）。
追蹤表：`docs/analysis/2026-07-07-known-pits-remediation-tracker.md`。

【branch】fix/v1.8.0-bugs，禁止切 branch；不對就停下問 user。

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
【本波鐵則】

- **零行為變更**：不掛任何新守門到任何端點、不寫 decorator、不修任何既有判定邏輯。403 行為必須跟今天一模一樣。
- **不動 jedi-* 套件**（P2 拍板）。
- 唯一新增物是 `require_super_admin()`，**只建不掛**。

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
【要做的事】

### 1. 建 `common/authz/` package（搬家）

| 新檔 | 來源 | 搬法 |
|------|------|------|
| `common/authz/platform.py` | `common/util/permission.py` | 整檔內容搬入（`require_platform_admin` / `viewer_is_platform_admin`） |
| `common/authz/project.py` | `common/util/participant_guard.py` | 整檔搬入（`assert_project_manager`） |
| `common/authz/ssp.py` | `common/middleware/permission/ssp_permission.py` | 整檔搬入（`SspPermissionChecker`） |
| `common/authz/workflow.py` | `common/util/workflow_project_guard.py` | 整檔搬入（**停用中的守門，原樣搬**，函式開頭的 `return` 與 FR-038 2B TODO 註解保留） |
| `common/authz/admin.py` | 新寫 | 見 §2 |
| `common/authz/__init__.py` | 新寫 | 顯式 re-export 全部公開名 + 決策表 docstring（表內容抄設計文件 §3.2 那張「你的端點是… → 用」表） |

**舊檔一律改成 shim（不刪）**，顯式 re-import 原名，例如：

```python
# common/util/permission.py
"""[DEPRECATED 2026-07 FR-048] 已遷移 common/authz/platform.py，新 code 請 import common.authz。"""
from common.authz.platform import require_platform_admin, viewer_is_platform_admin  # noqa: F401
```

（用顯式名字、不用 `import *`；四個舊檔都同樣處理。）

**為什麼要 shim（實查過，別省略）**：22 個檔案 import 舊路徑——participant 5 支 service、
SSP 11 支 service、`di_containers/oscal/oscal_containers.py:75`、`api/auth/routes/ui_route_route.py:9`、
`app/oscal/service/framework_version_edit_service.py:60`、多個測試檔。本波**全部不改 call site**，靠 shim 過。
測試不直接 patch guard 模組路徑（已實查確認，只透過 DI mock 注入），shim 風險低。

### 2. 新 helper `common/authz/admin.py`

```python
def viewer_is_super_admin() -> bool:
    """帳號層 super admin（UserContextDTO.is_admin = user.is_super_admin）。"""

def require_super_admin(error_code=GrcErrorCode.GRC_NOT_ADMIN) -> None:
    """非帳號層 super admin 一律 ForbiddenError。"""
```

- 讀 `get_user_context()`，context 為 None 或 `is_admin` 非 True → 拋 `ForbiddenError(error_code)`（fail-closed）。
- docstring **必須**寫明兩個 is_admin 的地雷：JWT claim 的 `is_admin`＝角色層 `any(role.is_admin==1)`；
  `UserContextDTO.is_admin`＝帳號層 `user.is_super_admin`。本 helper 走後者；要角色層語意請走 capability 軸（Phase 4）。
- 參考 `common/authz/platform.py` 的寫法風格（同為純 context 判定）。
- **本波不掛任何端點**。

### 3. 標註（不修）既有誤用

`common/authz/ssp.py` 的 `_require_admin_for_template`（原 ssp_permission.py:167-175）用了
`getattr(user, "is_admin", False)`——搬家時在該處**加註解**標明「此處為帳號層 super_admin 語意，
dev-stage gate，Phase 3 檢討是否改 require_super_admin/capability」，**行為不動**。

### 4. 文件三處

1. `docs/claude/domain-capabilities.md` 補一列：授權守門 canonical＝`common/authz/`
   （五軸：platform / super-admin / project-role / capability(Phase 4) / signed-token(Phase 4)），
   舊四路徑為 deprecated shim。這正是該索引「同功能多支、canonical 不明顯」要收的陷阱類。
2. CLAUDE.md「權限檢查的正確做法」段落補兩點（P1 已拍板）：
   - 守門一律用 `common/authz/`（決策表在其 `__init__.py`），禁止另立新 helper
   - 主體域守門（platform-admin / super-admin / capability）允許 route 層 decorator 形式
     （實作委派 DI guard service，route 不碰 session）；資源域守門（專案/SSP/workflow）維持 app service 層
3. 設計文件 §7 表 Phase 1 標 ✅（含完成日期）。

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
【驗證（做完必跑）】

1. `pytest test/` 全綠（participant / SSP 守門既有測試＝搬家回歸網）。
2. `grep -rn "common.util.permission\|common.util.participant_guard\|common.middleware.permission.ssp_permission\|common.util.workflow_project_guard" --include="*.py" api/ app/ di_containers/ test/` 列出的每個 call site 確認 shim 供得上原名（跑個 `python -c "from common.util.permission import require_platform_admin"` 級別的煙測即可）。
3. 請 user 重啟 BE（**只說「請重啟 BE」，不附命令**），啟動無 DI 錯誤即可；有異常先看 `log/app.log`。

【commit】可自行 commit（顯式 git add 檔名、禁 -am、不 push）。建議兩個 commit：
① `refactor(FR-048): 建 common/authz/ 收斂守門 helper（舊路徑留 shim，零行為變更）`
② `docs(FR-048): domain-capabilities/CLAUDE.md 登記 authz canonical + decorator 定調`

【收尾】commit 完停下，給一句話 status。spec/SUMMARY/memory 等收尾動作一律等 user 下令。
Phase 2（C 族補守門）/ Phase 3（B 族 decorator）是後續 session 的事，本波不要越界。
