Add migration and schema for accounts

Add account name availability endpoint
This commit is contained in:
Pim Kunis 2021-06-22 23:04:37 +02:00
parent e6e4472b94
commit 6e039524a4
10 changed files with 107 additions and 43 deletions

View file

@ -26,6 +26,8 @@ config :logger, :console,
# Use Jason for JSON parsing in Phoenix
config :phoenix, :json_library, Jason
config :matrix_server, MatrixServer.Repo, migration_timestamps: [type: :utc_datetime]
# Import environment specific config. This must remain at the bottom
# of this file so it overrides the configuration defined above.
import_config "#{Mix.env()}.exs"

View file

@ -2,8 +2,8 @@ use Mix.Config
# Configure your database
config :matrix_server, MatrixServer.Repo,
username: "postgres",
password: "postgres",
username: "matrix_server",
password: "matrix_server",
database: "matrix_server_dev",
hostname: "localhost",
show_sensitive_data_on_connection_error: true,
@ -55,3 +55,5 @@ config :phoenix, :stacktrace_depth, 20
# Initialize plugs at runtime for faster development compilation
config :phoenix, :plug_init_mode, :runtime
config :matrix_server, :server_name, "localhost"

View file

@ -0,0 +1,51 @@
defmodule MatrixServer.Account do
use Ecto.Schema
import Ecto.{Changeset, Query}
alias MatrixServer.{Repo, Account}
@max_mxid_length 255
@localpart_regex ~r/^([a-z0-9\._=\/])+$/
@primary_key {:localpart, :string, []}
schema "accounts" do
field :password_hash, :string, redact: true
timestamps(updated_at: false)
end
def available?(localpart) when is_binary(localpart) do
if Regex.match?(@localpart_regex, localpart) and
String.length(localpart) <= localpart_length() do
if Repo.one!(
Account
|> where([a], a.localpart == ^localpart)
|> select([a], count(a))
) == 0 do
:ok
else
{:error, :user_in_use}
end
else
{:error, :invalid_username}
end
end
def changeset(%Account{} = account, attrs) do
account
|> cast(attrs, [:localpart, :password_hash])
|> validate_required([:localpart, :password_hash])
|> validate_length(:password_hash, max: 60)
|> validate_format(:localpart, @localpart_regex)
|> validate_length(:localpart, max: localpart_length())
|> unique_constraint(:localpart, name: :accounts_pkey)
end
defp localpart_length do
# Subtract the "@" and ":" in the MXID.
@max_mxid_length - 2 - String.length(server_name())
end
defp server_name do
Application.get_env(:matrix_server, :server_name)
end
end

View file

@ -1,35 +0,0 @@
defmodule MatrixServerWeb.UserSocket do
use Phoenix.Socket
## Channels
# channel "room:*", MatrixServerWeb.RoomChannel
# Socket params are passed from the client and can
# be used to verify and authenticate a user. After
# verification, you can put default assigns into
# the socket that will be set for all channels, ie
#
# {:ok, assign(socket, :user_id, verified_user_id)}
#
# To deny connection, return `:error`.
#
# See `Phoenix.Token` documentation for examples in
# performing token verification on connect.
@impl true
def connect(_params, socket, _connect_info) do
{:ok, socket}
end
# Socket id's are topics that allow you to identify all sockets for a given user:
#
# def id(socket), do: "user_socket:#{socket.assigns.user_id}"
#
# Would allow you to broadcast a "disconnect" event and terminate
# all active sockets and channels for a given user:
#
# MatrixServerWeb.Endpoint.broadcast("user_socket:#{user.id}", "disconnect", %{})
#
# Returning `nil` makes this socket anonymous.
@impl true
def id(_socket), do: nil
end

View file

@ -0,0 +1,28 @@
defmodule MatrixServerWeb.AccountController do
use MatrixServerWeb, :controller
alias MatrixServer.Account
def register(conn, _params) do
conn
end
def available(conn, params) do
localpart = Map.get(params, "username", "")
{status, data} =
case Account.available?(localpart) do
:ok ->
{200, %{available: true}}
{:error, :user_in_use} ->
{400, %{errcode: "M_USER_IN_USE", error: "Desired user ID is already taken."}}
{:error, :invalid_username} ->
{400, %{errocode: "M_INVALID_USERNAME", error: "Desired user ID is invalid."}}
end
conn
|> put_status(status)
|> json(data)
end
end

View file

@ -10,10 +10,6 @@ defmodule MatrixServerWeb.Endpoint do
signing_salt: "IGPHtnAo"
]
socket "/socket", MatrixServerWeb.UserSocket,
websocket: true,
longpoll: false
# Serve at "/" the static files from "priv/static" directory.
#
# You should set gzip to true if you are running phx.digest

View file

@ -5,7 +5,12 @@ defmodule MatrixServerWeb.Router do
plug :accepts, ["json"]
end
scope "/api", MatrixServerWeb do
scope "/_matrix", MatrixServerWeb do
pipe_through :api
scope "/client/r0", as: :client do
post "/register", AccountController, :register
get "/register/available", AccountController, :available
end
end
end

