Implement client invite endpoint

Refactor room server to automatically determine auth events
Add documentation in various places
This commit is contained in:
Pim Kunis 2021-08-25 01:27:03 +02:00
parent 6f8c224d50
commit bcc6cbb24b
15 changed files with 345 additions and 209 deletions

View file

@ -6,12 +6,15 @@ defmodule MatrixServer.RoomServer do
The RoomServers are supervised by a DynamicSupervisor RoomServer.Supervisor.
"""
@typep t :: map()
use GenServer
import Ecto.Query
import Ecto.Changeset
alias MatrixServer.{Repo, Room, Event, StateResolution, JoinedRoom, Account}
alias MatrixServer.{Repo, Room, Event, StateResolution, Account, JoinedRoom}
alias MatrixServer.Types.UserId
alias MatrixServer.StateResolution.Authorization
alias MatrixServerWeb.Client.Request.CreateRoom
@ -35,7 +38,7 @@ defmodule MatrixServer.RoomServer do
def get_room_server(room_id) do
# TODO: Might be wise to use a transaction here to prevent race conditions.
case Repo.one(from r in Room, where: r.id == ^room_id) do
%Room{state: serialized_state_set} ->
%Room{state: serialized_state_set} = room ->
case Registry.lookup(@registry, room_id) do
[{pid, _}] ->
{:ok, pid}
@ -43,7 +46,7 @@ defmodule MatrixServer.RoomServer do
[] ->
opts = [
name: {:via, Registry, {@registry, room_id}},
room_id: room_id,
room: room,
serialized_state_set: serialized_state_set
]
@ -62,11 +65,7 @@ defmodule MatrixServer.RoomServer do
Events are inserted into the new room depending on the input `input` and according
to the [Matrix documentation](https://matrix.org/docs/spec/client_server/r0.6.1#post-matrix-client-r0-createroom).
"""
@spec create_room(
pid(),
MatrixServer.Account.t(),
MatrixServerWeb.Client.Request.CreateRoom.t()
) :: {:ok, String.t()} | {:error, atom()}
@spec create_room(pid(), Account.t(), CreateRoom.t()) :: {:ok, String.t()} | {:error, atom()}
def create_room(pid, account, input) do
GenServer.call(pid, {:create_room, account, input})
end
@ -100,11 +99,19 @@ defmodule MatrixServer.RoomServer do
GenServer.call(pid, {:get_state_ids_at_event, event})
end
@doc """
Invite the a user to this room.
"""
@spec invite(pid(), Account.t(), String.t()) :: :ok | {:error, atom()}
def invite(pid, account, user_id) do
GenServer.call(pid, {:invite, account, user_id})
end
### Implementation
@impl true
def init(opts) do
room_id = Keyword.fetch!(opts, :room_id)
room = Keyword.fetch!(opts, :room)
serialized_state_set = Keyword.fetch!(opts, :serialized_state_set)
state_event_ids = Enum.map(serialized_state_set, fn [_, _, event_id] -> event_id end)
@ -116,14 +123,16 @@ defmodule MatrixServer.RoomServer do
{{type, state_key}, event}
end)
{:ok, %{room_id: room_id, state_set: state_set}}
{:ok, %{room: room, state_set: state_set}}
end
@impl true
def handle_call({:create_room, account, input}, _from, %{room_id: room_id} = state) do
def handle_call(
{:create_room, account, input},
_from,
%{room: %Room{id: room_id} = room} = state
) do
# TODO: power_level_content_override, initial_state, invite, invite_3pid
room = Repo.one!(from r in Room, where: r.id == ^room_id)
case Repo.transaction(create_room_insert_events(room, account, input)) do
{:ok, state_set} -> {:reply, {:ok, room_id}, %{state | state_set: state_set}}
{:error, reason} -> {:reply, {:error, reason}, state}
@ -184,76 +193,95 @@ defmodule MatrixServer.RoomServer do
{:reply, {state_events, auth_chain}, state}
end
def handle_call({:invite, account, user_id}, _from, %{room: room, state_set: state_set} = state) do
case Repo.transaction(invite_insert_event(room, state_set, account, user_id)) do
{:ok, state_set} -> {:reply, :ok, %{state | state_set: state_set}}
{:error, reason} -> {:reply, {:error, reason}, state}
end
end
@spec invite_insert_event(Room.t(), t(), Account.t(), String.t()) ::
(() -> {:ok, t()} | {:error, atom()})
defp invite_insert_event(room, state_set, account, user_id) do
invite_event = Event.invite(room, account, user_id)
fn ->
case finalize_and_insert_event(invite_event, state_set, room) do
{:ok, state_set, room} ->
_ = update_room_state_set(room, state_set)
state_set
{:error, reason} ->
Repo.rollback(reason)
end
end
end
@spec create_room_insert_events(Room.t(), Account.t(), CreateRoom.t()) ::
(() -> {:ok, t()} | {:error, atom()})
defp create_room_insert_events(room, account, %CreateRoom{
room_version: room_version,
preset: preset,
name: name,
topic: topic
}) do
events =
([
Event.create_room(room, account, room_version),
Event.join(room, account),
Event.power_levels(room, account)
] ++
room_creation_preset(account, preset, room) ++
[
if(name, do: Event.name(room, account, name)),
if(topic, do: Event.topic(room, account, topic))
])
|> Enum.reject(&Kernel.is_nil/1)
fn ->
state_set = %{}
result =
Enum.reduce_while(events, {%{}, room}, fn event, {state_set, room} ->
case finalize_and_insert_event(event, state_set, room) do
{:ok, state_set, room} -> {:cont, {state_set, room}}
{:error, reason} -> {:halt, {:error, reason}}
end
end)
with create_room <- Event.create_room(room, account, room_version),
{:ok, state_set, create_room, room} <-
verify_and_insert_event(create_room, state_set, room),
join_creator <- Event.join(room, account, [create_room]),
{:ok, state_set, join_creator, 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]),
{:ok, state_set, pls, room} <- verify_and_insert_event(pls, state_set, room) do
auth_events = [create_room, join_creator, pls]
preset_events = room_creation_preset(account, preset, room, auth_events)
name_event = if name, do: Event.name(room, account, name, auth_events)
topic_event = if topic, do: Event.topic(room, account, topic, auth_events)
case result do
{:error, reason} ->
Repo.rollback(reason)
remaining_events =
Enum.reject(preset_events ++ [name_event, topic_event], &Kernel.is_nil/1)
result =
Enum.reduce_while(remaining_events, {state_set, room}, fn event, {state_set, room} ->
case verify_and_insert_event(event, state_set, room) do
{:ok, state_set, _event, room} -> {:cont, {state_set, room}}
{:error, reason} -> {:halt, {:error, reason}}
end
end)
case result do
{:error, reason} ->
Repo.rollback(reason)
{state_set, room} ->
serialized_state_set =
Enum.map(state_set, fn {{type, state_key}, event} ->
[type, state_key, event.event_id]
end)
Repo.update!(change(room, state: serialized_state_set))
state_set
end
else
_ -> Repo.rollback(:event_creation)
{state_set, room} ->
_ = update_room_state_set(room, state_set)
state_set
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})
@spec update_room_state_set(Room.t(), t()) :: Room.t()
defp update_room_state_set(room, state_set) do
serialized_state_set =
Enum.map(state_set, fn {{type, state_key}, event} ->
[type, state_key, event.event_id]
end)
Repo.update!(change(room, state: serialized_state_set))
end
# TODO: trusted_private_chat:
# 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
@spec room_creation_preset(Account.t(), String.t() | nil, Room.t()) :: [Event.t()]
defp room_creation_preset(account, nil, %Room{visibility: visibility} = room) do
preset =
case visibility do
:public -> "public_chat"
:private -> "private_chat"
end
room_creation_preset(account, preset, room, auth_events)
room_creation_preset(account, preset, room)
end
defp room_creation_preset(account, preset, room, auth_events) do
defp room_creation_preset(account, preset, room) do
{join_rule, his_vis, guest_access} =
case preset do
"private_chat" -> {"invite", "shared", "can_join"}
@ -262,17 +290,80 @@ defmodule MatrixServer.RoomServer do
end
[
Event.join_rules(room, account, join_rule, auth_events),
Event.history_visibility(room, account, his_vis, auth_events),
Event.guest_access(room, account, guest_access, auth_events)
Event.join_rules(room, account, join_rule),
Event.history_visibility(room, account, his_vis),
Event.guest_access(room, account, guest_access)
]
end
defp verify_and_insert_event(
@spec finalize_and_insert_event(Event.t(), t(), Room.t()) ::
{:ok, t(), Room.t()} | {:error, atom()}
defp finalize_and_insert_event(
event,
current_state_set,
state_set,
%Room{forward_extremities: forward_extremities} = room
) do
event =
event
|> Map.put(:auth_events, auth_events_for_event(event, state_set))
|> Map.put(:prev_events, forward_extremities)
case Event.post_process(event) do
{:ok, event} -> verify_and_insert_event(event, state_set, room)
_ -> {:error, :event_creation}
end
end
@spec auth_events_for_event(Event.t(), t()) :: [{String.t(), String.t()}]
defp auth_events_for_event(%Event{type: "m.room.create"}, _), do: []
defp auth_events_for_event(
%Event{sender: sender} = event,
state_set
) do
state_pairs =
[{"m.room.create", ""}, {"m.room.power_levels", ""}, {"m.room.member", to_string(sender)}] ++
auth_events_for_member_event(event)
state_set
|> Map.take(state_pairs)
|> Map.values()
|> Enum.map(& &1.event_id)
end
@spec auth_events_for_member_event(Event.t()) :: [{String.t(), String.t()}]
defp auth_events_for_member_event(
%Event{
type: "m.room.member",
state_key: state_key,
content: %{"membership" => membership}
} = event
) do
[
{"m.room.member", state_key},
if(membership in ["join", "invite"], do: {"m.room.join_rules", ""}),
third_party_invite_state_pair(event)
]
|> Enum.reject(&Kernel.is_nil/1)
end
defp auth_events_for_member_event(_), do: []
@spec third_party_invite_state_pair(Event.t()) :: {String.t(), String.t()} | nil
defp third_party_invite_state_pair(%Event{
content: %{
"membership" => "invite",
"third_party_invite" => %{"signed" => %{"token" => token}}
}
}) do
{"m.room.third_party_invite", token}
end
defp third_party_invite_state_pair(_), do: nil
@spec verify_and_insert_event(Event.t(), t(), Room.t()) ::
{:ok, t(), Room.t()} | {:error, atom()}
defp verify_and_insert_event(event, current_state_set, room) do
# TODO: Correct error values.
# Check the following things:
# 1. TODO: Is a valid event, otherwise it is dropped.
@ -281,10 +372,7 @@ defmodule MatrixServer.RoomServer do
# 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.
# 6. Passes authorization rules based on the current state of the room, otherwise it is "soft failed".
event = %Event{event | prev_events: forward_extremities}
with {:ok, event} <- Event.post_process(event),
true <- Event.prevalidate(event),
with true <- Event.prevalidate(event),
true <- Authorization.authorized_by_auth_events?(event),
state_set <- StateResolution.resolve(event, false),
true <- Authorization.authorized?(event, state_set),
@ -292,9 +380,28 @@ defmodule MatrixServer.RoomServer do
room = Room.update_forward_extremities(event, room)
event = Repo.insert!(event)
state_set = StateResolution.resolve_forward_extremities(event)
{:ok, state_set, event, room}
_ = update_joined_rooms(event, room)
{:ok, state_set, room}
else
_ -> {:error, :authorization}
end
end
@spec update_joined_rooms(Event.t(), Room.t()) :: JoinedRoom.t() | nil
defp update_joined_rooms(
%Event{
type: "m.room.member",
sender: %UserId{localpart: localpart, domain: domain},
content: %{"membership" => "join"}
},
%Room{id: room_id}
) do
# TODO: Also remove joined rooms.
if domain == MatrixServer.server_name() do
Repo.insert(%JoinedRoom{localpart: localpart, room_id: room_id})
end
end
defp update_joined_rooms(_, _), do: nil
end

View file

@ -74,11 +74,7 @@ defmodule MatrixServer.Event do
end
@spec create_room(Room.t(), Account.t(), String.t()) :: t()
def create_room(
room,
%Account{localpart: localpart} = creator,
room_version
) do
def create_room(room, %Account{localpart: localpart} = creator, room_version) do
mxid = MatrixServer.get_mxid(localpart)
%Event{
@ -92,8 +88,8 @@ defmodule MatrixServer.Event do
}
end
@spec join(Room.t(), Account.t(), [t()]) :: t()
def join(room, %Account{localpart: localpart} = sender, auth_events) do
@spec join(Room.t(), Account.t()) :: t()
def join(room, %Account{localpart: localpart} = sender) do
mxid = MatrixServer.get_mxid(localpart)
%Event{
@ -102,17 +98,12 @@ defmodule MatrixServer.Event do
state_key: mxid,
content: %{
"membership" => "join"
},
auth_events: Enum.map(auth_events, & &1.event_id)
}
}
end
@spec power_levels(Room.t(), Account.t(), [t()]) :: t()
def power_levels(
room,
%Account{localpart: localpart} = sender,
auth_events
) do
@spec power_levels(Room.t(), Account.t()) :: t()
def power_levels(room, %Account{localpart: localpart} = sender) do
mxid = MatrixServer.get_mxid(localpart)
%Event{
@ -134,73 +125,79 @@ defmodule MatrixServer.Event do
"notifications" => %{
"room" => 50
}
},
auth_events: Enum.map(auth_events, & &1.event_id)
}
}
end
@spec name(Room.t(), Account.t(), String.t(), [t()]) :: %Event{}
def name(room, sender, name, auth_events) do
@spec name(Room.t(), Account.t(), String.t()) :: %Event{}
def name(room, sender, name) do
%Event{
new(room, sender)
| type: "m.room.name",
state_key: "",
content: %{
"name" => name
},
auth_events: Enum.map(auth_events, & &1.event_id)
}
}
end
@spec topic(Room.t(), Account.t(), String.t(), [t()]) :: t()
def topic(room, sender, topic, auth_events) do
@spec topic(Room.t(), Account.t(), String.t()) :: t()
def topic(room, sender, topic) do
%Event{
new(room, sender)
| type: "m.room.topic",
state_key: "",
content: %{
"topic" => topic
},
auth_events: Enum.map(auth_events, & &1.event_id)
}
}
end
@spec join_rules(Room.t(), Account.t(), String.t(), [t()]) :: t()
def join_rules(room, sender, join_rule, auth_events) do
@spec join_rules(Room.t(), Account.t(), String.t()) :: t()
def join_rules(room, sender, join_rule) do
%Event{
new(room, sender)
| type: "m.room.join_rules",
state_key: "",
content: %{
"join_rule" => join_rule
},
auth_events: Enum.map(auth_events, & &1.event_id)
}
}
end
@spec history_visibility(Room.t(), Account.t(), String.t(), [t()]) :: t()
def history_visibility(room, sender, history_visibility, auth_events) do
@spec history_visibility(Room.t(), Account.t(), String.t()) :: t()
def history_visibility(room, sender, history_visibility) do
%Event{
new(room, sender)
| type: "m.room.history_visibility",
state_key: "",
content: %{
"history_visibility" => history_visibility
},
auth_events: Enum.map(auth_events, & &1.event_id)
}
}
end
@spec guest_access(Room.t(), Account.t(), String.t(), [t()]) :: t()
def guest_access(room, sender, guest_access, auth_events) do
@spec guest_access(Room.t(), Account.t(), String.t()) :: t()
def guest_access(room, sender, guest_access) do
%Event{
new(room, sender)
| type: "m.room.guest_access",
state_key: "",
content: %{
"guest_access" => guest_access
},
auth_events: Enum.map(auth_events, & &1.event_id)
}
}
end
@spec invite(Room.t(), Account.t(), String.t()) :: t()
def invite(room, sender, user_id) do
%Event{
new(room, sender)
| type: "m.room.member",
state_key: user_id,
content: %{
"membership" => "invite"
}
}
end

