From 827c82e013a0f842e8e717b21e0ff410c1d62a30 Mon Sep 17 00:00:00 2001 From: Chase Granberry Date: Wed, 7 Aug 2024 14:11:29 -0700 Subject: [PATCH 01/11] feat: check for private key on public Channel and error --- README.md | 1 + lib/realtime_web/channels/realtime_channel.ex | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/README.md b/README.md index 2658ae6c6..54b7f9acf 100644 --- a/README.md +++ b/README.md @@ -218,6 +218,7 @@ This is the list of operational codes that can help you understand your deployme | ErrorOnRpcCall | Error when calling another realtime node | | ErrorExecutingTransaction | Error executing a database transaction in tenant database | | SynInitializationError | Our framework to syncronize processes has failed to properly startup a connection to the database | +| PrivateKeyPublicChannelError | Incoming private message found on public Channel | | UnknownError | An unknown error occurred | ## License diff --git a/lib/realtime_web/channels/realtime_channel.ex b/lib/realtime_web/channels/realtime_channel.ex index a9c39c6ce..dd5f2b86d 100644 --- a/lib/realtime_web/channels/realtime_channel.ex +++ b/lib/realtime_web/channels/realtime_channel.ex @@ -352,6 +352,15 @@ defmodule RealtimeWeb.RealtimeChannel do end end + def handle_in( + "broadcast", + %{"private" => true}, + %{assigns: %{check_authorization?: false}} = socket + ) do + message = "PrivateKeyPublicChannelError: Incoming private message found on public Channel" + shutdown_response(socket, message) + end + def handle_in("broadcast", payload, socket) do BroadcastHandler.call(payload, socket) end From acf980bec5d37af16aa03a7a776c01e94c932252 Mon Sep 17 00:00:00 2001 From: Chase Granberry Date: Wed, 7 Aug 2024 15:33:48 -0700 Subject: [PATCH 02/11] fix: wrap in payload --- lib/realtime_web/channels/realtime_channel.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/realtime_web/channels/realtime_channel.ex b/lib/realtime_web/channels/realtime_channel.ex index dd5f2b86d..7dcc3c44a 100644 --- a/lib/realtime_web/channels/realtime_channel.ex +++ b/lib/realtime_web/channels/realtime_channel.ex @@ -354,7 +354,7 @@ defmodule RealtimeWeb.RealtimeChannel do def handle_in( "broadcast", - %{"private" => true}, + %{"payload" => %{"private" => true}}, %{assigns: %{check_authorization?: false}} = socket ) do message = "PrivateKeyPublicChannelError: Incoming private message found on public Channel" From 5754e1a2a8d06aadadb6f7a576a56ba95e02d665 Mon Sep 17 00:00:00 2001 From: Chase Granberry Date: Wed, 7 Aug 2024 16:24:50 -0700 Subject: [PATCH 03/11] chore: start test --- .../channels/realtime_channel_test.exs | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/test/realtime_web/channels/realtime_channel_test.exs b/test/realtime_web/channels/realtime_channel_test.exs index f79fe8b02..63a49335c 100644 --- a/test/realtime_web/channels/realtime_channel_test.exs +++ b/test/realtime_web/channels/realtime_channel_test.exs @@ -178,4 +178,37 @@ defmodule RealtimeWeb.RealtimeChannelTest do subscribe_and_join(socket, "realtime:test", %{}) end end + + describe "private messages" do + setup_with_mocks([ + {ChannelsAuthorization, [], + authorize_conn: fn _, _, _ -> + {:ok, %{"exp" => Joken.current_time() + 1_000, "role" => "postgres"}} + end} + ]) do + :ok + end + + test "private message on public Channel" do + {:ok, %Socket{} = socket} = connect(UserSocket, %{}, @default_conn_opts) + + socket = Socket.assign(socket, %{check_authorization?: false}) + + join_payload = %{ + "config" => %{ + "broadcase" => %{"self" => false}, + "presence" => %{"key" => ""}, + "postgres_changes" => [] + }, + "access_token" => "blah" + } + + {:ok, _, %Socket{} = socket} = + subscribe_and_join(socket, "realtime:test", join_payload) + |> IO.inspect() + + push(socket, "broadcast", %{private: true}) + assert_push("broadcast", %{private: true}) + end + end end From 8e6acc575d562a34b3c2c4bac35704ffd9d0f36a Mon Sep 17 00:00:00 2001 From: Chase Granberry Date: Wed, 7 Aug 2024 16:52:40 -0700 Subject: [PATCH 04/11] chore: tests --- .../realtime_web/channels/realtime_channel_test.exs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/test/realtime_web/channels/realtime_channel_test.exs b/test/realtime_web/channels/realtime_channel_test.exs index 63a49335c..127264517 100644 --- a/test/realtime_web/channels/realtime_channel_test.exs +++ b/test/realtime_web/channels/realtime_channel_test.exs @@ -192,8 +192,6 @@ defmodule RealtimeWeb.RealtimeChannelTest do test "private message on public Channel" do {:ok, %Socket{} = socket} = connect(UserSocket, %{}, @default_conn_opts) - socket = Socket.assign(socket, %{check_authorization?: false}) - join_payload = %{ "config" => %{ "broadcase" => %{"self" => false}, @@ -205,10 +203,15 @@ defmodule RealtimeWeb.RealtimeChannelTest do {:ok, _, %Socket{} = socket} = subscribe_and_join(socket, "realtime:test", join_payload) - |> IO.inspect() - push(socket, "broadcast", %{private: true}) - assert_push("broadcast", %{private: true}) + socket = Socket.assign(socket, check_authorization?: true) + + IO.inspect(socket) + + assert_received(%Phoenix.Socket.Message{topic: "realtime:test", event: "presence_state"}) + + push(socket, "broadcast", %{}) + assert_received(%{}) end end end From 774de20550ef33d817219d700d18295ff7872255 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filipe=20Caba=C3=A7o?= Date: Thu, 8 Aug 2024 13:29:53 +0100 Subject: [PATCH 05/11] test the private payload --- lib/realtime_web/channels/realtime_channel.ex | 7 ++-- test/integration/rt_channel_test.exs | 42 +++++++++++++++++++ 2 files changed, 45 insertions(+), 4 deletions(-) diff --git a/lib/realtime_web/channels/realtime_channel.ex b/lib/realtime_web/channels/realtime_channel.ex index 7dcc3c44a..17d35be2a 100644 --- a/lib/realtime_web/channels/realtime_channel.ex +++ b/lib/realtime_web/channels/realtime_channel.ex @@ -354,7 +354,7 @@ defmodule RealtimeWeb.RealtimeChannel do def handle_in( "broadcast", - %{"payload" => %{"private" => true}}, + %{"private" => true}, %{assigns: %{check_authorization?: false}} = socket ) do message = "PrivateKeyPublicChannelError: Incoming private message found on public Channel" @@ -371,7 +371,6 @@ defmodule RealtimeWeb.RealtimeChannel do def handle_in(type, payload, socket) do socket = count(socket) - # Log info here so that bad messages from clients won't flood Logflare # Can subscribe to a Channel with `log_level` `info` to see these messages message = "Unexpected message from client of type `#{type}` with payload: #{inspect(payload)}" @@ -396,7 +395,7 @@ defmodule RealtimeWeb.RealtimeChannel do wait end - def limit_joins(%{tenant: tenant, limits: limits}) do + defp limit_joins(%{tenant: tenant, limits: limits}) do id = Tenants.joins_per_second_key(tenant) GenCounter.new(id) @@ -424,7 +423,7 @@ defmodule RealtimeWeb.RealtimeChannel do end end - def limit_channels(%{assigns: %{tenant: tenant, limits: limits}, transport_pid: pid}) do + defp limit_channels(%{assigns: %{tenant: tenant, limits: limits}, transport_pid: pid}) do key = Tenants.channels_per_client_key(tenant) if Registry.count_match(Realtime.Registry, key, pid) > limits.max_channels_per_client do diff --git a/test/integration/rt_channel_test.exs b/test/integration/rt_channel_test.exs index d1725a0eb..0ee767314 100644 --- a/test/integration/rt_channel_test.exs +++ b/test/integration/rt_channel_test.exs @@ -276,6 +276,48 @@ defmodule Realtime.Integration.RtChannelTest do } end + @tag policies: [ + :authenticated_read_broadcast_and_presence, + :authenticated_write_broadcast_and_presence + ] + test "public subscriber tries to send private message closes the channel", %{ + topic: topic + } do + {socket, _} = get_connection("anon") + config = %{broadcast: %{self: true}, private: false} + topic = "realtime:#{topic}" + WebsocketClient.join(socket, topic, %{config: config}) + + assert_receive %Message{ + event: "phx_reply", + payload: %{ + "response" => %{"postgres_changes" => []}, + "status" => "ok" + }, + ref: "1", + topic: ^topic + } + + assert_receive %Message{} + + payload = %{ + "event" => "TEST", + "payload" => %{"msg" => 1}, + "type" => "broadcast", + "private" => true + } + + WebsocketClient.send_event(socket, topic, "broadcast", payload) + + %{payload: payload} = + assert_receive %Message{ + event: "system", + topic: ^topic + } + + assert payload["message"] =~ "PrivateKeyPublicChannelError" + end + @tag policies: [ :authenticated_read_broadcast_and_presence, :authenticated_write_broadcast_and_presence From 158c7fa61dba48f1759877fd60675cf945870ce6 Mon Sep 17 00:00:00 2001 From: Chase Granberry Date: Thu, 8 Aug 2024 15:45:14 -0700 Subject: [PATCH 06/11] chore: del broken test --- .../channels/realtime_channel_test.exs | 25 ------------------- 1 file changed, 25 deletions(-) diff --git a/test/realtime_web/channels/realtime_channel_test.exs b/test/realtime_web/channels/realtime_channel_test.exs index 127264517..c1ca67a10 100644 --- a/test/realtime_web/channels/realtime_channel_test.exs +++ b/test/realtime_web/channels/realtime_channel_test.exs @@ -188,30 +188,5 @@ defmodule RealtimeWeb.RealtimeChannelTest do ]) do :ok end - - test "private message on public Channel" do - {:ok, %Socket{} = socket} = connect(UserSocket, %{}, @default_conn_opts) - - join_payload = %{ - "config" => %{ - "broadcase" => %{"self" => false}, - "presence" => %{"key" => ""}, - "postgres_changes" => [] - }, - "access_token" => "blah" - } - - {:ok, _, %Socket{} = socket} = - subscribe_and_join(socket, "realtime:test", join_payload) - - socket = Socket.assign(socket, check_authorization?: true) - - IO.inspect(socket) - - assert_received(%Phoenix.Socket.Message{topic: "realtime:test", event: "presence_state"}) - - push(socket, "broadcast", %{}) - assert_received(%{}) - end end end From 50c25bb6cda7cbed15236ec814a67a920faa866d Mon Sep 17 00:00:00 2001 From: Chase Granberry Date: Thu, 8 Aug 2024 15:45:27 -0700 Subject: [PATCH 07/11] fix: bump version --- mix.exs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mix.exs b/mix.exs index dda42aae1..c67ac3ed8 100644 --- a/mix.exs +++ b/mix.exs @@ -4,7 +4,7 @@ defmodule Realtime.MixProject do def project do [ app: :realtime, - version: "2.33.10", + version: "2.33.11", elixir: "~> 1.16.0", elixirc_paths: elixirc_paths(Mix.env()), start_permanent: Mix.env() == :prod, From 56b34989f6ae61ab55b28b9db8b65ef75f36a3ca Mon Sep 17 00:00:00 2001 From: Chase Granberry Date: Thu, 8 Aug 2024 16:56:19 -0700 Subject: [PATCH 08/11] chore: upgrade supabase-js --- assets/package-lock.json | 321 ++++++++++----------------------------- assets/package.json | 2 +- 2 files changed, 79 insertions(+), 244 deletions(-) diff --git a/assets/package-lock.json b/assets/package-lock.json index 52ce3ce46..1adea22be 100644 --- a/assets/package-lock.json +++ b/assets/package-lock.json @@ -5,236 +5,95 @@ "packages": { "": { "dependencies": { - "@supabase/supabase-js": "^2.26.0" + "@supabase/supabase-js": "^2.45.1" + } + }, + "node_modules/@supabase/auth-js": { + "version": "2.64.4", + "resolved": "https://registry.npmjs.org/@supabase/auth-js/-/auth-js-2.64.4.tgz", + "integrity": "sha512-9ITagy4WP4FLl+mke1rchapOH0RQpf++DI+WSG2sO1OFOZ0rW3cwAM0nCrMOxu+Zw4vJ4zObc08uvQrXx590Tg==", + "dependencies": { + "@supabase/node-fetch": "^2.6.14" } }, "node_modules/@supabase/functions-js": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@supabase/functions-js/-/functions-js-2.1.2.tgz", - "integrity": "sha512-QCR6pwJs9exCl37bmpMisUd6mf+0SUBJ6mUpiAjEkSJ/+xW8TCuO14bvkWHADd5hElJK9MxNlMQXxSA4DRz9nQ==", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@supabase/functions-js/-/functions-js-2.4.1.tgz", + "integrity": "sha512-8sZ2ibwHlf+WkHDUZJUXqqmPvWQ3UHN0W30behOJngVh/qHHekhJLCFbh0AjkE9/FqqXtf9eoVvmYgfCLk5tNA==", "dependencies": { - "cross-fetch": "^3.1.5" + "@supabase/node-fetch": "^2.6.14" } }, - "node_modules/@supabase/gotrue-js": { - "version": "2.34.0", - "resolved": "https://registry.npmjs.org/@supabase/gotrue-js/-/gotrue-js-2.34.0.tgz", - "integrity": "sha512-j4up+jZDyutUwKcrwDXhVbeHFydUu9wLvotr4qREenz+ec0d3L7Zs0Nb1hP8B64HbJ4tmFXhpOG23IsvQtC58w==", + "node_modules/@supabase/node-fetch": { + "version": "2.6.15", + "resolved": "https://registry.npmjs.org/@supabase/node-fetch/-/node-fetch-2.6.15.tgz", + "integrity": "sha512-1ibVeYUacxWYi9i0cf5efil6adJ9WRyZBLivgjs+AUpewx1F3xPi7gLgaASI2SmIQxPoCEjAsLAzKPgMJVgOUQ==", "dependencies": { - "cross-fetch": "^3.1.5" + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" } }, "node_modules/@supabase/postgrest-js": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/@supabase/postgrest-js/-/postgrest-js-1.7.1.tgz", - "integrity": "sha512-xPRYLaZrkLbXNlzmHW6Wtf9hmcBLjjI5xUz2zj8oE2hgXGaYoZBBkpN9bmW9i17Z1f6Ujxa942AqK439XOA36A==", + "version": "1.15.8", + "resolved": "https://registry.npmjs.org/@supabase/postgrest-js/-/postgrest-js-1.15.8.tgz", + "integrity": "sha512-YunjXpoQjQ0a0/7vGAvGZA2dlMABXFdVI/8TuVKtlePxyT71sl6ERl6ay1fmIeZcqxiuFQuZw/LXUuStUG9bbg==", "dependencies": { - "cross-fetch": "^3.1.5" + "@supabase/node-fetch": "^2.6.14" } }, "node_modules/@supabase/realtime-js": { - "version": "2.7.3", - "resolved": "https://registry.npmjs.org/@supabase/realtime-js/-/realtime-js-2.7.3.tgz", - "integrity": "sha512-c7TzL81sx2kqyxsxcDduJcHL9KJdCOoKimGP6lQSqiZKX42ATlBZpWbyy9KFGFBjAP4nyopMf5JhPi2ZH9jyNw==", + "version": "2.10.2", + "resolved": "https://registry.npmjs.org/@supabase/realtime-js/-/realtime-js-2.10.2.tgz", + "integrity": "sha512-qyCQaNg90HmJstsvr2aJNxK2zgoKh9ZZA8oqb7UT2LCh3mj9zpa3Iwu167AuyNxsxrUE8eEJ2yH6wLCij4EApA==", "dependencies": { + "@supabase/node-fetch": "^2.6.14", "@types/phoenix": "^1.5.4", - "@types/websocket": "^1.0.3", - "websocket": "^1.0.34" + "@types/ws": "^8.5.10", + "ws": "^8.14.2" } }, "node_modules/@supabase/storage-js": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/@supabase/storage-js/-/storage-js-2.5.1.tgz", - "integrity": "sha512-nkR0fQA9ScAtIKA3vNoPEqbZv1k5B5HVRYEvRWdlP6mUpFphM9TwPL2jZ/ztNGMTG5xT6SrHr+H7Ykz8qzbhjw==", + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@supabase/storage-js/-/storage-js-2.6.0.tgz", + "integrity": "sha512-REAxr7myf+3utMkI2oOmZ6sdplMZZ71/2NEIEMBZHL9Fkmm3/JnaOZVSRqvG4LStYj2v5WhCruCzuMn6oD/Drw==", "dependencies": { - "cross-fetch": "^3.1.5" + "@supabase/node-fetch": "^2.6.14" } }, "node_modules/@supabase/supabase-js": { - "version": "2.26.0", - "resolved": "https://registry.npmjs.org/@supabase/supabase-js/-/supabase-js-2.26.0.tgz", - "integrity": "sha512-RXmTPTobaYAwkSobadHZmEVLmzX3SGrtRZIGfLWnLv92VzBRrjuXn0a+bJqKl50GUzsyqPA+j5pod7EwMkcH5A==", + "version": "2.45.1", + "resolved": "https://registry.npmjs.org/@supabase/supabase-js/-/supabase-js-2.45.1.tgz", + "integrity": "sha512-/PVe3lXmalazD8BGMIoI7+ttvT1mLXy13lNcoAPtjP1TDDY83g8csZbVR6l+0/RZtvJxl3LGXfTJT4bjWgC5Nw==", "dependencies": { - "@supabase/functions-js": "^2.1.0", - "@supabase/gotrue-js": "^2.31.0", - "@supabase/postgrest-js": "^1.7.0", - "@supabase/realtime-js": "^2.7.3", - "@supabase/storage-js": "^2.5.1", - "cross-fetch": "^3.1.5" + "@supabase/auth-js": "2.64.4", + "@supabase/functions-js": "2.4.1", + "@supabase/node-fetch": "2.6.15", + "@supabase/postgrest-js": "1.15.8", + "@supabase/realtime-js": "2.10.2", + "@supabase/storage-js": "2.6.0" } }, "node_modules/@types/node": { - "version": "20.3.2", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.3.2.tgz", - "integrity": "sha512-vOBLVQeCQfIcF/2Y7eKFTqrMnizK5lRNQ7ykML/5RuwVXVWxYkgwS7xbt4B6fKCUPgbSL5FSsjHQpaGQP/dQmw==" - }, - "node_modules/@types/phoenix": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@types/phoenix/-/phoenix-1.6.0.tgz", - "integrity": "sha512-qwfpsHmFuhAS/dVd4uBIraMxRd56vwBUYQGZ6GpXnFuM2XMRFJbIyruFKKlW2daQliuYZwe0qfn/UjFCDKic5g==" - }, - "node_modules/@types/websocket": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@types/websocket/-/websocket-1.0.5.tgz", - "integrity": "sha512-NbsqiNX9CnEfC1Z0Vf4mE1SgAJ07JnRYcNex7AJ9zAVzmiGHmjKFEk7O4TJIsgv2B1sLEb6owKFZrACwdYngsQ==", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/bufferutil": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.7.tgz", - "integrity": "sha512-kukuqc39WOHtdxtw4UScxF/WVnMFVSQVKhtx3AjZJzhd0RGZZldcrfSEbVsWWe6KNH253574cq5F+wpv0G9pJw==", - "hasInstallScript": true, - "dependencies": { - "node-gyp-build": "^4.3.0" - }, - "engines": { - "node": ">=6.14.2" - } - }, - "node_modules/cross-fetch": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.6.tgz", - "integrity": "sha512-riRvo06crlE8HiqOwIpQhxwdOk4fOeR7FVM/wXoxchFEqMNUjvbs3bfo4OTgMEMHzppd4DxFBDbyySj8Cv781g==", - "dependencies": { - "node-fetch": "^2.6.11" - } - }, - "node_modules/d": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", - "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", + "version": "22.1.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.1.0.tgz", + "integrity": "sha512-AOmuRF0R2/5j1knA3c6G3HOk523Ga+l+ZXltX8SF1+5oqcXijjfTd8fY3XRZqSihEu9XhtQnKYLmkFaoxgsJHw==", "dependencies": { - "es5-ext": "^0.10.50", - "type": "^1.0.1" + "undici-types": "~6.13.0" } }, - "node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/es5-ext": { - "version": "0.10.64", - "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.64.tgz", - "integrity": "sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg==", - "hasInstallScript": true, - "dependencies": { - "es6-iterator": "^2.0.3", - "es6-symbol": "^3.1.3", - "esniff": "^2.0.1", - "next-tick": "^1.1.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/es6-iterator": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", - "integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==", - "dependencies": { - "d": "1", - "es5-ext": "^0.10.35", - "es6-symbol": "^3.1.1" - } - }, - "node_modules/es6-symbol": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz", - "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==", - "dependencies": { - "d": "^1.0.1", - "ext": "^1.1.2" - } - }, - "node_modules/esniff": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/esniff/-/esniff-2.0.1.tgz", - "integrity": "sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg==", - "dependencies": { - "d": "^1.0.1", - "es5-ext": "^0.10.62", - "event-emitter": "^0.3.5", - "type": "^2.7.2" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/esniff/node_modules/type": { - "version": "2.7.2", - "resolved": "https://registry.npmjs.org/type/-/type-2.7.2.tgz", - "integrity": "sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw==" - }, - "node_modules/event-emitter": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", - "integrity": "sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==", - "dependencies": { - "d": "1", - "es5-ext": "~0.10.14" - } - }, - "node_modules/ext": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/ext/-/ext-1.7.0.tgz", - "integrity": "sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==", - "dependencies": { - "type": "^2.7.2" - } - }, - "node_modules/ext/node_modules/type": { - "version": "2.7.2", - "resolved": "https://registry.npmjs.org/type/-/type-2.7.2.tgz", - "integrity": "sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw==" - }, - "node_modules/is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==" - }, - "node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - }, - "node_modules/next-tick": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", - "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==" + "node_modules/@types/phoenix": { + "version": "1.6.5", + "resolved": "https://registry.npmjs.org/@types/phoenix/-/phoenix-1.6.5.tgz", + "integrity": "sha512-xegpDuR+z0UqG9fwHqNoy3rI7JDlvaPh2TY47Fl80oq6g+hXT+c/LEuE43X48clZ6lOfANl5WrPur9fYO1RJ/w==" }, - "node_modules/node-fetch": { - "version": "2.6.11", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.11.tgz", - "integrity": "sha512-4I6pdBY1EthSqDmJkiNk3JIT8cswwR9nfeW/cPdUagJYEQG7R95WRH74wpz7ma8Gh/9dI9FP+OU+0E4FvtA55w==", + "node_modules/@types/ws": { + "version": "8.5.12", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.12.tgz", + "integrity": "sha512-3tPRkv1EtkDpzlgyKyI8pGsGZAGPEaXeu0DOj5DI25Ja91bdAYddYHbADRYVrZMRbfW+1l5YwXVDKohDJNQxkQ==", "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } - } - }, - "node_modules/node-gyp-build": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.6.0.tgz", - "integrity": "sha512-NTZVKn9IylLwUzaKjkas1e4u2DLNcV4rdYagA4PWdPwW87Bi7z+BznyKSRwS/761tV/lzCGXplWsiaMjLqP2zQ==", - "bin": { - "node-gyp-build": "bin.js", - "node-gyp-build-optional": "optional.js", - "node-gyp-build-test": "build-test.js" + "@types/node": "*" } }, "node_modules/tr46": { @@ -242,52 +101,16 @@ "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" }, - "node_modules/type": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", - "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==" - }, - "node_modules/typedarray-to-buffer": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", - "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", - "dependencies": { - "is-typedarray": "^1.0.0" - } - }, - "node_modules/utf-8-validate": { - "version": "5.0.10", - "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.10.tgz", - "integrity": "sha512-Z6czzLq4u8fPOyx7TU6X3dvUZVvoJmxSQ+IcrlmagKhilxlhZgxPK6C5Jqbkw1IDUmFTM+cz9QDnnLTwDz/2gQ==", - "hasInstallScript": true, - "dependencies": { - "node-gyp-build": "^4.3.0" - }, - "engines": { - "node": ">=6.14.2" - } + "node_modules/undici-types": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.13.0.tgz", + "integrity": "sha512-xtFJHudx8S2DSoujjMd1WeWvn7KKWFRESZTMeL1RptAYERu29D6jphMjjY+vn96jvN3kVPDNxU/E13VTaXj6jg==" }, "node_modules/webidl-conversions": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" }, - "node_modules/websocket": { - "version": "1.0.34", - "resolved": "https://registry.npmjs.org/websocket/-/websocket-1.0.34.tgz", - "integrity": "sha512-PRDso2sGwF6kM75QykIesBijKSVceR6jL2G8NGYyq2XrItNC2P5/qL5XeR056GhA+Ly7JMFvJb9I312mJfmqnQ==", - "dependencies": { - "bufferutil": "^4.0.1", - "debug": "^2.2.0", - "es5-ext": "^0.10.50", - "typedarray-to-buffer": "^3.1.5", - "utf-8-validate": "^5.0.2", - "yaeti": "^0.0.6" - }, - "engines": { - "node": ">=4.0.0" - } - }, "node_modules/whatwg-url": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", @@ -297,12 +120,24 @@ "webidl-conversions": "^3.0.0" } }, - "node_modules/yaeti": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/yaeti/-/yaeti-0.0.6.tgz", - "integrity": "sha512-MvQa//+KcZCUkBTIC9blM+CU9J2GzuTytsOUwf2lidtvkx/6gnEp1QvJv34t9vdjhFmha/mUiNDbN0D0mJWdug==", + "node_modules/ws": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", + "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", "engines": { - "node": ">=0.10.32" + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } } } } diff --git a/assets/package.json b/assets/package.json index 9c0fe8fb0..11a818907 100644 --- a/assets/package.json +++ b/assets/package.json @@ -1,5 +1,5 @@ { "dependencies": { - "@supabase/supabase-js": "^2.26.0" + "@supabase/supabase-js": "^2.45.1" } } \ No newline at end of file From b88c3f1a2164ba19fef0b474c98dc22f5152e4d8 Mon Sep 17 00:00:00 2001 From: Chase Granberry Date: Thu, 8 Aug 2024 16:59:41 -0700 Subject: [PATCH 09/11] feat: inspector now compatible with private channels and messages --- assets/js/app.js | 23 ++++++++++++++----- .../inspector_live/conn_component.html.heex | 13 +++++++++++ .../live/inspector_live/index.html.heex | 14 ++++++++++- 3 files changed, 43 insertions(+), 7 deletions(-) diff --git a/assets/js/app.js b/assets/js/app.js index 6584a349f..b237a5644 100644 --- a/assets/js/app.js +++ b/assets/js/app.js @@ -11,7 +11,8 @@ import { createClient } from "@supabase/supabase-js"; let Hooks = {}; Hooks.payload = { initRealtime( - channelName, + channel_name, + private_channel, host, log_level, token, @@ -33,6 +34,10 @@ Hooks.payload = { }, }; + const privateChannel = private_channel + ? private_channel.toLowerCase() === "true" + : false; + this.realtimeSocket = createClient(host, token, opts); if (bearer != "") { @@ -42,8 +47,8 @@ Hooks.payload = { // Join the Channel 'any' // Channels can be named anything // All clients on the same Channel will get messages sent to that Channel - this.channel = this.realtimeSocket.channel(channelName, { - config: { broadcast: { self: true } }, + this.channel = this.realtimeSocket.channel(channel_name, { + config: { broadcast: { self: true }, private: privateChannel }, }); // Hack to confirm Postgres is subscribed @@ -131,7 +136,7 @@ Hooks.payload = { localStorage.setItem("host", host); localStorage.setItem("token", token); localStorage.setItem("log_level", log_level); - localStorage.setItem("channel", channelName); + localStorage.setItem("channel", channel_name); localStorage.setItem("schema", schema); localStorage.setItem("table", table); localStorage.setItem("filter", filter); @@ -187,12 +192,16 @@ Hooks.payload = { }); }, - sendRealtime(event, payload) { + sendRealtime(event, payload, privateMessage) { // Send a `broadcast` message over the Channel // All connected clients will receive this message if they're subscribed // to `broadcast` events and matching on the `event` name or using `*` to match all event names + + let priv = privateMessage ? privateMessage.toLowerCase() === "true" : false; + this.channel.send({ type: "broadcast", + private: priv, event: event, payload: payload, }); @@ -221,6 +230,7 @@ Hooks.payload = { bearer: localStorage.getItem("bearer"), enable_presence: localStorage.getItem("enable_presence"), enable_db_changes: localStorage.getItem("enable_db_changes"), + private_channel: localStorage.getItem("private_channel"), }; this.pushEventTo("#conn_form", "local_storage", params); @@ -228,6 +238,7 @@ Hooks.payload = { this.handleEvent("connect", ({ connection }) => this.initRealtime( connection.channel, + connection.private_channel, connection.host, connection.log_level, connection.token, @@ -241,7 +252,7 @@ Hooks.payload = { ); this.handleEvent("send_message", ({ message }) => - this.sendRealtime(message.event, message.payload) + this.sendRealtime(message.event, message.payload, message.private_message) ); this.handleEvent("disconnect", ({}) => this.disconnectRealtime()); diff --git a/lib/realtime_web/live/inspector_live/conn_component.html.heex b/lib/realtime_web/live/inspector_live/conn_component.html.heex index 5a620b749..9a0ce528e 100644 --- a/lib/realtime_web/live/inspector_live/conn_component.html.heex +++ b/lib/realtime_web/live/inspector_live/conn_component.html.heex @@ -92,6 +92,19 @@ <%= error_tag(f, :channel) %>

