92 lines
3.4 KiB
MySQL
92 lines
3.4 KiB
MySQL
|
|
-- ============================================================================
|
||
|
|
-- 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)';
|