Implement client joined rooms endpoint

Track which rooms a local account has joined
Add some documentation to modules
This commit is contained in:
Pim Kunis 2021-08-23 12:59:12 +02:00
parent 9d40f8bc8b
commit 6f8c224d50
8 changed files with 95 additions and 11 deletions

View file

@ -11,7 +11,7 @@ defmodule MatrixServer.RoomServer do
import Ecto.Query import Ecto.Query
import Ecto.Changeset import Ecto.Changeset
alias MatrixServer.{Repo, Room, Event, StateResolution} alias MatrixServer.{Repo, Room, Event, StateResolution, JoinedRoom, Account}
alias MatrixServer.StateResolution.Authorization alias MatrixServer.StateResolution.Authorization
alias MatrixServerWeb.Client.Request.CreateRoom alias MatrixServerWeb.Client.Request.CreateRoom
@ -199,6 +199,7 @@ defmodule MatrixServer.RoomServer do
join_creator <- Event.join(room, account, [create_room]), join_creator <- Event.join(room, account, [create_room]),
{:ok, state_set, join_creator, room} <- {:ok, state_set, join_creator, room} <-
verify_and_insert_event(join_creator, state_set, room), verify_and_insert_event(join_creator, state_set, room),
{:ok, _} <- insert_joined_room_assoc(account, room),
pls <- Event.power_levels(room, account, [create_room, join_creator]), pls <- Event.power_levels(room, account, [create_room, join_creator]),
{:ok, state_set, pls, room} <- verify_and_insert_event(pls, state_set, room) do {:ok, state_set, pls, room} <- verify_and_insert_event(pls, state_set, room) do
auth_events = [create_room, join_creator, pls] auth_events = [create_room, join_creator, pls]
@ -236,6 +237,10 @@ defmodule MatrixServer.RoomServer do
end end
end end
defp insert_joined_room_assoc(%Account{localpart: localpart}, %Room{id: room_id}) do
Repo.insert(%JoinedRoom{localpart: localpart, room_id: room_id})
end
# TODO: trusted_private_chat: # TODO: trusted_private_chat:
# All invitees are given the same power level as the room creator. # All invitees are given the same power level as the room creator.
defp room_creation_preset(account, nil, %Room{visibility: visibility} = room, auth_events) do defp room_creation_preset(account, nil, %Room{visibility: visibility} = room, auth_events) do

View file

@ -3,7 +3,7 @@ defmodule MatrixServer.Account do
import Ecto.{Changeset, Query} import Ecto.{Changeset, Query}
alias MatrixServer.{Repo, Account, Device} alias MatrixServer.{Repo, Account, Device, Room, JoinedRoom}
alias MatrixServerWeb.Client.Request.{Register, Login} alias MatrixServerWeb.Client.Request.{Register, Login}
alias Ecto.Multi alias Ecto.Multi
@ -17,6 +17,11 @@ defmodule MatrixServer.Account do
schema "accounts" do schema "accounts" do
field :password_hash, :string, redact: true field :password_hash, :string, redact: true
has_many :devices, Device, foreign_key: :localpart has_many :devices, Device, foreign_key: :localpart
many_to_many :joined_rooms, Room,
join_through: JoinedRoom,
join_keys: [localpart: :localpart, room_id: :id]
timestamps(updated_at: false) timestamps(updated_at: false)
end end

View file

@ -0,0 +1,16 @@
defmodule MatrixServer.JoinedRoom do
use Ecto.Schema
alias MatrixServer.{Account, Room}
@primary_key false
schema "joined_rooms" do
belongs_to :account, Account,
foreign_key: :localpart,
references: :localpart,
type: :string,
primary_key: true
belongs_to :room, Room, primary_key: true, type: :string
end
end

View file

