[verified] fix cabinet memberships recursive RLS
Some checks failed
supabase-ci / validate (push) Has been cancelled
Some checks failed
supabase-ci / validate (push) Has been cancelled
(cherry picked from commit facdfd21c9c0c17f09e1a3a5fbe2e2b253f76d82)
This commit is contained in:
parent
f8fcff600f
commit
0f2aca3a73
88
volumes/db/cc/76-cabinet-memberships-rls-definer.sql
Normal file
88
volumes/db/cc/76-cabinet-memberships-rls-definer.sql
Normal file
@ -0,0 +1,88 @@
|
||||
-- 76-cabinet-memberships-rls-definer.sql
|
||||
-- Fix cabinet_memberships/file_cabinets/files recursive RLS.
|
||||
--
|
||||
-- The original cabinet membership policies joined file_cabinets directly, while
|
||||
-- the file_cabinets/files membership policies joined cabinet_memberships
|
||||
-- directly. Under an authenticated as-user SELECT this creates an RLS cycle and
|
||||
-- PostgreSQL raises 42P17 (infinite recursion detected in policy for relation
|
||||
-- "cabinet_memberships").
|
||||
--
|
||||
-- SECURITY DEFINER helpers evaluate the ownership/membership checks as the
|
||||
-- function owner (bypassing the inner RLS checks) while still keying the result
|
||||
-- to auth.uid(), matching the class-management helper pattern in 71.
|
||||
--
|
||||
-- The existing public tables are owned by supabase_admin; Supabase migrations
|
||||
-- run as that table owner on dev/prod so the SECURITY DEFINER helpers are owned
|
||||
-- by a role that bypasses the inner RLS checks.
|
||||
|
||||
create or replace function public.is_cabinet_owner(p_cabinet uuid)
|
||||
returns boolean
|
||||
language sql
|
||||
stable
|
||||
security definer
|
||||
set search_path = public
|
||||
as $$
|
||||
select exists (
|
||||
select 1
|
||||
from public.file_cabinets c
|
||||
where c.id = p_cabinet
|
||||
and c.user_id = auth.uid()
|
||||
)
|
||||
$$;
|
||||
|
||||
create or replace function public.is_cabinet_member(p_cabinet uuid)
|
||||
returns boolean
|
||||
language sql
|
||||
stable
|
||||
security definer
|
||||
set search_path = public
|
||||
as $$
|
||||
select exists (
|
||||
select 1
|
||||
from public.cabinet_memberships m
|
||||
where m.cabinet_id = p_cabinet
|
||||
and m.profile_id = auth.uid()
|
||||
)
|
||||
$$;
|
||||
|
||||
-- Keep function execution available to the roles used by RLS policies. The
|
||||
-- functions disclose only a boolean about the caller's own auth.uid() state.
|
||||
revoke all on function public.is_cabinet_owner(uuid) from public, anon;
|
||||
revoke all on function public.is_cabinet_member(uuid) from public, anon;
|
||||
grant execute on function public.is_cabinet_owner(uuid) to authenticated, service_role;
|
||||
grant execute on function public.is_cabinet_member(uuid) to authenticated, service_role;
|
||||
|
||||
-- Re-declare cabinet_memberships policies without direct file_cabinets subqueries.
|
||||
drop policy if exists cm_read_self_or_owner on public.cabinet_memberships;
|
||||
create policy cm_read_self_or_owner on public.cabinet_memberships
|
||||
for select to authenticated
|
||||
using (profile_id = auth.uid() or public.is_cabinet_owner(cabinet_id));
|
||||
|
||||
drop policy if exists cm_insert_by_owner on public.cabinet_memberships;
|
||||
create policy cm_insert_by_owner on public.cabinet_memberships
|
||||
for insert to authenticated
|
||||
with check (public.is_cabinet_owner(cabinet_id));
|
||||
|
||||
drop policy if exists cm_update_by_owner on public.cabinet_memberships;
|
||||
create policy cm_update_by_owner on public.cabinet_memberships
|
||||
for update to authenticated
|
||||
using (public.is_cabinet_owner(cabinet_id))
|
||||
with check (public.is_cabinet_owner(cabinet_id));
|
||||
|
||||
drop policy if exists cm_delete_by_owner on public.cabinet_memberships;
|
||||
create policy cm_delete_by_owner on public.cabinet_memberships
|
||||
for delete to authenticated
|
||||
using (public.is_cabinet_owner(cabinet_id));
|
||||
|
||||
-- Re-declare membership-based cabinet/file read policies without direct
|
||||
-- cabinet_memberships subqueries, so selecting cabinets/files does not recurse
|
||||
-- back through cabinet_memberships RLS.
|
||||
drop policy if exists "User can access cabinets via membership" on public.file_cabinets;
|
||||
create policy "User can access cabinets via membership" on public.file_cabinets
|
||||
for select to authenticated
|
||||
using (public.is_cabinet_member(id));
|
||||
|
||||
drop policy if exists "User can access files via membership" on public.files;
|
||||
create policy "User can access files via membership" on public.files
|
||||
for select to authenticated
|
||||
using (public.is_cabinet_member(cabinet_id));
|
||||
Loading…
x
Reference in New Issue
Block a user