diff options
Diffstat (limited to 'lib/pleroma/web/admin_api')
-rw-r--r-- | lib/pleroma/web/admin_api/admin_api_controller.ex | 44 | ||||
-rw-r--r-- | lib/pleroma/web/admin_api/config.ex | 122 |
2 files changed, 115 insertions, 51 deletions
diff --git a/lib/pleroma/web/admin_api/admin_api_controller.ex b/lib/pleroma/web/admin_api/admin_api_controller.ex index c8abeff06..376f88061 100644 --- a/lib/pleroma/web/admin_api/admin_api_controller.ex +++ b/lib/pleroma/web/admin_api/admin_api_controller.ex @@ -4,6 +4,9 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do use Pleroma.Web, :controller + + import Pleroma.Web.ControllerHelper, only: [json_response: 3] + alias Pleroma.Activity alias Pleroma.ModerationLog alias Pleroma.Plugs.OAuthScopesPlug @@ -25,10 +28,11 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do alias Pleroma.Web.MastodonAPI.StatusView alias Pleroma.Web.Router - import Pleroma.Web.ControllerHelper, only: [json_response: 3] - require Logger + @descriptions_json Pleroma.Docs.JSON.compile() + @users_page_size 50 + plug( OAuthScopesPlug, %{scopes: ["read:accounts"], admin: true} @@ -93,8 +97,6 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do when action in [:relay_follow, :relay_unfollow, :config_update] ) - @users_page_size 50 - action_fallback(:errors) def user_delete(%{assigns: %{user: admin}} = conn, %{"nickname" => nickname}) do @@ -782,10 +784,22 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do end def migrate_from_db(conn, _params) do - Mix.Tasks.Pleroma.Config.run(["migrate_from_db", Pleroma.Config.get(:env), "true"]) + Mix.Tasks.Pleroma.Config.run([ + "migrate_from_db", + "--env", + to_string(Pleroma.Config.get(:env)), + "-d" + ]) + json(conn, %{}) end + def config_descriptions(conn, _params) do + conn + |> Plug.Conn.put_resp_content_type("application/json") + |> Plug.Conn.send_resp(200, @descriptions_json) + end + def config_show(conn, _params) do configs = Pleroma.Repo.all(Config) @@ -800,17 +814,27 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do updated = Enum.map(configs, fn %{"group" => group, "key" => key, "delete" => "true"} = params -> - {:ok, config} = Config.delete(%{group: group, key: key, subkeys: params["subkeys"]}) - config + with {:ok, config} <- + Config.delete(%{group: group, key: key, subkeys: params["subkeys"]}) do + config + end %{"group" => group, "key" => key, "value" => value} -> - {:ok, config} = Config.update_or_create(%{group: group, key: key, value: value}) - config + with {:ok, config} <- + Config.update_or_create(%{group: group, key: key, value: value}) do + config + end end) |> Enum.reject(&is_nil(&1)) Pleroma.Config.TransferTask.load_and_update_env() - Mix.Tasks.Pleroma.Config.run(["migrate_from_db", Pleroma.Config.get(:env), "false"]) + + Mix.Tasks.Pleroma.Config.run([ + "migrate_from_db", + "--env", + to_string(Pleroma.Config.get(:env)) + ]) + updated else [] diff --git a/lib/pleroma/web/admin_api/config.ex b/lib/pleroma/web/admin_api/config.ex index 1917a5580..a74acfbc6 100644 --- a/lib/pleroma/web/admin_api/config.ex +++ b/lib/pleroma/web/admin_api/config.ex @@ -24,6 +24,8 @@ defmodule Pleroma.Web.AdminAPI.Config do @spec changeset(Config.t(), map()) :: Changeset.t() def changeset(config, params \\ %{}) do + params = Map.put(params, :value, transform(params[:value])) + config |> cast(params, [:key, :group, :value]) |> validate_required([:key, :group, :value]) @@ -33,42 +35,43 @@ defmodule Pleroma.Web.AdminAPI.Config do @spec create(map()) :: {:ok, Config.t()} | {:error, Changeset.t()} def create(params) do %Config{} - |> changeset(Map.put(params, :value, transform(params[:value]))) + |> changeset(params) |> Repo.insert() end @spec update(Config.t(), map()) :: {:ok, Config} | {:error, Changeset.t()} def update(%Config{} = config, %{value: value}) do config - |> change(value: transform(value)) + |> changeset(%{value: value}) |> Repo.update() end @spec update_or_create(map()) :: {:ok, Config.t()} | {:error, Changeset.t()} def update_or_create(params) do - with %Config{} = config <- Config.get_by_params(Map.take(params, [:group, :key])) do + search_opts = Map.take(params, [:group, :key]) + + with %Config{} = config <- Config.get_by_params(search_opts) do Config.update(config, params) else nil -> Config.create(params) end end - @spec delete(map()) :: {:ok, Config.t()} | {:error, Changeset.t()} + @spec delete(map()) :: {:ok, Config.t()} | {:error, Changeset.t()} | {:ok, nil} def delete(params) do - with %Config{} = config <- Config.get_by_params(Map.delete(params, :subkeys)) do - if params[:subkeys] do - updated_value = - Keyword.drop( - :erlang.binary_to_term(config.value), - Enum.map(params[:subkeys], &do_transform_string(&1)) - ) - - Config.update(config, %{value: updated_value}) - else + search_opts = Map.delete(params, :subkeys) + + with %Config{} = config <- Config.get_by_params(search_opts), + {config, sub_keys} when is_list(sub_keys) <- {config, params[:subkeys]}, + old_value <- :erlang.binary_to_term(config.value), + keys <- Enum.map(sub_keys, &do_transform_string(&1)), + new_value <- Keyword.drop(old_value, keys) do + Config.update(config, %{value: new_value}) + else + {config, nil} -> Repo.delete(config) {:ok, nil} - end - else + nil -> err = dgettext("errors", "Config with params %{params} not found", params: inspect(params)) @@ -82,10 +85,22 @@ defmodule Pleroma.Web.AdminAPI.Config do @spec from_binary_with_convert(binary()) :: any() def from_binary_with_convert(binary) do - from_binary(binary) + binary + |> from_binary() |> do_convert() end + @spec from_string(String.t()) :: atom() | no_return() + def from_string(":" <> entity), do: String.to_existing_atom(entity) + + def from_string(entity) when is_binary(entity) do + if is_module_name?(entity) do + String.to_existing_atom("Elixir.#{entity}") + else + entity + end + end + defp do_convert(entity) when is_list(entity) do for v <- entity, into: [], do: do_convert(v) end @@ -97,6 +112,7 @@ defmodule Pleroma.Web.AdminAPI.Config do end defp do_convert({:dispatch, [entity]}), do: %{"tuple" => [":dispatch", [inspect(entity)]]} + # TODO: will become useless after removing hackney defp do_convert({:partial_chain, entity}), do: %{"tuple" => [":partial_chain", inspect(entity)]} defp do_convert(entity) when is_tuple(entity), @@ -105,21 +121,15 @@ defmodule Pleroma.Web.AdminAPI.Config do defp do_convert(entity) when is_boolean(entity) or is_number(entity) or is_nil(entity), do: entity - defp do_convert(entity) when is_atom(entity) do - string = to_string(entity) - - if String.starts_with?(string, "Elixir."), - do: do_convert(string), - else: ":" <> string - end - - defp do_convert("Elixir." <> module_name), do: module_name + defp do_convert(entity) when is_atom(entity), do: inspect(entity) defp do_convert(entity) when is_binary(entity), do: entity - @spec transform(any()) :: binary() + @spec transform(any()) :: binary() | no_return() def transform(entity) when is_binary(entity) or is_map(entity) or is_list(entity) do - :erlang.term_to_binary(do_transform(entity)) + entity + |> do_transform() + |> :erlang.term_to_binary() end def transform(entity), do: :erlang.term_to_binary(entity) @@ -131,6 +141,7 @@ defmodule Pleroma.Web.AdminAPI.Config do {:dispatch, [dispatch_settings]} end + # TODO: will become useless after removing hackney defp do_transform(%{"tuple" => [":partial_chain", entity]}) do {partial_chain, []} = do_eval(entity) {:partial_chain, partial_chain} @@ -149,34 +160,63 @@ defmodule Pleroma.Web.AdminAPI.Config do end defp do_transform(entity) when is_binary(entity) do - String.trim(entity) + entity + |> String.trim() |> do_transform_string() end defp do_transform(entity), do: entity - defp do_transform_string("~r/" <> pattern) do - modificator = String.split(pattern, "/") |> List.last() - pattern = String.trim_trailing(pattern, "/" <> modificator) + @delimiters ["/", "|", "\"", "'", {"(", ")"}, {"[", "]"}, {"{", "}"}, {"<", ">"}] + + defp find_valid_delimiter([], _string, _), + do: raise(ArgumentError, message: "valid delimiter for Regex expression not found") - case modificator do - "" -> ~r/#{pattern}/ - "i" -> ~r/#{pattern}/i - "u" -> ~r/#{pattern}/u - "s" -> ~r/#{pattern}/s + defp find_valid_delimiter([{leading, closing} = delimiter | others], pattern, regex_delimiter) + when is_tuple(delimiter) do + if String.contains?(pattern, closing) do + find_valid_delimiter(others, pattern, regex_delimiter) + else + {:ok, {leading, closing}} + end + end + + defp find_valid_delimiter([delimiter | others], pattern, regex_delimiter) do + if String.contains?(pattern, delimiter) do + find_valid_delimiter(others, pattern, regex_delimiter) + else + {:ok, {delimiter, delimiter}} + end + end + + @regex_parts ~r/^~r(?'delimiter'[\/|"'([{<]{1})(?'pattern'.+)[\/|"')\]}>]{1}(?'modifier'[uismxfU]*)/u + + defp do_transform_string("~r" <> _pattern = regex) do + with %{"modifier" => modifier, "pattern" => pattern, "delimiter" => regex_delimiter} <- + Regex.named_captures(@regex_parts, regex), + {:ok, {leading, closing}} <- find_valid_delimiter(@delimiters, pattern, regex_delimiter), + {result, _} <- Code.eval_string("~r#{leading}#{pattern}#{closing}#{modifier}") do + result end end defp do_transform_string(":" <> atom), do: String.to_atom(atom) 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 + if is_module_name?(value) do + String.to_existing_atom("Elixir." <> value) + else + value + end + end + + @spec is_module_name?(String.t()) :: boolean() + def is_module_name?(string) do + Regex.match?(~r/^(Pleroma|Phoenix|Tesla)\./, string) or string in ["Oban", "Ueberauth"] end defp do_eval(entity) do cleaned_string = String.replace(entity, ~r/[^\w|^{:,[|^,|^[|^\]^}|^\/|^\.|^"]^\s/, "") - Code.eval_string(cleaned_string, [], requires: [], macros: []) + Code.eval_string(cleaned_string) end end |