1+ -- Fix security vulnerability: Allow affiliates to access their own data
2+ -- This addresses the issue where affiliates couldn't see their own profile information
3+
4+ -- Add policy to allow affiliates to view their own data
5+ CREATE POLICY " Affiliates can view their own data"
6+ ON public .affiliates
7+ FOR SELECT
8+ USING (auth .uid () = user_id);
9+
10+ -- Add policy to allow affiliates to update their own basic profile data
11+ -- Note: Sensitive fields like commission_rate, tracking_code should only be updateable by campaign owners
12+ CREATE POLICY " Affiliates can update their own basic profile"
13+ ON public .affiliates
14+ FOR UPDATE
15+ USING (
16+ auth .uid () = user_id
17+ )
18+ WITH CHECK (
19+ auth .uid () = user_id
20+ );
21+
22+ -- Add security function to validate affiliate data access
23+ CREATE OR REPLACE FUNCTION public .validate_affiliate_data_access(affiliate_id uuid, requesting_user_id uuid DEFAULT auth .uid ())
24+ RETURNS boolean
25+ LANGUAGE plpgsql
26+ SECURITY DEFINER
27+ SET search_path TO ' public'
28+ AS $function$
29+ DECLARE
30+ affiliate_user_id uuid;
31+ campaign_owner_id uuid;
32+ BEGIN
33+ -- Get affiliate user_id and campaign owner
34+ SELECT a .user_id , c .user_id INTO affiliate_user_id, campaign_owner_id
35+ FROM affiliates a
36+ JOIN campaigns c ON c .id = a .campaign_id
37+ WHERE a .id = affiliate_id;
38+
39+ -- Allow access if user is the affiliate or campaign owner
40+ RETURN (requesting_user_id = affiliate_user_id OR requesting_user_id = campaign_owner_id);
41+ END;
42+ $function$;
43+
44+ -- Log security event for affiliate data access
45+ CREATE OR REPLACE FUNCTION public .log_affiliate_data_access(affiliate_id uuid, access_type text )
46+ RETURNS void
47+ LANGUAGE plpgsql
48+ SECURITY DEFINER
49+ SET search_path TO ' public'
50+ AS $function$
51+ BEGIN
52+ PERFORM public .log_security_event (
53+ ' affiliate_data_access' ,
54+ auth .uid (),
55+ jsonb_build_object(
56+ ' affiliate_id' , affiliate_id,
57+ ' access_type' , access_type,
58+ ' timestamp' , now()
59+ )
60+ );
61+ END;
62+ $function$;
63+
64+ -- Create trigger to prevent affiliates from modifying sensitive fields
65+ CREATE OR REPLACE FUNCTION public .prevent_affiliate_sensitive_field_changes()
66+ RETURNS trigger
67+ LANGUAGE plpgsql
68+ SECURITY DEFINER
69+ SET search_path TO ' public'
70+ AS $function$
71+ DECLARE
72+ is_campaign_owner boolean ;
73+ BEGIN
74+ -- Check if current user is the campaign owner
75+ SELECT EXISTS (
76+ SELECT 1 FROM campaigns
77+ WHERE id = NEW .campaign_id AND user_id = auth .uid ()
78+ ) INTO is_campaign_owner;
79+
80+ -- If not campaign owner, prevent changes to sensitive fields
81+ IF NOT is_campaign_owner THEN
82+ -- Preserve original values for sensitive fields
83+ NEW .commission_rate := OLD .commission_rate ;
84+ NEW .tracking_code := OLD .tracking_code ;
85+ NEW .campaign_id := OLD .campaign_id ;
86+ NEW .stripe_account_id := OLD .stripe_account_id ;
87+ NEW .stripe_account_status := OLD .stripe_account_status ;
88+ END IF;
89+
90+ RETURN NEW;
91+ END;
92+ $function$;
93+
94+ -- Apply the trigger to affiliates table
95+ CREATE TRIGGER prevent_affiliate_sensitive_changes
96+ BEFORE UPDATE ON public .affiliates
97+ FOR EACH ROW
98+ EXECUTE FUNCTION public .prevent_affiliate_sensitive_field_changes ();
0 commit comments