Merge branch 'features/poll-validation' into 'develop'
authorlain <lain@soykaf.club>
Fri, 7 Aug 2020 10:44:06 +0000 (10:44 +0000)
committerlain <lain@soykaf.club>
Fri, 7 Aug 2020 10:44:06 +0000 (10:44 +0000)
Poll and votes pipeline ingestion

Closes #1362 and #1852

See merge request pleroma/pleroma!2635

16 files changed:
CHANGELOG.md
lib/pleroma/application.ex
lib/pleroma/config.ex
lib/pleroma/upload/filter/exiftool.ex
lib/pleroma/upload/filter/mogrifun.ex
lib/pleroma/upload/filter/mogrify.ex
lib/pleroma/utils.ex
lib/pleroma/web/mastodon_api/views/filter_view.ex
lib/pleroma/web/oauth/oauth_controller.ex
mix.exs
priv/repo/migrations/20200802170532_fix_legacy_tags.exs [new file with mode: 0644]
test/config_test.exs
test/migrations/20200802170532_fix_legacy_tags_test.exs [new file with mode: 0644]
test/support/helpers.ex
test/upload/filter/exiftool_test.exs
test/web/mastodon_api/controllers/filter_controller_test.exs

index de017e30aab54b6bf99a59117d97f3aba5c3c817..572f9e84bab9e9bbcfc36a3a8425b6ccfcd5a609 100644 (file)
@@ -102,6 +102,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
 - Fix CSP policy generation to include remote Captcha services
 - Fix edge case where MediaProxy truncates media, usually caused when Caddy is serving content for the other Federated instance.
 - Emoji Packs could not be listed when instance was set to `public: false`
+- Fix whole_word always returning false on filter get requests
 
 ## [Unreleased (patch)]
 
index 0ffb55358f26d49e68a06d8b67fff010f59f6a2c..c0b5db9f16affbe235595194f114a5b315ceb443 100644 (file)
@@ -47,6 +47,7 @@ defmodule Pleroma.Application do
     Pleroma.ApplicationRequirements.verify!()
     setup_instrumenters()
     load_custom_modules()
+    check_system_commands()
     Pleroma.Docs.JSON.compile()
 
     adapter = Application.get_env(:tesla, :adapter)
@@ -249,4 +250,21 @@ defmodule Pleroma.Application do
   end
 
   defp http_children(_, _), do: []
+
+  defp check_system_commands do
+    filters = Config.get([Pleroma.Upload, :filters])
+
+    check_filter = fn filter, command_required ->
+      with true <- filter in filters,
+           false <- Pleroma.Utils.command_available?(command_required) do
+        Logger.error(
+          "#{filter} is specified in list of Pleroma.Upload filters, but the #{command_required} command is not found"
+        )
+      end
+    end
+
+    check_filter.(Pleroma.Upload.Filters.Exiftool, "exiftool")
+    check_filter.(Pleroma.Upload.Filters.Mogrify, "mogrify")
+    check_filter.(Pleroma.Upload.Filters.Mogrifun, "mogrify")
+  end
 end
index cc80deff5f16597c2c10b46cee74b038d97f9115..a8329cc1efbda910a5a0de6dda0068996d5b7d27 100644 (file)
@@ -11,12 +11,10 @@ defmodule Pleroma.Config do
 
   def get([key], default), do: get(key, default)
 
-  def get([parent_key | keys], default) do
-    case :pleroma
-         |> Application.get_env(parent_key)
-         |> get_in(keys) do
-      nil -> default
-      any -> any
+  def get([_ | _] = path, default) do
+    case fetch(path) do
+      {:ok, value} -> value
+      :error -> default
     end
   end
 
@@ -34,6 +32,24 @@ defmodule Pleroma.Config do
     end
   end
 
+  def fetch(key) when is_atom(key), do: fetch([key])
+
+  def fetch([root_key | keys]) do
+    Enum.reduce_while(keys, Application.fetch_env(:pleroma, root_key), fn
+      key, {:ok, config} when is_map(config) or is_list(config) ->
+        case Access.fetch(config, key) do
+          :error ->
+            {:halt, :error}
+
+          value ->
+            {:cont, value}
+        end
+
+      _key, _config ->
+        {:halt, :error}
+    end)
+  end
+
   def put([key], value), do: put(key, value)
 
   def put([parent_key | keys], value) do
