Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions app/lib/screens/study/onboarding/study_selection.dart
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,7 @@ class _InviteCodeDialogState extends State<InviteCodeDialog> {
.from('study_invite')
.select('preselected_intervention_ids')
.eq('code', _controller.text)
.eq('study_id', study.id)
.maybeSingle();
if (!context.mounted) return;
if (inviteResult != null &&
Expand Down
61 changes: 61 additions & 0 deletions database/migration/20260506_scope_invite_codes_to_study.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
BEGIN;

DO $$
BEGIN
IF NOT EXISTS (
SELECT 1
FROM pg_constraint
WHERE conname = 'study_invite_study_id_code_unique'
AND conrelid = 'public.study_invite'::regclass
) THEN
ALTER TABLE public.study_invite
ADD CONSTRAINT study_invite_study_id_code_unique UNIQUE (study_id, code);
END IF;
END $$;

ALTER TABLE public.study_subject
DROP CONSTRAINT IF EXISTS "study_subject_loginCode_fkey";

ALTER TABLE public.study_subject
DROP CONSTRAINT IF EXISTS study_subject_study_invite_fkey;

ALTER TABLE public.study_subject
ADD CONSTRAINT study_subject_study_invite_fkey
FOREIGN KEY (study_id, invite_code)
REFERENCES public.study_invite(study_id, code)
ON DELETE CASCADE;

CREATE OR REPLACE FUNCTION public.is_invite_code_for_study(
study_id uuid,
invite_code text
)
RETURNS boolean
LANGUAGE sql
STABLE
SECURITY DEFINER
SET search_path = ''
AS $$
SELECT EXISTS (
SELECT 1
FROM public.study_invite
WHERE study_invite.study_id = is_invite_code_for_study.study_id
AND study_invite.code = is_invite_code_for_study.invite_code
);
$$;

DROP POLICY IF EXISTS "Invite code must match study_id" ON public.study_subject;

CREATE POLICY "Invite code must match study_id"
ON public.study_subject
AS RESTRICTIVE
FOR INSERT
TO authenticated
WITH CHECK (
invite_code IS NULL
OR public.is_invite_code_for_study(study_id, invite_code)
);

REVOKE EXECUTE ON FUNCTION public.is_invite_code_for_study(uuid, text)
FROM public, anon;

COMMIT;
26 changes: 24 additions & 2 deletions database/studyu-schema.sql
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,22 @@ $$;
ALTER FUNCTION "public"."get_study_record_from_invite"("invite_code" "text") OWNER TO "postgres";


CREATE OR REPLACE FUNCTION "public"."is_invite_code_for_study"("study_id" "uuid", "invite_code" "text") RETURNS boolean
LANGUAGE "sql" STABLE SECURITY DEFINER
SET "search_path" TO ''
AS $$
SELECT EXISTS (
SELECT 1
FROM public.study_invite
WHERE study_invite.study_id = is_invite_code_for_study.study_id
AND study_invite.code = is_invite_code_for_study.invite_code
);
$$;


ALTER FUNCTION "public"."is_invite_code_for_study"("study_id" "uuid", "invite_code" "text") OWNER TO "postgres";


CREATE OR REPLACE FUNCTION "public"."handle_new_user"() RETURNS "trigger"
LANGUAGE "plpgsql" SECURITY DEFINER
SET "search_path" TO ''
Expand Down Expand Up @@ -611,6 +627,11 @@ ALTER TABLE ONLY "public"."study_invite"



ALTER TABLE ONLY "public"."study_invite"
ADD CONSTRAINT "study_invite_study_id_code_unique" UNIQUE ("study_id", "code");



ALTER TABLE ONLY "public"."study"
ADD CONSTRAINT "study_pkey" PRIMARY KEY ("id");

Expand Down Expand Up @@ -660,7 +681,7 @@ ALTER TABLE ONLY "public"."study_invite"


ALTER TABLE ONLY "public"."study_subject"
ADD CONSTRAINT "study_subject_loginCode_fkey" FOREIGN KEY ("invite_code") REFERENCES "public"."study_invite"("code") ON DELETE CASCADE;
ADD CONSTRAINT "study_subject_study_invite_fkey" FOREIGN KEY ("study_id", "invite_code") REFERENCES "public"."study_invite"("study_id", "code") ON DELETE CASCADE;



Expand Down Expand Up @@ -738,7 +759,7 @@ CREATE POLICY "Enable read access for study participants for fitbit credential"



CREATE POLICY "Invite code must match study_id" ON "public"."study_subject" AS RESTRICTIVE FOR INSERT TO "authenticated" WITH CHECK ((("invite_code" IS NULL) OR ("study_id" IN ( SELECT ("public"."get_study_record_from_invite"("study_subject"."invite_code"))."id" AS "id"))));
CREATE POLICY "Invite code must match study_id" ON "public"."study_subject" AS RESTRICTIVE FOR INSERT TO "authenticated" WITH CHECK ((("invite_code" IS NULL) OR "public"."is_invite_code_for_study"("study_id", "invite_code")));



Expand Down Expand Up @@ -848,6 +869,7 @@ REVOKE EXECUTE ON FUNCTION public.last_completed_task(uuid) FROM public, anon;

-- RPC/API functions
REVOKE EXECUTE ON FUNCTION public.get_study_record_from_invite(text) FROM public, anon;
REVOKE EXECUTE ON FUNCTION public.is_invite_code_for_study(uuid, text) FROM public, anon;

-- Trigger functions
REVOKE EXECUTE ON FUNCTION public.handle_new_user() FROM public, anon;
Expand Down
Loading