@@ -25,10 +25,15 @@ Configuration: `federation_incoming_replies_max_depth` option | |||
- Admin API: Return users' tags when querying reports | |||
- Admin API: Return avatar and display name when querying users | |||
- Admin API: Allow querying user by ID | |||
- Admin API: Added support for `tuples`. | |||
- Added synchronization of following/followers counters for external users | |||
- Configuration: `enabled` option for `Pleroma.Emails.Mailer`, defaulting to `false`. | |||
- Mastodon API: Add support for categories for custom emojis by reusing the group feature. <https://github.com/tootsuite/mastodon/pull/11196> | |||
### Changed | |||
- Configuration: Filter.AnonymizeFilename added ability to retain file extension with custom text | |||
- Admin API: changed json structure for saving config settings. | |||
## [1.0.0] - 2019-06-29 | |||
### Security | |||
- Mastodon API: Fix display names not being sanitized | |||
@@ -573,7 +573,7 @@ Note: Available `:permission_group` is currently moderator and admin. 404 is ret | |||
configs: [ | |||
{ | |||
"group": string, | |||
"key": string, | |||
"key": string or string with leading `:` for atoms, | |||
"value": string or {} or [] or {"tuple": []} | |||
} | |||
] | |||
@@ -583,10 +583,11 @@ Note: Available `:permission_group` is currently moderator and admin. 404 is ret | |||
## `/api/pleroma/admin/config` | |||
### Update config settings | |||
Module name can be passed as string, which starts with `Pleroma`, e.g. `"Pleroma.Upload"`. | |||
Atom or boolean value can be passed with `:` in the beginning, e.g. `":true"`, `":upload"`. For keys it is not needed. | |||
Integer with `i:`, e.g. `"i:150"`. | |||
Tuple with more than 2 values with `{"tuple": ["first_val", Pleroma.Module, []]}`. | |||
Atom keys and values can be passed with `:` in the beginning, e.g. `":upload"`. | |||
Tuples can be passed as `{"tuple": ["first_val", Pleroma.Module, []]}`. | |||
`{"tuple": ["some_string", "Pleroma.Some.Module", []]}` will be converted to `{"some_string", Pleroma.Some.Module, []}`. | |||
Keywords can be passed as lists with 2 child tuples, e.g. | |||
`[{"tuple": ["first_val", Pleroma.Module]}, {"tuple": ["second_val", true]}]`. | |||
Compile time settings (need instance reboot): | |||
- all settings by this keys: | |||
@@ -603,7 +604,7 @@ Compile time settings (need instance reboot): | |||
- Params: | |||
- `configs` => [ | |||
- `group` (string) | |||
- `key` (string) | |||
- `key` (string or string with leading `:` for atoms) | |||
- `value` (string, [], {} or {"tuple": []}) | |||
- `delete` = true (optional, if parameter must be deleted) | |||
] | |||
@@ -616,24 +617,25 @@ Compile time settings (need instance reboot): | |||
{ | |||
"group": "pleroma", | |||
"key": "Pleroma.Upload", | |||
"value": { | |||
"uploader": "Pleroma.Uploaders.Local", | |||
"filters": ["Pleroma.Upload.Filter.Dedupe"], | |||
"link_name": ":true", | |||
"proxy_remote": ":false", | |||
"proxy_opts": { | |||
"redirect_on_failure": ":false", | |||
"max_body_length": "i:1048576", | |||
"http": { | |||
"follow_redirect": ":true", | |||
"pool": ":upload" | |||
} | |||
}, | |||
"dispatch": { | |||
"value": [ | |||
{"tuple": [":uploader", "Pleroma.Uploaders.Local"]}, | |||
{"tuple": [":filters", ["Pleroma.Upload.Filter.Dedupe"]]}, | |||
{"tuple": [":link_name", true]}, | |||
{"tuple": [":proxy_remote", false]}, | |||
{"tuple": [":proxy_opts", [ | |||
{"tuple": [":redirect_on_failure", false]}, | |||
{"tuple": [":max_body_length", 1048576]}, | |||
{"tuple": [":http": [ | |||
{"tuple": [":follow_redirect", true]}, | |||
{"tuple": [":pool", ":upload"]}, | |||
]]} | |||
] | |||
]}, | |||
{"tuple": [":dispatch", { | |||
"tuple": ["/api/v1/streaming", "Pleroma.Web.MastodonAPI.WebsocketHandler", []] | |||
} | |||
} | |||
} | |||
}]} | |||
] | |||
} | |||
] | |||
} | |||
@@ -644,7 +646,7 @@ Compile time settings (need instance reboot): | |||
configs: [ | |||
{ | |||
"group": string, | |||
"key": string, | |||
"key": string or string with leading `:` for atoms, | |||
"value": string or {} or [] or {"tuple": []} | |||
} | |||
] | |||
@@ -371,13 +371,13 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do | |||
if Pleroma.Config.get([:instance, :dynamic_configuration]) do | |||
updated = | |||
Enum.map(configs, fn | |||
%{"group" => group, "key" => key, "value" => value} -> | |||
{:ok, config} = Config.update_or_create(%{group: group, key: key, value: value}) | |||
config | |||
%{"group" => group, "key" => key, "delete" => "true"} -> | |||
{:ok, _} = Config.delete(%{group: group, key: key}) | |||
nil | |||
%{"group" => group, "key" => key, "value" => value} -> | |||
{:ok, config} = Config.update_or_create(%{group: group, key: key, value: value}) | |||
config | |||
end) | |||
|> Enum.reject(&is_nil(&1)) | |||
@@ -67,99 +67,86 @@ defmodule Pleroma.Web.AdminAPI.Config do | |||
end | |||
@spec from_binary(binary()) :: term() | |||
def from_binary(value), do: :erlang.binary_to_term(value) | |||
def from_binary(binary), do: :erlang.binary_to_term(binary) | |||
@spec from_binary_to_map(binary()) :: any() | |||
def from_binary_to_map(binary) do | |||
@spec from_binary_with_convert(binary()) :: any() | |||
def from_binary_with_convert(binary) do | |||
from_binary(binary) | |||
|> do_convert() | |||
end | |||
defp do_convert([{k, v}] = value) when is_list(value) and length(value) == 1, | |||
do: %{k => do_convert(v)} | |||
defp do_convert(entity) when is_list(entity) do | |||
for v <- entity, into: [], do: do_convert(v) | |||
end | |||
defp do_convert(values) when is_list(values), do: for(val <- values, do: do_convert(val)) | |||
defp do_convert(entity) when is_map(entity) do | |||
for {k, v} <- entity, into: %{}, do: {do_convert(k), do_convert(v)} | |||
end | |||
defp do_convert({k, v} = value) when is_tuple(value), | |||
do: %{k => do_convert(v)} | |||
defp do_convert({:dispatch, [entity]}), do: %{"tuple" => [":dispatch", [inspect(entity)]]} | |||
defp do_convert(value) when is_tuple(value), do: %{"tuple" => do_convert(Tuple.to_list(value))} | |||
defp do_convert(entity) when is_tuple(entity), | |||
do: %{"tuple" => do_convert(Tuple.to_list(entity))} | |||
defp do_convert(value) when is_binary(value) or is_map(value) or is_number(value), do: value | |||
defp do_convert(entity) when is_boolean(entity) or is_number(entity) or is_nil(entity), | |||
do: entity | |||
defp do_convert(value) when is_atom(value) do | |||
string = to_string(value) | |||
defp do_convert(entity) when is_atom(entity) do | |||
string = to_string(entity) | |||
if String.starts_with?(string, "Elixir."), | |||
do: String.trim_leading(string, "Elixir."), | |||
else: value | |||
do: do_convert(string), | |||
else: ":" <> string | |||
end | |||
@spec transform(any()) :: binary() | |||
def transform(%{"tuple" => _} = entity), do: :erlang.term_to_binary(do_transform(entity)) | |||
def transform(entity) when is_map(entity) do | |||
tuples = | |||
for {k, v} <- entity, | |||
into: [], | |||
do: {if(is_atom(k), do: k, else: String.to_atom(k)), do_transform(v)} | |||
defp do_convert("Elixir." <> module_name), do: module_name | |||
Enum.reject(tuples, fn {_k, v} -> is_nil(v) end) | |||
|> Enum.sort() | |||
|> :erlang.term_to_binary() | |||
end | |||
defp do_convert(entity) when is_binary(entity), do: entity | |||
def transform(entity) when is_list(entity) do | |||
list = Enum.map(entity, &do_transform(&1)) | |||
:erlang.term_to_binary(list) | |||
@spec transform(any()) :: binary() | |||
def transform(entity) when is_binary(entity) or is_map(entity) or is_list(entity) do | |||
:erlang.term_to_binary(do_transform(entity)) | |||
end | |||
def transform(entity), do: :erlang.term_to_binary(entity) | |||
defp do_transform(%Regex{} = value) when is_map(value), do: value | |||
defp do_transform(%Regex{} = entity) when is_map(entity), do: entity | |||
defp do_transform(%{"tuple" => [k, values] = entity}) when length(entity) == 2 do | |||
{do_transform(k), do_transform(values)} | |||
defp do_transform(%{"tuple" => [":dispatch", [entity]]}) do | |||
cleaned_string = String.replace(entity, ~r/[^\w|^{:,[|^,|^[|^\]^}|^\/|^\.|^"]^\s/, "") | |||
{dispatch_settings, []} = Code.eval_string(cleaned_string, [], requires: [], macros: []) | |||
{:dispatch, [dispatch_settings]} | |||
end | |||
defp do_transform(%{"tuple" => values}) do | |||
Enum.reduce(values, {}, fn val, acc -> Tuple.append(acc, do_transform(val)) end) | |||
defp do_transform(%{"tuple" => entity}) do | |||
Enum.reduce(entity, {}, fn val, acc -> Tuple.append(acc, do_transform(val)) end) | |||
end | |||
defp do_transform(value) when is_map(value) do | |||
values = for {key, val} <- value, into: [], do: {String.to_atom(key), do_transform(val)} | |||
Enum.sort(values) | |||
defp do_transform(entity) when is_map(entity) do | |||
for {k, v} <- entity, into: %{}, do: {do_transform(k), do_transform(v)} | |||
end | |||
defp do_transform(value) when is_list(value) do | |||
Enum.map(value, &do_transform(&1)) | |||
defp do_transform(entity) when is_list(entity) do | |||
for v <- entity, into: [], do: do_transform(v) | |||
end | |||
defp do_transform(entity) when is_list(entity) and length(entity) == 1, do: hd(entity) | |||
defp do_transform(value) when is_binary(value) do | |||
String.trim(value) | |||
defp do_transform(entity) when is_binary(entity) do | |||
String.trim(entity) | |||
|> do_transform_string() | |||
end | |||
defp do_transform(value), do: value | |||
defp do_transform_string(value) when byte_size(value) == 0, do: nil | |||
defp do_transform_string(value) do | |||
cond do | |||
String.starts_with?(value, "Pleroma") or String.starts_with?(value, "Phoenix") -> | |||
String.to_existing_atom("Elixir." <> value) | |||
defp do_transform(entity), do: entity | |||
String.starts_with?(value, ":") -> | |||
String.replace(value, ":", "") |> String.to_existing_atom() | |||
defp do_transform_string("~r/" <> pattern) do | |||
pattern = String.trim_trailing(pattern, "/") | |||
~r/#{pattern}/ | |||
end | |||
String.starts_with?(value, "i:") -> | |||
String.replace(value, "i:", "") |> String.to_integer() | |||
defp do_transform_string(":" <> atom), do: String.to_atom(atom) | |||
true -> | |||
value | |||
end | |||
defp do_transform_string(value) do | |||
if String.starts_with?(value, "Pleroma") or String.starts_with?(value, "Phoenix"), | |||
do: String.to_existing_atom("Elixir." <> value), | |||
else: value | |||
end | |||
end |
@@ -15,7 +15,7 @@ defmodule Pleroma.Web.AdminAPI.ConfigView do | |||
%{ | |||
key: config.key, | |||
group: config.group, | |||
value: Pleroma.Web.AdminAPI.Config.from_binary_to_map(config.value) | |||
value: Pleroma.Web.AdminAPI.Config.from_binary_with_convert(config.value) | |||
} | |||
end | |||
end |
@@ -1408,13 +1408,18 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do | |||
configs: [ | |||
%{group: "pleroma", key: "key1", value: "value1"}, | |||
%{ | |||
group: "ueberauth", | |||
key: "Ueberauth.Strategy.Twitter.OAuth", | |||
value: [%{"tuple" => [":consumer_secret", "aaaa"]}] | |||
}, | |||
%{ | |||
group: "pleroma", | |||
key: "key2", | |||
value: %{ | |||
"nested_1" => "nested_value1", | |||
"nested_2" => [ | |||
%{"nested_22" => "nested_value222"}, | |||
%{"nested_33" => %{"nested_44" => "nested_444"}} | |||
":nested_1" => "nested_value1", | |||
":nested_2" => [ | |||
%{":nested_22" => "nested_value222"}, | |||
%{":nested_33" => %{":nested_44" => "nested_444"}} | |||
] | |||
} | |||
}, | |||
@@ -1423,13 +1428,13 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do | |||
key: "key3", | |||
value: [ | |||
%{"nested_3" => ":nested_3", "nested_33" => "nested_33"}, | |||
%{"nested_4" => ":true"} | |||
%{"nested_4" => true} | |||
] | |||
}, | |||
%{ | |||
group: "pleroma", | |||
key: "key4", | |||
value: %{"nested_5" => ":upload", "endpoint" => "https://example.com"} | |||
value: %{":nested_5" => ":upload", "endpoint" => "https://example.com"} | |||
}, | |||
%{ | |||
group: "idna", | |||
@@ -1447,30 +1452,33 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do | |||
"value" => "value1" | |||
}, | |||
%{ | |||
"group" => "ueberauth", | |||
"key" => "Ueberauth.Strategy.Twitter.OAuth", | |||
"value" => [%{"tuple" => [":consumer_secret", "aaaa"]}] | |||
}, | |||
%{ | |||
"group" => "pleroma", | |||
"key" => "key2", | |||
"value" => [ | |||
%{"nested_1" => "nested_value1"}, | |||
%{ | |||
"nested_2" => [ | |||
%{"nested_22" => "nested_value222"}, | |||
%{"nested_33" => %{"nested_44" => "nested_444"}} | |||
] | |||
} | |||
] | |||
"value" => %{ | |||
":nested_1" => "nested_value1", | |||
":nested_2" => [ | |||
%{":nested_22" => "nested_value222"}, | |||
%{":nested_33" => %{":nested_44" => "nested_444"}} | |||
] | |||
} | |||
}, | |||
%{ | |||
"group" => "pleroma", | |||
"key" => "key3", | |||
"value" => [ | |||
[%{"nested_3" => "nested_3"}, %{"nested_33" => "nested_33"}], | |||
%{"nested_3" => ":nested_3", "nested_33" => "nested_33"}, | |||
%{"nested_4" => true} | |||
] | |||
}, | |||
%{ | |||
"group" => "pleroma", | |||
"key" => "key4", | |||
"value" => [%{"endpoint" => "https://example.com"}, %{"nested_5" => "upload"}] | |||
"value" => %{"endpoint" => "https://example.com", ":nested_5" => ":upload"} | |||
}, | |||
%{ | |||
"group" => "idna", | |||
@@ -1482,23 +1490,23 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do | |||
assert Application.get_env(:pleroma, :key1) == "value1" | |||
assert Application.get_env(:pleroma, :key2) == [ | |||
assert Application.get_env(:pleroma, :key2) == %{ | |||
nested_1: "nested_value1", | |||
nested_2: [ | |||
[nested_22: "nested_value222"], | |||
[nested_33: [nested_44: "nested_444"]] | |||
%{nested_22: "nested_value222"}, | |||
%{nested_33: %{nested_44: "nested_444"}} | |||
] | |||
] | |||
} | |||
assert Application.get_env(:pleroma, :key3) == [ | |||
[nested_3: :nested_3, nested_33: "nested_33"], | |||
[nested_4: true] | |||
%{"nested_3" => :nested_3, "nested_33" => "nested_33"}, | |||
%{"nested_4" => true} | |||
] | |||
assert Application.get_env(:pleroma, :key4) == [ | |||
endpoint: "https://example.com", | |||
assert Application.get_env(:pleroma, :key4) == %{ | |||
"endpoint" => "https://example.com", | |||
nested_5: :upload | |||
] | |||
} | |||
assert Application.get_env(:idna, :key5) == {"string", Pleroma.Captcha.NotReal, []} | |||
end | |||
@@ -1507,11 +1515,22 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do | |||
config1 = insert(:config, key: "keyaa1") | |||
config2 = insert(:config, key: "keyaa2") | |||
insert(:config, | |||
group: "ueberauth", | |||
key: "Ueberauth.Strategy.Microsoft.OAuth", | |||
value: :erlang.term_to_binary([]) | |||
) | |||
conn = | |||
post(conn, "/api/pleroma/admin/config", %{ | |||
configs: [ | |||
%{group: config1.group, key: config1.key, value: "another_value"}, | |||
%{group: config2.group, key: config2.key, delete: "true"} | |||
%{group: config2.group, key: config2.key, delete: "true"}, | |||
%{ | |||
group: "ueberauth", | |||
key: "Ueberauth.Strategy.Microsoft.OAuth", | |||
delete: "true" | |||
} | |||
] | |||
}) | |||
@@ -1536,11 +1555,13 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do | |||
%{ | |||
"group" => "pleroma", | |||
"key" => "Pleroma.Captcha.NotReal", | |||
"value" => %{ | |||
"enabled" => ":false", | |||
"method" => "Pleroma.Captcha.Kocaptcha", | |||
"seconds_valid" => "i:60" | |||
} | |||
"value" => [ | |||
%{"tuple" => [":enabled", false]}, | |||
%{"tuple" => [":method", "Pleroma.Captcha.Kocaptcha"]}, | |||
%{"tuple" => [":seconds_valid", 60]}, | |||
%{"tuple" => [":path", ""]}, | |||
%{"tuple" => [":key1", nil]} | |||
] | |||
} | |||
] | |||
}) | |||
@@ -1551,9 +1572,11 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do | |||
"group" => "pleroma", | |||
"key" => "Pleroma.Captcha.NotReal", | |||
"value" => [ | |||
%{"enabled" => false}, | |||
%{"method" => "Pleroma.Captcha.Kocaptcha"}, | |||
%{"seconds_valid" => 60} | |||
%{"tuple" => [":enabled", false]}, | |||
%{"tuple" => [":method", "Pleroma.Captcha.Kocaptcha"]}, | |||
%{"tuple" => [":seconds_valid", 60]}, | |||
%{"tuple" => [":path", ""]}, | |||
%{"tuple" => [":key1", nil]} | |||
] | |||
} | |||
] | |||
@@ -1569,51 +1592,57 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do | |||
"key" => "Pleroma.Web.Endpoint.NotReal", | |||
"value" => [ | |||
%{ | |||
"http" => %{ | |||
"dispatch" => [ | |||
"tuple" => [ | |||
":http", | |||
[ | |||
%{ | |||
"tuple" => [ | |||
":_", | |||
":key2", | |||
[ | |||
%{ | |||
"tuple" => [ | |||
"/api/v1/streaming", | |||
"Pleroma.Web.MastodonAPI.WebsocketHandler", | |||
[] | |||
] | |||
}, | |||
%{ | |||
"tuple" => [ | |||
"/websocket", | |||
"Phoenix.Endpoint.CowboyWebSocket", | |||
%{ | |||
"tuple" => [ | |||
"Phoenix.Transports.WebSocket", | |||
%{ | |||
"tuple" => [ | |||
"Pleroma.Web.Endpoint", | |||
"Pleroma.Web.UserSocket", | |||
[] | |||
] | |||
} | |||
] | |||
} | |||
] | |||
}, | |||
%{ | |||
"tuple" => [ | |||
":_", | |||
"Phoenix.Endpoint.Cowboy2Handler", | |||
%{ | |||
"tuple" => ["Pleroma.Web.Endpoint", []] | |||
} | |||
[ | |||
%{ | |||
"tuple" => [ | |||
"/api/v1/streaming", | |||
"Pleroma.Web.MastodonAPI.WebsocketHandler", | |||
[] | |||
] | |||
}, | |||
%{ | |||
"tuple" => [ | |||
"/websocket", | |||
"Phoenix.Endpoint.CowboyWebSocket", | |||
%{ | |||
"tuple" => [ | |||
"Phoenix.Transports.WebSocket", | |||
%{ | |||
"tuple" => [ | |||
"Pleroma.Web.Endpoint", | |||
"Pleroma.Web.UserSocket", | |||
[] | |||
] | |||
} | |||
] | |||
} | |||
] | |||
}, | |||
%{ | |||
"tuple" => [ | |||
":_", | |||
"Phoenix.Endpoint.Cowboy2Handler", | |||
%{"tuple" => ["Pleroma.Web.Endpoint", []]} | |||
] | |||
} | |||
] | |||
] | |||
} | |||
] | |||
] | |||
} | |||
] | |||
} | |||
] | |||
} | |||
] | |||
} | |||
@@ -1627,41 +1656,206 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do | |||
"key" => "Pleroma.Web.Endpoint.NotReal", | |||
"value" => [ | |||
%{ | |||
"http" => %{ | |||
"dispatch" => %{ | |||
"_" => [ | |||
%{ | |||
"tuple" => [ | |||
"/api/v1/streaming", | |||
"Pleroma.Web.MastodonAPI.WebsocketHandler", | |||
[] | |||
] | |||
}, | |||
%{ | |||
"tuple" => [ | |||
"/websocket", | |||
"Phoenix.Endpoint.CowboyWebSocket", | |||
"tuple" => [ | |||
":http", | |||
[ | |||
%{ | |||
"tuple" => [ | |||
":key2", | |||
[ | |||
%{ | |||
"Elixir.Phoenix.Transports.WebSocket" => %{ | |||
"tuple" => [ | |||
"Pleroma.Web.Endpoint", | |||
"Pleroma.Web.UserSocket", | |||
[] | |||
"tuple" => [ | |||
":_", | |||
[ | |||
%{ | |||
"tuple" => [ | |||
"/api/v1/streaming", | |||
"Pleroma.Web.MastodonAPI.WebsocketHandler", | |||
[] | |||
] | |||
}, | |||
%{ | |||
"tuple" => [ | |||
"/websocket", | |||
"Phoenix.Endpoint.CowboyWebSocket", | |||
%{ | |||
"tuple" => [ | |||
"Phoenix.Transports.WebSocket", | |||
%{ | |||
"tuple" => [ | |||
"Pleroma.Web.Endpoint", | |||
"Pleroma.Web.UserSocket", | |||
[] | |||
] | |||
} | |||
] | |||
} | |||
] | |||
}, | |||
%{ | |||
"tuple" => [ | |||
":_", | |||
"Phoenix.Endpoint.Cowboy2Handler", | |||
%{"tuple" => ["Pleroma.Web.Endpoint", []]} | |||
] | |||
} | |||
] | |||
} | |||
] | |||
} | |||
] | |||
}, | |||
%{ | |||
"tuple" => [ | |||
"_", | |||
"Phoenix.Endpoint.Cowboy2Handler", | |||
%{"Elixir.Pleroma.Web.Endpoint" => []} | |||
] | |||
] | |||
} | |||
] | |||
] | |||
} | |||
] | |||
} | |||
] | |||
} | |||
end | |||
test "settings with nesting map", %{conn: conn} do | |||
conn = | |||
post(conn, "/api/pleroma/admin/config", %{ | |||
configs: [ | |||
%{ | |||
"group" => "pleroma", | |||
"key" => "key1", | |||
"value" => [ | |||
%{"tuple" => [":key2", "some_val"]}, | |||
%{ | |||
"tuple" => [ | |||
":key3", | |||
%{ | |||
":max_options" => 20, | |||
":max_option_chars" => 200, | |||
":min_expiration" => 0, | |||
":max_expiration" => 31_536_000, | |||
"nested" => %{ | |||
":max_options" => 20, | |||
":max_option_chars" => 200, | |||
":min_expiration" => 0, | |||
":max_expiration" => 31_536_000 | |||
} | |||
} | |||
] | |||
} | |||
] | |||
} | |||
] | |||
}) | |||
assert json_response(conn, 200) == | |||
%{ | |||
"configs" => [ | |||
%{ | |||
"group" => "pleroma", | |||
"key" => "key1", | |||
"value" => [ | |||
%{"tuple" => [":key2", "some_val"]}, | |||
%{ | |||
"tuple" => [ | |||
":key3", | |||
%{ | |||
":max_expiration" => 31_536_000, | |||
":max_option_chars" => 200, | |||
":max_options" => 20, | |||
":min_expiration" => 0, | |||
"nested" => %{ | |||
":max_expiration" => 31_536_000, | |||
":max_option_chars" => 200, | |||
":max_options" => 20, | |||
":min_expiration" => 0 | |||
} | |||
] | |||
} | |||
} | |||
] | |||
} | |||
] | |||
} | |||
] | |||
} | |||
end | |||
test "value as map", %{conn: conn} do | |||
conn = | |||
post(conn, "/api/pleroma/admin/config", %{ | |||
configs: [ | |||
%{ | |||
"group" => "pleroma", | |||
"key" => "key1", | |||
"value" => %{"key" => "some_val"} | |||
} | |||
] | |||
}) | |||
assert json_response(conn, 200) == | |||
%{ | |||
"configs" => [ | |||
%{ | |||
"group" => "pleroma", | |||
"key" => "key1", | |||
"value" => %{"key" => "some_val"} | |||
} | |||
] | |||
} | |||
end | |||
test "dispatch setting", %{conn: conn} do | |||
conn = | |||
post(conn, "/api/pleroma/admin/config", %{ | |||
configs: [ | |||
%{ | |||
"group" => "pleroma", | |||
"key" => "Pleroma.Web.Endpoint.NotReal", | |||
"value" => [ | |||
%{ | |||
"tuple" => [ | |||
":http", | |||
[ | |||
%{"tuple" => [":ip", %{"tuple" => [127, 0, 0, 1]}]}, | |||
%{"tuple" => [":dispatch", ["{:_, | |||
[ | |||
{\"/api/v1/streaming\", Pleroma.Web.MastodonAPI.WebsocketHandler, []}, | |||
{\"/websocket\", Phoenix.Endpoint.CowboyWebSocket, | |||
{Phoenix.Transports.WebSocket, | |||
{Pleroma.Web.Endpoint, Pleroma.Web.UserSocket, [path: \"/websocket\"]}}}, | |||
{:_, Phoenix.Endpoint.Cowboy2Handler, {Pleroma.Web.Endpoint, []}} | |||
]}"]]} | |||
] | |||
] | |||
} | |||
] | |||
} | |||
] | |||
}) | |||
dispatch_string = | |||
"{:_, [{\"/api/v1/streaming\", Pleroma.Web.MastodonAPI.WebsocketHandler, []}, " <> | |||
"{\"/websocket\", Phoenix.Endpoint.CowboyWebSocket, {Phoenix.Transports.WebSocket, " <> | |||
"{Pleroma.Web.Endpoint, Pleroma.Web.UserSocket, [path: \"/websocket\"]}}}, " <> | |||
"{:_, Phoenix.Endpoint.Cowboy2Handler, {Pleroma.Web.Endpoint, []}}]}" | |||
assert json_response(conn, 200) == %{ | |||
"configs" => [ | |||
%{ | |||
"group" => "pleroma", | |||
"key" => "Pleroma.Web.Endpoint.NotReal", | |||
"value" => [ | |||
%{ | |||
"tuple" => [ | |||
":http", | |||
[ | |||
%{"tuple" => [":ip", %{"tuple" => [127, 0, 0, 1]}]}, | |||
%{ | |||
"tuple" => [ | |||
":dispatch", | |||
[ | |||
dispatch_string | |||
] | |||
] | |||
} | |||
] | |||
] | |||
} | |||
] | |||
} | |||
@@ -61,117 +61,306 @@ defmodule Pleroma.Web.AdminAPI.ConfigTest do | |||
assert Config.from_binary(binary) == "value as string" | |||
end | |||
test "boolean" do | |||
binary = Config.transform(false) | |||
assert binary == :erlang.term_to_binary(false) | |||
assert Config.from_binary(binary) == false | |||
end | |||
test "nil" do | |||
binary = Config.transform(nil) | |||
assert binary == :erlang.term_to_binary(nil) | |||
assert Config.from_binary(binary) == nil | |||
end | |||
test "integer" do | |||
binary = Config.transform(150) | |||
assert binary == :erlang.term_to_binary(150) | |||
assert Config.from_binary(binary) == 150 | |||
end | |||
test "atom" do | |||
binary = Config.transform(":atom") | |||
assert binary == :erlang.term_to_binary(:atom) | |||
assert Config.from_binary(binary) == :atom | |||
end | |||
test "pleroma module" do | |||
binary = Config.transform("Pleroma.Bookmark") | |||
assert binary == :erlang.term_to_binary(Pleroma.Bookmark) | |||
assert Config.from_binary(binary) == Pleroma.Bookmark | |||
end | |||
test "phoenix module" do | |||
binary = Config.transform("Phoenix.Socket.V1.JSONSerializer") | |||
assert binary == :erlang.term_to_binary(Phoenix.Socket.V1.JSONSerializer) | |||
assert Config.from_binary(binary) == Phoenix.Socket.V1.JSONSerializer | |||
end | |||
test "sigil" do | |||
binary = Config.transform("~r/comp[lL][aA][iI][nN]er/") | |||
assert binary == :erlang.term_to_binary(~r/comp[lL][aA][iI][nN]er/) | |||
assert Config.from_binary(binary) == ~r/comp[lL][aA][iI][nN]er/ | |||
end | |||
test "2 child tuple" do | |||
binary = Config.transform(%{"tuple" => ["v1", ":v2"]}) | |||
assert binary == :erlang.term_to_binary({"v1", :v2}) | |||
assert Config.from_binary(binary) == {"v1", :v2} | |||
end | |||
test "tuple with n childs" do | |||
binary = | |||
Config.transform(%{ | |||
"tuple" => [ | |||
"v1", | |||
":v2", | |||
"Pleroma.Bookmark", | |||
150, | |||
false, | |||
"Phoenix.Socket.V1.JSONSerializer" | |||
] | |||
}) | |||
assert binary == | |||
:erlang.term_to_binary( | |||
{"v1", :v2, Pleroma.Bookmark, 150, false, Phoenix.Socket.V1.JSONSerializer} | |||
) | |||
assert Config.from_binary(binary) == | |||
{"v1", :v2, Pleroma.Bookmark, 150, false, Phoenix.Socket.V1.JSONSerializer} | |||
end | |||
test "tuple with dispatch key" do | |||
binary = Config.transform(%{"tuple" => [":dispatch", ["{:_, | |||
[ | |||
{\"/api/v1/streaming\", Pleroma.Web.MastodonAPI.WebsocketHandler, []}, | |||
{\"/websocket\", Phoenix.Endpoint.CowboyWebSocket, | |||
{Phoenix.Transports.WebSocket, | |||
{Pleroma.Web.Endpoint, Pleroma.Web.UserSocket, [path: \"/websocket\"]}}}, | |||
{:_, Phoenix.Endpoint.Cowboy2Handler, {Pleroma.Web.Endpoint, []}} | |||
]}"]]}) | |||
assert binary == | |||
:erlang.term_to_binary( | |||
{:dispatch, | |||
[ | |||
{:_, | |||
[ | |||
{"/api/v1/streaming", Pleroma.Web.MastodonAPI.WebsocketHandler, []}, | |||
{"/websocket", Phoenix.Endpoint.CowboyWebSocket, | |||
{Phoenix.Transports.WebSocket, | |||
{Pleroma.Web.Endpoint, Pleroma.Web.UserSocket, [path: "/websocket"]}}}, | |||
{:_, Phoenix.Endpoint.Cowboy2Handler, {Pleroma.Web.Endpoint, []}} | |||
]} | |||
]} | |||
) | |||
assert Config.from_binary(binary) == | |||
{:dispatch, | |||
[ | |||
{:_, | |||
[ | |||
{"/api/v1/streaming", Pleroma.Web.MastodonAPI.WebsocketHandler, []}, | |||
{"/websocket", Phoenix.Endpoint.CowboyWebSocket, | |||
{Phoenix.Transports.WebSocket, | |||
{Pleroma.Web.Endpoint, Pleroma.Web.UserSocket, [path: "/websocket"]}}}, | |||
{:_, Phoenix.Endpoint.Cowboy2Handler, {Pleroma.Web.Endpoint, []}} | |||
]} | |||
]} | |||
end | |||
test "map with string key" do | |||
binary = Config.transform(%{"key" => "value"}) | |||
assert binary == :erlang.term_to_binary(%{"key" => "value"}) | |||
assert Config.from_binary(binary) == %{"key" => "value"} | |||
end | |||
test "map with atom key" do | |||
binary = Config.transform(%{":key" => "value"}) | |||
assert binary == :erlang.term_to_binary(%{key: "value"}) | |||
assert Config.from_binary(binary) == %{key: "value"} | |||
end | |||
test "list of strings" do | |||
binary = Config.transform(["v1", "v2", "v3"]) | |||
assert binary == :erlang.term_to_binary(["v1", "v2", "v3"]) | |||
assert Config.from_binary(binary) == ["v1", "v2", "v3"] | |||
end | |||
test "list of modules" do | |||
binary = Config.transform(["Pleroma.Repo", "Pleroma.Activity"]) | |||
assert binary == :erlang.term_to_binary([Pleroma.Repo, Pleroma.Activity]) | |||
assert Config.from_binary(binary) == [Pleroma.Repo, Pleroma.Activity] | |||
end | |||
test "list of strings" do | |||
binary = Config.transform(["string1", "string2"]) | |||
assert binary == :erlang.term_to_binary(["string1", "string2"]) | |||
assert Config.from_binary(binary) == ["string1", "string2"] | |||
test "list of atoms" do | |||
binary = Config.transform([":v1", ":v2", ":v3"]) | |||
assert binary == :erlang.term_to_binary([:v1, :v2, :v3]) | |||
assert Config.from_binary(binary) == [:v1, :v2, :v3] | |||
end | |||
test "map" do | |||
test "list of mixed values" do | |||
binary = | |||
Config.transform(%{ | |||
"types" => "Pleroma.PostgresTypes", | |||
"telemetry_event" => ["Pleroma.Repo.Instrumenter"], | |||
"migration_lock" => "" | |||
}) | |||
Config.transform([ | |||
"v1", | |||
":v2", | |||
"Pleroma.Repo", | |||
"Phoenix.Socket.V1.JSONSerializer", | |||
15, | |||
false | |||
]) | |||
assert binary == | |||
:erlang.term_to_binary([ | |||
"v1", | |||
:v2, | |||
Pleroma.Repo, | |||
Phoenix.Socket.V1.JSONSerializer, | |||
15, | |||
false | |||
]) | |||
assert Config.from_binary(binary) == [ | |||
"v1", | |||
:v2, | |||
Pleroma.Repo, | |||
Phoenix.Socket.V1.JSONSerializer, | |||
15, | |||
false | |||
] | |||
end | |||
test "simple keyword" do | |||
binary = Config.transform([%{"tuple" => [":key", "value"]}]) | |||
assert binary == :erlang.term_to_binary([{:key, "value"}]) | |||
assert Config.from_binary(binary) == [{:key, "value"}] | |||
assert Config.from_binary(binary) == [key: "value"] | |||
end | |||
test "keyword" do | |||
binary = | |||
Config.transform([ | |||
%{"tuple" => [":types", "Pleroma.PostgresTypes"]}, | |||
%{"tuple" => [":telemetry_event", ["Pleroma.Repo.Instrumenter"]]}, | |||
%{"tuple" => [":migration_lock", nil]}, | |||
%{"tuple" => [":key1", 150]}, | |||
%{"tuple" => [":key2", "string"]} | |||
]) | |||
assert binary == | |||
:erlang.term_to_binary( | |||
types: Pleroma.PostgresTypes, | |||
telemetry_event: [Pleroma.Repo.Instrumenter], | |||
types: Pleroma.PostgresTypes | |||
migration_lock: nil, | |||
key1: 150, | |||
key2: "string" | |||
) | |||
assert Config.from_binary(binary) == [ | |||
types: Pleroma.PostgresTypes, | |||
telemetry_event: [Pleroma.Repo.Instrumenter], | |||
types: Pleroma.PostgresTypes | |||
migration_lock: nil, | |||
key1: 150, | |||
key2: "string" | |||
] | |||
end | |||
test "complex map with nested integers, lists and atoms" do | |||
test "complex keyword with nested mixed childs" do | |||
binary = | |||
Config.transform(%{ | |||
"uploader" => "Pleroma.Uploaders.Local", | |||
"filters" => ["Pleroma.Upload.Filter.Dedupe"], | |||
"link_name" => ":true", | |||
"proxy_remote" => ":false", | |||
"proxy_opts" => %{ | |||
"redirect_on_failure" => ":false", | |||
"max_body_length" => "i:1048576", | |||
"http" => %{ | |||
"follow_redirect" => ":true", | |||
"pool" => ":upload" | |||
} | |||
Config.transform([ | |||
%{"tuple" => [":uploader", "Pleroma.Uploaders.Local"]}, | |||
%{"tuple" => [":filters", ["Pleroma.Upload.Filter.Dedupe"]]}, | |||
%{"tuple" => [":link_name", true]}, | |||
%{"tuple" => [":proxy_remote", false]}, | |||
%{"tuple" => [":common_map", %{":key" => "value"}]}, | |||
%{ | |||
"tuple" => [ | |||
":proxy_opts", | |||
[ | |||
%{"tuple" => [":redirect_on_failure", false]}, | |||
%{"tuple" => [":max_body_length", 1_048_576]}, | |||
%{ | |||
"tuple" => [ | |||
":http", | |||
[%{"tuple" => [":follow_redirect", true]}, %{"tuple" => [":pool", ":upload"]}] | |||
] | |||
} | |||
] | |||
] | |||
} | |||
}) | |||
]) | |||
assert binary == | |||
:erlang.term_to_binary( | |||
uploader: Pleroma.Uploaders.Local, | |||
filters: [Pleroma.Upload.Filter.Dedupe], | |||
link_name: true, | |||
proxy_remote: false, | |||
common_map: %{key: "value"}, | |||
proxy_opts: [ | |||
redirect_on_failure: false, | |||
max_body_length: 1_048_576, | |||
http: [ | |||
follow_redirect: true, | |||
pool: :upload | |||
], | |||
max_body_length: 1_048_576, | |||
redirect_on_failure: false | |||
], | |||
proxy_remote: false, | |||
uploader: Pleroma.Uploaders.Local | |||
] | |||
] | |||
) | |||
assert Config.from_binary(binary) == | |||
[ | |||
uploader: Pleroma.Uploaders.Local, | |||
filters: [Pleroma.Upload.Filter.Dedupe], | |||
link_name: true, | |||
proxy_remote: false, | |||
common_map: %{key: "value"}, | |||
proxy_opts: [ | |||
redirect_on_failure: false, | |||
max_body_length: 1_048_576, | |||
http: [ | |||
follow_redirect: true, | |||
pool: :upload | |||
], | |||
max_body_length: 1_048_576, | |||
redirect_on_failure: false | |||
], | |||
proxy_remote: false, | |||
uploader: Pleroma.Uploaders.Local | |||
] | |||
] | |||
] | |||
end | |||
test "keyword" do | |||
test "common keyword" do | |||
binary = | |||
Config.transform(%{ | |||
"level" => ":warn", | |||
"meta" => [":all"], | |||
"webhook_url" => "https://hooks.slack.com/services/YOUR-KEY-HERE" | |||
}) | |||
Config.transform([ | |||
%{"tuple" => [":level", ":warn"]}, | |||
%{"tuple" => [":meta", [":all"]]}, | |||
%{"tuple" => [":path", ""]}, | |||
%{"tuple" => [":val", nil]}, | |||
%{"tuple" => [":webhook_url", "https://hooks.slack.com/services/YOUR-KEY-HERE"]} | |||
]) | |||
assert binary == | |||
:erlang.term_to_binary( | |||
level: :warn, | |||
meta: [:all], | |||
path: "", | |||
val: nil, | |||
webhook_url: "https://hooks.slack.com/services/YOUR-KEY-HERE" | |||
) | |||
assert Config.from_binary(binary) == [ | |||
level: :warn, | |||
meta: [:all], | |||
path: "", | |||
val: nil, | |||
webhook_url: "https://hooks.slack.com/services/YOUR-KEY-HERE" | |||
] | |||
end | |||
test "complex map with sigil" do | |||
test "complex keyword with sigil" do | |||
binary = | |||
Config.transform(%{ | |||
federated_timeline_removal: [], | |||
reject: [~r/comp[lL][aA][iI][nN]er/], | |||
replace: [] | |||
}) | |||
Config.transform([ | |||
%{"tuple" => [":federated_timeline_removal", []]}, | |||
%{"tuple" => [":reject", ["~r/comp[lL][aA][iI][nN]er/"]]}, | |||
%{"tuple" => [":replace", []]} | |||
]) | |||
assert binary == | |||
:erlang.term_to_binary( | |||
@@ -184,54 +373,68 @@ defmodule Pleroma.Web.AdminAPI.ConfigTest do | |||
[federated_timeline_removal: [], reject: [~r/comp[lL][aA][iI][nN]er/], replace: []] | |||
end | |||
test "complex map with tuples with more than 2 values" do | |||
test "complex keyword with tuples with more than 2 values" do | |||
binary = | |||
Config.transform(%{ | |||
"http" => %{ | |||
"dispatch" => [ | |||
%{ | |||
"tuple" => [ | |||
":_", | |||
[ | |||
%{ | |||
"tuple" => [ | |||
"/api/v1/streaming", | |||
"Pleroma.Web.MastodonAPI.WebsocketHandler", | |||
[] | |||
] | |||
}, | |||
%{ | |||
"tuple" => [ | |||
"/websocket", | |||
"Phoenix.Endpoint.CowboyWebSocket", | |||
%{ | |||
"tuple" => [ | |||
"Phoenix.Transports.WebSocket", | |||
%{"tuple" => ["Pleroma.Web.Endpoint", "Pleroma.Web.UserSocket", []]} | |||
Config.transform([ | |||
%{ | |||
"tuple" => [ | |||
":http", | |||
[ | |||
%{ | |||
"tuple" => [ | |||
":key1", | |||
[ | |||
%{ | |||
"tuple" => [ | |||
":_", | |||
[ | |||
%{ | |||
"tuple" => [ | |||
"/api/v1/streaming", | |||
"Pleroma.Web.MastodonAPI.WebsocketHandler", | |||
[] | |||
] | |||
}, | |||
%{ | |||
"tuple" => [ | |||
"/websocket", | |||
"Phoenix.Endpoint.CowboyWebSocket", | |||
%{ | |||
"tuple" => [ | |||
"Phoenix.Transports.WebSocket", | |||
%{ | |||
"tuple" => [ | |||
"Pleroma.Web.Endpoint", | |||
"Pleroma.Web.UserSocket", | |||
[] | |||
] | |||
} | |||
] | |||
} | |||
] | |||
}, | |||
%{ | |||
"tuple" => [ | |||
":_", | |||
"Phoenix.Endpoint.Cowboy2Handler", | |||
%{"tuple" => ["Pleroma.Web.Endpoint", []]} | |||
] | |||
} | |||
] | |||
} | |||
] | |||
}, | |||
%{ | |||
"tuple" => [ | |||
":_", | |||
"Phoenix.Endpoint.Cowboy2Handler", | |||
%{ | |||
"tuple" => ["Pleroma.Web.Endpoint", []] | |||
} | |||
] | |||
} | |||
] | |||
} | |||
] | |||
] | |||
] | |||
} | |||
} | |||
] | |||
] | |||
} | |||
}) | |||
]) | |||
assert binary == | |||
:erlang.term_to_binary( | |||
http: [ | |||
dispatch: [ | |||
key1: [ | |||
_: [ | |||
{"/api/v1/streaming", Pleroma.Web.MastodonAPI.WebsocketHandler, []}, | |||
{"/websocket", Phoenix.Endpoint.CowboyWebSocket, | |||
@@ -245,7 +448,7 @@ defmodule Pleroma.Web.AdminAPI.ConfigTest do | |||
assert Config.from_binary(binary) == [ | |||
http: [ | |||
dispatch: [ | |||
key1: [ | |||
{:_, | |||
[ | |||
{"/api/v1/streaming", Pleroma.Web.MastodonAPI.WebsocketHandler, []}, | |||