diff --git a/docs/aidlc/unit-of-work.md b/docs/aidlc/unit-of-work.md index 13f1b7d..0a5feef 100644 --- a/docs/aidlc/unit-of-work.md +++ b/docs/aidlc/unit-of-work.md @@ -25,7 +25,7 @@ V1 を実装可能な単位に分ける。各単位は**単独で本番に出せ | | | | --- | --- | -| 満たす要件 | FR-4.1・4.2・4.3・4.6 | +| 満たす要件 | FR-4.2・4.3・4.6 | | 内容 | ロール・権限・部員ロール・ロール権限のテーブルを作る。部員でない相手に付けられない制約を入れる | | 影響 | 追加のみ。既存の動作は変わらない | | 完了条件 | 現在の管理者2名に相当するロールと割り当てが入っており、`app_accounts.role` と一致する | @@ -41,6 +41,31 @@ V1 を実装可能な単位に分ける。各単位は**単独で本番に出せ | 影響 | 追加のみ。まだ誰も使わない | | 完了条件 | 検査が動き、存在しない権限名を書くと失敗する | +### U-2.5 ロール付与を偽装できないようにする + +| | | +| --- | --- | +| 満たす要件 | C-4、NFR-1.1 | +| 内容 | `member_roles` への書き込みを、アプリが申告したロールだけで許さない。`members` と同じく、データベース側で `app_accounts`(U-4 以降は `member_roles` 自身)の実値と突き合わせる | +| 影響 | 追加のみ | +| 完了条件 | 申告だけでは付与できない。突き合わせに失敗した書き込みが拒否される | + +**U-3 より前に必ず終わらせる。** U-1 のレビューで見つかった不足への対応。 + +U-1 が入れたポリシーは `app.current_user_role` の値だけを見ている。 + +```sql +with check ((select current_setting('app.current_user_role', true)) = 'admin') +``` + +`members` にはこれに加えて `app_private.enforce_member_workflow` があり、 +申告されたロールを `app_accounts` の実値と突き合わせている。アプリ層が侵害されても +権限を偽装できないようにするためで、これが C-4 の趣旨である。 + +`member_roles` は全権限の源泉であり、`members` より価値の高い標的でありながら、 +現状その突き合わせがない。U-3 で判定を移すまでは誰もこのテーブルを使わないため +現時点の危険はないが、U-3 の前提として閉じておく必要がある。 + ### U-3 判定を置き換える | | | @@ -137,7 +162,7 @@ V1 を実装可能な単位に分ける。各単位は**単独で本番に出せ 依存関係で決まる部分と、選べる部分がある。 ``` -U-1 → U-2 → U-3 → U-4 ロールの土台(この順でなければならない) +U-1 → U-2 → U-2.5 → U-3 → U-4 ロールの土台(この順でなければならない) ↓ U-5, U-6 引き継ぎと退部連動(U-4 完了後) @@ -151,7 +176,7 @@ U-10 → U-11 分離(最後。他が安定してから) ### 推奨する順 1. **U-9** — 先にやると U-3 での衝突が減る。外から見た挙動が変わらないため安全 -2. **U-1 → U-2 → U-3 → U-4** — ロールの土台 +2. **U-1 → U-2 → U-2.5 → U-3 → U-4** — ロールの土台 3. **U-5・U-6** — 引き継ぎ。ここで運用上の欠陥が解消する 4. **U-7 → U-8** — 表示と一覧 5. **U-10 → U-11** — 分離 diff --git a/supabase/migrations/20260904000000_add_role_tables.sql b/supabase/migrations/20260904000000_add_role_tables.sql index 40834f1..65006dd 100644 --- a/supabase/migrations/20260904000000_add_role_tables.sql +++ b/supabase/migrations/20260904000000_add_role_tables.sql @@ -126,8 +126,7 @@ insert into public.member_roles (member_id, role_key, granted_by_user_id) select account.member_id, 'admin', 'system:20260904000000' from public.app_accounts account join public.members member on member.member_id = account.member_id -where account.role = 'admin' -on conflict do nothing; +where account.role = 'admin'; grant select on public.permissions to app_rls; grant select on public.app_roles to app_rls;