2021-07-10 21:16:00 +00:00
|
|
|
defmodule MatrixServer.RoomServer do
|
|
|
|
use GenServer
|
|
|
|
|
2021-07-25 12:57:52 +00:00
|
|
|
import Ecto.Query
|
|
|
|
|
2021-07-24 20:08:01 +00:00
|
|
|
alias MatrixServer.{Repo, Room, Event, Account, StateResolution}
|
2021-07-10 21:16:00 +00:00
|
|
|
alias MatrixServerWeb.API.CreateRoom
|
|
|
|
|
2021-07-23 19:00:01 +00:00
|
|
|
@registry MatrixServer.RoomServer.Registry
|
|
|
|
@supervisor MatrixServer.RoomServer.Supervisor
|
|
|
|
|
|
|
|
def create_room(input, account) do
|
2021-07-23 22:13:12 +00:00
|
|
|
%Room{id: room_id} = room = Repo.insert!(Room.create_changeset(input))
|
2021-07-23 19:00:01 +00:00
|
|
|
|
2021-07-23 22:13:12 +00:00
|
|
|
opts = [
|
|
|
|
name: {:via, Registry, {@registry, room_id}},
|
|
|
|
input: input,
|
|
|
|
account: account,
|
|
|
|
room: room
|
|
|
|
]
|
|
|
|
|
|
|
|
DynamicSupervisor.start_child(@supervisor, {__MODULE__, opts})
|
2021-07-10 21:16:00 +00:00
|
|
|
end
|
|
|
|
|
2021-07-25 15:39:22 +00:00
|
|
|
def start_link(opts) do
|
|
|
|
{name, opts} = Keyword.pop(opts, :name)
|
|
|
|
GenServer.start_link(__MODULE__, opts, name: name)
|
|
|
|
end
|
|
|
|
|
2021-07-10 21:16:00 +00:00
|
|
|
@impl true
|
2021-07-23 19:00:01 +00:00
|
|
|
def init(opts) do
|
2021-07-26 09:50:18 +00:00
|
|
|
%Room{id: room_id} = room = Keyword.fetch!(opts, :room)
|
2021-07-23 19:00:01 +00:00
|
|
|
input = Keyword.fetch!(opts, :input)
|
|
|
|
account = Keyword.fetch!(opts, :account)
|
|
|
|
|
2021-07-26 09:50:18 +00:00
|
|
|
state_set = %{}
|
|
|
|
|
2021-07-23 19:00:01 +00:00
|
|
|
Repo.transaction(fn ->
|
2021-07-26 09:50:18 +00:00
|
|
|
with {:ok, create_room_event, state_set} <-
|
|
|
|
room_creation_create_room(account, input, room, state_set),
|
2021-07-26 17:47:38 +00:00
|
|
|
{:ok, join_creator_event, state_set} <-
|
|
|
|
room_creation_join_creator(account, room, state_set, create_room_event),
|
|
|
|
{:ok, _power_levels_event, state_set} <-
|
|
|
|
room_creation_power_levels(
|
|
|
|
account,
|
|
|
|
room,
|
|
|
|
state_set,
|
|
|
|
create_room_event,
|
|
|
|
join_creator_event
|
|
|
|
) do
|
2021-07-23 22:13:12 +00:00
|
|
|
{:ok, %{room_id: room_id, state_set: state_set}}
|
2021-07-25 15:39:22 +00:00
|
|
|
else
|
|
|
|
_ -> {:error, :something}
|
2021-07-23 19:00:01 +00:00
|
|
|
end
|
|
|
|
end)
|
|
|
|
end
|
|
|
|
|
2021-07-26 09:50:18 +00:00
|
|
|
defp room_creation_create_room(
|
2021-07-23 19:00:01 +00:00
|
|
|
%Account{localpart: localpart},
|
|
|
|
%CreateRoom{room_version: room_version},
|
2021-07-26 09:50:18 +00:00
|
|
|
%Room{id: room_id},
|
|
|
|
_state_set
|
2021-07-23 19:00:01 +00:00
|
|
|
) do
|
2021-07-26 09:50:18 +00:00
|
|
|
Event.create_room(room_id, MatrixServer.get_mxid(localpart), room_version)
|
|
|
|
|> verify_and_insert_event(%{})
|
|
|
|
end
|
2021-07-24 20:54:03 +00:00
|
|
|
|
2021-07-26 09:50:18 +00:00
|
|
|
defp room_creation_join_creator(
|
|
|
|
%Account{localpart: localpart},
|
|
|
|
%Room{id: room_id},
|
|
|
|
state_set,
|
2021-07-26 17:47:38 +00:00
|
|
|
%Event{event_id: create_room_id}
|
2021-07-26 09:50:18 +00:00
|
|
|
) do
|
|
|
|
Event.join(room_id, MatrixServer.get_mxid(localpart))
|
2021-07-26 17:47:38 +00:00
|
|
|
|> Map.put(:auth_events, [create_room_id])
|
|
|
|
|> Map.put(:prev_events, [create_room_id])
|
|
|
|
|> verify_and_insert_event(state_set)
|
|
|
|
end
|
|
|
|
|
|
|
|
defp room_creation_power_levels(
|
|
|
|
%Account{localpart: localpart},
|
|
|
|
%Room{id: room_id},
|
|
|
|
state_set,
|
|
|
|
%Event{event_id: create_room_id},
|
|
|
|
%Event{event_id: join_creator_id}
|
|
|
|
) do
|
|
|
|
Event.power_levels(room_id, MatrixServer.get_mxid(localpart))
|
|
|
|
|> Map.put(:auth_events, [create_room_id, join_creator_id])
|
|
|
|
|> Map.put(:prev_events, [join_creator_id])
|
2021-07-26 09:50:18 +00:00
|
|
|
|> verify_and_insert_event(state_set)
|
2021-07-24 20:08:01 +00:00
|
|
|
end
|
2021-07-23 22:13:12 +00:00
|
|
|
|
2021-07-25 15:39:22 +00:00
|
|
|
defp verify_and_insert_event(event, current_state_set) do
|
2021-07-24 20:08:01 +00:00
|
|
|
# Check the following things:
|
|
|
|
# 1. TODO: Is a valid event, otherwise it is dropped.
|
|
|
|
# 2. TODO: Passes signature checks, otherwise it is dropped.
|
|
|
|
# 3. TODO: Passes hash checks, otherwise it is redacted before being processed further.
|
|
|
|
# 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.
|
2021-07-25 15:39:22 +00:00
|
|
|
# 6. Passes authorization rules based on the current state of the room, otherwise it is "soft failed".
|
2021-07-26 17:47:38 +00:00
|
|
|
if Event.prevalidate(event) do
|
|
|
|
if StateResolution.is_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
|
|
|
|
# We assume here that the event is always a forward extremity.
|
|
|
|
Room.update_forward_extremities(event)
|
|
|
|
{:ok, event} = Repo.insert(event)
|
|
|
|
state_set = StateResolution.resolve_forward_extremities(event)
|
|
|
|
{:ok, event, state_set}
|
|
|
|
else
|
|
|
|
{:error, :soft_failed}
|
|
|
|
end
|
2021-07-25 15:39:22 +00:00
|
|
|
else
|
2021-07-26 17:47:38 +00:00
|
|
|
{:error, :rejected}
|
2021-07-25 15:39:22 +00:00
|
|
|
end
|
2021-07-25 12:57:52 +00:00
|
|
|
else
|
|
|
|
{:error, :rejected}
|
|
|
|
end
|
2021-07-24 20:08:01 +00:00
|
|
|
else
|
2021-07-26 17:47:38 +00:00
|
|
|
{:error, :invalid}
|
2021-07-24 20:08:01 +00:00
|
|
|
end
|
2021-07-10 21:16:00 +00:00
|
|
|
end
|
2021-07-25 12:57:52 +00:00
|
|
|
|
|
|
|
def testing do
|
|
|
|
account = Repo.one!(from a in Account, limit: 1)
|
|
|
|
create_room(%CreateRoom{}, account)
|
|
|
|
end
|
2021-07-10 21:16:00 +00:00
|
|
|
end
|