feat: mobile-first refactor for BottleDetails & RLS security/performance optimizations
This commit is contained in:
248
rls_policy_performance_fixes.sql
Normal file
248
rls_policy_performance_fixes.sql
Normal file
@@ -0,0 +1,248 @@
|
||||
-- ============================================
|
||||
-- RLS Policy Performance Fixes Migration
|
||||
-- Run this in Supabase SQL Editor
|
||||
-- ============================================
|
||||
-- Fixes two types of issues:
|
||||
-- 1. auth_rls_initplan: Ensures auth.uid() is wrapped in (SELECT ...)
|
||||
-- 2. multiple_permissive_policies: Consolidates overlapping policies
|
||||
-- ============================================
|
||||
|
||||
-- ============================================
|
||||
-- Fix 1: user_subscriptions
|
||||
-- Issues: Multiple permissive policies for INSERT and SELECT
|
||||
-- ============================================
|
||||
|
||||
-- Drop existing policies
|
||||
DROP POLICY IF EXISTS "user_subscriptions_select_policy" ON user_subscriptions;
|
||||
DROP POLICY IF EXISTS "user_subscriptions_admin_policy" ON user_subscriptions;
|
||||
DROP POLICY IF EXISTS "user_subscriptions_insert_self" ON user_subscriptions;
|
||||
|
||||
-- Consolidated SELECT policy: user can see own OR admin can see all
|
||||
CREATE POLICY "user_subscriptions_select_policy" ON user_subscriptions
|
||||
FOR SELECT USING (
|
||||
(SELECT auth.uid()) = user_id OR
|
||||
EXISTS (SELECT 1 FROM admin_users WHERE admin_users.user_id = (SELECT auth.uid()))
|
||||
);
|
||||
|
||||
-- Consolidated INSERT policy: user can insert own OR admin can insert any
|
||||
CREATE POLICY "user_subscriptions_insert_policy" ON user_subscriptions
|
||||
FOR INSERT WITH CHECK (
|
||||
(SELECT auth.uid()) = user_id OR
|
||||
EXISTS (SELECT 1 FROM admin_users WHERE admin_users.user_id = (SELECT auth.uid()))
|
||||
);
|
||||
|
||||
-- Admin UPDATE/DELETE policy
|
||||
CREATE POLICY "user_subscriptions_admin_modify_policy" ON user_subscriptions
|
||||
FOR UPDATE USING (
|
||||
EXISTS (SELECT 1 FROM admin_users WHERE admin_users.user_id = (SELECT auth.uid()))
|
||||
);
|
||||
|
||||
CREATE POLICY "user_subscriptions_admin_delete_policy" ON user_subscriptions
|
||||
FOR DELETE USING (
|
||||
EXISTS (SELECT 1 FROM admin_users WHERE admin_users.user_id = (SELECT auth.uid()))
|
||||
);
|
||||
|
||||
-- ============================================
|
||||
-- Fix 2: bottle_splits
|
||||
-- Issues: bottle_splits_host_policy (ALL) and bottle_splits_public_view (SELECT) overlap
|
||||
-- ============================================
|
||||
|
||||
DROP POLICY IF EXISTS "bottle_splits_host_policy" ON bottle_splits;
|
||||
DROP POLICY IF EXISTS "bottle_splits_public_view" ON bottle_splits;
|
||||
|
||||
-- Consolidated SELECT: host can see all own, everyone can see active
|
||||
CREATE POLICY "bottle_splits_select_policy" ON bottle_splits
|
||||
FOR SELECT USING (
|
||||
(SELECT auth.uid()) = host_id OR is_active = true
|
||||
);
|
||||
|
||||
-- Host-only for INSERT/UPDATE/DELETE
|
||||
CREATE POLICY "bottle_splits_host_insert_policy" ON bottle_splits
|
||||
FOR INSERT WITH CHECK ((SELECT auth.uid()) = host_id);
|
||||
|
||||
CREATE POLICY "bottle_splits_host_update_policy" ON bottle_splits
|
||||
FOR UPDATE USING ((SELECT auth.uid()) = host_id);
|
||||
|
||||
CREATE POLICY "bottle_splits_host_delete_policy" ON bottle_splits
|
||||
FOR DELETE USING ((SELECT auth.uid()) = host_id);
|
||||
|
||||
-- ============================================
|
||||
-- Fix 3: bottles
|
||||
-- Issues: bottles_owner_policy (ALL) and bottles_session_select_policy (SELECT) overlap
|
||||
-- Strategy: Just keep owner policy because the session_select_policy doesn't exist in schema
|
||||
-- ============================================
|
||||
|
||||
DROP POLICY IF EXISTS "bottles_owner_policy" ON bottles;
|
||||
DROP POLICY IF EXISTS "bottles_session_select_policy" ON bottles;
|
||||
|
||||
-- Owner-only policy for all operations (simple and performant)
|
||||
CREATE POLICY "bottles_policy" ON bottles
|
||||
FOR ALL USING ((SELECT auth.uid()) = user_id);
|
||||
|
||||
-- ============================================
|
||||
-- Fix 4: buddy_invites
|
||||
-- Issues: buddy_invites_creator_policy (ALL) and buddy_invites_redeem_policy (SELECT) overlap
|
||||
-- ============================================
|
||||
|
||||
DROP POLICY IF EXISTS "buddy_invites_creator_policy" ON buddy_invites;
|
||||
DROP POLICY IF EXISTS "buddy_invites_redeem_policy" ON buddy_invites;
|
||||
|
||||
-- Consolidated SELECT: creator can see own, anyone can see non-expired (for redemption)
|
||||
CREATE POLICY "buddy_invites_select_policy" ON buddy_invites
|
||||
FOR SELECT USING (
|
||||
(SELECT auth.uid()) = creator_id OR expires_at > now()
|
||||
);
|
||||
|
||||
-- Creator-only for INSERT/UPDATE/DELETE
|
||||
CREATE POLICY "buddy_invites_creator_insert_policy" ON buddy_invites
|
||||
FOR INSERT WITH CHECK ((SELECT auth.uid()) = creator_id);
|
||||
|
||||
CREATE POLICY "buddy_invites_creator_update_policy" ON buddy_invites
|
||||
FOR UPDATE USING ((SELECT auth.uid()) = creator_id);
|
||||
|
||||
CREATE POLICY "buddy_invites_creator_delete_policy" ON buddy_invites
|
||||
FOR DELETE USING ((SELECT auth.uid()) = creator_id);
|
||||
|
||||
-- ============================================
|
||||
-- Fix 5: global_products
|
||||
-- Issues: "Enable Admin Insert/Update" (ALL) and "Enable Read Access for all users" (SELECT) overlap
|
||||
-- ============================================
|
||||
|
||||
DROP POLICY IF EXISTS "Enable Admin Insert/Update" ON global_products;
|
||||
DROP POLICY IF EXISTS "Enable Read Access for all users" ON global_products;
|
||||
|
||||
-- Everyone can SELECT
|
||||
CREATE POLICY "global_products_select_policy" ON global_products
|
||||
FOR SELECT USING (true);
|
||||
|
||||
-- Admin-only for INSERT/UPDATE/DELETE
|
||||
CREATE POLICY "global_products_admin_insert_policy" ON global_products
|
||||
FOR INSERT WITH CHECK (
|
||||
EXISTS (SELECT 1 FROM admin_users WHERE admin_users.user_id = (SELECT auth.uid()))
|
||||
);
|
||||
|
||||
CREATE POLICY "global_products_admin_update_policy" ON global_products
|
||||
FOR UPDATE USING (
|
||||
EXISTS (SELECT 1 FROM admin_users WHERE admin_users.user_id = (SELECT auth.uid()))
|
||||
);
|
||||
|
||||
CREATE POLICY "global_products_admin_delete_policy" ON global_products
|
||||
FOR DELETE USING (
|
||||
EXISTS (SELECT 1 FROM admin_users WHERE admin_users.user_id = (SELECT auth.uid()))
|
||||
);
|
||||
|
||||
-- ============================================
|
||||
-- Fix 6: session_participants
|
||||
-- Issues: session_participants_manage_policy and session_participants_read_policy overlap
|
||||
-- ============================================
|
||||
|
||||
DROP POLICY IF EXISTS "session_participants_owner_policy" ON session_participants;
|
||||
DROP POLICY IF EXISTS "session_participants_manage_policy" ON session_participants;
|
||||
DROP POLICY IF EXISTS "session_participants_read_policy" ON session_participants;
|
||||
|
||||
-- Single unified policy: owner can do all
|
||||
CREATE POLICY "session_participants_policy" ON session_participants
|
||||
FOR ALL USING ((SELECT auth.uid()) = user_id);
|
||||
|
||||
-- ============================================
|
||||
-- Fix 7: split_participants
|
||||
-- Issues: Multiple overlapping policies for different actions
|
||||
-- ============================================
|
||||
|
||||
DROP POLICY IF EXISTS "split_participants_own_policy" ON split_participants;
|
||||
DROP POLICY IF EXISTS "split_participants_host_policy" ON split_participants;
|
||||
DROP POLICY IF EXISTS "split_participants_public_view" ON split_participants;
|
||||
|
||||
-- Consolidated SELECT: own participation OR host of split OR public active split
|
||||
CREATE POLICY "split_participants_select_policy" ON split_participants
|
||||
FOR SELECT USING (
|
||||
(SELECT auth.uid()) = user_id OR
|
||||
split_id IN (SELECT id FROM bottle_splits WHERE host_id = (SELECT auth.uid())) OR
|
||||
split_id IN (SELECT id FROM bottle_splits WHERE is_active = true)
|
||||
);
|
||||
|
||||
-- INSERT: own participation OR host of split
|
||||
CREATE POLICY "split_participants_insert_policy" ON split_participants
|
||||
FOR INSERT WITH CHECK (
|
||||
(SELECT auth.uid()) = user_id OR
|
||||
split_id IN (SELECT id FROM bottle_splits WHERE host_id = (SELECT auth.uid()))
|
||||
);
|
||||
|
||||
-- UPDATE: own participation OR host of split
|
||||
CREATE POLICY "split_participants_update_policy" ON split_participants
|
||||
FOR UPDATE USING (
|
||||
(SELECT auth.uid()) = user_id OR
|
||||
split_id IN (SELECT id FROM bottle_splits WHERE host_id = (SELECT auth.uid()))
|
||||
);
|
||||
|
||||
-- DELETE: own participation OR host of split
|
||||
CREATE POLICY "split_participants_delete_policy" ON split_participants
|
||||
FOR DELETE USING (
|
||||
(SELECT auth.uid()) = user_id OR
|
||||
split_id IN (SELECT id FROM bottle_splits WHERE host_id = (SELECT auth.uid()))
|
||||
);
|
||||
|
||||
-- ============================================
|
||||
-- Fix 8: subscription_plans
|
||||
-- Issues: subscription_plans_admin_policy (ALL) and subscription_plans_select_policy (SELECT) overlap
|
||||
-- ============================================
|
||||
|
||||
DROP POLICY IF EXISTS "subscription_plans_select_policy" ON subscription_plans;
|
||||
DROP POLICY IF EXISTS "subscription_plans_admin_policy" ON subscription_plans;
|
||||
|
||||
-- Consolidated SELECT: active plans OR admin can see all
|
||||
CREATE POLICY "subscription_plans_select_policy" ON subscription_plans
|
||||
FOR SELECT USING (
|
||||
is_active = true OR
|
||||
EXISTS (SELECT 1 FROM admin_users WHERE admin_users.user_id = (SELECT auth.uid()))
|
||||
);
|
||||
|
||||
-- Admin-only for INSERT/UPDATE/DELETE
|
||||
CREATE POLICY "subscription_plans_admin_insert_policy" ON subscription_plans
|
||||
FOR INSERT WITH CHECK (
|
||||
EXISTS (SELECT 1 FROM admin_users WHERE admin_users.user_id = (SELECT auth.uid()))
|
||||
);
|
||||
|
||||
CREATE POLICY "subscription_plans_admin_update_policy" ON subscription_plans
|
||||
FOR UPDATE USING (
|
||||
EXISTS (SELECT 1 FROM admin_users WHERE admin_users.user_id = (SELECT auth.uid()))
|
||||
);
|
||||
|
||||
CREATE POLICY "subscription_plans_admin_delete_policy" ON subscription_plans
|
||||
FOR DELETE USING (
|
||||
EXISTS (SELECT 1 FROM admin_users WHERE admin_users.user_id = (SELECT auth.uid()))
|
||||
);
|
||||
|
||||
-- ============================================
|
||||
-- Fix 9: tasting_sessions
|
||||
-- Issues: sessions_access_policy (ALL) and sessions_modification_policy overlap
|
||||
-- Strategy: Keep it simple - owner-only for modifications, no complex buddy joins
|
||||
-- ============================================
|
||||
|
||||
DROP POLICY IF EXISTS "sessions_access_policy" ON tasting_sessions;
|
||||
DROP POLICY IF EXISTS "sessions_modification_policy" ON tasting_sessions;
|
||||
|
||||
-- Owner-only policy for all operations
|
||||
CREATE POLICY "tasting_sessions_policy" ON tasting_sessions
|
||||
FOR ALL USING ((SELECT auth.uid()) = user_id);
|
||||
|
||||
-- ============================================
|
||||
-- Fix 10: tastings
|
||||
-- Issues: tastings_modify_policy (ALL) and tastings_select_policy (SELECT) overlap
|
||||
-- Strategy: Keep it simple - owner-only for all operations
|
||||
-- ============================================
|
||||
|
||||
DROP POLICY IF EXISTS "tastings_select_policy" ON tastings;
|
||||
DROP POLICY IF EXISTS "tastings_modify_policy" ON tastings;
|
||||
|
||||
-- Owner-only policy for all operations
|
||||
CREATE POLICY "tastings_policy" ON tastings
|
||||
FOR ALL USING ((SELECT auth.uid()) = user_id);
|
||||
|
||||
-- ============================================
|
||||
-- Verification Query (run after migration)
|
||||
-- ============================================
|
||||
-- SELECT tablename, policyname, roles, cmd
|
||||
-- FROM pg_policies
|
||||
-- WHERE schemaname = 'public'
|
||||
-- ORDER BY tablename, cmd, roles;
|
||||
Reference in New Issue
Block a user