@@ -50,12 +66,15 @@ defmodule Pleroma.Config do
 
   def delete([key]), do: delete(key)
 
-  def delete([parent_key | keys]) do
-    {_, parent} =
-      Application.get_env(:pleroma, parent_key)
-      |> get_and_update_in(keys, fn _ -> :pop end)
+  def delete([parent_key | keys] = path) do
+    with {:ok, _} <- fetch(path) do
+      {_, parent} =
+        parent_key
+        |> get()
+        |> get_and_update_in(keys, fn _ -> :pop end)
 
-    Application.put_env(:pleroma, parent_key, parent)
+      Application.put_env(:pleroma, parent_key, parent)
+    end
   end
 
   def delete(key) do
index c7fb6aefa95e87d69b5ce3fd24681ce2984a9ce3..ea8798fe39ef4d15549843b42aa963ec6ce219cf 100644 (file)
@@ -9,9 +9,17 @@ defmodule Pleroma.Upload.Filter.Exiftool do
   """
   @behaviour Pleroma.Upload.Filter
 
+  @spec filter(Pleroma.Upload.t()) :: :ok | {:error, String.t()}
   def filter(%Pleroma.Upload{tempfile: file, content_type: "image" <> _}) do
-    System.cmd("exiftool", ["-overwrite_original", "-gps:all=", file], parallelism: true)
-    :ok
+    try do
+      case System.cmd("exiftool", ["-overwrite_original", "-gps:all=", file], parallelism: true) do
+        {_response, 0} -> :ok
+        {error, 1} -> {:error, error}
+      end
+    rescue
+      _e in ErlangError ->
+        {:error, "exiftool command not found"}
+    end
   end
 
   def filter(_), do: :ok
index 7d95577a4a8029b1d941432de0ed35eb6dbf1136..a8503ac2422bb9d68d84a4b282b79699434cc72e 100644 (file)
@@ -34,10 +34,15 @@ defmodule Pleroma.Upload.Filter.Mogrifun do
     [{"fill", "yellow"}, {"tint", "40"}]
   ]
 
+  @spec filter(Pleroma.Upload.t()) :: :ok | {:error, String.t()}
   def filter(%Pleroma.Upload{tempfile: file, content_type: "image" <> _}) do
-    Filter.Mogrify.do_filter(file, [Enum.random(@filters)])
-
-    :ok
+    try do
+      Filter.Mogrify.do_filter(file, [Enum.random(@filters)])
+      :ok
+    rescue
+      _e in ErlangError ->
+        {:error, "mogrify command not found"}
+    end
   end
 
   def filter(_), do: :ok
index 2eb75800659993110500e8d38e1f1abc907fae92..7a45add5a2cda00662d43bd17d31b67e90797655 100644 (file)
@@ -8,11 +8,15 @@ defmodule Pleroma.Upload.Filter.Mogrify do
   @type conversion :: action :: String.t() | {action :: String.t(), opts :: String.t()}
   @type conversions :: conversion() | [conversion()]
 
+  @spec filter(Pleroma.Upload.t()) :: :ok | {:error, String.t()}
   def filter(%Pleroma.Upload{tempfile: file, content_type: "image" <> _}) do
-    filters = Pleroma.Config.get!([__MODULE__, :args])
-
-    do_filter(file, filters)
-    :ok
+    try do
+      do_filter(file, Pleroma.Config.get!([__MODULE__, :args]))
+      :ok
+    rescue
+      _e in ErlangError ->
+        {:error, "mogrify command not found"}
+    end
   end
 
   def filter(_), do: :ok
index 6b8e3accf1e04018561ceafe3a4660aad30c4137..21d1159be84383426262ee49bb7d4758dccb3523 100644 (file)
@@ -9,4 +9,19 @@ defmodule Pleroma.Utils do
     |> Enum.map(&Path.join(dir, &1))
     |> Kernel.ParallelCompiler.compile()
   end
+
+  @doc """
+  POSIX-compliant check if command is available in the system
+
+  ## Examples
+      iex> command_available?("git")
+      true
+      iex> command_available?("wrongcmd")
+      false
+
+  """
+  @spec command_available?(String.t()) :: boolean()
+  def command_available?(command) do
+    match?({_output, 0}, System.cmd("sh", ["-c", "command -v #{command}"]))
+  end
 end
index aeff646f559d6268d97d700d5163ace67e881c22..c37f624e071380bd55a1fd841f6ac1259a0f98d5 100644 (file)
@@ -25,7 +25,7 @@ defmodule Pleroma.Web.MastodonAPI.FilterView do
       context: filter.context,
       expires_at: expires_at,
       irreversible: filter.hide,
-      whole_word: false
+      whole_word: filter.whole_word
     }
   end
 end
index f29b3cb5705f7d46160f8dc70167f5e32322d322..dd00600ea5ce3c7e67450e8706b8fdc0fb2f122f 100644 (file)
@@ -76,6 +76,13 @@ defmodule Pleroma.Web.OAuth.OAuthController do
     available_scopes = (app && app.scopes) || []
     scopes = Scopes.fetch_scopes(params, available_scopes)
 
+    scopes =
+      if scopes == [] do
+        available_scopes
+      else
+        scopes
+      end
+
     # Note: `params` might differ from `conn.params`; use `@params` not `@conn.params` in template
     render(conn, Authenticator.auth_template(), %{
       response_type: params["response_type"],
diff --git a/mix.exs b/mix.exs
index 63142dee768c8891c4f3501675069e89b6409cf8..aab833c5ea525dce41160974c81b4b12d11a1b0c 100644 (file)
--- a/mix.exs
+++ b/mix.exs
@@ -229,10 +229,10 @@ defmodule Pleroma.Mixfile do
   defp version(version) do
     identifier_filter = ~r/[^0-9a-z\-]+/i
 
-    {_cmdgit, cmdgit_err} = System.cmd("sh", ["-c", "command -v git"])
+    git_available? = match?({_output, 0}, System.cmd("sh", ["-c", "command -v git"]))
 
     git_pre_release =
-      if cmdgit_err == 0 do
+      if git_available? do
         {tag, tag_err} =
           System.cmd("git", ["describe", "--tags", "--abbrev=0"], stderr_to_stdout: true)
 
@@ -258,7 +258,7 @@ defmodule Pleroma.Mixfile do
 
     # Branch name as pre-release version component, denoted with a dot
     branch_name =
-      with 0 <- cmdgit_err,
+      with true <- git_available?,
            {branch_name, 0} <- System.cmd("git", ["rev-parse", "--abbrev-ref", "HEAD"]),
            branch_name <- String.trim(branch_name),
            branch_name <- System.get_env("PLEROMA_BUILD_BRANCH") || branch_name,
diff --git a/priv/repo/migrations/20200802170532_fix_legacy_tags.exs b/priv/repo/migrations/20200802170532_fix_legacy_tags.exs
new file mode 100644 (file)
index 0000000..f7274b4
--- /dev/null
@@ -0,0 +1,37 @@
+# Fix legacy tags set by AdminFE that don't align with TagPolicy MRF
+
+defmodule Pleroma.Repo.Migrations.FixLegacyTags do
+  use Ecto.Migration
+  alias Pleroma.Repo
+  alias Pleroma.User
+  import Ecto.Query
+
+  @old_new_map %{
+    "force_nsfw" => "mrf_tag:media-force-nsfw",
+    "strip_media" => "mrf_tag:media-strip",
+    "force_unlisted" => "mrf_tag:force-unlisted",
+    "sandbox" => "mrf_tag:sandbox",
+    "disable_remote_subscription" => "mrf_tag:disable-remote-subscription",
+    "disable_any_subscription" => "mrf_tag:disable-any-subscription"
+  }
+
+  def change do
+    legacy_tags = Map.keys(@old_new_map)
+
+    from(u in User, where: fragment("? && ?", u.tags, ^legacy_tags))
+    |> Repo.all()
+    |> Enum.each(fn user ->
+      fix_tags_changeset(user)
+      |> Repo.update()
+    end)
+  end
+
+  defp fix_tags_changeset(%User{tags: tags} = user) do
+    new_tags =
+      Enum.map(tags, fn tag ->
+        Map.get(@old_new_map, tag, tag)
+      end)
+
+    Ecto.Changeset.change(user, tags: new_tags)
+  end
+end
index a46ab43023b14443a802870ad6773a2fe5d4a700..1556e4237420cf35c253f7a19f43b7b57495dca6 100644 (file)
@@ -28,6 +28,34 @@ defmodule Pleroma.ConfigTest do
     assert Pleroma.Config.get([:azerty, :uiop], true) == true
   end
 
+  describe "nil values" do
+    setup do
+      Pleroma.Config.put(:lorem, nil)
+      Pleroma.Config.put(:ipsum, %{dolor: [sit: nil]})
+      Pleroma.Config.put(:dolor, sit: %{amet: nil})
+
+      on_exit(fn -> Enum.each(~w(lorem ipsum dolor)a, &Pleroma.Config.delete/1) end)
+    end
+
+    test "get/1 with an atom for nil value" do
+      assert Pleroma.Config.get(:lorem) == nil
+    end
+
+    test "get/2 with an atom for nil value" do
+      assert Pleroma.Config.get(:lorem, true) == nil
+    end
+
+    test "get/1 with a list of keys for nil value" do
+      assert Pleroma.Config.get([:ipsum, :dolor, :sit]) == nil
+      assert Pleroma.Config.get([:dolor, :sit, :amet]) == nil
+    end
+
+    test "get/2 with a list of keys for nil value" do
+      assert Pleroma.Config.get([:ipsum, :dolor, :sit], true) == nil
+      assert Pleroma.Config.get([:dolor, :sit, :amet], true) == nil
+    end
+  end
+
   test "get/1 when value is false" do
     Pleroma.Config.put([:instance, :false_test], false)
     Pleroma.Config.put([:instance, :nested], [])
@@ -89,5 +117,23 @@ defmodule Pleroma.ConfigTest do
     Pleroma.Config.put([:delete_me, :delete_me], hello: "world", world: "Hello")
     Pleroma.Config.delete([:delete_me, :delete_me, :world])
     assert Pleroma.Config.get([:delete_me, :delete_me]) == [hello: "world"]
+
+    assert Pleroma.Config.delete([:this_key_does_not_exist])
+    assert Pleroma.Config.delete([:non, :existing, :key])
+  end
+
+  test "fetch/1" do
+    Pleroma.Config.put([:lorem], :ipsum)
+    Pleroma.Config.put([:ipsum], dolor: :sit)
+
+    assert Pleroma.Config.fetch([:lorem]) == {:ok, :ipsum}
+    assert Pleroma.Config.fetch(:lorem) == {:ok, :ipsum}
+    assert Pleroma.Config.fetch([:ipsum, :dolor]) == {:ok, :sit}
+    assert Pleroma.Config.fetch([:lorem, :ipsum]) == :error
+    assert Pleroma.Config.fetch([:loremipsum]) == :error
+    assert Pleroma.Config.fetch(:loremipsum) == :error
+
+    Pleroma.Config.delete([:lorem])
+    Pleroma.Config.delete([:ipsum])
   end
 end
diff --git a/test/migrations/20200802170532_fix_legacy_tags_test.exs b/test/migrations/20200802170532_fix_legacy_tags_test.exs
new file mode 100644 (file)
index 0000000..3b4dee4
--- /dev/null
@@ -0,0 +1,24 @@
+defmodule Pleroma.Repo.Migrations.FixLegacyTagsTest do
+  alias Pleroma.User
+  use Pleroma.DataCase
+  import Pleroma.Factory
+  import Pleroma.Tests.Helpers
+
+  setup_all do: require_migration("20200802170532_fix_legacy_tags")
+
+  test "change/0 converts legacy user tags into correct values", %{migration: migration} do
+    user = insert(:user, tags: ["force_nsfw", "force_unlisted", "verified"])
+    user2 = insert(:user)
+
+    assert :ok == migration.change()
+
+    fixed_user = User.get_by_id(user.id)
+    fixed_user2 = User.get_by_id(user2.id)
+
+    assert fixed_user.tags == ["mrf_tag:media-force-nsfw", "mrf_tag:force-unlisted", "verified"]
+    assert fixed_user2.tags == []
+
+    # user2 should not have been updated
+    assert fixed_user2.updated_at == fixed_user2.inserted_at
+  end
+end
index 5cbf2e29197c14de391dcef39aea3126ef3401d3..ecd4b1e185889cb5b3398511f7fb804a2d2a7aad 100644 (file)
@@ -17,9 +17,19 @@ defmodule Pleroma.Tests.Helpers do
 
   defmacro clear_config(config_path, do: yield) do
     quote do
-      initial_setting = Config.get(unquote(config_path))
+      initial_setting = Config.fetch(unquote(config_path))
       unquote(yield)
-      on_exit(fn -> Config.put(unquote(config_path), initial_setting) end)
+
+      on_exit(fn ->
+        case initial_setting do
+          :error ->
+            Config.delete(unquote(config_path))
+
+          {:ok, value} ->
+            Config.put(unquote(config_path), value)
+        end
+      end)
+
       :ok
     end
   end
index a1b7e46cd3e84acd82e4822fc1400bd3a739ab51..8ed7d650bfb74ef7a255a11422d3e0f973dac99e 100644 (file)
@@ -7,6 +7,8 @@ defmodule Pleroma.Upload.Filter.ExiftoolTest do
   alias Pleroma.Upload.Filter
 
   test "apply exiftool filter" do
+    assert Pleroma.Utils.command_available?("exiftool")
+
     File.cp!(
       "test/fixtures/DSCN0010.jpg",
       "test/fixtures/DSCN0010_tmp.jpg"
index f29547d13c00e6dcd4196405fb76f292b17a5409..0d426ec342ccf788f7b3d36b02d991d8891cdf37 100644 (file)
@@ -64,11 +64,31 @@ defmodule Pleroma.Web.MastodonAPI.FilterControllerTest do
   test "get a filter" do
     %{user: user, conn: conn} = oauth_access(["read:filters"])
 
+    # check whole_word false
     query = %Pleroma.Filter{
       user_id: user.id,
       filter_id: 2,
       phrase: "knight",
-      context: ["home"]
+      context: ["home"],
+      whole_word: false
+    }
+
+    {:ok, filter} = Pleroma.Filter.create(query)
+
+    conn = get(conn, "/api/v1/filters/#{filter.filter_id}")
+
+    assert response = json_response_and_validate_schema(conn, 200)
+    assert response["whole_word"] == false
+
+    # check whole_word true
+    %{user: user, conn: conn} = oauth_access(["read:filters"])
+
+    query = %Pleroma.Filter{
+      user_id: user.id,
+      filter_id: 3,
+      phrase: "knight",
+      context: ["home"],
+      whole_word: true
     }
 
     {:ok, filter} = Pleroma.Filter.create(query)
@@ -76,6 +96,7 @@ defmodule Pleroma.Web.MastodonAPI.FilterControllerTest do
     conn = get(conn, "/api/v1/filters/#{filter.filter_id}")
 
     assert response = json_response_and_validate_schema(conn, 200)
+    assert response["whole_word"] == true
   end
 
   test "update a filter" do
@@ -86,7 +107,8 @@ defmodule Pleroma.Web.MastodonAPI.FilterControllerTest do
       filter_id: 2,
       phrase: "knight",
       context: ["home"],
-      hide: true
+      hide: true,
+      whole_word: true
     }
 
     {:ok, _filter} = Pleroma.Filter.create(query)
@@ -108,6 +130,7 @@ defmodule Pleroma.Web.MastodonAPI.FilterControllerTest do
     assert response["phrase"] == new.phrase
     assert response["context"] == new.context
     assert response["irreversible"] == true
+    assert response["whole_word"] == true
   end
 
   test "delete a filter" do