Save name and topic events when creating room

This commit is contained in:
Pim Kunis 2021-07-26 23:42:35 +02:00
parent 7a74440433
commit 3c93ddd768
4 changed files with 119 additions and 76 deletions

View file

@ -17,7 +17,7 @@ defmodule MatrixServer.Event do
belongs_to :room, Room, type: :string belongs_to :room, Room, type: :string
end end
def new(room_id, sender) do def new(%Room{id: room_id}, sender) do
%Event{ %Event{
room_id: room_id, room_id: room_id,
sender: sender, sender: sender,
@ -28,9 +28,9 @@ defmodule MatrixServer.Event do
} }
end end
def create_room(room_id, creator, room_version) do def create_room(room, creator, room_version) do
%Event{ %Event{
new(room_id, creator) new(room, creator)
| type: "m.room.create", | type: "m.room.create",
state_key: "", state_key: "",
content: %{ content: %{
@ -40,9 +40,9 @@ defmodule MatrixServer.Event do
} }
end end
def join(room_id, sender) do def join(room, sender) do
%Event{ %Event{
new(room_id, sender) new(room, sender)
| type: "m.room.member", | type: "m.room.member",
state_key: sender, state_key: sender,
content: %{ content: %{
@ -51,9 +51,9 @@ defmodule MatrixServer.Event do
} }
end end
def power_levels(room_id, sender) do def power_levels(room, sender) do
%Event{ %Event{
new(room_id, sender) new(room, sender)
| type: "m.room.power_levels", | type: "m.room.power_levels",
state_key: "", state_key: "",
content: %{ content: %{
@ -75,9 +75,9 @@ defmodule MatrixServer.Event do
} }
end end
def room_name(room_id, sender, name) do def name(room, sender, name) do
%Event{ %Event{
new(room_id, sender) new(room, sender)
| type: "m.room.name", | type: "m.room.name",
state_key: "", state_key: "",
content: %{ content: %{
@ -86,9 +86,9 @@ defmodule MatrixServer.Event do
} }
end end
def room_topic(room_id, sender, topic) do def topic(room, sender, topic) do
%Event{ %Event{
new(room_id, sender) new(room, sender)
| type: "m.room.topic", | type: "m.room.topic",
state_key: "", state_key: "",
content: %{ content: %{
@ -122,13 +122,15 @@ defmodule MatrixServer.Event do
# We assume that required keys, as well as in the content, is already validated. # We assume that required keys, as well as in the content, is already validated.
# Rule 1.4 is left to changeset validation. # Rule 1.4 is left to changeset validation.
def prevalidate(%Event{ def prevalidate(
type: "m.room.create", %Event{
prev_events: prev_events, type: "m.room.create",
auth_events: auth_events, prev_events: prev_events,
room_id: room_id, auth_events: auth_events,
sender: sender room_id: room_id,
}) do sender: sender
} = event
) do
# TODO: error check on domains? # TODO: error check on domains?
# TODO: rule 1.3 # TODO: rule 1.3

View file

@ -10,6 +10,7 @@ defmodule MatrixServer.Room do
@primary_key {:id, :string, []} @primary_key {:id, :string, []}
schema "rooms" do schema "rooms" do
field :visibility, Ecto.Enum, values: [:public, :private] field :visibility, Ecto.Enum, values: [:public, :private]
field :state, :map
field :forward_extremities, {:array, :string} field :forward_extremities, {:array, :string}
has_many :events, Event, foreign_key: :event_id has_many :events, Event, foreign_key: :event_id
end end
@ -21,28 +22,24 @@ defmodule MatrixServer.Room do
def create_changeset(%CreateRoom{} = input) do def create_changeset(%CreateRoom{} = input) do
visibility = input.visibility || :public visibility = input.visibility || :public
%Room{} %Room{id: generate_room_id(), forward_extremities: [], state: %{}}
|> changeset(%{visibility: visibility}) |> changeset(%{visibility: visibility})
|> put_change(:id, generate_room_id())
end end
def generate_room_id do def generate_room_id do
"!" <> MatrixServer.random_string(18) <> ":" <> MatrixServer.server_name() "!" <> MatrixServer.random_string(18) <> ":" <> MatrixServer.server_name()
end end
def update_forward_extremities(%Event{ def update_forward_extremities(
event_id: event_id, %Event{
prev_events: prev_event_ids, event_id: event_id,
room_id: room_id prev_events: prev_event_ids
}) do },
%Room{forward_extremities: forward_extremities} = %Room{id: room_id, forward_extremities: forward_extremities}
Room ) do
|> where([r], r.id == ^room_id)
|> Repo.one!()
new_forward_extremities = [event_id | forward_extremities -- prev_event_ids] new_forward_extremities = [event_id | forward_extremities -- prev_event_ids]
{_, [room | _]} = {_, [room]} =
from(r in Room, where: r.id == ^room_id, select: r) from(r in Room, where: r.id == ^room_id, select: r)
|> Repo.update_all(set: [forward_extremities: new_forward_extremities]) |> Repo.update_all(set: [forward_extremities: new_forward_extremities])

View file

@ -29,28 +29,37 @@ defmodule MatrixServer.RoomServer do
@impl true @impl true
def init(opts) do def init(opts) do
%Room{id: room_id} = room = Keyword.fetch!(opts, :room) room = Keyword.fetch!(opts, :room)
input = Keyword.fetch!(opts, :input) input = Keyword.fetch!(opts, :input)
account = Keyword.fetch!(opts, :account) account = Keyword.fetch!(opts, :account)
state_set = %{}
Repo.transaction(fn -> Repo.transaction(fn ->
with {:ok, create_room_event, state_set} <- with {:ok, create_room_id, state_set, room} <-
room_creation_create_room(account, input, room, state_set), room_creation_create_room(account, input, room),
{:ok, join_creator_event, state_set} <- {:ok, join_creator_id, state_set, room} <-
room_creation_join_creator(account, room, state_set, create_room_event), room_creation_join_creator(account, room, state_set, [create_room_id]),
{:ok, _power_levels_event, state_set} <- {:ok, pl_id, state_set, room} <-
room_creation_power_levels( room_creation_power_levels(
account, account,
room, room,
state_set, state_set,
create_room_event, [create_room_id, join_creator_id]
join_creator_event ),
) do {:ok, _, state_set, room} <-
{:ok, %{room_id: room_id, state_set: state_set}} 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 else
_ -> {:error, :something} _ -> {:error, :validation}
end end
end) end)
end end
@ -58,39 +67,71 @@ defmodule MatrixServer.RoomServer do
defp room_creation_create_room( defp room_creation_create_room(
%Account{localpart: localpart}, %Account{localpart: localpart},
%CreateRoom{room_version: room_version}, %CreateRoom{room_version: room_version},
%Room{id: room_id}, room
_state_set
) do ) do
Event.create_room(room_id, MatrixServer.get_mxid(localpart), room_version) Event.create_room(room, MatrixServer.get_mxid(localpart), room_version)
|> verify_and_insert_event(%{}) |> verify_and_insert_event(%{}, room)
end end
defp room_creation_join_creator( defp room_creation_join_creator(
%Account{localpart: localpart}, %Account{localpart: localpart},
%Room{id: room_id}, room,
state_set, state_set,
%Event{event_id: create_room_id} auth_events
) do ) do
Event.join(room_id, MatrixServer.get_mxid(localpart)) Event.join(room, MatrixServer.get_mxid(localpart))
|> Map.put(:auth_events, [create_room_id]) |> Map.put(:auth_events, auth_events)
|> Map.put(:prev_events, [create_room_id]) |> verify_and_insert_event(state_set, room)
|> verify_and_insert_event(state_set)
end end
defp room_creation_power_levels( defp room_creation_power_levels(
%Account{localpart: localpart}, %Account{localpart: localpart},
%Room{id: room_id}, room,
state_set, state_set,
%Event{event_id: create_room_id}, auth_events
%Event{event_id: join_creator_id}
) do ) do
Event.power_levels(room_id, MatrixServer.get_mxid(localpart)) Event.power_levels(room, MatrixServer.get_mxid(localpart))
|> Map.put(:auth_events, [create_room_id, join_creator_id]) |> Map.put(:auth_events, auth_events)
|> Map.put(:prev_events, [join_creator_id]) |> verify_and_insert_event(state_set, room)
|> verify_and_insert_event(state_set)
end end
defp verify_and_insert_event(event, current_state_set) do defp room_creation_name(_, %CreateRoom{name: nil}, room, state_set, _) 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)
|> Map.put(:auth_events, auth_events)
|> verify_and_insert_event(state_set, room)
end
defp room_creation_topic(_, %CreateRoom{topic: nil}, room, state_set, _) 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)
|> Map.put(:auth_events, auth_events)
|> verify_and_insert_event(state_set, room)
end
defp verify_and_insert_event(
%Event{event_id: event_id} = event,
current_state_set,
%Room{forward_extremities: forward_extremities} = room
) do
# Check the following things: # Check the following things:
# 1. TODO: Is a valid event, otherwise it is dropped. # 1. TODO: Is a valid event, otherwise it is dropped.
# 2. TODO: Passes signature checks, otherwise it is dropped. # 2. TODO: Passes signature checks, otherwise it is dropped.
@ -98,6 +139,8 @@ defmodule MatrixServer.RoomServer do
# 4. Passes authorization rules based on the event's auth events, otherwise it is rejected. # 4. Passes authorization rules based on the event's auth events, otherwise it is rejected.
# 5. Passes authorization rules based on the state at the event, otherwise it is rejected. # 5. Passes authorization rules based on the state at the event, otherwise it is rejected.
# 6. Passes authorization rules based on the current state of the room, otherwise it is "soft failed". # 6. Passes authorization rules based on the current state of the room, otherwise it is "soft failed".
event = %Event{event | prev_events: forward_extremities}
if Event.prevalidate(event) do if Event.prevalidate(event) do
if StateResolution.is_authorized_by_auth_events(event) do if StateResolution.is_authorized_by_auth_events(event) do
state_set = StateResolution.resolve(event, false) state_set = StateResolution.resolve(event, false)
@ -105,10 +148,10 @@ defmodule MatrixServer.RoomServer do
if StateResolution.is_authorized(event, state_set) do if StateResolution.is_authorized(event, state_set) do
if StateResolution.is_authorized(event, current_state_set) do if StateResolution.is_authorized(event, current_state_set) do
# We assume here that the event is always a forward extremity. # We assume here that the event is always a forward extremity.
Room.update_forward_extremities(event) room = Room.update_forward_extremities(event, room)
{:ok, event} = Repo.insert(event) {:ok, event} = Repo.insert(event)
state_set = StateResolution.resolve_forward_extremities(event) state_set = StateResolution.resolve_forward_extremities(event)
{:ok, event, state_set} {:ok, event_id, state_set, room}
else else
{:error, :soft_failed} {:error, :soft_failed}
end end
@ -125,6 +168,6 @@ defmodule MatrixServer.RoomServer do
def testing do def testing do
account = Repo.one!(from a in Account, limit: 1) account = Repo.one!(from a in Account, limit: 1)
create_room(%CreateRoom{}, account) create_room(%CreateRoom{name: "Sneed", topic: "City slickers"}, account)
end end
end end

View file

@ -14,19 +14,20 @@ Repo.insert(%Device{
# Auth difference example from here: # Auth difference example from here:
# https://matrix.org/docs/guides/implementing-stateres#auth-differences # https://matrix.org/docs/guides/implementing-stateres#auth-differences
Repo.insert!(%Room{ room =
id: "room1", Repo.insert!(%Room{
visibility: :public id: "room1",
}) visibility: :public
})
Repo.insert!( Repo.insert!(
Event.create_room("room1", "alice", "v1") Event.create_room(room, "alice", "v1")
|> Map.put(:origin_server_ts, 0) |> Map.put(:origin_server_ts, 0)
|> Map.put(:event_id, "create") |> Map.put(:event_id, "create")
) )
Repo.insert!( Repo.insert!(
Event.join("room1", "alice") Event.join(room, "alice")
|> Map.put(:prev_events, ["create"]) |> Map.put(:prev_events, ["create"])
|> Map.put(:auth_events, ["create"]) |> Map.put(:auth_events, ["create"])
|> Map.put(:origin_server_ts, 1) |> Map.put(:origin_server_ts, 1)
@ -34,7 +35,7 @@ Repo.insert!(
) )
Repo.insert!( Repo.insert!(
Event.join("room1", "bob") Event.join(room, "bob")
|> Map.put(:prev_events, ["join_alice"]) |> Map.put(:prev_events, ["join_alice"])
|> Map.put(:auth_events, ["create"]) |> Map.put(:auth_events, ["create"])
|> Map.put(:origin_server_ts, 2) |> Map.put(:origin_server_ts, 2)
@ -42,14 +43,14 @@ Repo.insert!(
) )
Repo.insert!( Repo.insert!(
Event.join("room1", "charlie") Event.join(room, "charlie")
|> Map.put(:prev_events, ["join_bob"]) |> Map.put(:prev_events, ["join_bob"])
|> Map.put(:auth_events, ["create"]) |> Map.put(:auth_events, ["create"])
|> Map.put(:origin_server_ts, 3) |> Map.put(:origin_server_ts, 3)
|> Map.put(:event_id, "join_charlie") |> Map.put(:event_id, "join_charlie")
) )
%Event{content: content} = event = Event.power_levels("room1", "alice") %Event{content: content} = event = Event.power_levels(room, "alice")
event = %Event{event | content: %{content | "users" => %{"alice" => 100, "bob" => 100}}} event = %Event{event | content: %{content | "users" => %{"alice" => 100, "bob" => 100}}}
Repo.insert!( Repo.insert!(
@ -60,7 +61,7 @@ Repo.insert!(
|> Map.put(:event_id, "a") |> Map.put(:event_id, "a")
) )
%Event{content: content} = event = Event.power_levels("room1", "bob") %Event{content: content} = event = Event.power_levels(room, "bob")
event = %Event{ event = %Event{
event event
@ -76,7 +77,7 @@ Repo.insert!(
) )
Repo.insert!( Repo.insert!(
Event.room_topic("room1", "alice", "sneed") Event.topic(room, "alice", "sneed")
|> Map.put(:prev_events, ["a"]) |> Map.put(:prev_events, ["a"])
|> Map.put(:auth_events, ["create", "join_alice", "a"]) |> Map.put(:auth_events, ["create", "join_alice", "a"])
|> Map.put(:origin_server_ts, 5) |> Map.put(:origin_server_ts, 5)