Implement federation room state snapshot endpoint
This commit is contained in:
parent
e510c3bb6a
commit
ba3e290bf1
8 changed files with 86 additions and 18 deletions
|
@ -1,6 +1,6 @@
|
||||||
# https://github.com/michalmuskala/jason/issues/69
|
# https://github.com/michalmuskala/jason/issues/69
|
||||||
defmodule MatrixServer.EncodableMap do
|
defmodule MatrixServer.EncodableMap do
|
||||||
alias MatrixServer.{EncodableMap, Event}
|
alias MatrixServer.EncodableMap
|
||||||
alias MatrixServer.Types.{UserId, RoomId, EventId, GroupId, AliasId}
|
alias MatrixServer.Types.{UserId, RoomId, EventId, GroupId, AliasId}
|
||||||
|
|
||||||
defstruct pairs: []
|
defstruct pairs: []
|
||||||
|
|
|
@ -60,6 +60,10 @@ defmodule MatrixServer.RoomServer do
|
||||||
GenServer.call(pid, {:server_in_room, domain})
|
GenServer.call(pid, {:server_in_room, domain})
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def get_state_at_event(pid, event) do
|
||||||
|
GenServer.call(pid, {:get_state_at_event, event})
|
||||||
|
end
|
||||||
|
|
||||||
### Implementation
|
### Implementation
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
|
@ -104,6 +108,26 @@ defmodule MatrixServer.RoomServer do
|
||||||
{:reply, result, state}
|
{:reply, result, state}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def handle_call({:get_state_at_event, %Event{room_id: room_id} = event}, _from, state) do
|
||||||
|
room_events =
|
||||||
|
Event
|
||||||
|
|> where([e], e.room_id == ^room_id)
|
||||||
|
|> select([e], {e.event_id, e})
|
||||||
|
|> Repo.all()
|
||||||
|
|> Enum.into(%{})
|
||||||
|
|
||||||
|
state_set = StateResolution.resolve(event, false)
|
||||||
|
state_events = Map.values(state_set)
|
||||||
|
|
||||||
|
auth_chain =
|
||||||
|
state_set
|
||||||
|
|> Map.values()
|
||||||
|
|> StateResolution.full_auth_chain(room_events)
|
||||||
|
|> Enum.map(&room_events[&1])
|
||||||
|
|
||||||
|
{:reply, {state_events, auth_chain}, state}
|
||||||
|
end
|
||||||
|
|
||||||
defp create_room_insert_events(room, account, %CreateRoom{
|
defp create_room_insert_events(room, account, %CreateRoom{
|
||||||
room_version: room_version,
|
room_version: room_version,
|
||||||
preset: preset,
|
preset: preset,
|
||||||
|
|
|
@ -55,6 +55,12 @@ defmodule MatrixServer.StateResolution do
|
||||||
|> do_resolve(room_events)
|
|> do_resolve(room_events)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def full_auth_chain(events, room_events) do
|
||||||
|
events
|
||||||
|
|> Enum.map(&auth_chain(&1, room_events))
|
||||||
|
|> Enum.reduce(MapSet.new(), &MapSet.union/2)
|
||||||
|
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
|
||||||
|
@ -150,12 +156,6 @@ defmodule MatrixServer.StateResolution do
|
||||||
MapSet.difference(auth_chain_union, auth_chain_intersection)
|
MapSet.difference(auth_chain_union, auth_chain_intersection)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp full_auth_chain(events, room_events) do
|
|
||||||
events
|
|
||||||
|> Enum.map(&auth_chain(&1, room_events))
|
|
||||||
|> Enum.reduce(MapSet.new(), &MapSet.union/2)
|
|
||||||
end
|
|
||||||
|
|
||||||
defp auth_chain(%Event{auth_events: auth_events}, room_events) do
|
defp auth_chain(%Event{auth_events: auth_events}, room_events) do
|
||||||
auth_events
|
auth_events
|
||||||
|> Enum.map(&room_events[&1])
|
|> Enum.map(&room_events[&1])
|
||||||
|
|
|
@ -13,6 +13,7 @@ defmodule MatrixServerWeb.Error do
|
||||||
{400, "M_INVALID_ROOM_STATE", "The request would leave the room in an invalid state."},
|
{400, "M_INVALID_ROOM_STATE", "The request would leave the room in an invalid state."},
|
||||||
unauthorized: {400, "M_UNAUTHORIZED", "The request was unauthorized."},
|
unauthorized: {400, "M_UNAUTHORIZED", "The request was unauthorized."},
|
||||||
invalid_param: {400, "M_INVALID_PARAM", "A request parameter was invalid."},
|
invalid_param: {400, "M_INVALID_PARAM", "A request parameter was invalid."},
|
||||||
|
missing_param: {400, "M_MISSING_PARAM", "A request parameter is missing."},
|
||||||
unknown_token: {401, "M_UNKNOWN_TOKEN", "Invalid access token."},
|
unknown_token: {401, "M_UNKNOWN_TOKEN", "Invalid access token."},
|
||||||
missing_token: {401, "M_MISSING_TOKEN", "Access token required."},
|
missing_token: {401, "M_MISSING_TOKEN", "Access token required."},
|
||||||
not_found: {404, "M_NOT_FOUND", "The requested resource was not found."},
|
not_found: {404, "M_NOT_FOUND", "The requested resource was not found."},
|
||||||
|
|
|
@ -25,11 +25,7 @@ defmodule MatrixServerWeb.Federation.EventController do
|
||||||
|> put_status(200)
|
|> put_status(200)
|
||||||
|> json(data)
|
|> json(data)
|
||||||
else
|
else
|
||||||
put_error(
|
put_error(conn, :unauthorized, "Origin server is not participating in room.")
|
||||||
conn,
|
|
||||||
:unauthorized,
|
|
||||||
"Origin server is not allowed to see requested event."
|
|
||||||
)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
_ ->
|
_ ->
|
||||||
|
@ -41,5 +37,44 @@ defmodule MatrixServerWeb.Federation.EventController do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def event(conn, _), do: put_error(conn, :bad_json)
|
def event(conn, _), do: put_error(conn, :missing_param)
|
||||||
|
|
||||||
|
def state(%Plug.Conn{assigns: %{origin: origin}} = conn, %{
|
||||||
|
"event_id" => event_id,
|
||||||
|
"room_id" => room_id
|
||||||
|
}) do
|
||||||
|
query =
|
||||||
|
Event
|
||||||
|
|> where([e], e.event_id == ^event_id and e.room_id == ^room_id)
|
||||||
|
|> preload(:room)
|
||||||
|
|
||||||
|
case Repo.one(query) do
|
||||||
|
%Event{room: room} = event ->
|
||||||
|
case RoomServer.get_room_server(room) do
|
||||||
|
{:ok, pid} ->
|
||||||
|
if RoomServer.server_in_room(pid, origin) do
|
||||||
|
{state_events, auth_chain} = RoomServer.get_state_at_event(pid, event)
|
||||||
|
|
||||||
|
data = %{
|
||||||
|
auth_chain: auth_chain,
|
||||||
|
pdus: state_events
|
||||||
|
}
|
||||||
|
|
||||||
|
conn
|
||||||
|
|> put_status(200)
|
||||||
|
|> json(data)
|
||||||
|
else
|
||||||
|
put_error(conn, :unauthorized, "Origin server is not participating in room.")
|
||||||
|
end
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
put_error(conn, :unknown)
|
||||||
|
end
|
||||||
|
|
||||||
|
nil ->
|
||||||
|
put_error(conn, :not_found, "Event or room not found.")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def state(conn, _), do: put_error(conn, :missing_param)
|
||||||
end
|
end
|
||||||
|
|
|
@ -66,6 +66,13 @@ defmodule MatrixServerWeb.Federation.HTTPClient do
|
||||||
Tesla.get(client, path)
|
Tesla.get(client, path)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def get_state(client, room_id, event_id) do
|
||||||
|
path =
|
||||||
|
RouteHelpers.event_path(Endpoint, :state, room_id) |> Tesla.build_url(event_id: event_id)
|
||||||
|
|
||||||
|
Tesla.get(client, path)
|
||||||
|
end
|
||||||
|
|
||||||
defp tesla_request(method, client, path, request_schema) do
|
defp tesla_request(method, client, path, request_schema) do
|
||||||
with {:ok, %Tesla.Env{body: body}} <- Tesla.request(client, url: path, method: method),
|
with {:ok, %Tesla.Env{body: body}} <- Tesla.request(client, url: path, method: method),
|
||||||
%Ecto.Changeset{valid?: true} = cs <- apply(request_schema, :changeset, [body]) do
|
%Ecto.Changeset{valid?: true} = cs <- apply(request_schema, :changeset, [body]) do
|
||||||
|
|
|
@ -65,6 +65,7 @@ defmodule MatrixServerWeb.Router do
|
||||||
scope "/v1" do
|
scope "/v1" do
|
||||||
get "/query/profile", QueryController, :profile
|
get "/query/profile", QueryController, :profile
|
||||||
get "/event/:event_id", EventController, :event
|
get "/event/:event_id", EventController, :event
|
||||||
|
get "/state/:room_id", EventController, :state
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue