FR-048 Phase 4a 交接 prompt(軸④ capability guard+過渡名單換裝)

下面整段是給執行 session 的 prompt。前置:Phase 3 過渡名單已齊(矩陣 §6.3)。 與 4b(signed-token)檔案面不重疊,但不要同時跑(同一 working tree)。


【接手主題】FR-048 Phase 4a — 建 common/authz/capability.py(軸④ RBAC 能力點守門)、seed 能力點、把 Phase 3 的 @require_super_admin_route 過渡名單逐支換 @require_capability

必讀(照順序):

  1. docs/analysis/2026-07-07-unified-auth-guard-design.md §3.3(P3 拍板:不 enrich context,per-request 查+flask.g 快取)、§4(capability guard 形狀)、§2.0(super-admin 降格 break-glass)
  2. 矩陣 §6.3 過渡名單=工作清單(各端點群的建議能力點名都列好了)
  3. docs/claude/sql-migration-conventions.md+權限 SOP(docs/system-design/permission/,5 步驟:capabilities → ui_routes → route_capabilities → role_capabilities)
  4. 可抄模板:scripts/sql/2026-06-19-fr039-file-agent-manage-permission.sql(caps+role_capabilities 灌法)、2026-06-29-platform-capability-flag-and-guard.sql(is_platform)

【branch】fix/v1.8.0-bugs,禁止切 branch。不動 jedi- 套件*(capability 查詢用 jedi-auth 既有 service/association 唯讀,先 grep 既有查法——web-menu 的 user→roles→role.capabilities 鏈就是現成的,禁另寫查詢路徑)。

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 【1. common/authz/capability.py

def require_capability(*names, mode="any"):
    """Route decorator(軸④):當前 user 經 user→roles→role_capabilities 需持有指定能力點。"""

設計要點(P3 拍板 + §2.0):

  • 查詢:guard 內委派 DI 注入的 CapabilityGuard service(自帶 @transaction),走 jedi-auth 既有 user/role 查詢鏈取能力點名集合;結果存 flask.g per-request memoize(同 request 第二次守門不再查)。
  • break-glassviewer_is_super_admin() 為 True 直接放行(super-admin 是旁路 flag,防止換裝後把平台操作者鎖在門外——帳號層 super_admin 不必然持有 Administrator 角色)。docstring 寫明此語意。
  • 掛法順序同既有慣例:@jwt_required() 外、@require_capability(...) 內、@inject 最內。
  • 錯誤碼:GRC_CAPABILITY_REQUIREDgrc_error_code.py 403 序號續編,新增)。
  • 單元測試:有能力點過/無能力點 403/super-admin bypass/memoize(同 request 只查一次)。

【2. Seeding SQL migration(一支檔案)】

  • 對照矩陣 §6.3 建議能力點名(user.managerole.manageorg_unit.managesystem_config.managesystem_menu.managedevice.manageinformation_system.managebulletin.managenotification.testauth_provider.manageremote_agent.managefeedback.exportmodule_frame.manage、survey/flow_template 依 §6.3 完整名單),先 SELECT 現有 capabilities 表比對,缺的才 INSERT(幂等:WHERE NOT EXISTS)。
  • role_capabilities 配給 Administrator 系統角色(tenant_id IS NULL,跨環境用 name 查不 hardcode id)。
  • 同步 seed 軸①的 platform 級能力點is_platform=true,如 tenant.managelog.manage——為 Phase 5 併軸鋪路),但軸① decorator 本波不換(仍 @require_platform_admin_route)。
  • SQL 鐵則照 conventions:檔頭 -- Date:、逐語句日期註解、新表才需 GRANT(本波無新表)、收尾 INSERT public.schema_migrationspsql --single-transaction -v ON_ERROR_STOP=1cmmgr 套 DEV。只套 DEV,STG/POC 列入交付清單等 user 指示。

【3. 過渡名單換裝】

  • 矩陣 §6.3 逐端點群:@require_super_admin_route@require_capability("<能力點>")(一行換一行)。
  • 換完 grep -rn "require_super_admin_route" api/只剩零星未定調者(若剩,列清單回報);矩陣 §6.3 逐列銷帳。
  • 語意變化要點(寫進手測 checklist):換裝後判定從「帳號層 super_admin」變「持有能力點(或 super_admin bypass)」——持 Administrator 角色的一般帳號現在也能過(原本會 403)。這是放寬到 RBAC 正軌,符合設計;但要在回報中明示,讓 user 知道權限面變了誰。

【4. 驗證】

  • 單測+baseline worktree 集合 diff(新增失敗必須 0)。
  • DEV 實測(migration 套完後):無能力點的一般帳號打 system_config 寫入 → 403 GRC_CAPABILITY_REQUIRED;配了能力點的角色帳號 → 過;super_admin → 過。
  • FE 角色管理 UI 打開能看到新能力點可勾(capabilities 表 UI 直讀);名稱顯示若是 raw name 需不需要 i18n → 查 FE 現有 capability 顯示慣例後回報(不擅自加翻譯管線)。

【commit】分批:① capability.py+測試;② SQL migration;③ 換裝(可按模組再分)。顯式 add、禁 -am、不 push。收尾:矩陣銷帳+一句話 status+手測 checklist(含語意變化說明),收尾動作等 user 下令。