xtablo-source/supabase/migrations_backup/37_secure_active_subscriptions.sql

92 lines
3.4 KiB
MySQL
Raw Normal View History

2025-11-03 08:47:34 +00:00
-- ============================================================================
-- Secure Active Subscriptions Access
-- ============================================================================
-- This migration fixes a security issue with the active_subscriptions view
-- which could expose all users' subscription data.
--
-- Changes:
-- 1. Drop the public.active_subscriptions view (insecure)
-- 2. Create a secure function that only returns the current user's subscription
-- 3. Add RLS-compatible function for admin access if needed
-- ============================================================================
-- ============================================================================
-- Drop the insecure view
-- ============================================================================
drop view if exists public.active_subscriptions;
-- ============================================================================
-- Create secure function to get current user's active subscription
-- ============================================================================
-- Function to get the authenticated user's active subscription
create or replace function public.get_my_active_subscription()
returns table (
subscription_id text,
user_id uuid,
user_email text,
first_name text,
last_name text,
status text,
current_period_start timestamp with time zone,
current_period_end timestamp with time zone,
cancel_at_period_end boolean,
product_name text,
currency text,
unit_amount integer,
billing_interval text,
plan subscription_plan
)
language plpgsql
security definer
-- Set search path for security
set search_path = public, stripe
as $$
begin
-- Only return data for the authenticated user
return query
select
s.id as subscription_id,
(c.metadata->>'user_id')::uuid as user_id,
p.email as user_email,
p.first_name,
p.last_name,
s.status::text,
to_timestamp(si.current_period_start) as current_period_start,
to_timestamp(si.current_period_end) as current_period_end,
s.cancel_at_period_end,
pr.name as product_name,
pc.currency,
pc.unit_amount,
pc.recurring->>'interval' as billing_interval,
p.plan
from stripe.subscriptions s
inner join stripe.customers c on c.id = s.customer
inner join stripe.subscription_items si on si.subscription = s.id
inner join public.profiles p on p.id = (c.metadata->>'user_id')::uuid
left join stripe.prices pc on pc.id = si.price
left join stripe.products pr on pr.id = pc.product
where (c.metadata->>'user_id')::uuid = auth.uid() -- Filter by authenticated user only!
and s.status::text in ('active', 'trialing')
and si.current_period_end is not null
and to_timestamp(si.current_period_end) > now()
order by si.current_period_end desc
limit 1;
end;
$$;
-- ============================================================================
-- Grant appropriate permissions
-- ============================================================================
-- Grant access to authenticated users for their own subscription
grant execute on function public.get_my_active_subscription() to authenticated;
-- ============================================================================
-- Comments for documentation
-- ============================================================================
comment on function public.get_my_active_subscription is
'Returns the current authenticated user''s active subscription (secure, RLS-compliant)';