View file

@ -3,6 +3,11 @@ defmodule MatrixServer.JoinedRoom do
alias MatrixServer.{Account, Room}
@type t :: %__MODULE__{
localpart: String.t(),
room_id: String.t()
}
@primary_key false
schema "joined_rooms" do
belongs_to :account, Account,

View file

@ -20,9 +20,12 @@ defmodule MatrixServer.StateResolution.Authorization do
do: prev_events == []
# Check rule: 5.2.1
def authorized?(%Event{type: "m.room.member", state_key: state_key}, %{
{"m.room.create", ""} => %Event{content: %{"creator" => creator}}
}),
def authorized?(
%Event{type: "m.room.member", state_key: state_key, prev_events: [create_id]},
%{
{"m.room.create", ""} => %Event{event_id: create_id, content: %{"creator" => creator}}
}
),
do: state_key == creator
def authorized?(
@ -309,6 +312,9 @@ defmodule MatrixServer.StateResolution.Authorization do
|> Repo.all()
|> Enum.reduce(%{}, &update_state_set/2)
IO.inspect(event)
IO.inspect(state_set)
authorized?(event, state_set)
end
end

View file

@ -7,6 +7,11 @@ defmodule MatrixServerWeb.Client.AccountController do
alias MatrixServer.{Account, Repo}
alias Plug.Conn
@doc """
Checks to see if a username is available, and valid, for the server.
Action for GET /_matrix/client/r0/register/available.
"""
def available(conn, params) do
localpart = Map.get(params, "username", "")
@ -21,6 +26,11 @@ defmodule MatrixServerWeb.Client.AccountController do
end
end
@doc """
Gets information about the owner of a given access token.
Action for GET /_matrix/client/r0/account/whoami.
"""
def whoami(%Conn{assigns: %{account: %Account{localpart: localpart}}} = conn, _params) do
data = %{user_id: get_mxid(localpart)}
@ -29,6 +39,11 @@ defmodule MatrixServerWeb.Client.AccountController do
|> json(data)
end
@doc """
Invalidates an existing access token, so that it can no longer be used for authorization.
Action for POST /_matrix/client/r0/logout.
"""
def logout(%Conn{assigns: %{device: device}} = conn, _params) do
case Repo.delete(device) do
{:ok, _} ->
@ -41,6 +56,12 @@ defmodule MatrixServerWeb.Client.AccountController do
end
end
@doc """
Invalidates all access tokens for a user, so that they can no longer be used
for authorization.
Action for POST /_matrix/client/r0/logout/all.
"""
def logout_all(%Conn{assigns: %{account: account}} = conn, _params) do
Repo.delete_all(Ecto.assoc(account, :devices))

View file

@ -5,6 +5,11 @@ defmodule MatrixServerWeb.Client.AliasesController do
alias MatrixServer.Alias
@doc """
Create a new mapping from room alias to room ID.
Action for PUT /_matrix/client/r0/directory/room/{roomAlias}.
"""
def create(conn, %{"alias" => alias, "room_id" => room_id}) do
case Alias.create(alias, room_id) do
{:ok, _} ->

View file

@ -5,6 +5,11 @@ defmodule MatrixServerWeb.Client.InfoController do
@supported_versions ["r0.6.1"]
@doc """
Gets the versions of the specification supported by the server.
Action for GET /_matrix/client/versions.
"""
def versions(conn, _params) do
data = %{versions: @supported_versions}

View file

@ -10,6 +10,11 @@ defmodule MatrixServerWeb.Client.LoginController do
@login_type "m.login.password"
@doc """
Gets the homeserver's supported login types to authenticate users.
Action for GET /_matrix/client/r0/login.
"""
def login_types(conn, _params) do
data = %{flows: [%{type: @login_type}]}
@ -18,6 +23,12 @@ defmodule MatrixServerWeb.Client.LoginController do
|> json(data)
end
@doc """
Authenticates the user, and issues an access token they can use to
authorize themself in subsequent requests.
Action for POST /_matrix/client/r0/login.
"""
def login(
conn,
%{"type" => @login_type, "identifier" => %{"type" => "m.id.user"}} = params

View file

@ -10,6 +10,11 @@ defmodule MatrixServerWeb.Client.RegisterController do
@register_type "m.login.dummy"
@doc """
Register for an account on this homeserver.
Action for POST /_matrix/client/r0/register.
"""
def register(conn, %{"auth" => %{"type" => @register_type}} = params) do
case Register.changeset(params) do
%Changeset{valid?: true} = cs ->

View file

@ -4,11 +4,17 @@ defmodule MatrixServerWeb.Client.RoomController do
import MatrixServerWeb.Error
import Ecto.{Changeset, Query}
alias MatrixServer.{Repo, Room}
alias MatrixServer.{Repo, Room, RoomServer}
alias MatrixServer.Types.UserId
alias MatrixServerWeb.Client.Request.CreateRoom
alias Ecto.Changeset
alias Plug.Conn
@doc """
Create a new room with various configuration options.
Action for POST /_matrix/client/r0/createRoom.
"""
def create(%Conn{assigns: %{account: account}} = conn, params) do
case CreateRoom.changeset(params) do
%Changeset{valid?: true} = cs ->
@ -32,11 +38,17 @@ defmodule MatrixServerWeb.Client.RoomController do
end
end
@doc """
This API returns a list of the user's current rooms.
Action for GET /_matrix/client/r0/joined_rooms.
"""
def joined_rooms(%Conn{assigns: %{account: account}} = conn, _params) do
joined_room_ids = account
|> Ecto.assoc(:joined_rooms)
|> select([jr], jr.id)
|> Repo.all()
joined_room_ids =
account
|> Ecto.assoc(:joined_rooms)
|> select([jr], jr.id)
|> Repo.all()
data = %{
joined_rooms: joined_room_ids
@ -46,4 +58,32 @@ defmodule MatrixServerWeb.Client.RoomController do
|> put_status(200)
|> json(data)
end
@doc """
This API invites a user to participate in a particular room.
Action for POST /_matrix/client/r0/rooms/{roomId}/invite.
"""
def invite(%Conn{assigns: %{account: account}} = conn, %{
"room_id" => room_id,
"user_id" => user_id
}) do
with {:ok, _} <- UserId.cast(user_id),
{:ok, pid} <- RoomServer.get_room_server(room_id) do
case RoomServer.invite(pid, account, user_id) do
:ok ->
conn
|> send_resp(200, [])
|> halt()
{:error, _} ->
put_error(conn, :unknown)
end
else
:error -> put_error(conn, :invalid_param, "Given user ID is invalid.")
{:error, :not_found} -> put_error(conn, :not_found, "The given room was not found.")
end
end
def invite(conn, _), do: put_error(conn, :missing_param)
end

View file

@ -8,6 +8,11 @@ defmodule MatrixServerWeb.Federation.EventController do
alias MatrixServer.{Repo, Event, RoomServer}
alias MatrixServerWeb.Federation.Transaction
@doc """
Retrieves a single event.
Action for GET /_matrix/federation/v1/event/{eventId}.
"""
def event(%Plug.Conn{assigns: %{origin: origin}} = conn, %{"event_id" => event_id}) do
query =
Event
@ -39,6 +44,11 @@ defmodule MatrixServerWeb.Federation.EventController do
def event(conn, _), do: put_error(conn, :missing_param)
@doc """
Retrieves a snapshot of a room's state at a given event.
Action for GET /_matrix/federation/v1/state/{roomId}.
"""
def state(%Plug.Conn{assigns: %{origin: origin}} = conn, %{
"event_id" => event_id,
"room_id" => room_id
@ -48,6 +58,11 @@ defmodule MatrixServerWeb.Federation.EventController do
def state(conn, _), do: put_error(conn, :missing_param)
@doc """
Retrieves a snapshot of a room's state at a given event, in the form of event IDs.
Action for GET /_matrix/federation/v1/state_ids/{roomId}.
"""
def state_ids(%Plug.Conn{assigns: %{origin: origin}} = conn, %{
"event_id" => event_id,
"room_id" => room_id

View file

@ -5,6 +5,11 @@ defmodule MatrixServerWeb.Federation.KeyController do
alias MatrixServer.KeyServer
@doc """
Gets the homeserver's published signing keys.
Action for GET /_matrix/key/v2/server/{keyId}.
"""
def get_signing_keys(conn, _params) do
keys =
KeyServer.get_own_signing_keys()

View file

@ -31,6 +31,12 @@ defmodule MatrixServerWeb.Federation.QueryController do
end
end
@doc """
Performs a query to get profile information, such as a display name or avatar,
for a given user.
Action for GET /_matrix/federation/v1/query/profile.
"""
def profile(conn, params) do
with {:ok, %ProfileRequest{user_id: %UserId{localpart: localpart, domain: domain}}} <-
ProfileRequest.validate(params) do

View file

@ -56,6 +56,10 @@ defmodule MatrixServerWeb.Router do
scope "/directory/room" do
put "/:alias", AliasesController, :create
end
scope "/rooms/:room_id" do
post "/invite", RoomController, :invite
end
end
end

View file

@ -12,99 +12,3 @@ Repo.insert(%Device{
display_name: "My Android",
localpart: "chuck"
})
# 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",
visibility: :public
})
create_room =
Repo.insert!(
Event.create_room(room, alice, "v1", false)
|> Map.put(:origin_server_ts, timestamp.(0))
|> Event.post_process()
|> elem(1)
)
Repo.insert!(
Event.join(room, alice, false)
|> Map.put(:prev_events, ["create"])
|> Map.put(:auth_events, ["create"])
|> Map.put(:origin_server_ts, timestamp.(1))
|> Map.put(:event_id, "join_alice")
)
Repo.insert!(
Event.join(room, bob)
|> elem(1)
|> Map.put(:prev_events, ["join_alice"])
|> Map.put(:auth_events, ["create"])
|> Map.put(:origin_server_ts, timestamp.(2))
|> Map.put(:event_id, "join_bob")
)
Repo.insert!(
Event.join(room, charlie)
|> elem(1)
|> Map.put(:prev_events, ["join_bob"])
|> Map.put(:auth_events, ["create"])
|> Map.put(:origin_server_ts, timestamp.(3))
|> Map.put(:event_id, "join_charlie")
)
%Event{content: content} = event = Event.power_levels(room, alice) |> elem(1)
event = %Event{event | content: %{content | "users" => %{"alice" => 100, "bob" => 100}}}
Repo.insert!(
event
|> Map.put(:prev_events, ["join_alice"])
|> Map.put(:auth_events, ["create", "join_alice"])
|> Map.put(:origin_server_ts, timestamp.(4))
|> Map.put(:event_id, "a")
)
%Event{content: content} = event = Event.power_levels(room, bob) |> elem(1)
event = %Event{
event
| content: %{content | "users" => %{"alice" => 100, "bob" => 100, "charlie" => 100}}
}
Repo.insert!(
event
|> Map.put(:prev_events, ["a"])
|> Map.put(:auth_events, ["create", "join_bob", "a"])
|> Map.put(:origin_server_ts, timestamp.(5))
|> Map.put(:event_id, "b")
)
Repo.insert!(
Event.topic(room, alice, "sneed")
|> elem(1)
|> Map.put(:prev_events, ["a"])
|> Map.put(:auth_events, ["create", "join_alice", "a"])
|> Map.put(:origin_server_ts, timestamp.(5))
|> Map.put(:event_id, "fork")
)