下面整段是給執行 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。
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 【本波鐵則】
require_super_admin(),只建不掛。━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 【要做的事】
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 原名,例如:
# 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 風險低。
common/authz/admin.pydef 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)。is_admin=角色層 any(role.is_admin==1); UserContextDTO.is_admin=帳號層 user.is_super_admin。本 helper 走後者;要角色層語意請走 capability 軸(Phase 4)。common/authz/platform.py 的寫法風格(同為純 context 判定)。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」,行為不動。
docs/claude/domain-capabilities.md 補一列:授權守門 canonical=common/authz/ (五軸:platform / super-admin / project-role / capability(Phase 4) / signed-token(Phase 4)), 舊四路徑為 deprecated shim。這正是該索引「同功能多支、canonical 不明顯」要收的陷阱類。common/authz/(決策表在其 __init__.py),禁止另立新 helper━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 【驗證(做完必跑)】
pytest test/ 全綠(participant / SSP 守門既有測試=搬家回歸網)。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" 級別的煙測即可)。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 的事,本波不要越界。