The Channel to connect to

+
+ <%= label(f, :private_channel, class: "block text-gray-700 text-sm font-bold mb-2") %> + <%= checkbox(f, :private_channel, class: " + my-1 + block + rounded-md + border-gray-300 + shadow-sm + focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50 + ") %> + <%= error_tag(f, :private_channel) %> +

Use RLS to enforce policies on this Channel

+
<%= label(f, :schema, class: "block text-gray-700 text-sm font-bold mb-2") %> <%= text_input(f, :schema, placeholder: "public", class: " diff --git a/lib/realtime_web/live/inspector_live/index.html.heex b/lib/realtime_web/live/inspector_live/index.html.heex index 5f296be26..c38a16695 100644 --- a/lib/realtime_web/live/inspector_live/index.html.heex +++ b/lib/realtime_web/live/inspector_live/index.html.heex @@ -102,7 +102,19 @@ <%= error_tag(m, :payload) %>

Message payload

- +
+ <%= label(m, :private_message, class: "block text-gray-700 text-sm font-bold mb-2") %> + <%= checkbox(m, :private_message, class: " + my-1 + block + rounded-md + border-gray-300 + shadow-sm + focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50 + ") %> + <%= error_tag(m, :private_message) %> +

Private message

+
<%= submit("Send", phx_disable_with: "Sending...", class: From 1ed60094cef9fee12cfaddab9ac3dc89e2b7c7aa Mon Sep 17 00:00:00 2001 From: Chase Granberry Date: Thu, 8 Aug 2024 17:03:17 -0700 Subject: [PATCH 10/11] chore: delete unused test setup --- test/realtime_web/channels/realtime_channel_test.exs | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/test/realtime_web/channels/realtime_channel_test.exs b/test/realtime_web/channels/realtime_channel_test.exs index c1ca67a10..f79fe8b02 100644 --- a/test/realtime_web/channels/realtime_channel_test.exs +++ b/test/realtime_web/channels/realtime_channel_test.exs @@ -178,15 +178,4 @@ defmodule RealtimeWeb.RealtimeChannelTest do subscribe_and_join(socket, "realtime:test", %{}) end end - - describe "private messages" do - setup_with_mocks([ - {ChannelsAuthorization, [], - authorize_conn: fn _, _, _ -> - {:ok, %{"exp" => Joken.current_time() + 1_000, "role" => "postgres"}} - end} - ]) do - :ok - end - end end From 47953d11efbc7490cbe5143e445843aa6b76605f Mon Sep 17 00:00:00 2001 From: Chase Granberry Date: Fri, 9 Aug 2024 06:06:46 -0700 Subject: [PATCH 11/11] fix: format --- lib/realtime_web/live/inspector_live/conn_component.html.heex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/realtime_web/live/inspector_live/conn_component.html.heex b/lib/realtime_web/live/inspector_live/conn_component.html.heex index 9a0ce528e..6cdb81c96 100644 --- a/lib/realtime_web/live/inspector_live/conn_component.html.heex +++ b/lib/realtime_web/live/inspector_live/conn_component.html.heex @@ -92,7 +92,7 @@ <%= error_tag(f, :channel) %>

The Channel to connect to

-
+
<%= label(f, :private_channel, class: "block text-gray-700 text-sm font-bold mb-2") %> <%= checkbox(f, :private_channel, class: " my-1