Fix prev events not being hashed and signed during room creation.
This commit is contained in:
parent
1781575e75
commit
7b011123f8
3 changed files with 102 additions and 108 deletions
|
@ -74,16 +74,10 @@ defmodule MatrixServer.RoomServer do
|
||||||
# TODO: power_level_content_override, initial_state, invite, invite_3pid
|
# TODO: power_level_content_override, initial_state, invite, invite_3pid
|
||||||
room = Repo.one!(from r in Room, where: r.id == ^room_id)
|
room = Repo.one!(from r in Room, where: r.id == ^room_id)
|
||||||
|
|
||||||
case create_room_events(room, account, input) do
|
case Repo.transaction(create_room_insert_events(room, account, input)) do
|
||||||
events when is_list(events) ->
|
{:ok, state_set} -> {:reply, {:ok, room_id}, %{state | state_set: state_set}}
|
||||||
case Repo.transaction(create_room_insert_events(room, events)) do
|
{:error, reason} -> {:reply, {:error, reason}, state}
|
||||||
{:ok, state_set} -> {:reply, {:ok, room_id}, %{state | state_set: state_set}}
|
_ -> {:reply, {:error, :unknown}, state}
|
||||||
{:error, reason} -> {:reply, {:error, reason}, state}
|
|
||||||
_ -> {:reply, {:error, :unknown}, state}
|
|
||||||
end
|
|
||||||
|
|
||||||
:error ->
|
|
||||||
{:reply, {:error, :event_creation}, state}
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -100,51 +94,54 @@ defmodule MatrixServer.RoomServer do
|
||||||
{:reply, result, state}
|
{:reply, result, state}
|
||||||
end
|
end
|
||||||
|
|
||||||
defp create_room_events(room, account, %CreateRoom{
|
defp create_room_insert_events(room, account, %CreateRoom{
|
||||||
room_version: room_version,
|
room_version: room_version,
|
||||||
|
preset: preset,
|
||||||
name: name,
|
name: name,
|
||||||
topic: topic,
|
topic: topic
|
||||||
preset: preset
|
|
||||||
}) do
|
}) do
|
||||||
with {:ok, create_room} <- Event.create_room(room, account, room_version),
|
|
||||||
{:ok, join_creator} <- Event.join(room, account, [create_room]),
|
|
||||||
{:ok, pls} <- Event.power_levels(room, account, [create_room, join_creator]),
|
|
||||||
auth_events <- [create_room, join_creator, pls],
|
|
||||||
{:ok, preset_events} <- room_creation_preset(account, preset, room, auth_events),
|
|
||||||
{:ok, name_event} <-
|
|
||||||
if(name, do: Event.name(room, account, name, auth_events), else: {:ok, nil}),
|
|
||||||
{:ok, topic_event} <-
|
|
||||||
if(topic, do: Event.topic(room, account, topic, auth_events), else: {:ok, nil}) do
|
|
||||||
events = [create_room, join_creator, pls] ++ preset_events ++ [name_event, topic_event]
|
|
||||||
|
|
||||||
Enum.reject(events, &Kernel.is_nil/1)
|
|
||||||
else
|
|
||||||
_ -> :error
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
defp create_room_insert_events(room, events) do
|
|
||||||
fn ->
|
fn ->
|
||||||
result =
|
state_set = %{}
|
||||||
Enum.reduce_while(events, {%{}, room}, fn event, {state_set, room} ->
|
|
||||||
case verify_and_insert_event(event, state_set, room) do
|
|
||||||
{:ok, state_set, room} -> {:cont, {state_set, room}}
|
|
||||||
{:error, reason} -> {:halt, {:error, reason}}
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
|
|
||||||
case result do
|
with create_room <- Event.create_room(room, account, room_version),
|
||||||
{:error, reason} ->
|
{:ok, state_set, create_room, room} <-
|
||||||
Repo.rollback(reason)
|
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),
|
||||||
|
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)
|
||||||
|
|
||||||
{state_set, room} ->
|
remaining_events =
|
||||||
serialized_state_set =
|
Enum.reject(preset_events ++ [name_event, topic_event], &Kernel.is_nil/1)
|
||||||
Enum.map(state_set, fn {{type, state_key}, event} ->
|
|
||||||
[type, state_key, event.event_id]
|
|
||||||
end)
|
|
||||||
|
|
||||||
Repo.update!(change(room, state: serialized_state_set))
|
result =
|
||||||
state_set
|
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)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -169,11 +166,11 @@ defmodule MatrixServer.RoomServer do
|
||||||
"public_chat" -> {"public", "shared", "forbidden"}
|
"public_chat" -> {"public", "shared", "forbidden"}
|
||||||
end
|
end
|
||||||
|
|
||||||
with {:ok, join_rules} <- Event.join_rules(room, account, join_rule, auth_events),
|
[
|
||||||
{:ok, his_vis} <- Event.history_visibility(room, account, his_vis, auth_events),
|
Event.join_rules(room, account, join_rule, auth_events),
|
||||||
{:ok, guest_access} <- Event.guest_access(room, account, guest_access, auth_events) do
|
Event.history_visibility(room, account, his_vis, auth_events),
|
||||||
{:ok, [join_rules, his_vis, guest_access]}
|
Event.guest_access(room, account, guest_access, auth_events)
|
||||||
end
|
]
|
||||||
end
|
end
|
||||||
|
|
||||||
defp verify_and_insert_event(
|
defp verify_and_insert_event(
|
||||||
|
@ -181,6 +178,7 @@ defmodule MatrixServer.RoomServer do
|
||||||
current_state_set,
|
current_state_set,
|
||||||
%Room{forward_extremities: forward_extremities} = room
|
%Room{forward_extremities: forward_extremities} = room
|
||||||
) do
|
) do
|
||||||
|
# TODO: Correct error values.
|
||||||
# Check the following things:
|
# Check the following things:
|
||||||
# 1. TODO: Is a valid event, otherwise it is dropped.
|
# 1. TODO: Is a valid event, otherwise it is dropped.
|
||||||
# 2. TODO: Passes signature checks, otherwise it is dropped.
|
# 2. TODO: Passes signature checks, otherwise it is dropped.
|
||||||
|
@ -190,7 +188,8 @@ defmodule MatrixServer.RoomServer do
|
||||||
# 6. Passes authorization rules based on the current state of the room, otherwise it is "soft failed".
|
# 6. Passes authorization rules based on the current state of the room, otherwise it is "soft failed".
|
||||||
event = %Event{event | prev_events: forward_extremities}
|
event = %Event{event | prev_events: forward_extremities}
|
||||||
|
|
||||||
with true <- Event.prevalidate(event),
|
with {:ok, event} <- Event.post_process(event),
|
||||||
|
true <- Event.prevalidate(event),
|
||||||
true <- Authorization.authorized_by_auth_events?(event),
|
true <- Authorization.authorized_by_auth_events?(event),
|
||||||
state_set <- StateResolution.resolve(event, false),
|
state_set <- StateResolution.resolve(event, false),
|
||||||
true <- Authorization.authorized?(event, state_set),
|
true <- Authorization.authorized?(event, state_set),
|
||||||
|
@ -198,7 +197,7 @@ defmodule MatrixServer.RoomServer do
|
||||||
room = Room.update_forward_extremities(event, room)
|
room = Room.update_forward_extremities(event, room)
|
||||||
event = Repo.insert!(event)
|
event = Repo.insert!(event)
|
||||||
state_set = StateResolution.resolve_forward_extremities(event)
|
state_set = StateResolution.resolve_forward_extremities(event)
|
||||||
{:ok, state_set, room}
|
{:ok, state_set, event, room}
|
||||||
else
|
else
|
||||||
_ -> {:error, :authorization}
|
_ -> {:error, :authorization}
|
||||||
end
|
end
|
||||||
|
|
|
@ -14,11 +14,10 @@ defmodule MatrixServer.Event do
|
||||||
field :content, :map
|
field :content, :map
|
||||||
field :prev_events, {:array, :string}
|
field :prev_events, {:array, :string}
|
||||||
field :auth_events, {:array, :string}
|
field :auth_events, {:array, :string}
|
||||||
# TODO: make these database fields eventually?
|
field :unsigned, :map
|
||||||
field :signing_keys, :map, virtual: true, default: %{}
|
field :signatures, {:map, {:map, :string}}
|
||||||
field :unsigned, :map, virtual: true, default: %{}
|
field :hashes, {:map, :string}
|
||||||
field :signatures, :map, virtual: true, default: %{}
|
|
||||||
field :hashes, :map, virtual: true, default: %{}
|
|
||||||
belongs_to :room, Room, type: :string
|
belongs_to :room, Room, type: :string
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -35,12 +34,11 @@ defmodule MatrixServer.Event do
|
||||||
def create_room(
|
def create_room(
|
||||||
room,
|
room,
|
||||||
%Account{localpart: localpart} = creator,
|
%Account{localpart: localpart} = creator,
|
||||||
room_version,
|
room_version
|
||||||
generate_id \\ true
|
|
||||||
) do
|
) do
|
||||||
mxid = MatrixServer.get_mxid(localpart)
|
mxid = MatrixServer.get_mxid(localpart)
|
||||||
|
|
||||||
event = %Event{
|
%Event{
|
||||||
new(room, creator)
|
new(room, creator)
|
||||||
| type: "m.room.create",
|
| type: "m.room.create",
|
||||||
state_key: "",
|
state_key: "",
|
||||||
|
@ -49,14 +47,12 @@ defmodule MatrixServer.Event do
|
||||||
"room_version" => room_version || MatrixServer.default_room_version()
|
"room_version" => room_version || MatrixServer.default_room_version()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if generate_id, do: set_event_id(event), else: event
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def join(room, %Account{localpart: localpart} = sender, auth_events, generate_id \\ true) do
|
def join(room, %Account{localpart: localpart} = sender, auth_events) do
|
||||||
mxid = MatrixServer.get_mxid(localpart)
|
mxid = MatrixServer.get_mxid(localpart)
|
||||||
|
|
||||||
event = %Event{
|
%Event{
|
||||||
new(room, sender)
|
new(room, sender)
|
||||||
| type: "m.room.member",
|
| type: "m.room.member",
|
||||||
state_key: mxid,
|
state_key: mxid,
|
||||||
|
@ -65,19 +61,16 @@ defmodule MatrixServer.Event do
|
||||||
},
|
},
|
||||||
auth_events: Enum.map(auth_events, & &1.event_id)
|
auth_events: Enum.map(auth_events, & &1.event_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
if generate_id, do: set_event_id(event), else: event
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def power_levels(
|
def power_levels(
|
||||||
room,
|
room,
|
||||||
%Account{localpart: localpart} = sender,
|
%Account{localpart: localpart} = sender,
|
||||||
auth_events,
|
auth_events
|
||||||
generate_id \\ true
|
|
||||||
) do
|
) do
|
||||||
mxid = MatrixServer.get_mxid(localpart)
|
mxid = MatrixServer.get_mxid(localpart)
|
||||||
|
|
||||||
event = %Event{
|
%Event{
|
||||||
new(room, sender)
|
new(room, sender)
|
||||||
| type: "m.room.power_levels",
|
| type: "m.room.power_levels",
|
||||||
state_key: "",
|
state_key: "",
|
||||||
|
@ -99,12 +92,10 @@ defmodule MatrixServer.Event do
|
||||||
},
|
},
|
||||||
auth_events: Enum.map(auth_events, & &1.event_id)
|
auth_events: Enum.map(auth_events, & &1.event_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
if generate_id, do: set_event_id(event), else: event
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def name(room, sender, name, auth_events, generate_id \\ true) do
|
def name(room, sender, name, auth_events) do
|
||||||
event = %Event{
|
%Event{
|
||||||
new(room, sender)
|
new(room, sender)
|
||||||
| type: "m.room.name",
|
| type: "m.room.name",
|
||||||
state_key: "",
|
state_key: "",
|
||||||
|
@ -113,12 +104,10 @@ defmodule MatrixServer.Event do
|
||||||
},
|
},
|
||||||
auth_events: Enum.map(auth_events, & &1.event_id)
|
auth_events: Enum.map(auth_events, & &1.event_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
if generate_id, do: set_event_id(event), else: event
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def topic(room, sender, topic, auth_events, generate_id \\ true) do
|
def topic(room, sender, topic, auth_events) do
|
||||||
event = %Event{
|
%Event{
|
||||||
new(room, sender)
|
new(room, sender)
|
||||||
| type: "m.room.topic",
|
| type: "m.room.topic",
|
||||||
state_key: "",
|
state_key: "",
|
||||||
|
@ -127,12 +116,10 @@ defmodule MatrixServer.Event do
|
||||||
},
|
},
|
||||||
auth_events: Enum.map(auth_events, & &1.event_id)
|
auth_events: Enum.map(auth_events, & &1.event_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
if generate_id, do: set_event_id(event), else: event
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def join_rules(room, sender, join_rule, auth_events, generate_id \\ true) do
|
def join_rules(room, sender, join_rule, auth_events) do
|
||||||
event = %Event{
|
%Event{
|
||||||
new(room, sender)
|
new(room, sender)
|
||||||
| type: "m.room.join_rules",
|
| type: "m.room.join_rules",
|
||||||
state_key: "",
|
state_key: "",
|
||||||
|
@ -141,12 +128,10 @@ defmodule MatrixServer.Event do
|
||||||
},
|
},
|
||||||
auth_events: Enum.map(auth_events, & &1.event_id)
|
auth_events: Enum.map(auth_events, & &1.event_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
if generate_id, do: set_event_id(event), else: event
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def history_visibility(room, sender, history_visibility, auth_events, generate_id \\ true) do
|
def history_visibility(room, sender, history_visibility, auth_events) do
|
||||||
event = %Event{
|
%Event{
|
||||||
new(room, sender)
|
new(room, sender)
|
||||||
| type: "m.room.history_visibility",
|
| type: "m.room.history_visibility",
|
||||||
state_key: "",
|
state_key: "",
|
||||||
|
@ -155,12 +140,10 @@ defmodule MatrixServer.Event do
|
||||||
},
|
},
|
||||||
auth_events: Enum.map(auth_events, & &1.event_id)
|
auth_events: Enum.map(auth_events, & &1.event_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
if generate_id, do: set_event_id(event), else: event
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def guest_access(room, sender, guest_access, auth_events, generate_id \\ true) do
|
def guest_access(room, sender, guest_access, auth_events) do
|
||||||
event = %Event{
|
%Event{
|
||||||
new(room, sender)
|
new(room, sender)
|
||||||
| type: "m.room.guest_access",
|
| type: "m.room.guest_access",
|
||||||
state_key: "",
|
state_key: "",
|
||||||
|
@ -169,8 +152,6 @@ defmodule MatrixServer.Event do
|
||||||
},
|
},
|
||||||
auth_events: Enum.map(auth_events, & &1.event_id)
|
auth_events: Enum.map(auth_events, & &1.event_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
if generate_id, do: set_event_id(event), else: event
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def is_control_event(%Event{type: "m.room.power_levels", state_key: ""}), do: true
|
def is_control_event(%Event{type: "m.room.power_levels", state_key: ""}), do: true
|
||||||
|
@ -294,28 +275,15 @@ defmodule MatrixServer.Event do
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
def sign(event) do
|
|
||||||
content_hash =
|
|
||||||
event
|
|
||||||
|> calculate_content_hash()
|
|
||||||
|> MatrixServer.encode_unpadded_base64()
|
|
||||||
|
|
||||||
event
|
|
||||||
|> Map.put(:hashes, %{"sha256" => content_hash})
|
|
||||||
|> redact()
|
|
||||||
|> KeyServer.sign_object()
|
|
||||||
end
|
|
||||||
|
|
||||||
defp calculate_content_hash(event) do
|
defp calculate_content_hash(event) do
|
||||||
m =
|
m =
|
||||||
event
|
event
|
||||||
|> MatrixServer.to_serializable_map()
|
|> MatrixServer.to_serializable_map()
|
||||||
|> Map.drop([:unsigned, :signature, :hashes])
|
|> Map.drop([:unsigned, :signature, :hashes])
|
||||||
|> EncodableMap.from_map()
|
|> EncodableMap.from_map()
|
||||||
|> Jason.encode()
|
|
||||||
|
|
||||||
with {:ok, json} <- Jason.encode(m) do
|
with {:ok, json} <- Jason.encode(m) do
|
||||||
:crypto.hash(:sha256, json)
|
{:ok, :crypto.hash(:sha256, json)}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -367,6 +335,22 @@ defmodule MatrixServer.Event do
|
||||||
|
|
||||||
defp redact_content(_, _), do: %{}
|
defp redact_content(_, _), do: %{}
|
||||||
|
|
||||||
|
# Adds content hash, adds signature and calculates event id.
|
||||||
|
def post_process(event) do
|
||||||
|
with {:ok, content_hash} <- calculate_content_hash(event) do
|
||||||
|
encoded_hash = MatrixServer.encode_unpadded_base64(content_hash)
|
||||||
|
event = %Event{event | hashes: %{"sha256" => encoded_hash}}
|
||||||
|
|
||||||
|
with {:ok, sig, key_id} <- KeyServer.sign_object(redact(event)) do
|
||||||
|
event = %Event{event | signatures: %{MatrixServer.server_name() => %{key_id => sig}}}
|
||||||
|
|
||||||
|
with {:ok, event} <- set_event_id(event) do
|
||||||
|
{:ok, event}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def set_event_id(event) do
|
def set_event_id(event) do
|
||||||
with {:ok, event_id} <- generate_event_id(event) do
|
with {:ok, event_id} <- generate_event_id(event) do
|
||||||
{:ok, %Event{event | event_id: event_id}}
|
{:ok, %Event{event | event_id: event_id}}
|
||||||
|
|
11
priv/repo/migrations/20210818133912_add_fields_to_events.exs
Normal file
11
priv/repo/migrations/20210818133912_add_fields_to_events.exs
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
defmodule MatrixServer.Repo.Migrations.AddFieldsToEvents do
|
||||||
|
use Ecto.Migration
|
||||||
|
|
||||||
|
def change do
|
||||||
|
alter table(:events) do
|
||||||
|
add :unsigned, :map, default: %{}, null: true
|
||||||
|
add :hashes, :map, null: false
|
||||||
|
add :signatures, :map, null: false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in a new issue