View file

@ -40,7 +40,8 @@ defmodule MatrixServer.MixProject do
{:telemetry_metrics, "~> 0.4"},
{:telemetry_poller, "~> 0.4"},
{:jason, "~> 1.0"},
{:plug_cowboy, "~> 2.0"}
{:plug_cowboy, "~> 2.0"},
{:bcrypt_elixir, "~> 2.3"}
]
end

View file

@ -1,4 +1,6 @@
%{
"bcrypt_elixir": {:hex, :bcrypt_elixir, "2.3.0", "6cb662d5c1b0a8858801cf20997bd006e7016aa8c52959c9ef80e0f34fb60b7a", [:make, :mix], [{:comeonin, "~> 5.3", [hex: :comeonin, repo: "hexpm", optional: false]}, {:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "2c81d61d4f6ed0e5cf7bf27a9109b791ff216a1034b3d541327484f46dd43769"},
"comeonin": {:hex, :comeonin, "5.3.2", "5c2f893d05c56ae3f5e24c1b983c2d5dfb88c6d979c9287a76a7feb1e1d8d646", [:mix], [], "hexpm", "d0993402844c49539aeadb3fe46a3c9bd190f1ecf86b6f9ebd71957534c95f04"},
"connection": {:hex, :connection, "1.1.0", "ff2a49c4b75b6fb3e674bfc5536451607270aac754ffd1bdfe175abe4a6d7a68", [:mix], [], "hexpm", "722c1eb0a418fbe91ba7bd59a47e28008a189d47e37e0e7bb85585a016b2869c"},
"cowboy": {:hex, :cowboy, "2.9.0", "865dd8b6607e14cf03282e10e934023a1bd8be6f6bacf921a7e2a96d800cd452", [:make, :rebar3], [{:cowlib, "2.11.0", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "1.8.0", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "2c729f934b4e1aa149aff882f57c6372c15399a20d54f65c8d67bef583021bde"},
"cowboy_telemetry": {:hex, :cowboy_telemetry, "0.3.1", "ebd1a1d7aff97f27c66654e78ece187abdc646992714164380d8a041eda16754", [:rebar3], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "3a6efd3366130eab84ca372cbd4a7d3c3a97bdfcfb4911233b035d117063f0af"},
@ -7,6 +9,7 @@
"decimal": {:hex, :decimal, "2.0.0", "a78296e617b0f5dd4c6caf57c714431347912ffb1d0842e998e9792b5642d697", [:mix], [], "hexpm", "34666e9c55dea81013e77d9d87370fe6cb6291d1ef32f46a1600230b1d44f577"},
"ecto": {:hex, :ecto, "3.6.2", "efdf52acfc4ce29249bab5417415bd50abd62db7b0603b8bab0d7b996548c2bc", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "efad6dfb04e6f986b8a3047822b0f826d9affe8e4ebdd2aeedbfcb14fd48884e"},
"ecto_sql": {:hex, :ecto_sql, "3.6.2", "9526b5f691701a5181427634c30655ac33d11e17e4069eff3ae1176c764e0ba3", [:mix], [{:db_connection, "~> 2.2", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.6.2", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.4.0 or ~> 0.5.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.15.0 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.1", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "5ec9d7e6f742ea39b63aceaea9ac1d1773d574ea40df5a53ef8afbd9242fdb6b"},
"elixir_make": {:hex, :elixir_make, "0.6.2", "7dffacd77dec4c37b39af867cedaabb0b59f6a871f89722c25b28fcd4bd70530", [:mix], [], "hexpm", "03e49eadda22526a7e5279d53321d1cced6552f344ba4e03e619063de75348d9"},
"jason": {:hex, :jason, "1.2.2", "ba43e3f2709fd1aa1dce90aaabfd039d000469c05c56f0b8e31978e03fa39052", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "18a228f5f0058ee183f29f9eae0805c6e59d61c3b006760668d8d18ff0d12179"},
"mime": {:hex, :mime, "1.6.0", "dabde576a497cef4bbdd60aceee8160e02a6c89250d6c0b29e56c0dfb00db3d2", [:mix], [], "hexpm", "31a1a8613f8321143dde1dafc36006a17d28d02bdfecb9e95a880fa7aabd19a7"},
"phoenix": {:hex, :phoenix, "1.5.9", "a6368d36cfd59d917b37c44386e01315bc89f7609a10a45a22f47c007edf2597", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_html, "~> 2.13 or ~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 2.0", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:plug, "~> 1.10", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 1.0 or ~> 2.2", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:plug_crypto, "~> 1.1.2 or ~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "7e4bce20a67c012f1fbb0af90e5da49fa7bf0d34e3a067795703b74aef75427d"},

View file

@ -0,0 +1,11 @@
defmodule MatrixServer.Repo.Migrations.AddAccountsTable do
use Ecto.Migration
def change do
create table(:accounts, primary_key: false) do
add :localpart, :string, primary_key: true, null: false
add :password_hash, :string, size: 60, null: false
timestamps(updated_at: false)
end
end
end