diff --git a/lib/matrix_server/event.ex b/lib/matrix_server/event.ex index f5b6840..032c8bc 100644 --- a/lib/matrix_server/event.ex +++ b/lib/matrix_server/event.ex @@ -3,7 +3,7 @@ defmodule MatrixServer.Event do import Ecto.Query - alias MatrixServer.{Repo, Room, Event} + alias MatrixServer.{Repo, Room, Event, Account} @primary_key {:event_id, :string, []} schema "events" do @@ -17,10 +17,10 @@ defmodule MatrixServer.Event do belongs_to :room, Room, type: :string end - def new(%Room{id: room_id}, sender) do + def new(%Room{id: room_id}, %Account{localpart: localpart}) do %Event{ room_id: room_id, - sender: sender, + sender: MatrixServer.get_mxid(localpart), event_id: generate_event_id(), origin_server_ts: DateTime.utc_now() |> DateTime.to_unix(), prev_events: [], @@ -28,30 +28,36 @@ defmodule MatrixServer.Event do } end - def create_room(room, creator, room_version) do + def create_room(room, %Account{localpart: localpart} = creator, room_version) do + mxid = MatrixServer.get_mxid(localpart) + %Event{ new(room, creator) | type: "m.room.create", state_key: "", content: %{ - "creator" => creator, + "creator" => mxid, "room_version" => room_version || MatrixServer.default_room_version() } } end - def join(room, sender) do + def join(room, %Account{localpart: localpart} = sender) do + mxid = MatrixServer.get_mxid(localpart) + %Event{ new(room, sender) | type: "m.room.member", - state_key: sender, + state_key: mxid, content: %{ "membership" => "join" } } end - def power_levels(room, sender) do + def power_levels(room, %Account{localpart: localpart} = sender) do + mxid = MatrixServer.get_mxid(localpart) + %Event{ new(room, sender) | type: "m.room.power_levels", @@ -65,7 +71,7 @@ defmodule MatrixServer.Event do "redact" => 50, "state_default" => 50, "users" => %{ - sender => 50 + mxid => 50 }, "users_default" => 0, "notifications" => %{ @@ -97,6 +103,39 @@ defmodule MatrixServer.Event do } end + def join_rules(room, sender, join_rule) do + %Event{ + new(room, sender) + | type: "m.room.join_rules", + state_key: "", + content: %{ + "join_rule" => join_rule + } + } + end + + def history_visibility(room, sender, history_visibility) do + %Event{ + new(room, sender) + | type: "m.room.history_visibility", + state_key: "", + content: %{ + "history_visibility" => history_visibility + } + } + end + + def guest_access(room, sender, guest_access) do + %Event{ + new(room, sender) + | type: "m.room.guest_access", + state_key: "", + content: %{ + "guest_access" => guest_access + } + } + end + def generate_event_id do "$" <> MatrixServer.random_string(17) <> ":" <> MatrixServer.server_name() end @@ -122,15 +161,13 @@ defmodule MatrixServer.Event do # We assume that required keys, as well as in the content, is already validated. # Rule 1.4 is left to changeset validation. - def prevalidate( - %Event{ - type: "m.room.create", - prev_events: prev_events, - auth_events: auth_events, - room_id: room_id, - sender: sender - } = event - ) do + def prevalidate(%Event{ + type: "m.room.create", + prev_events: prev_events, + auth_events: auth_events, + room_id: room_id, + sender: sender + }) do # TODO: error check on domains? # TODO: rule 1.3 diff --git a/lib/matrix_server/room_server.ex b/lib/matrix_server/room_server.ex index 79d9462..eb8489a 100644 --- a/lib/matrix_server/room_server.ex +++ b/lib/matrix_server/room_server.ex @@ -45,6 +45,12 @@ defmodule MatrixServer.RoomServer do 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, @@ -64,33 +70,74 @@ defmodule MatrixServer.RoomServer do end) end - defp room_creation_create_room( - %Account{localpart: localpart}, - %CreateRoom{room_version: room_version}, - room - ) do - Event.create_room(room, MatrixServer.get_mxid(localpart), room_version) + defp room_creation_create_room(account, %CreateRoom{room_version: room_version}, room) do + Event.create_room(room, account, room_version) |> verify_and_insert_event(%{}, room) end - defp room_creation_join_creator( - %Account{localpart: localpart}, - room, - state_set, - auth_events - ) do - Event.join(room, MatrixServer.get_mxid(localpart)) + defp room_creation_join_creator(account, room, state_set, auth_events) do + Event.join(room, account) |> Map.put(:auth_events, auth_events) |> verify_and_insert_event(state_set, room) end - defp room_creation_power_levels( - %Account{localpart: localpart}, - room, + defp room_creation_power_levels(account, room, state_set, auth_events) do + Event.power_levels(room, account) + |> Map.put(:auth_events, auth_events) + |> verify_and_insert_event(state_set, room) + end + + # TODO: trusted_private_chat: + # All invitees are given the same power level as the room creator. + defp room_creation_preset( + account, + %CreateRoom{preset: nil}, + %Room{visibility: visibility} = room, state_set, auth_events ) do - Event.power_levels(room, MatrixServer.get_mxid(localpart)) + preset = + case visibility do + :public -> "public_chat" + :private -> "private_chat" + end + + room_creation_preset(account, preset, room, state_set, auth_events) + end + + defp room_creation_preset(account, %CreateRoom{preset: preset}, room, state_set, auth_events) do + room_creation_preset(account, preset, room, state_set, auth_events) + end + + defp room_creation_preset(account, preset, room, state_set, auth_events) do + {join_rule, his_vis, guest_access} = + case preset do + "private_chat" -> {"invite", "shared", "can_join"} + "trusted_private_chat" -> {"invite", "shared", "can_join"} + "public_chat" -> {"public", "shared", "forbidden"} + end + + with {:ok, _, _, _} <- + room_creation_join_rules(account, join_rule, room, state_set, auth_events), + {:ok, _, _, _} <- room_creation_his_vis(account, his_vis, room, state_set, auth_events) do + room_creation_guest_access(account, guest_access, room, state_set, auth_events) + end + end + + defp room_creation_join_rules(account, join_rule, room, state_set, auth_events) do + Event.join_rules(room, account, join_rule) + |> Map.put(:auth_events, auth_events) + |> verify_and_insert_event(state_set, room) + end + + defp room_creation_his_vis(account, his_vis, room, state_set, auth_events) do + Event.history_visibility(room, account, his_vis) + |> Map.put(:auth_events, auth_events) + |> verify_and_insert_event(state_set, room) + end + + defp room_creation_guest_access(account, guest_access, room, state_set, auth_events) do + Event.guest_access(room, account, guest_access) |> Map.put(:auth_events, auth_events) |> verify_and_insert_event(state_set, room) end @@ -99,14 +146,8 @@ defmodule MatrixServer.RoomServer do {:ok, nil, state_set, room} end - defp room_creation_name( - %Account{localpart: localpart}, - %CreateRoom{name: name}, - room, - state_set, - auth_events - ) do - Event.name(room, MatrixServer.get_mxid(localpart), name) + defp room_creation_name(account, %CreateRoom{name: name}, room, state_set, auth_events) do + Event.name(room, account, name) |> Map.put(:auth_events, auth_events) |> verify_and_insert_event(state_set, room) end @@ -115,14 +156,8 @@ defmodule MatrixServer.RoomServer do {:ok, nil, state_set, room} end - defp room_creation_topic( - %Account{localpart: localpart}, - %CreateRoom{topic: topic}, - room, - state_set, - auth_events - ) do - Event.topic(room, MatrixServer.get_mxid(localpart), topic) + defp room_creation_topic(account, %CreateRoom{topic: topic}, room, state_set, auth_events) do + Event.topic(room, account, topic) |> Map.put(:auth_events, auth_events) |> verify_and_insert_event(state_set, room) end diff --git a/lib/matrix_server_web/api/create_room.ex b/lib/matrix_server_web/api/create_room.ex index 2eaa0d6..fa019a9 100644 --- a/lib/matrix_server_web/api/create_room.ex +++ b/lib/matrix_server_web/api/create_room.ex @@ -13,14 +13,24 @@ defmodule MatrixServerWeb.API.CreateRoom do field :topic, :string field :invite, {:array, :string} field :room_version, :string + field :preset, :string # TODO: unimplemented: - # creation_content, initial_state, invite_3pid, initial_state, preset, + # creation_content, initial_state, invite_3pid, initial_state, # is_direct, power_level_content_override end def changeset(params) do %__MODULE__{} - |> cast(params, [:visibility, :room_alias_name, :name, :topic, :invite, :room_version]) + |> cast(params, [ + :visibility, + :room_alias_name, + :name, + :topic, + :invite, + :room_version, + :preset + ]) + |> validate_inclusion(:preset, ["private_chat", "public_chat", "trusted_private_chat"]) end def get_error(%Changeset{errors: [error | _]}), do: get_error(error) diff --git a/priv/repo/seeds.exs b/priv/repo/seeds.exs index 8cc72ea..7b6d604 100644 --- a/priv/repo/seeds.exs +++ b/priv/repo/seeds.exs @@ -14,6 +14,24 @@ Repo.insert(%Device{ # Auth difference example from here: # https://matrix.org/docs/guides/implementing-stateres#auth-differences +alice = + Repo.insert!(%Account{ + localpart: "alice", + password_hash: Bcrypt.hash_pwd_salt("sneed") + }) + +bob = + Repo.insert!(%Account{ + localpart: "bob", + password_hash: Bcrypt.hash_pwd_salt("sneed") + }) + +charlie = + Repo.insert!(%Account{ + localpart: "charlie", + password_hash: Bcrypt.hash_pwd_salt("sneed") + }) + room = Repo.insert!(%Room{ id: "room1", @@ -21,13 +39,13 @@ room = }) Repo.insert!( - Event.create_room(room, "alice", "v1") + Event.create_room(room, alice, "v1") |> Map.put(:origin_server_ts, 0) |> Map.put(:event_id, "create") ) Repo.insert!( - Event.join(room, "alice") + Event.join(room, alice) |> Map.put(:prev_events, ["create"]) |> Map.put(:auth_events, ["create"]) |> Map.put(:origin_server_ts, 1) @@ -35,7 +53,7 @@ Repo.insert!( ) Repo.insert!( - Event.join(room, "bob") + Event.join(room, bob) |> Map.put(:prev_events, ["join_alice"]) |> Map.put(:auth_events, ["create"]) |> Map.put(:origin_server_ts, 2) @@ -43,14 +61,14 @@ Repo.insert!( ) Repo.insert!( - Event.join(room, "charlie") + Event.join(room, charlie) |> Map.put(:prev_events, ["join_bob"]) |> Map.put(:auth_events, ["create"]) |> Map.put(:origin_server_ts, 3) |> Map.put(:event_id, "join_charlie") ) -%Event{content: content} = event = Event.power_levels(room, "alice") +%Event{content: content} = event = Event.power_levels(room, alice) event = %Event{event | content: %{content | "users" => %{"alice" => 100, "bob" => 100}}} Repo.insert!( @@ -61,7 +79,7 @@ Repo.insert!( |> Map.put(:event_id, "a") ) -%Event{content: content} = event = Event.power_levels(room, "bob") +%Event{content: content} = event = Event.power_levels(room, bob) event = %Event{ event @@ -77,7 +95,7 @@ Repo.insert!( ) Repo.insert!( - Event.topic(room, "alice", "sneed") + Event.topic(room, alice, "sneed") |> Map.put(:prev_events, ["a"]) |> Map.put(:auth_events, ["create", "join_alice", "a"]) |> Map.put(:origin_server_ts, 5)