From 83ecfa5e76cbf521142c747fa97ff2ff00e64cc9 Mon Sep 17 00:00:00 2001 From: Wen Bo Xie <5532241+w3b6x9@users.noreply.github.com> Date: Fri, 28 Jun 2024 18:04:02 -0400 Subject: [PATCH] fix: simplify rooms_users policies (#27629) --- .../nextjs-authorization-demo/README.md | 57 +++++++++---------- .../app/protected/page.tsx | 10 ++-- .../components/create-room-modal.tsx | 4 +- 3 files changed, 34 insertions(+), 37 deletions(-) diff --git a/examples/realtime/nextjs-authorization-demo/README.md b/examples/realtime/nextjs-authorization-demo/README.md index e8826a14405..735b8591549 100644 --- a/examples/realtime/nextjs-authorization-demo/README.md +++ b/examples/realtime/nextjs-authorization-demo/README.md @@ -41,25 +41,23 @@ We'll be using: ```sql CREATE TABLE public.rooms ( id bigint GENERATED BY default AS IDENTITY PRIMARY KEY, - topic text NOT NULL + topic text NOT NULL UNIQUE ); - -CREATE UNIQUE INDEX "unique_topic" on public.rooms (topic); +ALTER TABLE public.rooms ENABLE ROW LEVEL SECURITY; CREATE TABLE public.profiles ( - id bigint GENERATED BY default AS IDENTITY PRIMARY KEY, + id uuid NOT NULL REFERENCES auth.users ON DELETE CASCADE, email text NOT NULL, - user_id uuid REFERENCES auth.users (id) + + PRIMARY KEY (id) ); +ALTER TABLE public.profiles ENABLE ROW LEVEL SECURITY; CREATE TABLE public.rooms_users ( user_id uuid REFERENCES auth.users (id), - room_id bigint REFERENCES public.rooms (id), + room_topic text REFERENCES public.rooms (topic), created_at timestamptz DEFAULT CURRENT_TIMESTAMP ); - -ALTER TABLE public.rooms ENABLE ROW LEVEL SECURITY; -ALTER TABLE public.profiles ENABLE ROW LEVEL SECURITY; ALTER TABLE public.rooms_users ENABLE ROW LEVEL SECURITY; ``` @@ -82,7 +80,6 @@ AS PERMISSIVE FOR INSERT TO supabase_auth_admin WITH CHECK (true); - CREATE POLICY "authenticated users can read rooms" ON "public"."rooms" AS PERMISSIVE FOR SELECT @@ -95,7 +92,6 @@ AS PERMISSIVE FOR INSERT TO authenticated WITH CHECK (TRUE); - CREATE POLICY "authenticated users can read rooms_users" ON "public"."rooms_users" AS PERMISSIVE FOR SELECT @@ -108,30 +104,30 @@ AS PERMISSIVE FOR INSERT TO authenticated WITH CHECK (TRUE); -CREATE POLICY "authenticated user can enter room" +CREATE POLICY "authenticated user can read broadcast messages and presence state" ON "realtime"."messages" AS PERMISSIVE FOR SELECT TO authenticated USING ( EXISTS ( - SELECT ru.user_id - FROM public.rooms_users ru - JOIN public.rooms r ON r.id = ru.room_id AND r.topic = realtime.topic() - WHERE ru.user_id = auth.uid() + SELECT 1 + FROM public.rooms_users + WHERE user_id = auth.uid() + AND room_topic = realtime.topic() AND realtime.messages.extension in ('broadcast', 'presence') ) ); -CREATE POLICY "authenticated user can broadcast in room" +CREATE POLICY "authenticated user can send broadcast messages and track presence" ON "realtime"."messages" AS PERMISSIVE FOR INSERT TO authenticated WITH CHECK ( EXISTS ( - SELECT ru.user_id - FROM public.rooms_users ru - JOIN public.rooms r ON r.id = ru.room_id AND r.topic = realtime.topic() - WHERE ru.user_id = auth.uid() + SELECT 1 + FROM public.rooms_users + WHERE user_id = auth.uid() + AND room_topic = realtime.topic() AND realtime.messages.extension in ('broadcast', 'presence') ) ); @@ -145,7 +141,7 @@ We need to create a database function and trigger to add an entry to `public.pro CREATE OR REPLACE FUNCTION insert_user() RETURNS TRIGGER AS $$ BEGIN - INSERT INTO public.profiles (user_id, email) VALUES (NEW.id, NEW.email); RETURN NEW; + INSERT INTO public.profiles (id, email) VALUES (NEW.id, NEW.email); RETURN NEW; END; $$ LANGUAGE plpgsql; @@ -159,18 +155,19 @@ GRANT INSERT ON TABLE public.profiles TO supabase_auth_admin; ## Coding Concerns -* Check we're using the `2.10.0` version of `@supabase/realtime-js` +* Check that you're using `@supabase/realtime-js` v2.44.0 or later. * You need to define that the channel is private using the new configuration field during channel creation: - ```typescript - let channel = supabase.realtime.channel(selectedRoom, { - config: { broadcast: { self: true }, private: true }, - }) - ``` -You can check `app/protected/page.tsx` for more information regarding this +```typescript + const channel = supabase.channel('room-1', { + config: { private: true }, + }) +``` + +You can check `app/protected/page.tsx` to see how we've set it up in the demo. ## Adding user to channel Type `/invite ` of an existing user and they will be added to the channel. -![](invite.png) \ No newline at end of file +![invite user slash command](invite.png) diff --git a/examples/realtime/nextjs-authorization-demo/app/protected/page.tsx b/examples/realtime/nextjs-authorization-demo/app/protected/page.tsx index 44ffd952573..0c497343775 100644 --- a/examples/realtime/nextjs-authorization-demo/app/protected/page.tsx +++ b/examples/realtime/nextjs-authorization-demo/app/protected/page.tsx @@ -23,15 +23,15 @@ export default function Chat() { } const addUserToChannel = async (email: string) => { - const user = await supabase.from('profiles').select('user_id').eq('email', email) + const user = await supabase.from('profiles').select('id').eq('email', email) if (!user.data?.length) { addMessage(true, true, `User ${email} not found`) } else { - const room = await supabase.from('rooms').select('id').eq('topic', selectedRoom) + const room = await supabase.from('rooms').select('topic').eq('topic', selectedRoom) await supabase .from('rooms_users') - .upsert({ user_id: user.data?.[0].user_id, room_id: room.data?.[0].id }) + .upsert({ user_id: user.data?.[0].id, room_topic: room.data?.[0].topic }) addMessage(true, true, `Added ${email} to channel ${selectedRoom}`) } } @@ -65,7 +65,7 @@ export default function Chat() { await supabase.auth.getUser() const token = (await supabase.auth.getSession()).data.session?.access_token! supabase.realtime.setAuth(token) - let main = supabase.realtime + let main = supabase .channel('supaslack') .on('broadcast', { event: 'new_room' }, () => getChannels()) .subscribe() @@ -86,7 +86,7 @@ export default function Chat() { channel?.unsubscribe() setUsers(new Set()) - let newChannel = supabase.realtime.channel(selectedRoom, { + let newChannel = supabase.channel(selectedRoom, { config: { broadcast: { self: true }, private: true, // This line will tell the server that you want to use a private channel for this connection diff --git a/examples/realtime/nextjs-authorization-demo/components/create-room-modal.tsx b/examples/realtime/nextjs-authorization-demo/components/create-room-modal.tsx index e37b0f7651a..a59d898b4e4 100644 --- a/examples/realtime/nextjs-authorization-demo/components/create-room-modal.tsx +++ b/examples/realtime/nextjs-authorization-demo/components/create-room-modal.tsx @@ -14,12 +14,12 @@ export default function CreateRoomModal({ channel }: { channel: RealtimeChannel supabase.realtime.setAuth(token) - const rooms_response = await supabase.from('rooms').insert({ topic }).select('id') + const rooms_response = await supabase.from('rooms').insert({ topic }).select('topic') if (rooms_response.data) { await supabase .from('rooms_users') - .insert({ user_id: user.data.user!.id, room_id: rooms_response.data![0].id }) + .insert({ user_id: user.data.user!.id, room_topic: rooms_response.data![0].topic }) await channel?.send({ type: 'broadcast',