Add dialyzer dependency
Add typespecs to several functions
This commit is contained in:
parent
7b011123f8
commit
58d3e17259
12 changed files with 79 additions and 44 deletions
|
@ -1,39 +1,33 @@
|
|||
defmodule MatrixServer do
|
||||
alias MatrixServer.EncodableMap
|
||||
|
||||
@random_string_alphabet Enum.into(?a..?z, []) ++ Enum.into(?A..?Z, [])
|
||||
|
||||
@spec get_mxid(String.t()) :: String.t()
|
||||
def get_mxid(localpart) when is_binary(localpart) do
|
||||
"@#{localpart}:#{server_name()}"
|
||||
end
|
||||
|
||||
@spec server_name() :: String.t()
|
||||
def server_name do
|
||||
Application.get_env(:matrix_server, :server_name)
|
||||
end
|
||||
|
||||
def maybe_update_map(map, old_key, new_key) do
|
||||
maybe_update_map(map, old_key, new_key, &Function.identity/1)
|
||||
end
|
||||
|
||||
def maybe_update_map(map, old_key, new_key, fun) when is_map_key(map, old_key) do
|
||||
value = Map.fetch!(map, old_key)
|
||||
|
||||
map
|
||||
|> Map.put(new_key, fun.(value))
|
||||
|> Map.delete(old_key)
|
||||
end
|
||||
|
||||
def maybe_update_map(map, _, _, _), do: map
|
||||
|
||||
@spec localpart_regex() :: Regex.t()
|
||||
def localpart_regex, do: ~r/^([a-z0-9\._=\/])+$/
|
||||
|
||||
@alphabet Enum.into(?a..?z, []) ++ Enum.into(?A..?Z, [])
|
||||
def random_string(length), do: random_string(length, @alphabet)
|
||||
@spec random_string(pos_integer()) :: String.t()
|
||||
def random_string(length), do: random_string(length, @random_string_alphabet)
|
||||
|
||||
@spec random_string(pos_integer(), Enum.t()) :: String.t()
|
||||
def random_string(length, alphabet) when length >= 1 do
|
||||
for _ <- 1..length, into: "", do: <<Enum.random(alphabet)>>
|
||||
end
|
||||
|
||||
@spec default_room_version() :: String.t()
|
||||
def default_room_version, do: "7"
|
||||
|
||||
@spec get_domain(String.t()) :: String.t() | nil
|
||||
def get_domain(id) do
|
||||
case String.split(id, ":", parts: 2) do
|
||||
[_, server_name] -> server_name
|
||||
|
@ -42,6 +36,7 @@ defmodule MatrixServer do
|
|||
end
|
||||
|
||||
# TODO Eventually move to regex with named captures.
|
||||
@spec get_localpart(String.t()) :: String.t() | nil
|
||||
def get_localpart(id) do
|
||||
with [part, _] <- String.split(id, ":", parts: 2),
|
||||
{_, localpart} <- String.split_at(part, 1) do
|
||||
|
@ -52,6 +47,7 @@ defmodule MatrixServer do
|
|||
end
|
||||
|
||||
# https://elixirforum.com/t/22709/9
|
||||
@spec has_duplicates?(list()) :: boolean()
|
||||
def has_duplicates?(list) do
|
||||
list
|
||||
|> Enum.reduce_while(%MapSet{}, fn x, acc ->
|
||||
|
@ -61,6 +57,7 @@ defmodule MatrixServer do
|
|||
end
|
||||
|
||||
# https://matrix.org/docs/spec/appendices#unpadded-base64
|
||||
@spec encode_unpadded_base64(String.t()) :: String.t()
|
||||
def encode_unpadded_base64(data) do
|
||||
data
|
||||
|> Base.encode64()
|
||||
|
@ -68,12 +65,14 @@ defmodule MatrixServer do
|
|||
end
|
||||
|
||||
# Decode (possibly unpadded) base64.
|
||||
@spec decode_base64(String.t()) :: {:ok, String.t()} | :error
|
||||
def decode_base64(data) when is_binary(data) do
|
||||
rem = rem(String.length(data), 4)
|
||||
padded_data = if rem > 0, do: data <> String.duplicate("=", 4 - rem), else: data
|
||||
Base.decode64(padded_data)
|
||||
end
|
||||
|
||||
@spec encode_canonical_json(map()) :: {:ok, String.t()} | {:error, Jason.EncodeError.t()}
|
||||
def encode_canonical_json(object) do
|
||||
object
|
||||
|> EncodableMap.from_map()
|
||||
|
@ -81,6 +80,7 @@ defmodule MatrixServer do
|
|||
end
|
||||
|
||||
# https://stackoverflow.com/questions/41523762/41671211
|
||||
@spec to_serializable_map(struct()) :: map()
|
||||
def to_serializable_map(struct) do
|
||||
association_fields = struct.__struct__.__schema__(:associations)
|
||||
waste_fields = association_fields ++ [:__meta__]
|
||||
|
@ -90,6 +90,7 @@ defmodule MatrixServer do
|
|||
|> Map.drop(waste_fields)
|
||||
end
|
||||
|
||||
@spec serialize_and_encode(struct()) :: {:ok, String.t()} | {:error, Jason.EncodeError.t()}
|
||||
def serialize_and_encode(struct) do
|
||||
# TODO: handle nil values in struct?
|
||||
struct
|
||||
|
@ -97,6 +98,7 @@ defmodule MatrixServer do
|
|||
|> encode_canonical_json()
|
||||
end
|
||||
|
||||
@spec add_signature(map(), String.t(), String.t()) :: map()
|
||||
def add_signature(object, key_id, sig) when not is_map_key(object, :signatures) do
|
||||
Map.put(object, :signatures, %{MatrixServer.server_name() => %{key_id => sig}})
|
||||
end
|
||||
|
@ -108,6 +110,7 @@ defmodule MatrixServer do
|
|||
%{object | signatures: new_sigs}
|
||||
end
|
||||
|
||||
@spec validate_change_simple(Ecto.Changeset.t(), atom(), (term() -> boolean())) :: Ecto.Changeset.t()
|
||||
def validate_change_simple(changeset, field, func) do
|
||||
augmented_func = fn _, val ->
|
||||
if func.(val), do: [], else: [{field, "invalid"}]
|
||||
|
@ -118,6 +121,7 @@ defmodule MatrixServer do
|
|||
|
||||
# Returns a Boolean whether the signature is valid.
|
||||
# Also returns false on ArgumentError.
|
||||
@spec sign_verify(binary(), String.t(), binary()) :: boolean()
|
||||
def sign_verify(sig, text, key) do
|
||||
try do
|
||||
:enacl.sign_verify_detached(sig, text, key)
|
||||
|
@ -126,6 +130,7 @@ defmodule MatrixServer do
|
|||
end
|
||||
end
|
||||
|
||||
@spec min_datetime(DateTime.t(), DateTime.t()) :: DateTime.t()
|
||||
def min_datetime(datetime1, datetime2) do
|
||||
if DateTime.compare(datetime1, datetime2) == :gt do
|
||||
datetime2
|
||||
|
@ -134,6 +139,7 @@ defmodule MatrixServer do
|
|||
end
|
||||
end
|
||||
|
||||
@spec encode_url_safe_base64(String.t()) :: String.t()
|
||||
def encode_url_safe_base64(data) do
|
||||
data
|
||||
|> encode_unpadded_base64()
|
||||
|
|
|
@ -10,10 +10,12 @@ defmodule MatrixServer.KeyServer do
|
|||
GenServer.start_link(__MODULE__, opts, name: __MODULE__)
|
||||
end
|
||||
|
||||
@spec sign_object(map()) :: {:ok, String.t(), String.t()} | :error
|
||||
def sign_object(object) do
|
||||
GenServer.call(__MODULE__, {:sign_object, object})
|
||||
end
|
||||
|
||||
@spec get_own_signing_keys() :: list({String.t(), binary()})
|
||||
def get_own_signing_keys() do
|
||||
GenServer.call(__MODULE__, :get_own_signing_keys)
|
||||
end
|
||||
|
@ -41,6 +43,7 @@ defmodule MatrixServer.KeyServer do
|
|||
end
|
||||
|
||||
# https://blog.swwomm.com/2020/09/elixir-ed25519-signatures-with-enacl.html
|
||||
@spec sign_object(map(), binary()) :: {:ok, String.t()} | {:error, Jason.EncodeError.t()}
|
||||
defp sign_object(object, private_key) do
|
||||
object = Map.drop(object, [:signatures, :unsigned])
|
||||
|
||||
|
@ -55,7 +58,8 @@ defmodule MatrixServer.KeyServer do
|
|||
end
|
||||
|
||||
# TODO: not sure if there is a better way to do this...
|
||||
def read_keys do
|
||||
@spec read_keys() :: {binary(), binary()}
|
||||
defp read_keys do
|
||||
raw_priv_key =
|
||||
Application.get_env(:matrix_server, :private_key_file)
|
||||
|> File.read!()
|
||||
|
|
|
@ -1,14 +0,0 @@
|
|||
defmodule MatrixServer.QuickCheck do
|
||||
import Ecto.Query
|
||||
|
||||
alias MatrixServer.{Repo, Room, Account, RoomServer}
|
||||
alias MatrixServerWeb.Client.Request.CreateRoom
|
||||
|
||||
def create_room(name \\ nil, topic \\ nil) do
|
||||
account = Repo.one!(from a in Account, limit: 1)
|
||||
input = %CreateRoom{name: name, topic: topic}
|
||||
%Room{id: room_id} = Repo.insert!(Room.create_changeset(input))
|
||||
{:ok, pid} = RoomServer.get_room_server(room_id)
|
||||
RoomServer.create_room(pid, account, input)
|
||||
end
|
||||
end
|
|
@ -20,6 +20,7 @@ defmodule MatrixServer.RoomServer do
|
|||
|
||||
# Get room server pid, or spin one up for the room.
|
||||
# If the room does not exist, return an error.
|
||||
@spec get_room_server(String.t()) :: {:error, :not_found} | DynamicSupervisor.on_start_child()
|
||||
def get_room_server(room_id) do
|
||||
case Repo.one(from r in Room, where: r.id == ^room_id) do
|
||||
nil ->
|
||||
|
@ -42,10 +43,12 @@ defmodule MatrixServer.RoomServer do
|
|||
end
|
||||
end
|
||||
|
||||
@spec create_room(pid(), MatrixServer.Account.t(), MatrixServerWeb.Client.Request.CreateRoom.t()) :: {:ok, String.t()} | {:error, atom()}
|
||||
def create_room(pid, account, input) do
|
||||
GenServer.call(pid, {:create_room, account, input})
|
||||
end
|
||||
|
||||
@spec server_in_room(pid(), String.t()) :: boolean()
|
||||
def server_in_room(pid, domain) do
|
||||
GenServer.call(pid, {:server_in_room, domain})
|
||||
end
|
||||
|
|
|
@ -7,6 +7,10 @@ defmodule MatrixServer.Account do
|
|||
alias MatrixServerWeb.Client.Request.{Register, Login}
|
||||
alias Ecto.Multi
|
||||
|
||||
@type t :: %__MODULE__{
|
||||
password_hash: String.t()
|
||||
}
|
||||
|
||||
@max_mxid_length 255
|
||||
|
||||
@primary_key {:localpart, :string, []}
|
||||
|
|
|
@ -7,6 +7,12 @@ defmodule MatrixServer.Room do
|
|||
alias MatrixServer.{Repo, Room, Event, Alias, RoomServer}
|
||||
alias MatrixServerWeb.Client.Request.CreateRoom
|
||||
|
||||
@type t :: %__MODULE__{
|
||||
visibility: :public | :private,
|
||||
state: list(list(String.t())),
|
||||
forward_extremities: list(String.t())
|
||||
}
|
||||
|
||||
@primary_key {:id, :string, []}
|
||||
schema "rooms" do
|
||||
field :visibility, Ecto.Enum, values: [:public, :private]
|
||||
|
|
|
@ -129,8 +129,10 @@ defmodule MatrixServer.StateResolution.Authorization do
|
|||
end
|
||||
|
||||
defp _authorized?(%Event{type: "m.room.third_party_invite", sender: sender}, state_set) do
|
||||
power_levels = get_power_levels(state_set)
|
||||
# Check rule: 7.1
|
||||
has_power_level(sender, state_set, :invite)
|
||||
|
||||
has_power_level(sender, power_levels, :invite)
|
||||
end
|
||||
|
||||
defp _authorized?(%Event{state_key: state_key, sender: sender} = event, state_set) do
|
||||
|
@ -202,8 +204,10 @@ defmodule MatrixServer.StateResolution.Authorization do
|
|||
defp get_action_power_level(:invite, _), do: 50
|
||||
defp get_action_power_level(:ban, %{"ban" => pl}), do: pl
|
||||
defp get_action_power_level(:ban, _), do: 50
|
||||
defp get_action_power_level(:redact, %{"redact" => pl}), do: pl
|
||||
defp get_action_power_level(:redact, _), do: 50
|
||||
# defp get_action_power_level(:redact, %{"redact" => pl}), do: pl
|
||||
# defp get_action_power_level(:redact, _), do: 50
|
||||
# defp get_action_power_level(:kick, %{"kick" => pl}), do: pl
|
||||
# defp get_action_power_level(:kick, _), do: 50
|
||||
|
||||
defp get_action_power_level({:event, %Event{type: type}}, %{"events" => events})
|
||||
when is_map_key(events, type),
|
||||
|
|
|
@ -5,6 +5,16 @@ defmodule MatrixServerWeb.Client.Request.CreateRoom do
|
|||
|
||||
alias Ecto.Changeset
|
||||
|
||||
@type t :: %__MODULE__{
|
||||
visibility: String.t(),
|
||||
room_alias_name: String.t(),
|
||||
name: String.t(),
|
||||
topic: String.t(),
|
||||
invite: list(String.t()),
|
||||
room_version: String.t(),
|
||||
preset: String.t()
|
||||
}
|
||||
|
||||
@primary_key false
|
||||
embedded_schema do
|
||||
field :visibility, :string
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue