-- Remove the insecure anon policies DROP POLICY IF EXISTS "Anon can select own voter record by token" ON public.election_eligible_voters; DROP POLICY IF EXISTS "Anon can update voted status by token" ON public.election_eligible_voters; -- Function 1: Lookup voter by vote_token (used on vote page load) CREATE OR REPLACE FUNCTION public.lookup_voter_by_token(p_vote_token uuid) RETURNS TABLE( id uuid, election_id uuid, owner_id uuid, unit_id uuid, has_consent boolean, consent_date timestamptz, vote_token uuid, has_voted boolean, voted_at timestamptz, created_at timestamptz, access_code text ) LANGUAGE sql STABLE SECURITY DEFINER SET search_path = public AS $$ SELECT id, election_id, owner_id, unit_id, has_consent, consent_date, vote_token, has_voted, voted_at, created_at, access_code FROM public.election_eligible_voters WHERE election_eligible_voters.vote_token = p_vote_token LIMIT 1; $$; -- Function 2: Lookup vote_token by access code CREATE OR REPLACE FUNCTION public.lookup_voter_by_access_code(p_election_id uuid, p_access_code text) RETURNS TABLE(vote_token uuid) LANGUAGE sql STABLE SECURITY DEFINER SET search_path = public AS $$ SELECT vote_token FROM public.election_eligible_voters WHERE election_id = p_election_id AND access_code = p_access_code LIMIT 1; $$; -- Function 3: Mark voter as voted CREATE OR REPLACE FUNCTION public.mark_voter_as_voted(p_vote_token uuid) RETURNS void LANGUAGE plpgsql SECURITY DEFINER SET search_path = public AS $$ BEGIN UPDATE public.election_eligible_voters SET has_voted = true, voted_at = now() WHERE vote_token = p_vote_token; END; $$;