-- ============================================================================ -- 빅뱅 Phase 5 실증 중 발견: team_members RLS 정책이 자기 테이블을 재조회해서 -- "infinite recursion detected in policy for relation team_members" 에러. -- -- meetings / meeting_memos / meeting_documents / team_invites 정책이 -- team_members 서브쿼리를 경유하면서 재귀가 트리거되어 pull 전부 실패 + -- Realtime 구독 TIMED_OUT 발생. -- -- 해결: SECURITY DEFINER 함수로 team_id 조회를 감싸 RLS 우회 + 재귀 끊기. -- 이건 Supabase 공식 권장 패턴. -- ============================================================================ -- ── 헬퍼 함수 ──────────────────────────────────────────────────────────── CREATE OR REPLACE FUNCTION public.user_team_ids(uid uuid) RETURNS SETOF uuid LANGUAGE sql STABLE SECURITY DEFINER SET search_path = public, pg_temp AS $$ SELECT team_id FROM public.team_members WHERE user_id = uid; $$; CREATE OR REPLACE FUNCTION public.user_admin_team_ids(uid uuid) RETURNS SETOF uuid LANGUAGE sql STABLE SECURITY DEFINER SET search_path = public, pg_temp AS $$ SELECT team_id FROM public.team_members WHERE user_id = uid AND role IN ('owner', 'admin'); $$; REVOKE ALL ON FUNCTION public.user_team_ids(uuid) FROM PUBLIC; REVOKE ALL ON FUNCTION public.user_admin_team_ids(uuid) FROM PUBLIC; GRANT EXECUTE ON FUNCTION public.user_team_ids(uuid) TO authenticated, service_role; GRANT EXECUTE ON FUNCTION public.user_admin_team_ids(uuid) TO authenticated, service_role; -- ── team_members: 재귀 제거 ─────────────────────────────────────────────── DROP POLICY IF EXISTS "team_members_read_same_team" ON public.team_members; CREATE POLICY "team_members_read_same_team" ON public.team_members FOR SELECT USING ( user_id = auth.uid() OR team_id IN (SELECT public.user_team_ids(auth.uid())) ); DROP POLICY IF EXISTS "team_members_insert_admin" ON public.team_members; CREATE POLICY "team_members_insert_admin" ON public.team_members FOR INSERT WITH CHECK ( team_id IN (SELECT public.user_admin_team_ids(auth.uid())) ); DROP POLICY IF EXISTS "team_members_update_admin" ON public.team_members; CREATE POLICY "team_members_update_admin" ON public.team_members FOR UPDATE USING ( team_id IN (SELECT public.user_admin_team_ids(auth.uid())) ); DROP POLICY IF EXISTS "team_members_delete_admin_or_self" ON public.team_members; CREATE POLICY "team_members_delete_admin_or_self" ON public.team_members FOR DELETE USING ( user_id = auth.uid() OR team_id IN (SELECT public.user_admin_team_ids(auth.uid())) ); -- ── meetings: team_members 직접 참조 → 함수 호출로 교체 ─────────────────── DROP POLICY IF EXISTS "meetings_read" ON public.meetings; CREATE POLICY "meetings_read" ON public.meetings FOR SELECT USING ( user_id = auth.uid() OR (team_id IS NOT NULL AND team_id IN (SELECT public.user_team_ids(auth.uid()))) ); DROP POLICY IF EXISTS "meetings_update" ON public.meetings; CREATE POLICY "meetings_update" ON public.meetings FOR UPDATE USING ( user_id = auth.uid() OR (team_id IS NOT NULL AND team_id IN (SELECT public.user_admin_team_ids(auth.uid()))) ); -- ── meeting_memos: meetings RLS 경유 + insert 시 team 체크 ─────────────── DROP POLICY IF EXISTS "meeting_memos_insert" ON public.meeting_memos; CREATE POLICY "meeting_memos_insert" ON public.meeting_memos FOR INSERT WITH CHECK ( user_id = auth.uid() AND meeting_id IN ( SELECT id FROM public.meetings WHERE user_id = auth.uid() OR (team_id IS NOT NULL AND team_id IN (SELECT public.user_team_ids(auth.uid()))) ) ); -- ── meeting_documents: 동일 패턴 ────────────────────────────────────────── DROP POLICY IF EXISTS "meeting_documents_insert" ON public.meeting_documents; CREATE POLICY "meeting_documents_insert" ON public.meeting_documents FOR INSERT WITH CHECK ( user_id = auth.uid() AND meeting_id IN ( SELECT id FROM public.meetings WHERE user_id = auth.uid() OR (team_id IS NOT NULL AND team_id IN (SELECT public.user_team_ids(auth.uid()))) ) ); DROP POLICY IF EXISTS "meeting_documents_update" ON public.meeting_documents; CREATE POLICY "meeting_documents_update" ON public.meeting_documents FOR UPDATE USING ( user_id = auth.uid() OR meeting_id IN ( SELECT id FROM public.meetings WHERE team_id IN (SELECT public.user_admin_team_ids(auth.uid())) ) ); -- ── team_invites: 같은 패턴 오염 ────────────────────────────────────────── DROP POLICY IF EXISTS "team_invites_read" ON public.team_invites; CREATE POLICY "team_invites_read" ON public.team_invites FOR SELECT USING ( team_id IN (SELECT public.user_team_ids(auth.uid())) OR lower(email) = lower((SELECT email FROM auth.users WHERE id = auth.uid())) );