diff --git a/lib/matrix_server/room_server.ex b/lib/matrix_server/room_server.ex index 6693d85..c68fef7 100644 --- a/lib/matrix_server/room_server.ex +++ b/lib/matrix_server/room_server.ex @@ -15,7 +15,7 @@ defmodule MatrixServer.RoomServer do alias MatrixServer.{Repo, Room, Event, StateResolution, Account, JoinedRoom} alias MatrixServer.StateResolution.Authorization - alias MatrixServerWeb.Client.Request.{CreateRoom, Kick} + alias MatrixServerWeb.Client.Request.{CreateRoom, Kick, Ban} @registry MatrixServer.RoomServer.Registry @supervisor MatrixServer.RoomServer.Supervisor @@ -130,6 +130,22 @@ defmodule MatrixServer.RoomServer do GenServer.call(pid, {:kick, account, request}) end + @doc """ + Ban a user from this room. + """ + @spec ban(pid(), Account.t(), Ban.t()) :: :ok | {:error, atom()} + def ban(pid, account, request) do + GenServer.call(pid, {:ban, account, request}) + end + + @doc """ + Unban a user from this room. + """ + @spec unban(pid(), Account.t(), String.t()) :: :ok | {:error, atom()} + def unban(pid, account, user_id) do + GenServer.call(pid, {:unban, account, user_id}) + end + ### Implementation @impl true @@ -268,7 +284,29 @@ defmodule MatrixServer.RoomServer do end end - @spec insert_single_event(Room.t(), t(), Event.t()) :: {:ok, t(), Room.t()} | {:error, atom()} + def handle_call( + {:ban, account, %Kick{user_id: user_id, reason: reason}}, + _from, + %{room: room, state_set: state_set} = state + ) do + ban_event = Event.ban(room, account, user_id, reason) + + case insert_single_event(room, state_set, ban_event) do + {:ok, {state_set, room}} -> {:reply, :ok, %{state | state_set: state_set, room: room}} + {:error, reason} -> {:reply, {:error, reason}, state} + end + end + + def handle_call({:unban, account, user_id},_from,%{room: room, state_set: state_set} = state) do + unban_event = Event.unban(room, account, user_id) + + case insert_single_event(room, state_set, unban_event) do + {:ok, {state_set, room}} -> {:reply, :ok, %{state | state_set: state_set, room: room}} + {:error, reason} -> {:reply, {:error, reason}, state} + end + end + + @spec insert_single_event(Room.t(), t(), Event.t()) :: {:ok, {t(), Room.t()}} | {:error, atom()} defp insert_single_event(room, state_set, event) do Repo.transaction(fn -> case finalize_and_insert_event(event, state_set, room) do diff --git a/lib/matrix_server/schema/event.ex b/lib/matrix_server/schema/event.ex index eec5e6c..3da76eb 100644 --- a/lib/matrix_server/schema/event.ex +++ b/lib/matrix_server/schema/event.ex @@ -226,6 +226,31 @@ defmodule MatrixServer.Event do } end + @spec ban(Room.t(), Account.t(), String.t(), String.t() | nil) :: t() + def ban(room, sender, user_id, reason \\ nil) do + content = %{"membership" => "ban"} + content = if reason, do: Map.put(content, "reason", reason), else: content + + %Event{ + new(room, sender) + | type: "m.room.member", + state_key: user_id, + content: content + } + end + + @spec unban(Room.t(), Account.t(), String.t()) :: t() + def unban(room, sender, user_id) do + %Event{ + new(room, sender) + | type: "m.room.member", + state_key: user_id, + content: %{ + "membership" => "leave" + } + } + end + @spec is_control_event(t()) :: boolean() def is_control_event(%Event{type: "m.room.power_levels", state_key: ""}), do: true def is_control_event(%Event{type: "m.room.join_rules", state_key: ""}), do: true diff --git a/lib/matrix_server_web/client/controllers/room_controller.ex b/lib/matrix_server_web/client/controllers/room_controller.ex index 8b0ea8d..4b68336 100644 --- a/lib/matrix_server_web/client/controllers/room_controller.ex +++ b/lib/matrix_server_web/client/controllers/room_controller.ex @@ -6,7 +6,7 @@ defmodule MatrixServerWeb.Client.RoomController do alias MatrixServer.{Repo, Room, RoomServer} alias MatrixServer.Types.UserId - alias MatrixServerWeb.Client.Request.{CreateRoom, Kick} + alias MatrixServerWeb.Client.Request.{CreateRoom, Kick, Ban} alias Ecto.Changeset alias Plug.Conn @@ -156,4 +156,55 @@ defmodule MatrixServerWeb.Client.RoomController do {:error, :not_found} -> put_error(conn, :not_found, "Room not found.") end end + + @doc """ + Ban a user in the room. + + Action for POST /_matrix/client/r0/rooms/{roomId}/ban. + """ + def ban(%Conn{assigns: %{account: account}} = conn, %{"room_id" => room_id} = params) do + with {:ok, request} <- Ban.parse(params), + {:ok, pid} <- RoomServer.get_room_server(room_id) do + case RoomServer.ban(pid, account, request) do + :ok -> + conn + |> send_resp(200, []) + |> halt() + + {:error, _} -> + put_error(conn, :unknown) + end + else + {:error, %Ecto.Changeset{}} -> put_error(conn, :bad_json) + {:error, :not_found} -> put_error(conn, :not_found, "Room not found.") + end + end + + @doc """ + Unban a user from the room. + + Action for POST /_matrix/client/r0/rooms/{roomId}/unban. + """ + def unban(%Conn{assigns: %{account: account}} = conn, %{ + "room_id" => room_id, + "user_id" => user_id + }) do + case RoomServer.get_room_server(room_id) do + {:ok, pid} -> + case RoomServer.unban(pid, account, user_id) do + :ok -> + conn + |> send_resp(200, []) + |> halt() + + {:error, _} -> + put_error(conn, :unknown) + end + + {:error, :not_found} -> + put_error(conn, :not_found, "The given room was not found.") + end + end + + def unban(conn, _), do: put_error(conn, :missing_param) end diff --git a/lib/matrix_server_web/client/request/ban.ex b/lib/matrix_server_web/client/request/ban.ex new file mode 100644 index 0000000..8906ae6 --- /dev/null +++ b/lib/matrix_server_web/client/request/ban.ex @@ -0,0 +1,20 @@ +defmodule MatrixServerWeb.Client.Request.Ban do + use MatrixServerWeb.Request + + @type t :: %__MODULE__{ + user_id: String.t(), + reason: String.t() | nil + } + + @primary_key false + embedded_schema do + field :user_id, :string + field :reason, :string + end + + def changeset(data, params) do + data + |> cast(params, [:user_id, :reason]) + |> validate_required([:user_id]) + end +end diff --git a/lib/matrix_server_web/router.ex b/lib/matrix_server_web/router.ex index 10a10e1..b7004fa 100644 --- a/lib/matrix_server_web/router.ex +++ b/lib/matrix_server_web/router.ex @@ -62,6 +62,8 @@ defmodule MatrixServerWeb.Router do post "/join", RoomController, :join post "/leave", RoomController, :leave post "/kick", RoomController, :kick + post "/ban", RoomController, :ban + post "/unban", RoomController, :unban end end end