From 65368dc2d469da463831213b1be0297c4b6533dc Mon Sep 17 00:00:00 2001 From: Pim Kunis Date: Wed, 28 Jul 2021 17:23:48 +0200 Subject: [PATCH] Extract authorization functions from state resolution Add function to get room server's pid --- lib/matrix_server/quick_check.ex | 14 + lib/matrix_server/room_server.ex | 144 +++++---- lib/matrix_server/state_resolution.ex | 292 +----------------- .../state_resolution/authorization.ex | 280 +++++++++++++++++ .../controllers/room_controller.ex | 10 +- 5 files changed, 391 insertions(+), 349 deletions(-) create mode 100644 lib/matrix_server/quick_check.ex create mode 100644 lib/matrix_server/state_resolution/authorization.ex diff --git a/lib/matrix_server/quick_check.ex b/lib/matrix_server/quick_check.ex new file mode 100644 index 0000000..09c4250 --- /dev/null +++ b/lib/matrix_server/quick_check.ex @@ -0,0 +1,14 @@ +defmodule MatrixServer.QuickCheck do + import Ecto.Query + + alias MatrixServer.{Repo, Room, Account, RoomServer} + alias MatrixServerWeb.API.CreateRoom + + def create_room do + account = Repo.one!(from a in Account, limit: 1) + input = %CreateRoom{} + %Room{id: room_id} = Repo.insert!(Room.create_changeset(input)) + {:ok, pid} = RoomServer.get_room_server(room_id) + RoomServer.create_room(pid, account, input) + end +end diff --git a/lib/matrix_server/room_server.ex b/lib/matrix_server/room_server.ex index eb8489a..2524e77 100644 --- a/lib/matrix_server/room_server.ex +++ b/lib/matrix_server/room_server.ex @@ -3,71 +3,102 @@ defmodule MatrixServer.RoomServer do import Ecto.Query - alias MatrixServer.{Repo, Room, Event, Account, StateResolution} + alias MatrixServer.{Repo, Room, Event, StateResolution} alias MatrixServerWeb.API.CreateRoom + alias MatrixServer.StateResolution.Authorization @registry MatrixServer.RoomServer.Registry @supervisor MatrixServer.RoomServer.Supervisor - def create_room(input, account) do - %Room{id: room_id} = room = Repo.insert!(Room.create_changeset(input)) - - opts = [ - name: {:via, Registry, {@registry, room_id}}, - input: input, - account: account, - room: room - ] - - DynamicSupervisor.start_child(@supervisor, {__MODULE__, opts}) - end + ### Interface def start_link(opts) do {name, opts} = Keyword.pop(opts, :name) GenServer.start_link(__MODULE__, opts, name: name) end + # Get room server pid, or spin one up for the room. + # If the room does not exist, return an error. + def get_room_server(room_id) do + case Repo.one(from r in Room, where: r.id == ^room_id) do + nil -> + {:error, :not_found} + + %Room{} -> + case Registry.lookup(@registry, room_id) do + [{pid, _}] -> + {:ok, pid} + + [] -> + opts = [ + name: {:via, Registry, {@registry, room_id}}, + room_id: room_id + ] + + DynamicSupervisor.start_child(@supervisor, {__MODULE__, opts}) + end + end + end + + def create_room(pid, account, input) do + GenServer.call(pid, {:create_room, account, input}) + end + + ### Implementation + @impl true def init(opts) do - room = Keyword.fetch!(opts, :room) - input = Keyword.fetch!(opts, :input) - account = Keyword.fetch!(opts, :account) + room_id = Keyword.fetch!(opts, :room_id) - Repo.transaction(fn -> - with {:ok, create_room_id, state_set, room} <- - room_creation_create_room(account, input, room), - {:ok, join_creator_id, state_set, room} <- - room_creation_join_creator(account, room, state_set, [create_room_id]), - {:ok, pl_id, state_set, room} <- - room_creation_power_levels( - account, - room, - state_set, - [create_room_id, join_creator_id] - ), - {:ok, _, state_set, room} <- - room_creation_preset(account, input, room, state_set, [ - create_room_id, - join_creator_id, - pl_id - ]), - {:ok, _, state_set, room} <- - room_creation_name(account, input, room, state_set, [ - create_room_id, - join_creator_id, - pl_id - ]), - {:ok, _, state_set, room} <- - room_creation_topic(account, input, room, state_set, [ - create_room_id, - join_creator_id, - pl_id - ]) do - {:ok, %{room: room, state_set: state_set}} - else - _ -> {:error, :validation} - end - end) + {:ok, %{room_id: room_id, state_set: %{}}} + end + + @impl true + def handle_call({:create_room, account, input}, _from, %{room_id: room_id} = state) do + result = + Repo.transaction(fn -> + # TODO: power_level_content_override, initial_state, invite, invite_3pid + with room <- Repo.one!(from r in Room, where: r.id == ^room_id), + {:ok, create_room_id, state_set, room} <- + room_creation_create_room(account, input, room), + {:ok, join_creator_id, state_set, room} <- + room_creation_join_creator(account, room, state_set, [create_room_id]), + {:ok, pl_id, state_set, room} <- + room_creation_power_levels( + account, + room, + state_set, + [create_room_id, join_creator_id] + ), + {:ok, _, state_set, room} <- + room_creation_preset(account, input, room, state_set, [ + create_room_id, + join_creator_id, + pl_id + ]), + {:ok, _, state_set, room} <- + room_creation_name(account, input, room, state_set, [ + create_room_id, + join_creator_id, + pl_id + ]), + {:ok, _, state_set, _} <- + room_creation_topic(account, input, room, state_set, [ + create_room_id, + join_creator_id, + pl_id + ]) do + state_set + else + reason -> + Repo.rollback(reason) + end + end) + + case result do + {:ok, state_set} -> {:reply, :ok, %{state | state_set: state_set}} + {:error, reason} -> {:reply, {:error, reason}, state} + end end defp room_creation_create_room(account, %CreateRoom{room_version: room_version}, room) do @@ -177,11 +208,11 @@ defmodule MatrixServer.RoomServer do event = %Event{event | prev_events: forward_extremities} if Event.prevalidate(event) do - if StateResolution.is_authorized_by_auth_events(event) do + if Authorization.authorized_by_auth_events?(event) do state_set = StateResolution.resolve(event, false) - if StateResolution.is_authorized(event, state_set) do - if StateResolution.is_authorized(event, current_state_set) do + if Authorization.authorized?(event, state_set) do + if Authorization.authorized?(event, current_state_set) do # We assume here that the event is always a forward extremity. room = Room.update_forward_extremities(event, room) {:ok, event} = Repo.insert(event) @@ -200,9 +231,4 @@ defmodule MatrixServer.RoomServer do {:error, :invalid} end end - - def testing do - account = Repo.one!(from a in Account, limit: 1) - create_room(%CreateRoom{name: "Sneed", topic: "City slickers"}, account) - end end diff --git a/lib/matrix_server/state_resolution.ex b/lib/matrix_server/state_resolution.ex index 0528497..884633d 100644 --- a/lib/matrix_server/state_resolution.ex +++ b/lib/matrix_server/state_resolution.ex @@ -2,6 +2,7 @@ defmodule MatrixServer.StateResolution do import Ecto.Query alias MatrixServer.{Repo, Event, Room} + alias MatrixServer.StateResolution.Authorization def resolve(event), do: resolve(event, true) @@ -35,17 +36,6 @@ defmodule MatrixServer.StateResolution do end end - def is_authorized_by_auth_events(%Event{auth_events: auth_event_ids} = event) do - # We assume the auth events are validated beforehand. - state_set = - Event - |> where([e], e.event_id in ^auth_event_ids) - |> Repo.all() - |> Enum.reduce(%{}, &update_state_set/2) - - is_authorized(event, state_set) - end - def resolve_forward_extremities(%Event{room_id: room_id}) do room_events = Event @@ -63,278 +53,6 @@ defmodule MatrixServer.StateResolution do |> do_resolve(room_events) end - def is_authorized(%Event{type: "m.room.create", prev_events: prev_events}, %{}), - do: prev_events == [] - - # Check rule: 5.2.1 - def is_authorized(%Event{type: "m.room.member", state_key: state_key}, %{ - {"m.room.create", ""} => %Event{content: %{"creator" => creator}} - }), - do: state_key == creator - - def is_authorized( - %Event{type: "m.room.member", sender: sender, content: %{"membership" => "join"}}, - state_set - ) do - join_rule = get_join_rule(state_set) - membership = get_membership(sender, state_set) - - # Check rules: 5.2.3, 5.2.4, 5.2.5 - cond do - membership == "ban" -> false - join_rule == "invite" -> membership in ["invite", "join"] - join_rule == "public" -> true - true -> false - end - end - - # TODO: rule 5.3.1 - def is_authorized( - %Event{ - type: "m.room.member", - content: %{"membership" => "invite", "third_party_invite" => _} - }, - _ - ), - do: false - - def is_authorized( - %Event{ - type: "m.room.member", - sender: sender, - content: %{"membership" => "invite"}, - state_key: state_key - }, - state_set - ) do - sender_membership = get_membership(sender, state_set) - target_membership = get_membership(state_key, state_set) - power_levels = get_power_levels(state_set) - - # Check rules: 5.3.2, 5.3.3, 5.3.4 - cond do - sender_membership != "join" -> false - target_membership in ["join", "ban"] -> false - has_power_level(sender, power_levels, :invite) -> true - true -> false - end - end - - def is_authorized( - %Event{ - type: "m.room.member", - sender: sender, - content: %{"membership" => "leave"}, - state_key: sender - }, - state_set - ) do - # Check rule: 5.4.1 - get_membership(sender, state_set) in ["invite", "join"] - end - - def is_authorized( - %Event{ - type: "m.room.member", - sender: sender, - content: %{"membership" => "leave"}, - state_key: state_key - }, - state_set - ) do - sender_membership = get_membership(sender, state_set) - target_membership = get_membership(state_key, state_set) - power_levels = get_power_levels(state_set) - sender_pl = get_user_power_level(sender, power_levels) - target_pl = get_user_power_level(state_key, power_levels) - - # Check rules: 5.4.2, 5.4.3, 5.4.4 - cond do - sender_membership != "join" -> false - target_membership == "ban" and not has_power_level(sender, power_levels, :ban) -> false - has_power_level(sender, power_levels, :kick) and target_pl < sender_pl -> true - true -> false - end - end - - def is_authorized( - %Event{ - type: "m.room.member", - sender: sender, - content: %{"membership" => "ban"}, - state_key: state_key - }, - state_set - ) do - sender_membership = get_membership(sender, state_set) - power_levels = get_power_levels(state_set) - sender_pl = get_user_power_level(sender, power_levels) - target_pl = get_user_power_level(state_key, power_levels) - - # Check rules: 5.5.1, 5.5.2 - cond do - sender_membership != "join" -> false - has_power_level(sender, power_levels, :ban) and target_pl < sender_pl -> true - true -> false - end - end - - # Check rule: 5.6 - def is_authorized(%Event{type: "m.room.member"}, _), do: false - - def is_authorized(%Event{sender: sender} = event, state_set) do - # Check rule: 6 - get_membership(sender, state_set) == "join" and _is_authorized(event, state_set) - end - - defp _is_authorized(%Event{type: "m.room.third_party_invite", sender: sender}, state_set) do - # Check rule: 7.1 - has_power_level(sender, state_set, :invite) - end - - defp _is_authorized(%Event{state_key: state_key, sender: sender} = event, state_set) do - power_levels = get_power_levels(state_set) - - # Check rules: 8, 9 - cond do - not has_power_level(sender, power_levels, {:event, event}) -> false - String.starts_with?(state_key, "@") and state_key != sender -> false - true -> __is_authorized(event, state_set) - end - end - - defp __is_authorized( - %Event{type: "m.room.power_levels", sender: sender, content: content}, - state_set - ) do - current_pls = get_power_levels(state_set) - new_pls = content - sender_pl = get_user_power_level(sender, new_pls) - - # Check rules: 10.2, 10.3, 10.4, 10.5 - cond do - not is_map_key(state_set, {"m.room.power_levels", ""}) -> true - not authorize_power_levels(sender, sender_pl, current_pls, new_pls) -> false - true -> true - end - end - - # TODO: Rule 11 - - defp __is_authorized(_, _), do: true - - defp authorize_power_levels( - user, - user_pl, - %{"events" => current_events, "users" => current_users} = current_pls, - %{"events" => new_events, "users" => new_users} = new_pls - ) do - keys = ["users_default", "events_default", "state_default", "ban", "redact", "kick", "invite"] - - valid_power_level_key_changes(Map.take(current_pls, keys), Map.take(new_pls, keys), user_pl) and - valid_power_level_key_changes(current_events, new_events, user_pl) and - valid_power_level_key_changes(current_users, new_users, user_pl) and - valid_power_level_users_changes(current_users, new_users, user, user_pl) - end - - defp has_power_level(user, power_levels, action) do - user_pl = get_user_power_level(user, power_levels) - action_pl = get_action_power_level(action, power_levels) - - user_pl >= action_pl - end - - defp get_user_power_level(user, %{"users" => users}) when is_map_key(users, user), - do: users[user] - - defp get_user_power_level(_, %{"users_default" => pl}), do: pl - defp get_user_power_level(_, _), do: 0 - - defp get_action_power_level(:invite, %{"invite" => pl}), do: pl - defp get_action_power_level(:invite, _), do: 50 - defp get_action_power_level(:ban, %{"ban" => pl}), do: pl - defp get_action_power_level(:ban, _), do: 50 - defp get_action_power_level(:redact, %{"redact" => pl}), do: pl - defp get_action_power_level(:redact, _), do: 50 - - defp get_action_power_level({:event, %Event{type: type}}, %{"events" => events}) - when is_map_key(events, type), - do: events[type] - - defp get_action_power_level({:event, event}, power_levels) do - if Event.is_state_event(event) do - case power_levels do - %{"state_default" => pl} -> pl - %{} -> 50 - _ -> 0 - end - else - case power_levels do - %{"events_default" => pl} -> pl - _ -> 0 - end - end - end - - defp get_power_levels(state_set) do - case state_set[{"m.room.power_levels", ""}] do - %Event{content: content} -> content - nil -> nil - end - end - - defp get_join_rule(state_set) do - case state_set[{"m.room.join_rules", ""}] do - %Event{content: %{"join_rule" => join_rule}} -> join_rule - nil -> nil - end - end - - defp get_membership(user, state_set) do - case state_set[{"m.room.member", user}] do - %Event{content: %{"membership" => membership}} -> membership - nil -> nil - end - end - - defp valid_power_level_key_changes(l1, l2, user_pl) do - set1 = MapSet.new(l1) - set2 = MapSet.new(l2) - - MapSet.difference( - MapSet.union(set1, set2), - MapSet.intersection(set1, set2) - ) - |> Enum.group_by(&elem(&1, 0), &elem(&1, 1)) - |> Enum.all?(fn {_k, values} -> - Enum.all?(values, &(&1 <= user_pl)) - end) - end - - defp valid_power_level_users_changes(current_users, new_users, user, user_pl) do - set1 = MapSet.new(current_users) - set2 = MapSet.new(new_users) - - MapSet.difference( - MapSet.union(set1, set2), - MapSet.intersection(set1, set2) - ) - |> Enum.all?(fn - {_k, values} when length(values) != 2 -> true - {k, _} when k == user -> true - {_k, [old_value, _]} -> old_value != user_pl - end) - end - - def testing do - %Event{content: content} = event = Event.power_levels("room1", "charlie") - event = %Event{event | content: %{content | "ban" => 0}} - - event - |> Map.put(:prev_events, ["b", "fork"]) - |> Map.put(:auth_events, ["create", "join_charlie", "b"]) - end - defp do_resolve([], _), do: %{} defp do_resolve(state_sets, room_events) do @@ -541,23 +259,23 @@ defmodule MatrixServer.StateResolution do defp iterative_auth_checks(events, state_set, room_events) do Enum.reduce(events, state_set, fn event, acc -> - if is_authorized2(event, acc, room_events), do: update_state_set(event, acc), else: acc + if authorized?(event, acc, room_events), do: update_state_set(event, acc), else: acc end) end - defp update_state_set( + def update_state_set( %Event{type: event_type, state_key: state_key} = event, state_set ) do Map.put(state_set, {event_type, state_key}, event) end - defp is_authorized2(%Event{auth_events: auth_event_ids} = event, state_set, room_events) do + defp authorized?(%Event{auth_events: auth_event_ids} = event, state_set, room_events) do state_set = auth_event_ids |> Enum.map(&room_events[&1]) |> Enum.reduce(state_set, &update_state_set/2) - is_authorized(event, state_set) + Authorization.authorized?(event, state_set) end end diff --git a/lib/matrix_server/state_resolution/authorization.ex b/lib/matrix_server/state_resolution/authorization.ex new file mode 100644 index 0000000..854ab18 --- /dev/null +++ b/lib/matrix_server/state_resolution/authorization.ex @@ -0,0 +1,280 @@ +defmodule MatrixServer.StateResolution.Authorization do + import MatrixServer.StateResolution + import Ecto.Query + + alias MatrixServer.{Repo, Event} + + def authorized?(%Event{type: "m.room.create", prev_events: prev_events}, %{}), + do: prev_events == [] + + # Check rule: 5.2.1 + def authorized?(%Event{type: "m.room.member", state_key: state_key}, %{ + {"m.room.create", ""} => %Event{content: %{"creator" => creator}} + }), + do: state_key == creator + + def authorized?( + %Event{type: "m.room.member", sender: sender, content: %{"membership" => "join"}}, + state_set + ) do + join_rule = get_join_rule(state_set) + membership = get_membership(sender, state_set) + + # Check rules: 5.2.3, 5.2.4, 5.2.5 + cond do + membership == "ban" -> false + join_rule == "invite" -> membership in ["invite", "join"] + join_rule == "public" -> true + true -> false + end + end + + # TODO: rule 5.3.1 + def authorized?( + %Event{ + type: "m.room.member", + content: %{"membership" => "invite", "third_party_invite" => _} + }, + _ + ), + do: false + + def authorized?( + %Event{ + type: "m.room.member", + sender: sender, + content: %{"membership" => "invite"}, + state_key: state_key + }, + state_set + ) do + sender_membership = get_membership(sender, state_set) + target_membership = get_membership(state_key, state_set) + power_levels = get_power_levels(state_set) + + # Check rules: 5.3.2, 5.3.3, 5.3.4 + cond do + sender_membership != "join" -> false + target_membership in ["join", "ban"] -> false + has_power_level(sender, power_levels, :invite) -> true + true -> false + end + end + + def authorized?( + %Event{ + type: "m.room.member", + sender: sender, + content: %{"membership" => "leave"}, + state_key: sender + }, + state_set + ) do + # Check rule: 5.4.1 + get_membership(sender, state_set) in ["invite", "join"] + end + + def authorized?( + %Event{ + type: "m.room.member", + sender: sender, + content: %{"membership" => "leave"}, + state_key: state_key + }, + state_set + ) do + sender_membership = get_membership(sender, state_set) + target_membership = get_membership(state_key, state_set) + power_levels = get_power_levels(state_set) + sender_pl = get_user_power_level(sender, power_levels) + target_pl = get_user_power_level(state_key, power_levels) + + # Check rules: 5.4.2, 5.4.3, 5.4.4 + cond do + sender_membership != "join" -> false + target_membership == "ban" and not has_power_level(sender, power_levels, :ban) -> false + has_power_level(sender, power_levels, :kick) and target_pl < sender_pl -> true + true -> false + end + end + + def authorized?( + %Event{ + type: "m.room.member", + sender: sender, + content: %{"membership" => "ban"}, + state_key: state_key + }, + state_set + ) do + sender_membership = get_membership(sender, state_set) + power_levels = get_power_levels(state_set) + sender_pl = get_user_power_level(sender, power_levels) + target_pl = get_user_power_level(state_key, power_levels) + + # Check rules: 5.5.1, 5.5.2 + cond do + sender_membership != "join" -> false + has_power_level(sender, power_levels, :ban) and target_pl < sender_pl -> true + true -> false + end + end + + # Check rule: 5.6 + def authorized?(%Event{type: "m.room.member"}, _), do: false + + def authorized?(%Event{sender: sender} = event, state_set) do + # Check rule: 6 + get_membership(sender, state_set) == "join" and _authorized?(event, state_set) + end + + defp _authorized?(%Event{type: "m.room.third_party_invite", sender: sender}, state_set) do + # Check rule: 7.1 + has_power_level(sender, state_set, :invite) + end + + defp _authorized?(%Event{state_key: state_key, sender: sender} = event, state_set) do + power_levels = get_power_levels(state_set) + + # Check rules: 8, 9 + cond do + not has_power_level(sender, power_levels, {:event, event}) -> false + String.starts_with?(state_key, "@") and state_key != sender -> false + true -> __authorized?(event, state_set) + end + end + + defp __authorized?( + %Event{type: "m.room.power_levels", sender: sender, content: content}, + state_set + ) do + current_pls = get_power_levels(state_set) + new_pls = content + sender_pl = get_user_power_level(sender, new_pls) + + # Check rules: 10.2, 10.3, 10.4, 10.5 + cond do + not is_map_key(state_set, {"m.room.power_levels", ""}) -> true + not authorize_power_levels(sender, sender_pl, current_pls, new_pls) -> false + true -> true + end + end + + # TODO: Rule 11 + + defp __authorized?(_, _), do: true + + defp get_power_levels(state_set) do + case state_set[{"m.room.power_levels", ""}] do + %Event{content: content} -> content + nil -> nil + end + end + + defp get_join_rule(state_set) do + case state_set[{"m.room.join_rules", ""}] do + %Event{content: %{"join_rule" => join_rule}} -> join_rule + nil -> nil + end + end + + defp get_membership(user, state_set) do + case state_set[{"m.room.member", user}] do + %Event{content: %{"membership" => membership}} -> membership + nil -> nil + end + end + + defp has_power_level(user, power_levels, action) do + user_pl = get_user_power_level(user, power_levels) + action_pl = get_action_power_level(action, power_levels) + + user_pl >= action_pl + end + + defp get_user_power_level(user, %{"users" => users}) when is_map_key(users, user), + do: users[user] + + defp get_user_power_level(_, %{"users_default" => pl}), do: pl + defp get_user_power_level(_, _), do: 0 + + defp get_action_power_level(:invite, %{"invite" => pl}), do: pl + defp get_action_power_level(:invite, _), do: 50 + defp get_action_power_level(:ban, %{"ban" => pl}), do: pl + defp get_action_power_level(:ban, _), do: 50 + defp get_action_power_level(:redact, %{"redact" => pl}), do: pl + defp get_action_power_level(:redact, _), do: 50 + + defp get_action_power_level({:event, %Event{type: type}}, %{"events" => events}) + when is_map_key(events, type), + do: events[type] + + defp get_action_power_level({:event, event}, power_levels) do + if Event.is_state_event(event) do + case power_levels do + %{"state_default" => pl} -> pl + %{} -> 50 + _ -> 0 + end + else + case power_levels do + %{"events_default" => pl} -> pl + _ -> 0 + end + end + end + + defp authorize_power_levels( + user, + user_pl, + %{"events" => current_events, "users" => current_users} = current_pls, + %{"events" => new_events, "users" => new_users} = new_pls + ) do + keys = ["users_default", "events_default", "state_default", "ban", "redact", "kick", "invite"] + + valid_power_level_key_changes(Map.take(current_pls, keys), Map.take(new_pls, keys), user_pl) and + valid_power_level_key_changes(current_events, new_events, user_pl) and + valid_power_level_key_changes(current_users, new_users, user_pl) and + valid_power_level_users_changes(current_users, new_users, user, user_pl) + end + + defp valid_power_level_key_changes(l1, l2, user_pl) do + set1 = MapSet.new(l1) + set2 = MapSet.new(l2) + + MapSet.difference( + MapSet.union(set1, set2), + MapSet.intersection(set1, set2) + ) + |> Enum.group_by(&elem(&1, 0), &elem(&1, 1)) + |> Enum.all?(fn {_k, values} -> + Enum.all?(values, &(&1 <= user_pl)) + end) + end + + defp valid_power_level_users_changes(current_users, new_users, user, user_pl) do + set1 = MapSet.new(current_users) + set2 = MapSet.new(new_users) + + MapSet.difference( + MapSet.union(set1, set2), + MapSet.intersection(set1, set2) + ) + |> Enum.all?(fn + {_k, values} when length(values) != 2 -> true + {k, _} when k == user -> true + {_k, [old_value, _]} -> old_value != user_pl + end) + end + + def authorized_by_auth_events?(%Event{auth_events: auth_event_ids} = event) do + # We assume the auth events are validated beforehand. + state_set = + Event + |> where([e], e.event_id in ^auth_event_ids) + |> Repo.all() + |> Enum.reduce(%{}, &update_state_set/2) + + authorized?(event, state_set) + end +end diff --git a/lib/matrix_server_web/controllers/room_controller.ex b/lib/matrix_server_web/controllers/room_controller.ex index 611d64c..2ccd9b4 100644 --- a/lib/matrix_server_web/controllers/room_controller.ex +++ b/lib/matrix_server_web/controllers/room_controller.ex @@ -4,6 +4,7 @@ defmodule MatrixServerWeb.RoomController do import MatrixServerWeb.Plug.Error import Ecto.Changeset + alias MatrixServer.{Repo, Room, RoomServer} alias MatrixServerWeb.API.{CreateRoom} alias Ecto.Changeset alias Plug.Conn @@ -11,9 +12,12 @@ defmodule MatrixServerWeb.RoomController do def create(%Conn{assigns: %{account: account}} = conn, params) do case CreateRoom.changeset(params) do %Changeset{valid?: true} = cs -> - cs - |> apply_changes() - |> MatrixServer.RoomServer.create_room(account) + input = apply_changes(cs) + + # TODO: refactor + %Room{id: room_id} = Repo.insert!(Room.create_changeset(input)) + {:ok, pid} = RoomServer.get_room_server(room_id) + RoomServer.create_room(pid, account, input) conn |> put_status(200)