@ -1,4 +1,23 @@
defmodule MatrixServer.StateResolution do defmodule MatrixServer.StateResolution do
@moduledoc """
Functions for resolving the state of a Matrix room.
Currently, only state resolution from room version 2 is supported,
see [the Matrix docs](https://spec.matrix.org/unstable/rooms/v2/).
The current implementation of the state resolution algorithm performs
rather badly.
Each time state is resolved, all events in the room are fetched from
the database and loaded into memory.
This is mostly so I didn't have to worry about fetching events from the
database when developing this initial implementation.
Then, the state is calculated using the new event's previous events and auth
events.
To prevent loading all events into memory, and calculating the whole state each
time, we should make snapshots of the state of a room at regular intervals.
It looks like Dendrite does this too.
"""
import Ecto.Query import Ecto.Query
alias MatrixServer.{Repo, Event, Room} alias MatrixServer.{Repo, Event, Room}
@ -61,6 +80,13 @@ defmodule MatrixServer.StateResolution do
|> Enum.reduce(MapSet.new(), &MapSet.union/2) |> Enum.reduce(MapSet.new(), &MapSet.union/2)
end end
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 do_resolve([], _), do: %{} defp do_resolve([], _), do: %{}
defp do_resolve(state_sets, room_events) do defp do_resolve(state_sets, room_events) do
@ -265,13 +291,6 @@ defmodule MatrixServer.StateResolution do
end) end)
end end
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 authorized?(%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 = state_set =
auth_event_ids auth_event_ids

View file

@ -1,4 +1,11 @@
defmodule MatrixServer.StateResolution.Authorization do defmodule MatrixServer.StateResolution.Authorization do
@moduledoc """
Implementation of Matrix event authorization rules for stat resolution.
Note that some authorization rules are already checked in
`MatrixServer.Event.prevalidate/1` so they are skipped here.
"""
import MatrixServer.StateResolution import MatrixServer.StateResolution
import Ecto.Query import Ecto.Query

View file

@ -2,9 +2,9 @@ defmodule MatrixServerWeb.Client.RoomController do
use MatrixServerWeb, :controller use MatrixServerWeb, :controller
import MatrixServerWeb.Error import MatrixServerWeb.Error
import Ecto.Changeset import Ecto.{Changeset, Query}
alias MatrixServer.Room alias MatrixServer.{Repo, Room}
alias MatrixServerWeb.Client.Request.CreateRoom alias MatrixServerWeb.Client.Request.CreateRoom
alias Ecto.Changeset alias Ecto.Changeset
alias Plug.Conn alias Plug.Conn
@ -31,4 +31,19 @@ defmodule MatrixServerWeb.Client.RoomController do
put_error(conn, :bad_json) put_error(conn, :bad_json)
end end
end end
def joined_rooms(%Conn{assigns: %{account: account}} = conn, _params) do
joined_room_ids = account
|> Ecto.assoc(:joined_rooms)
|> select([jr], jr.id)
|> Repo.all()
data = %{
joined_rooms: joined_room_ids
}
conn
|> put_status(200)
|> json(data)
end
end end

View file

@ -51,6 +51,7 @@ defmodule MatrixServerWeb.Router do
post "/logout", AccountController, :logout post "/logout", AccountController, :logout
post "/logout/all", AccountController, :logout_all post "/logout/all", AccountController, :logout_all
post "/createRoom", RoomController, :create post "/createRoom", RoomController, :create
get "/joined_rooms", RoomController, :joined_rooms
scope "/directory/room" do scope "/directory/room" do
put "/:alias", AliasesController, :create put "/:alias", AliasesController, :create

View file

@ -0,0 +1,16 @@
defmodule MatrixServer.Repo.Migrations.CreateJoinedRoomsTable do
use Ecto.Migration
def change do
create table(:joined_rooms, primary_key: false) do
add :localpart,
references(:accounts, column: :localpart, type: :string),
primary_key: true,
null: false
add :room_id, references(:rooms, type: :string),
primary_key: true,
null: false
end
end
end