diff --git a/lib/matrix_server.ex b/lib/matrix_server.ex index 84e8b25..e9e3e38 100644 --- a/lib/matrix_server.ex +++ b/lib/matrix_server.ex @@ -34,6 +34,7 @@ defmodule MatrixServer do {get_errcode_status(errcode), %{errcode: errcode, error: get_errcode_error(errcode)}} end + # TODO: make a plug for this? def get_errcode_error("M_BAD_JSON"), do: "Bad request." def get_errcode_error("M_USER_IN_USE"), do: "Username is already taken." def get_errcode_error("M_INVALID_USERNAME"), do: "Invalid username." diff --git a/lib/matrix_server/account.ex b/lib/matrix_server/account.ex index 95cdd91..93d6a4a 100644 --- a/lib/matrix_server/account.ex +++ b/lib/matrix_server/account.ex @@ -44,6 +44,15 @@ defmodule MatrixServer.Account do |> Multi.run(:device_with_access_token, &Device.generate_access_token/2) end + def get_by_access_token(access_token) do + from(a in Account, + join: d in assoc(a, :devices), + where: d.access_token == ^access_token, + preload: [devices: d] + ) + |> Repo.one() + end + def changeset(account, params \\ %{}) do account |> cast(params, [:localpart, :password_hash]) diff --git a/lib/matrix_server/device.ex b/lib/matrix_server/device.ex index 6c27065..607ff11 100644 --- a/lib/matrix_server/device.ex +++ b/lib/matrix_server/device.ex @@ -23,8 +23,12 @@ defmodule MatrixServer.Device do |> unique_constraint([:localpart, :device_id], name: :devices_pkey) end - def generate_access_token(repo, %{device: %Device{localpart: localpart, device_id: device_id} = device}) do - access_token = Phoenix.Token.encrypt(MatrixServerWeb.Endpoint, "access_token", {localpart, device_id}) + def generate_access_token(repo, %{ + device: %Device{localpart: localpart, device_id: device_id} = device + }) do + access_token = + Phoenix.Token.encrypt(MatrixServerWeb.Endpoint, "access_token", {localpart, device_id}) + device |> change(%{access_token: access_token}) |> repo.update() diff --git a/lib/matrix_server_web/authenticate.ex b/lib/matrix_server_web/authenticate.ex new file mode 100644 index 0000000..d517c62 --- /dev/null +++ b/lib/matrix_server_web/authenticate.ex @@ -0,0 +1,45 @@ +defmodule MatrixServerWeb.Authenticate do + import Plug.Conn + import Phoenix.Controller, only: [json: 2] + + alias MatrixServer.Account + alias Plug.Conn + + def init(options), do: options + + def call(%Conn{params: %{"access_token" => access_token}} = conn, _opts) do + authenticate(conn, access_token) + end + + def call(%Conn{req_headers: headers} = conn, _opts) do + case List.keyfind(headers, "authorization", 0) do + {_, "Bearer " <> access_token} -> + authenticate(conn, access_token) + + _ -> + data = %{errcode: "M_MISSING_TOKEN", error: "Access token missing."} + + conn + |> put_status(401) + |> json(data) + |> halt() + end + end + + defp authenticate(conn, access_token) do + case Account.get_by_access_token(access_token) do + %Account{devices: [device]} = account -> + conn + |> assign(:account, account) + |> assign(:device, device) + + nil -> + data = %{errcode: "M_UNKNOWN_TOKEN", error: "Invalid access token."} + + conn + |> put_status(401) + |> json(data) + |> halt() + end + end +end diff --git a/lib/matrix_server_web/controllers/account_controller.ex b/lib/matrix_server_web/controllers/account_controller.ex index 8b1c010..e5fe220 100644 --- a/lib/matrix_server_web/controllers/account_controller.ex +++ b/lib/matrix_server_web/controllers/account_controller.ex @@ -1,6 +1,10 @@ defmodule MatrixServerWeb.AccountController do use MatrixServerWeb, :controller + + import MatrixServer, only: [get_mxid: 1] + alias MatrixServer.Account + alias Plug.Conn def available(conn, params) do localpart = Map.get(params, "username", "") @@ -21,4 +25,12 @@ defmodule MatrixServerWeb.AccountController do |> put_status(status) |> json(data) end + + def whoami(%Conn{assigns: %{account: %Account{localpart: localpart}}} = conn, _params) do + data = %{user_id: get_mxid(localpart)} + + conn + |> put_status(200) + |> json(data) + end end diff --git a/lib/matrix_server_web/router.ex b/lib/matrix_server_web/router.ex index 4bb1044..66378bd 100644 --- a/lib/matrix_server_web/router.ex +++ b/lib/matrix_server_web/router.ex @@ -1,12 +1,17 @@ defmodule MatrixServerWeb.Router do use MatrixServerWeb, :router - pipeline :api do + pipeline :public do plug :accepts, ["json"] end + pipeline :authenticated do + plug :accepts, ["json"] + plug MatrixServerWeb.Authenticate + end + scope "/_matrix", MatrixServerWeb do - pipe_through :api + pipe_through :public scope "/client/r0", as: :client do post "/register", AuthController, :register @@ -16,4 +21,12 @@ defmodule MatrixServerWeb.Router do get "/client/versions", InfoController, :versions end + + scope "/_matrix", MatrixServerWeb do + pipe_through :authenticated + + scope "/client/r0", as: :client do + get "/account/whoami", AccountController, :whoami + end + end end diff --git a/priv/repo/migrations/20210623145023_add_devices_table.exs b/priv/repo/migrations/20210623145023_add_devices_table.exs index 631079b..cae257e 100644 --- a/priv/repo/migrations/20210623145023_add_devices_table.exs +++ b/priv/repo/migrations/20210623145023_add_devices_table.exs @@ -13,6 +13,7 @@ defmodule MatrixServer.Repo.Migrations.AddDevicesTable do null: false end + # Compound primary already indexes device_id. create index(:devices, [:localpart]) end end diff --git a/priv/repo/migrations/20210625205812_add_access_token_index_to_devices.exs b/priv/repo/migrations/20210625205812_add_access_token_index_to_devices.exs new file mode 100644 index 0000000..7242c51 --- /dev/null +++ b/priv/repo/migrations/20210625205812_add_access_token_index_to_devices.exs @@ -0,0 +1,7 @@ +defmodule MatrixServer.Repo.Migrations.AddAccessTokenIndexToDevices do + use Ecto.Migration + + def change do + create index(:devices, [:access_token], unique: true) + end +end