diff --git a/lib/realtime/tenants/authorization.ex b/lib/realtime/tenants/authorization.ex index 8a1ace7da..5819fdd54 100644 --- a/lib/realtime/tenants/authorization.ex +++ b/lib/realtime/tenants/authorization.ex @@ -15,8 +15,6 @@ defmodule Realtime.Tenants.Authorization do alias Realtime.Api.Message alias Realtime.Repo alias Realtime.Tenants.Authorization.Policies - alias Realtime.Tenants.Authorization.Policies.BroadcastPolicies - alias Realtime.Tenants.Authorization.Policies.PresencePolicies defstruct [:topic, :headers, :jwt, :claims, :role] @@ -85,7 +83,6 @@ defmodule Realtime.Tenants.Authorization do * role: The role of the user * realtime.topic: The name of the channel being accessed * request.jwt.claim.role: The role of the user - * request.jwt: The JWT token * request.jwt.claim.sub: The sub claim of the JWT token * request.jwt.claims: The claims of the JWT token * request.headers: The headers of the request @@ -96,7 +93,6 @@ defmodule Realtime.Tenants.Authorization do %__MODULE__{ topic: topic, headers: headers, - jwt: jwt, claims: claims, role: role } = authorization_context @@ -110,15 +106,13 @@ defmodule Realtime.Tenants.Authorization do SELECT set_config('role', $1, true), set_config('realtime.topic', $2, true), - set_config('request.jwt', $3, true), - set_config('request.jwt.claims', $4, true), - set_config('request.headers', $5, true) + set_config('request.jwt.claims', $3, true), + set_config('request.headers', $4, true) """, - [role, topic, jwt, claims, headers] + [role, topic, claims, headers] ) end - @policies_mods [BroadcastPolicies, PresencePolicies] defp get_policies_for_connection(conn, authorization_context) do Database.transaction(conn, fn transaction_conn -> messages = [ @@ -131,24 +125,23 @@ defmodule Realtime.Tenants.Authorization do {[%{id: broadcast_id}], [%{id: presence_id}]} = Enum.split_with(messages, &(&1.extension == :broadcast)) - ids = %{presence_id: presence_id, broadcast_id: broadcast_id} - set_conn_config(transaction_conn, authorization_context) policies = %Policies{} policies = get_read_policy_for_connection_and_extension( transaction_conn, - ids, - policies, - authorization_context + authorization_context, + broadcast_id, + presence_id, + policies ) policies = get_write_policy_for_connection_and_extension( transaction_conn, - policies, - authorization_context + authorization_context, + policies ) Postgrex.query!(transaction_conn, "ROLLBACK AND CHAIN", []) @@ -157,27 +150,47 @@ defmodule Realtime.Tenants.Authorization do end) end - defp get_read_policy_for_connection_and_extension(conn, ids, policies, authorization_context) do - Enum.reduce_while(@policies_mods, policies, fn policy_mod, policies -> - res = policy_mod.check_read_policies(conn, ids, policies, authorization_context) - - case res do - {:error, error} -> {:halt, {:error, error}} - %DBConnection.TransactionError{} = err -> {:halt, err} - {:ok, policy} -> {:cont, policy} - end - end) + import Ecto.Query + + defp get_read_policy_for_connection_and_extension( + conn, + authorization_context, + broadcast_id, + presence_id, + policies + ) do + query = + from(m in Message, + where: [topic: ^authorization_context.topic], + where: [extension: :broadcast, id: ^broadcast_id], + or_where: [extension: :presence, id: ^presence_id] + ) + + {:ok, res} = Repo.all(conn, query, Message) + can_presence? = Enum.any?(res, fn %{id: id} -> id == presence_id end) + can_broadcast? = Enum.any?(res, fn %{id: id} -> id == broadcast_id end) + + policies + |> Policies.update_policies(:presence, :read, can_presence?) + |> Policies.update_policies(:broadcast, :read, can_broadcast?) end - defp get_write_policy_for_connection_and_extension(conn, policies, authorization_context) do - Enum.reduce_while(@policies_mods, policies, fn policy_mod, policies -> - res = policy_mod.check_write_policies(conn, policies, authorization_context) + defp get_write_policy_for_connection_and_extension( + conn, + authorization_context, + policies + ) do + broadcast_changeset = + Message.changeset(%Message{}, %{topic: authorization_context.topic, extension: :broadcast}) - case res do - {:error, error} -> {:halt, {:error, error}} - %DBConnection.TransactionError{} = err -> {:halt, err} - {:ok, policy} -> {:cont, policy} - end - end) + presence_changeset = + Message.changeset(%Message{}, %{topic: authorization_context.topic, extension: :presence}) + + broadcast_result = Repo.insert(conn, broadcast_changeset, Message, mode: :savepoint) + presence_result = Repo.insert(conn, presence_changeset, Message, mode: :savepoint) + + policies + |> Policies.update_policies(:presence, :write, match?({:ok, _}, presence_result)) + |> Policies.update_policies(:broadcast, :write, match?({:ok, _}, broadcast_result)) end end diff --git a/lib/realtime/tenants/authorization/policies.ex b/lib/realtime/tenants/authorization/policies.ex index bd73428a4..57a4df401 100644 --- a/lib/realtime/tenants/authorization/policies.ex +++ b/lib/realtime/tenants/authorization/policies.ex @@ -2,14 +2,11 @@ defmodule Realtime.Tenants.Authorization.Policies do @moduledoc """ Policies structure that holds the required authorization information for a given connection. - Also defines a behaviour to be used by the different authorization modules to build and check policies within the context of an entity. - Currently there are two types of policies: - * Realtime.Tenants.Authorization.Policies.BroadcastPolicies - Used to check access to the Broadcast feature on a given Topic - * Realtime.Tenants.Authorization.Policies.PresencePolicies - Used to check access to Presence feature on a given Topic + * Realtime.Tenants.Authorization.Policies.BroadcastPolicies - Used to store the access to Broadcast feature on a given Topic + * Realtime.Tenants.Authorization.Policies.PresencePolicies - Used to store the access to Presence feature on a given Topic """ - alias Realtime.Tenants.Authorization alias Realtime.Tenants.Authorization.Policies.BroadcastPolicies alias Realtime.Tenants.Authorization.Policies.PresencePolicies @@ -21,27 +18,6 @@ defmodule Realtime.Tenants.Authorization.Policies do presence: PresencePolicies.t() } - @doc """ - Implementation of the method on how to check read policies for a given entity within the context of a database connection - - Arguments: - * `db_conn` - The database connection with the required context to properly run checks - * `policies` - The policies struct to which the result will be accumulated - * `authorization` - The authorization struct with required information for Policy checking - """ - @callback check_read_policies(DBConnection.t(), map(), t(), Authorization.t()) :: - {:ok, t()} | {:error, any()} - @doc """ - Implementation of the method on how to check write policies for a given entity within the context of a database connection - - Arguments: - * `db_conn` - The database connection with the required context to properly run checks - * `policies` - The policies struct to which the result will be accumulated - * `authorization` - The authorization struct with required information for policy checking - """ - @callback check_write_policies(DBConnection.t(), t(), Authorization.t()) :: - {:ok, t()} | {:error, any()} - @doc """ Updates the Policies struct sub key with the given value. """ diff --git a/lib/realtime/tenants/authorization/policies/broadcast_policies.ex b/lib/realtime/tenants/authorization/policies/broadcast_policies.ex index d6d2245ba..2102b232c 100644 --- a/lib/realtime/tenants/authorization/policies/broadcast_policies.ex +++ b/lib/realtime/tenants/authorization/policies/broadcast_policies.ex @@ -1,83 +1,13 @@ defmodule Realtime.Tenants.Authorization.Policies.BroadcastPolicies do @moduledoc """ BroadcastPolicies structure that holds the required authorization information for a given connection within the scope of a sending / receiving broadcasts messages - - Uses the Realtime.Api.Broadcast to try reads and writes on the database to determine authorization for a given connection. - - Implements Realtime.Tenants.Authorization behaviour """ require Logger - import Ecto.Query - import Realtime.Logs - alias Realtime.Api.Message - alias Realtime.Repo - alias Realtime.Tenants.Authorization - alias Realtime.Tenants.Authorization.Policies defstruct read: false, write: false - @behaviour Realtime.Tenants.Authorization.Policies - @type t :: %__MODULE__{ read: boolean(), write: boolean() } - @impl true - def check_read_policies(_conn, _, policies, %Authorization{topic: nil}) do - {:ok, Policies.update_policies(policies, :broadcast, :read, false)} - end - - def check_read_policies(conn, %{broadcast_id: id}, %Policies{} = policies, %Authorization{ - topic: topic - }) do - query = - from(m in Message, - where: m.topic == ^topic, - where: m.extension == :broadcast, - where: m.id == ^id - ) - - case Repo.all(conn, query, Message, mode: :savepoint) do - {:ok, []} -> - {:ok, Policies.update_policies(policies, :broadcast, :read, false)} - - {:ok, [%Message{}]} -> - {:ok, Policies.update_policies(policies, :broadcast, :read, true)} - - {:error, %Postgrex.Error{postgres: %{code: :insufficient_privilege}}} -> - {:ok, Policies.update_policies(policies, :broadcast, :read, false)} - - {:error, error} -> - log_error( - "UnableToSetPolicies", - "Error getting policies for connection: #{to_log(error)}" - ) - - Postgrex.rollback(conn, error) - end - end - - @impl true - def check_write_policies(_conn, policies, %Authorization{topic: nil}) do - {:ok, Policies.update_policies(policies, :broadcast, :write, false)} - end - - def check_write_policies(conn, policies, %Authorization{topic: topic}) do - changeset = - Message.changeset(%Message{}, %{topic: topic, extension: :broadcast}) - - case Repo.insert(conn, changeset, Message, mode: :savepoint) do - {:ok, %Message{}} -> - {:ok, Policies.update_policies(policies, :broadcast, :write, true)} - - {:error, %Postgrex.Error{postgres: %{code: :insufficient_privilege}}} -> - {:ok, Policies.update_policies(policies, :broadcast, :write, false)} - - {:error, error} -> - log_error( - "UnableToSetPolicies", - "Error getting policies for connection: #{to_log(error)}" - ) - end - end end diff --git a/lib/realtime/tenants/authorization/policies/presence_policies.ex b/lib/realtime/tenants/authorization/policies/presence_policies.ex index cd5d7ff9c..b724f8df7 100644 --- a/lib/realtime/tenants/authorization/policies/presence_policies.ex +++ b/lib/realtime/tenants/authorization/policies/presence_policies.ex @@ -1,85 +1,13 @@ defmodule Realtime.Tenants.Authorization.Policies.PresencePolicies do @moduledoc """ PresencePolicies structure that holds the required authorization information for a given connection within the scope of a tracking / receiving presence messages - - Uses the Realtime.Api.Presence to try reads and writes on the database to determine authorization for a given connection. - - Implements Realtime.Tenants.Authorization behaviour """ require Logger - import Ecto.Query - import Realtime.Logs - - alias Realtime.Api.Message - alias Realtime.Repo - alias Realtime.Tenants.Authorization - alias Realtime.Tenants.Authorization.Policies defstruct read: false, write: false - @behaviour Realtime.Tenants.Authorization.Policies - @type t :: %__MODULE__{ read: boolean(), write: boolean() } - @impl true - def check_read_policies(_conn, _, policies, %Authorization{topic: nil}) do - {:ok, Policies.update_policies(policies, :presence, :read, false)} - end - - def check_read_policies(conn, %{presence_id: id}, %Policies{} = policies, %Authorization{ - topic: topic - }) do - query = - from(m in Message, - where: m.topic == ^topic, - where: m.extension == :presence, - where: m.id == ^id - ) - - case Repo.all(conn, query, Message, mode: :savepoint) do - {:ok, []} -> - {:ok, Policies.update_policies(policies, :presence, :read, false)} - - {:ok, [%Message{}]} -> - {:ok, Policies.update_policies(policies, :presence, :read, true)} - - {:error, %Postgrex.Error{postgres: %{code: :insufficient_privilege}}} -> - {:ok, Policies.update_policies(policies, :presence, :read, false)} - - {:error, error} -> - log_error( - "UnableToSetPolicies", - "Error getting policies for connection: #{to_log(error)}" - ) - - Postgrex.rollback(conn, error) - end - end - - @impl true - def check_write_policies(_conn, policies, %Authorization{topic: nil}) do - {:ok, Policies.update_policies(policies, :presence, :write, false)} - end - - def check_write_policies(conn, policies, %Authorization{topic: topic}) do - changeset = Message.changeset(%Message{}, %{topic: topic, extension: :presence}) - - case Repo.insert(conn, changeset, Message, mode: :savepoint) do - {:ok, %Message{}} -> - {:ok, Policies.update_policies(policies, :presence, :write, true)} - - {:error, %Postgrex.Error{postgres: %{code: :insufficient_privilege}}} -> - {:ok, Policies.update_policies(policies, :presence, :write, false)} - - {:error, error} -> - log_error( - "UnableToSetPolicies", - "Error getting policies for connection: #{to_log(error)}" - ) - - Postgrex.rollback(conn, error) - end - end end diff --git a/mix.exs b/mix.exs index 6d9d5ee25..9e3ca8086 100644 --- a/mix.exs +++ b/mix.exs @@ -4,7 +4,7 @@ defmodule Realtime.MixProject do def project do [ app: :realtime, - version: "2.33.61", + version: "2.33.62", elixir: "~> 1.17.3", elixirc_paths: elixirc_paths(Mix.env()), start_permanent: Mix.env() == :prod, diff --git a/test/realtime/tenants/authorization/permissions/broadcast_policies_test.exs b/test/realtime/tenants/authorization/permissions/broadcast_policies_test.exs deleted file mode 100644 index ff8ee6b8f..000000000 --- a/test/realtime/tenants/authorization/permissions/broadcast_policies_test.exs +++ /dev/null @@ -1,186 +0,0 @@ -defmodule Realtime.Tenants.Authorization.Policies.BroadcastPoliciesTest do - # async: false due to the fact that multiple operations against the database will use the same connection - use Realtime.DataCase, async: false - - alias Realtime.Api.Message - alias Realtime.Database - alias Realtime.Tenants.Authorization - alias Realtime.Tenants.Authorization.Policies - alias Realtime.Tenants.Authorization.Policies.BroadcastPolicies - alias Realtime.Tenants.Migrations - - alias RealtimeWeb.Joken.CurrentTime - - describe "check_read_policies/3" do - setup [:rls_context] - - @tag role: "authenticated", policies: [:authenticated_read_broadcast] - test "authenticated user has read policies", context do - Postgrex.transaction(context.db_conn, fn transaction_conn -> - Authorization.set_conn_config(transaction_conn, context.authorization_context) - - assert {:ok, result} = - BroadcastPolicies.check_read_policies( - transaction_conn, - context.ids, - %Policies{}, - context.authorization_context - ) - - assert result == %Policies{broadcast: %BroadcastPolicies{read: true}} - end) - end - - @tag role: "anon", policies: [:authenticated_read_broadcast] - test "anon user has read policies", context do - Postgrex.transaction(context.db_conn, fn transaction_conn -> - Authorization.set_conn_config(transaction_conn, context.authorization_context) - - assert {:ok, result} = - BroadcastPolicies.check_read_policies( - transaction_conn, - context.ids, - %Policies{}, - context.authorization_context - ) - - assert result == %Policies{broadcast: %BroadcastPolicies{read: false}} - end) - end - - @tag role: "anon", policies: [] - test "no topic in context returns false policies", context do - authorization_context = %{context.authorization_context | topic: nil} - - Postgrex.transaction(context.db_conn, fn transaction_conn -> - Authorization.set_conn_config(transaction_conn, context.authorization_context) - - assert {:ok, result} = - BroadcastPolicies.check_read_policies( - transaction_conn, - context.ids, - %Policies{}, - authorization_context - ) - - assert result == %Policies{broadcast: %BroadcastPolicies{read: false}} - end) - end - - @tag role: "anon", policies: [] - test "handles database errors", context do - Postgrex.transaction(context.db_conn, fn transaction_conn -> - Authorization.set_conn_config(transaction_conn, context.authorization_context) - Process.unlink(context.db_conn) - Process.exit(context.db_conn, :shutdown) - :timer.sleep(100) - - assert {:error, _} = - BroadcastPolicies.check_read_policies( - transaction_conn, - context.ids, - %Policies{}, - context.authorization_context - ) - end) - end - end - - describe "check_write_policies/3" do - setup [:rls_context] - - @tag role: "authenticated", - policies: [:authenticated_read_broadcast, :authenticated_write_broadcast] - test "authenticated user has write policies and reverts updated_at", context do - query = from(m in Message, where: m.topic == ^context.topic) - assert {:ok, %Message{}} = Repo.one(context.db_conn, query, Message) - - Postgrex.transaction(context.db_conn, fn transaction_conn -> - Authorization.set_conn_config(transaction_conn, context.authorization_context) - - assert {:ok, result} = - BroadcastPolicies.check_write_policies( - transaction_conn, - %Policies{}, - context.authorization_context - ) - - assert result == %Policies{broadcast: %BroadcastPolicies{write: true}} - end) - end - - @tag role: "anon", policies: [:authenticated_read_broadcast, :authenticated_write_broadcast] - test "anon user has no write policies", context do - Postgrex.transaction(context.db_conn, fn transaction_conn -> - Authorization.set_conn_config(transaction_conn, context.authorization_context) - - assert {:ok, result} = - BroadcastPolicies.check_write_policies( - transaction_conn, - %Policies{}, - context.authorization_context - ) - - assert result == %Policies{broadcast: %BroadcastPolicies{write: false}} - end) - end - - @tag role: "anon", policies: [] - test "no topic in context returns false", context do - authorization_context = %{context.authorization_context | topic: nil} - - Postgrex.transaction(context.db_conn, fn transaction_conn -> - Authorization.set_conn_config(transaction_conn, context.authorization_context) - - assert {:ok, result} = - BroadcastPolicies.check_write_policies( - transaction_conn, - %Policies{}, - authorization_context - ) - - assert result == %Policies{broadcast: %BroadcastPolicies{write: false}} - end) - end - end - - def rls_context(context) do - start_supervised!(CurrentTime.Mock) - tenant = tenant_fixture() - [%{settings: settings} | _] = tenant.extensions - - migrations = %Migrations{ - tenant_external_id: tenant.external_id, - settings: settings - } - - Migrations.run_migrations(migrations) - - {:ok, db_conn} = Database.connect(tenant, "realtime_test", 1) - - clean_table(db_conn, "realtime", "messages") - message = message_fixture(tenant, %{extension: :broadcast}) - - create_rls_policies(db_conn, context.policies, message) - - claims = %{sub: random_string(), role: context.role, exp: Joken.current_time() + 1_000} - signer = Joken.Signer.create("HS256", "secret") - jwt = Joken.generate_and_sign!(%{}, claims, signer) - - authorization_context = - Authorization.build_authorization_params(%{ - topic: message.topic, - headers: [{"header-1", "value-1"}], - jwt: jwt, - claims: claims, - role: claims.role - }) - - %{ - ids: %{broadcast_id: message.id}, - topic: message.topic, - db_conn: db_conn, - authorization_context: authorization_context - } - end -end diff --git a/test/realtime/tenants/authorization/permissions/presence_policies_test.exs b/test/realtime/tenants/authorization/permissions/presence_policies_test.exs deleted file mode 100644 index 97ffd9c36..000000000 --- a/test/realtime/tenants/authorization/permissions/presence_policies_test.exs +++ /dev/null @@ -1,186 +0,0 @@ -defmodule Realtime.Tenants.Authorization.Policies.PresencePoliciesTest do - # async: false due to the fact that multiple operations against the database will use the same connection - use Realtime.DataCase, async: false - - alias Realtime.Api.Message - alias Realtime.Tenants.Authorization - alias Realtime.Tenants.Authorization.Policies - alias Realtime.Tenants.Authorization.Policies.PresencePolicies - alias Realtime.Tenants.Connect - alias Realtime.Tenants.Migrations - - alias RealtimeWeb.Joken.CurrentTime - - describe "check_read_policies/3" do - setup [:rls_context] - - @tag role: "authenticated", policies: [:authenticated_read_presence] - test "authenticated user has read policies", context do - Postgrex.transaction(context.db_conn, fn transaction_conn -> - Authorization.set_conn_config(transaction_conn, context.authorization_context) - - assert {:ok, result} = - PresencePolicies.check_read_policies( - transaction_conn, - context.ids, - %Policies{}, - context.authorization_context - ) - - assert result == %Policies{presence: %PresencePolicies{read: true}} - end) - end - - @tag role: "anon", policies: [:authenticated_read_presence] - test "anon user has read policies", context do - Postgrex.transaction(context.db_conn, fn transaction_conn -> - Authorization.set_conn_config(transaction_conn, context.authorization_context) - - assert {:ok, result} = - PresencePolicies.check_read_policies( - transaction_conn, - context.ids, - %Policies{}, - context.authorization_context - ) - - assert result == %Policies{presence: %PresencePolicies{read: false}} - end) - end - - @tag role: "anon", policies: [] - test "no topic in context returns false policies", context do - authorization_context = %{context.authorization_context | topic: nil} - - Postgrex.transaction(context.db_conn, fn transaction_conn -> - Authorization.set_conn_config(transaction_conn, context.authorization_context) - - assert {:ok, result} = - PresencePolicies.check_read_policies( - transaction_conn, - context.ids, - %Policies{}, - authorization_context - ) - - assert result == %Policies{presence: %PresencePolicies{read: false}} - end) - end - - @tag role: "anon", policies: [] - test "handles database errors", context do - Postgrex.transaction(context.db_conn, fn transaction_conn -> - Authorization.set_conn_config(transaction_conn, context.authorization_context) - Process.unlink(context.db_conn) - Process.exit(context.db_conn, :shutdown) - :timer.sleep(100) - - assert {:error, _} = - PresencePolicies.check_read_policies( - transaction_conn, - context.ids, - %Policies{}, - context.authorization_context - ) - end) - end - end - - describe "check_write_policies/3" do - setup [:rls_context] - - @tag role: "authenticated", - policies: [:authenticated_read_presence, :authenticated_write_presence] - test "authenticated user has write policies and reverts updated_at", context do - query = from(m in Message, where: m.topic == ^context.topic) - assert {:ok, %Message{}} = Repo.one(context.db_conn, query, Message) - - Postgrex.transaction(context.db_conn, fn transaction_conn -> - Authorization.set_conn_config(transaction_conn, context.authorization_context) - - assert {:ok, result} = - PresencePolicies.check_write_policies( - transaction_conn, - %Policies{}, - context.authorization_context - ) - - assert result == %Policies{presence: %PresencePolicies{write: true}} - end) - end - - @tag role: "anon", policies: [:authenticated_read_presence, :authenticated_write_presence] - test "anon user has no write policies", context do - Postgrex.transaction(context.db_conn, fn transaction_conn -> - Authorization.set_conn_config(transaction_conn, context.authorization_context) - - assert {:ok, result} = - PresencePolicies.check_write_policies( - transaction_conn, - %Policies{}, - context.authorization_context - ) - - assert result == %Policies{presence: %PresencePolicies{write: false}} - end) - end - - @tag role: "anon", policies: [] - test "no topic in context returns false", context do - authorization_context = %{context.authorization_context | topic: nil} - - Postgrex.transaction(context.db_conn, fn transaction_conn -> - Authorization.set_conn_config(transaction_conn, context.authorization_context) - - assert {:ok, result} = - PresencePolicies.check_write_policies( - transaction_conn, - %Policies{}, - authorization_context - ) - - assert result == %Policies{presence: %PresencePolicies{write: false}} - end) - end - end - - def rls_context(context) do - start_supervised!(CurrentTime.Mock) - tenant = tenant_fixture() - [%{settings: settings} | _] = tenant.extensions - - migrations = %Migrations{ - tenant_external_id: tenant.external_id, - settings: settings - } - - Migrations.run_migrations(migrations) - {:ok, _} = start_supervised({Connect, tenant_id: tenant.external_id}, restart: :transient) - {:ok, db_conn} = Connect.get_status(tenant.external_id) - - clean_table(db_conn, "realtime", "messages") - message = message_fixture(tenant, %{extension: :presence}) - - create_rls_policies(db_conn, context.policies, message) - - claims = %{sub: random_string(), role: context.role, exp: Joken.current_time() + 1_000} - signer = Joken.Signer.create("HS256", "secret") - jwt = Joken.generate_and_sign!(%{}, claims, signer) - - authorization_context = - Authorization.build_authorization_params(%{ - topic: message.topic, - headers: [{"header-1", "value-1"}], - jwt: jwt, - claims: claims, - role: claims.role - }) - - %{ - ids: %{presence_id: message.id}, - topic: message.topic, - db_conn: db_conn, - authorization_context: authorization_context - } - end -end diff --git a/test/realtime/tenants/authorization_test.exs b/test/realtime/tenants/authorization_test.exs index 0ac7f25a1..12d589615 100644 --- a/test/realtime/tenants/authorization_test.exs +++ b/test/realtime/tenants/authorization_test.exs @@ -20,7 +20,6 @@ defmodule Realtime.Tenants.AuthorizationTest do describe "get_authorizations for Plug.Conn" do @tag role: "authenticated", policies: [ - :authenticated_read_topic, :authenticated_read_broadcast, :authenticated_read_presence ] @@ -38,10 +37,45 @@ defmodule Realtime.Tenants.AuthorizationTest do } = conn.assigns.policies end + @tag role: "authenticated", + policies: [ + :authenticated_read_broadcast + ] + test "authenticated user has expected mixed policies", context do + {:ok, conn} = + Authorization.get_authorizations( + Phoenix.ConnTest.build_conn(), + context.db_conn, + context.authorization_context + ) + + assert %Policies{ + broadcast: %BroadcastPolicies{read: true, write: false}, + presence: %PresencePolicies{read: false, write: false} + } = conn.assigns.policies + end + + @tag role: "authenticated", + policies: [ + :authenticated_read_broadcast, + :authenticated_write_broadcast + ] + test "authenticated user has expected mixed extensions policies", context do + {:ok, conn} = + Authorization.get_authorizations( + Phoenix.ConnTest.build_conn(), + context.db_conn, + context.authorization_context + ) + + assert %Policies{ + broadcast: %BroadcastPolicies{read: true, write: true}, + presence: %PresencePolicies{read: false, write: false} + } = conn.assigns.policies + end + @tag role: "anon", policies: [ - :authenticated_read_topic, - :authenticated_write_topic, :authenticated_read_broadcast, :authenticated_write_broadcast, :authenticated_read_presence,