建議 model:Sonnet 5,effort:medium——語法簡單(一條 policy+四行 ALTER VIEW),但
vw_user_job_queue跨 12 張表、是「我的任務」頁的唯一資料源,改完必須整頁實測。本卡屬 FR-094(母卡見兄弟卡段),.2 開隔離的第 4 棒(整案第 6 棒)。無硬前置,可與 .2a/.2b 平行;但與 .2c 要同一輪驗我的任務頁。
兩件事。①工作流程執行紀錄(workflow_executions,11,607 筆)的隔離規則只寫了新增/改/刪三條,缺「查詢」那條,開關也關著。②四支 view(事先存好的查詢)用「建立 view 的人」(資料庫最高權限帳號)的身分去查底層表,天生繞過所有客戶隔離——PostgreSQL 預設如此,要明確設 security_invoker = true 改成「用正在查的人」的身分。其中 vw_user_job_queue 就是「我的任務」頁的資料源,決策者親測看到別家 39 筆待辦就是它。
首腦核對(2026-09-13 DEV localhost 實查):
compliance.workflow_executions:RLS 關、3 policy(workflow_executions_insert WITH CHECK app_tenant_allowed_for_session(tenant_id);_update USING 同;_delete USING tenant AND app_org_allowed_for_session(org_unit_id))——無 select、三條都沒有 is_super_admin 分支(02-schema.sql:24157-24174)。security_invoker:全部 unset(pg_options_to_table(reloptions) 查)。vw_user_job_queue 讀者:infra/readmodel/tasks/my_grc_jobs_query.py:50(我的任務頁)、infra/readmodel/audit/flow_control_dashboard_repo_impl.py:249(儀表板待辦數);view 定義在 scripts/sql/view/vw_user_job_queue.sql(底層含 job_executions/workflow_executions/projects/task_assignees 等 12 張)。v_user_capabilities 讀者:jedi-iam CapabilityGuard、app/license/adapter/tenant_admin_directory.py(找租戶管理員)——底層 user_roles/role_capabilities/capabilities 三表 JOIN。與 .2b 相依:.2b 給 user_roles 開 RLS 後,本 view 加 invoker 才會真的以呼叫者身分過濾——順序:.2b 先或本棒先都不會壞,但兩棒都收完才算對。v_user_routes/v_role_routes:grep BE 主專案+jedi-iam+jedi-common 零 Python 讀者(選單走 UiRouteRepoImpl.get_viewable_by_user_uid 直查底層表)。v_user_routes 定義引用 v_role_routes。~/Projects/Billows/Audit-Manager/compliance-manager-be/scripts/sql/2026-09-XX-fr094-cm<本卡號>-rls-workflow-executions-view-security-invoker.sql (新)本棒 migration
~/Projects/Billows/Audit-Manager/compliance-manager-be/scripts/sql/manifest.tsv 加一列
~/Projects/Billows/Audit-Manager/compliance-manager-be/scripts/sql/view/vw_user_job_queue.sql view 定義正本——CREATE OR REPLACE VIEW 加 WITH (security_invoker = true),讓重建也帶著
~/Projects/Billows/Audit-Manager/compliance-manager-be/scripts/init/02-schema.sql:24157-24174 workflow_executions 現有 3 policy
~/Projects/Billows/Audit-Manager/compliance-manager-be/infra/readmodel/tasks/my_grc_jobs_query.py:50 我的任務讀者(只驗不改)
~/Projects/Billows/Audit-Manager/compliance-manager-be/infra/readmodel/audit/flow_control_dashboard_repo_impl.py:249 儀表板待辦數(只驗不改)
workflow_executions:CREATE POLICY workflow_executions_select ... FOR SELECT USING (is_super_admin='t' OR app_tenant_allowed_for_session(tenant_id));既有三條補 is_super_admin OR 分支(比照 .2a 對 projects 的做法,用 DROP IF EXISTS+CREATE 重建);ENABLE ROW LEVEL SECURITY。ALTER VIEW public.vw_user_job_queue SET (security_invoker = true); 同 v_user_capabilities。先確認 PostgreSQL 版本 ≥ 15(select version();security_invoker 是 15 新增),DEV/基線/STG/POC 四處都要 ≥15,否則回寫問首腦。同時把 scripts/sql/view/vw_user_job_queue.sql 的 CREATE OR REPLACE VIEW 加 WITH (security_invoker = true),避免下次重建 view 掉回預設。v_user_routes/v_role_routes:本棒再 grep 一次 FE repo(~/Projects/Billows/Audit-Manager/compliance-manager-fe/src)與 e2e repo 的 SQL 字串,以及 DEV pg_views/函式定義(select proname from pg_proc where prosrc ilike '%v_user_routes%')——view 與 SQL 字串是 grep 盲區。三方皆零讀者 → 本棒只加 security_invoker、不 DROP,回寫本卡列證據問首腦是否 DROP(DROP 屬退役動作,等令);有讀者 → 加 invoker 並列出讀者。select count(*) from public.vw_user_job_queue 與 select count(*) from compliance.workflow_executions——開前全量,開後只剩 102;模擬「切到子租戶的 blsadmin」(allowed_tenant_paths 用該子租戶路徑、is_super_admin='f')查 view 應 0 筆(決策者親測情境:應 0 實 39)。test/ 若有 my_grc_jobs 相關既有測試跑一次。ALTER VIEW vw_user_job_queue RESET (security_invoker) → 模擬子租戶查 view 回到全量(39 之類)→ 設回 true 後回 0。這證明「view 那一行設定確實是隔離生效的關鍵」。