Add part of room creation endpoint
This commit is contained in:
parent
585de861d6
commit
9be94751dc
12 changed files with 168 additions and 48 deletions
|
@ -29,4 +29,6 @@ defmodule MatrixServer do
|
||||||
def random_string(length, alphabet) when length >= 1 do
|
def random_string(length, alphabet) when length >= 1 do
|
||||||
for _ <- 1..length, into: "", do: <<Enum.random(alphabet)>>
|
for _ <- 1..length, into: "", do: <<Enum.random(alphabet)>>
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def default_room_version, do: "7"
|
||||||
end
|
end
|
||||||
|
|
|
@ -33,18 +33,18 @@ defmodule MatrixServer.Account do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def register(%Register{} = api) do
|
def register(%Register{} = input) do
|
||||||
account_params = %{
|
account_params = %{
|
||||||
localpart: api.username || MatrixServer.random_string(10, ?a..?z),
|
localpart: input.username || MatrixServer.random_string(10, ?a..?z),
|
||||||
password_hash: Bcrypt.hash_pwd_salt(api.password)
|
password_hash: Bcrypt.hash_pwd_salt(input.password)
|
||||||
}
|
}
|
||||||
|
|
||||||
Multi.new()
|
Multi.new()
|
||||||
|> Multi.insert(:account, changeset(%Account{}, account_params))
|
|> Multi.insert(:account, changeset(%Account{}, account_params))
|
||||||
|> Multi.insert(:device, fn %{account: account} ->
|
|> Multi.insert(:device, fn %{account: account} ->
|
||||||
device_params = %{
|
device_params = %{
|
||||||
display_name: api.initial_device_display_name,
|
display_name: input.initial_device_display_name,
|
||||||
device_id: api.device_id || Device.generate_device_id(account.localpart)
|
device_id: input.device_id || Device.generate_device_id(account.localpart)
|
||||||
}
|
}
|
||||||
|
|
||||||
Ecto.build_assoc(account, :devices)
|
Ecto.build_assoc(account, :devices)
|
||||||
|
@ -53,14 +53,14 @@ defmodule MatrixServer.Account do
|
||||||
|> Multi.run(:device_with_access_token, &Device.insert_new_access_token/2)
|
|> Multi.run(:device_with_access_token, &Device.insert_new_access_token/2)
|
||||||
end
|
end
|
||||||
|
|
||||||
def login(%Login{} = api) do
|
def login(%Login{} = input) do
|
||||||
localpart = try_get_localpart(api.identifier.user)
|
localpart = try_get_localpart(input.identifier.user)
|
||||||
|
|
||||||
fn repo ->
|
fn repo ->
|
||||||
case repo.one(from a in Account, where: a.localpart == ^localpart) do
|
case repo.one(from a in Account, where: a.localpart == ^localpart) do
|
||||||
%Account{password_hash: hash} = account ->
|
%Account{password_hash: hash} = account ->
|
||||||
if Bcrypt.verify_pass(api.password, hash) do
|
if Bcrypt.verify_pass(input.password, hash) do
|
||||||
case Device.login(api, account) do
|
case Device.login(input, account) do
|
||||||
{:ok, device} ->
|
{:ok, device} ->
|
||||||
device
|
device
|
||||||
|
|
||||||
|
@ -87,6 +87,7 @@ defmodule MatrixServer.Account do
|
||||||
end
|
end
|
||||||
|
|
||||||
def changeset(account, params \\ %{}) do
|
def changeset(account, params \\ %{}) do
|
||||||
|
# TODO: fix password_hash in params
|
||||||
account
|
account
|
||||||
|> cast(params, [:localpart, :password_hash])
|
|> cast(params, [:localpart, :password_hash])
|
||||||
|> validate_required([:localpart, :password_hash])
|
|> validate_required([:localpart, :password_hash])
|
||||||
|
|
|
@ -50,16 +50,16 @@ defmodule MatrixServer.Device do
|
||||||
"#{localpart}_#{time_string}"
|
"#{localpart}_#{time_string}"
|
||||||
end
|
end
|
||||||
|
|
||||||
def login(%Login{} = api, account) do
|
def login(%Login{} = input, account) do
|
||||||
device_id = api.device_id || generate_device_id(account.localpart)
|
device_id = input.device_id || generate_device_id(account.localpart)
|
||||||
access_token = generate_access_token(account.localpart, device_id)
|
access_token = generate_access_token(account.localpart, device_id)
|
||||||
|
|
||||||
update_query =
|
update_query =
|
||||||
from(d in Device)
|
from(d in Device)
|
||||||
|> update(set: [access_token: ^access_token, device_id: ^device_id])
|
|> update(set: [access_token: ^access_token, device_id: ^device_id])
|
||||||
|> then(fn q ->
|
|> then(fn q ->
|
||||||
if api.initial_device_display_name do
|
if input.initial_device_display_name do
|
||||||
update(q, set: [display_name: ^api.initial_device_display_name])
|
update(q, set: [display_name: ^input.initial_device_display_name])
|
||||||
else
|
else
|
||||||
q
|
q
|
||||||
end
|
end
|
||||||
|
@ -67,7 +67,7 @@ defmodule MatrixServer.Device do
|
||||||
|
|
||||||
device_params = %{
|
device_params = %{
|
||||||
device_id: device_id,
|
device_id: device_id,
|
||||||
display_name: api.initial_device_display_name
|
display_name: input.initial_device_display_name
|
||||||
}
|
}
|
||||||
|
|
||||||
Ecto.build_assoc(account, :devices)
|
Ecto.build_assoc(account, :devices)
|
||||||
|
|
|
@ -3,17 +3,19 @@ defmodule MatrixServer.Event do
|
||||||
|
|
||||||
import Ecto.Changeset
|
import Ecto.Changeset
|
||||||
|
|
||||||
alias MatrixServer.Room
|
alias MatrixServer.{Room, Event, Account}
|
||||||
|
alias MatrixServerWeb.API.CreateRoom
|
||||||
|
|
||||||
|
@primary_key {:event_id, :string, []}
|
||||||
schema "events" do
|
schema "events" do
|
||||||
field :type, :string
|
field :type, :string
|
||||||
field :timestamp, :naive_datetime
|
field :origin_server_ts, :integer
|
||||||
field :state_key, :string
|
field :state_key, :string
|
||||||
field :sender, :string
|
field :sender, :string
|
||||||
field :content, :string
|
field :content, :map
|
||||||
field :prev_events, {:array, :string}
|
field :prev_events, {:array, :string}
|
||||||
field :auth_events, {:array, :string}
|
field :auth_events, {:array, :string}
|
||||||
belongs_to :room, Room
|
belongs_to :room, Room, type: :string
|
||||||
end
|
end
|
||||||
|
|
||||||
def changeset(event, params \\ %{}) do
|
def changeset(event, params \\ %{}) do
|
||||||
|
@ -22,4 +24,84 @@ defmodule MatrixServer.Event do
|
||||||
|> cast(params, [:type, :timestamp, :state_key, :sender, :content])
|
|> cast(params, [:type, :timestamp, :state_key, :sender, :content])
|
||||||
|> validate_required([:type, :timestamp, :sender])
|
|> validate_required([:type, :timestamp, :sender])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def new(room_id, sender) do
|
||||||
|
%Event{
|
||||||
|
room_id: room_id,
|
||||||
|
sender: sender,
|
||||||
|
event_id: generate_event_id(),
|
||||||
|
origin_server_ts: DateTime.utc_now() |> DateTime.to_unix(),
|
||||||
|
prev_events: [],
|
||||||
|
auth_events: []
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def create_room(room_id, creator, room_version) do
|
||||||
|
%Event{
|
||||||
|
new(room_id, creator)
|
||||||
|
| type: "m.room.create",
|
||||||
|
state_key: "",
|
||||||
|
content: %{
|
||||||
|
creator: creator,
|
||||||
|
room_version: room_version || MatrixServer.default_room_version()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def join(room_id, sender) do
|
||||||
|
%Event{
|
||||||
|
new(room_id, sender)
|
||||||
|
| type: "m.room.member",
|
||||||
|
state_key: sender,
|
||||||
|
content: %{
|
||||||
|
membership: "invite"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def room_creation_create_room(%CreateRoom{room_version: room_version}, %Account{
|
||||||
|
localpart: localpart
|
||||||
|
}) do
|
||||||
|
fn repo, %{room: %Room{id: room_id}} ->
|
||||||
|
# TODO: state resolution
|
||||||
|
create_room(room_id, MatrixServer.get_mxid(localpart), room_version)
|
||||||
|
|> repo.insert()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def room_creation_join_creator do
|
||||||
|
fn repo,
|
||||||
|
%{
|
||||||
|
create_room_event: %Event{sender: creator, event_id: create_room_event_id},
|
||||||
|
room: %Room{id: room_id}
|
||||||
|
} ->
|
||||||
|
# TODO: state resolution
|
||||||
|
join(room_id, creator)
|
||||||
|
|> Map.put(:prev_events, [create_room_event_id])
|
||||||
|
|> Map.put(:auth_events, [create_room_event_id])
|
||||||
|
|> repo.insert()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def room_creation_power_levels(_input) do
|
||||||
|
fn _repo, %{} ->
|
||||||
|
{:ok, :ok}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def room_creation_name(_input) do
|
||||||
|
fn _repo, %{} ->
|
||||||
|
{:ok, :ok}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def room_creation_topic(_input) do
|
||||||
|
fn _repo, %{} ->
|
||||||
|
{:ok, :ok}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def generate_event_id do
|
||||||
|
"$" <> MatrixServer.random_string(17) <> ":" <> MatrixServer.server_name()
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -12,18 +12,19 @@ defmodule MatrixServer.Room do
|
||||||
field :visibility, Ecto.Enum, values: [:public, :private]
|
field :visibility, Ecto.Enum, values: [:public, :private]
|
||||||
end
|
end
|
||||||
|
|
||||||
def create(%CreateRoom{} = api) do
|
|
||||||
Multi.new()
|
|
||||||
|> Multi.insert(:room, Room.create_changeset(api))
|
|
||||||
end
|
|
||||||
|
|
||||||
def changeset(room, params \\ %{}) do
|
def changeset(room, params \\ %{}) do
|
||||||
room
|
cast(room, params, [:visibility])
|
||||||
|> cast(params, [:visibility])
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def create_changeset(%CreateRoom{} = api) do
|
def create_changeset(%CreateRoom{} = input) do
|
||||||
%Room{visibility: api.visibility, id: MatrixServer.random_string(18)}
|
visibility = input.visibility || :public
|
||||||
|> changeset()
|
|
||||||
|
%Room{}
|
||||||
|
|> changeset(%{visibility: visibility})
|
||||||
|
|> put_change(:id, generate_room_id())
|
||||||
|
end
|
||||||
|
|
||||||
|
def generate_room_id do
|
||||||
|
"!" <> MatrixServer.random_string(18) <> "@" <> MatrixServer.server_name()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,15 +1,16 @@
|
||||||
defmodule MatrixServer.RoomServer do
|
defmodule MatrixServer.RoomServer do
|
||||||
use GenServer
|
use GenServer
|
||||||
|
|
||||||
alias MatrixServer.{Repo, Room}
|
alias MatrixServer.{Repo, Room, Event}
|
||||||
alias MatrixServerWeb.API.CreateRoom
|
alias MatrixServerWeb.API.CreateRoom
|
||||||
|
alias Ecto.Multi
|
||||||
|
|
||||||
def start_link(_opts) do
|
def start_link(_opts) do
|
||||||
GenServer.start_link(__MODULE__, :ok, name: __MODULE__)
|
GenServer.start_link(__MODULE__, :ok, name: __MODULE__)
|
||||||
end
|
end
|
||||||
|
|
||||||
def create_room(params) do
|
def create_room(%CreateRoom{} = input, account) do
|
||||||
GenServer.call(__MODULE__, {:create_room, params})
|
GenServer.call(__MODULE__, {:create_room, input, account})
|
||||||
end
|
end
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
|
@ -18,10 +19,18 @@ defmodule MatrixServer.RoomServer do
|
||||||
end
|
end
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
def handle_call({:create_room, %CreateRoom{} = api}, _from, state) do
|
def handle_call({:create_room, input, account}, _from, state) do
|
||||||
Room.create(api)
|
# TODO: preset events, initial_state events, invite, invite_3pid
|
||||||
|
result =
|
||||||
|
Multi.new()
|
||||||
|
|> Multi.insert(:room, Room.create_changeset(input))
|
||||||
|
|> Multi.run(:create_room_event, Event.room_creation_create_room(input, account))
|
||||||
|
|> Multi.run(:join_creator_event, Event.room_creation_join_creator())
|
||||||
|
|> Multi.run(:power_levels_event, Event.room_creation_power_levels(input))
|
||||||
|
|> Multi.run(:name_event, Event.room_creation_name(input))
|
||||||
|
|> Multi.run(:topic_event, Event.room_creation_topic(input))
|
||||||
|> Repo.transaction()
|
|> Repo.transaction()
|
||||||
|
|
||||||
{:reply, :ok, state}
|
{:reply, result, state}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -14,14 +14,14 @@ defmodule MatrixServerWeb.AuthController do
|
||||||
def register(conn, %{"auth" => %{"type" => @register_type}} = params) do
|
def register(conn, %{"auth" => %{"type" => @register_type}} = params) do
|
||||||
case Register.changeset(params) do
|
case Register.changeset(params) do
|
||||||
%Changeset{valid?: true} = cs ->
|
%Changeset{valid?: true} = cs ->
|
||||||
api = apply_changes(cs)
|
input = apply_changes(cs)
|
||||||
|
|
||||||
case Account.register(api) |> Repo.transaction() do
|
case Account.register(input) |> Repo.transaction() do
|
||||||
{:ok, %{device_with_access_token: device}} ->
|
{:ok, %{device_with_access_token: device}} ->
|
||||||
data = %{user_id: MatrixServer.get_mxid(device.localpart)}
|
data = %{user_id: MatrixServer.get_mxid(device.localpart)}
|
||||||
|
|
||||||
data =
|
data =
|
||||||
if not api.inhibit_login do
|
if not input.inhibit_login do
|
||||||
data
|
data
|
||||||
|> Map.put(:device_id, device.device_id)
|
|> Map.put(:device_id, device.device_id)
|
||||||
|> Map.put(:access_token, device.access_token)
|
|> Map.put(:access_token, device.access_token)
|
||||||
|
@ -73,13 +73,9 @@ defmodule MatrixServerWeb.AuthController do
|
||||||
) do
|
) do
|
||||||
case Login.changeset(params) do
|
case Login.changeset(params) do
|
||||||
%Changeset{valid?: true} = cs ->
|
%Changeset{valid?: true} = cs ->
|
||||||
api = apply_changes(cs)
|
input = apply_changes(cs)
|
||||||
# input =
|
|
||||||
# apply_changes(cs)
|
|
||||||
# |> Map.from_struct()
|
|
||||||
# |> MatrixServer.maybe_update_map(:initial_device_display_name, :display_name)
|
|
||||||
|
|
||||||
case Account.login(api) |> Repo.transaction() do
|
case Account.login(input) |> Repo.transaction() do
|
||||||
{:ok, device} ->
|
{:ok, device} ->
|
||||||
data = %{
|
data = %{
|
||||||
user_id: MatrixServer.get_mxid(device.localpart),
|
user_id: MatrixServer.get_mxid(device.localpart),
|
||||||
|
|
|
@ -6,13 +6,14 @@ defmodule MatrixServerWeb.RoomController do
|
||||||
|
|
||||||
alias MatrixServerWeb.API.{CreateRoom}
|
alias MatrixServerWeb.API.{CreateRoom}
|
||||||
alias Ecto.Changeset
|
alias Ecto.Changeset
|
||||||
|
alias Plug.Conn
|
||||||
|
|
||||||
def create(conn, params) do
|
def create(%Conn{assigns: %{account: account}} = conn, params) do
|
||||||
case CreateRoom.changeset(params) do
|
case CreateRoom.changeset(params) do
|
||||||
%Changeset{valid?: true} = cs ->
|
%Changeset{valid?: true} = cs ->
|
||||||
api_struct = apply_changes(cs)
|
cs
|
||||||
|
|> apply_changes()
|
||||||
MatrixServer.RoomServer.create_room(api_struct)
|
|> MatrixServer.RoomServer.create_room(account)
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|> put_status(200)
|
|> put_status(200)
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
defmodule MatrixServer.Repo.Migrations.ChangeEventContentToJson do
|
||||||
|
use Ecto.Migration
|
||||||
|
|
||||||
|
def change do
|
||||||
|
execute(
|
||||||
|
"alter table events alter column content type jsonb using (content::jsonb);",
|
||||||
|
"alter table events alter column content type character varying(255);"
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,10 @@
|
||||||
|
defmodule MatrixServer.Repo.Migrations.ChangeEventTimestampToInteger do
|
||||||
|
use Ecto.Migration
|
||||||
|
|
||||||
|
def change do
|
||||||
|
alter table(:events) do
|
||||||
|
remove :timestamp, :string, null: false
|
||||||
|
add :origin_server_ts, :integer, null: false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,7 @@
|
||||||
|
defmodule MatrixServer.Repo.Migrations.ChangeEventIdName do
|
||||||
|
use Ecto.Migration
|
||||||
|
|
||||||
|
def change do
|
||||||
|
rename table(:events), :id, to: :event_id
|
||||||
|
end
|
||||||
|
end
|
|
@ -123,6 +123,7 @@ defmodule MatrixServerWeb.AuthControllerTest do
|
||||||
|
|
||||||
test "handles wrong password", %{conn: conn} do
|
test "handles wrong password", %{conn: conn} do
|
||||||
Factory.insert(:account, localpart: "sneed", password_hash: Bcrypt.hash_pwd_salt("surprise"))
|
Factory.insert(:account, localpart: "sneed", password_hash: Bcrypt.hash_pwd_salt("surprise"))
|
||||||
|
|
||||||
conn = post_json(conn, Routes.auth_path(Endpoint, :login), @basic_params)
|
conn = post_json(conn, Routes.auth_path(Endpoint, :login), @basic_params)
|
||||||
|
|
||||||
assert %{"errcode" => "M_FORBIDDEN"} = json_response(conn, 400)
|
assert %{"errcode" => "M_FORBIDDEN"} = json_response(conn, 400)
|
||||||
|
|
Loading…
Reference in a new issue