aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergey Suprunenko <suprunenko.s@gmail.com>2019-11-16 22:54:13 +0100
committerAlexander Strizhakov <alex.strizhakov@gmail.com>2020-07-06 09:28:21 +0300
commit4a8c26654eb7ca7ce049dd4c485c16672b5837a6 (patch)
treea85c4f8b2e949b7f9e8952c30091b1182b4956a0
parentc2a052a346d5104c3657343a885255d4d7179c75 (diff)
downloadpleroma-4a8c26654eb7ca7ce049dd4c485c16672b5837a6.tar.gz
Restrict statuses that contain user's irreversible filters
-rw-r--r--lib/pleroma/filter.ex42
-rw-r--r--lib/pleroma/web/activity_pub/activity_pub.ex22
-rw-r--r--lib/pleroma/web/mastodon_api/controllers/filter_controller.ex2
-rw-r--r--test/filter_test.exs2
-rw-r--r--test/support/factory.ex8
-rw-r--r--test/web/activity_pub/activity_pub_test.exs69
6 files changed, 141 insertions, 4 deletions
diff --git a/lib/pleroma/filter.ex b/lib/pleroma/filter.ex
index 4d61b3650..91884c6b3 100644
--- a/lib/pleroma/filter.ex
+++ b/lib/pleroma/filter.ex
@@ -34,10 +34,18 @@ defmodule Pleroma.Filter do
Repo.one(query)
end
- def get_filters(%User{id: user_id} = _user) do
+ def get_active(query) do
+ from(f in query, where: is_nil(f.expires_at) or f.expires_at > ^NaiveDateTime.utc_now())
+ end
+
+ def get_irreversible(query) do
+ from(f in query, where: f.hide)
+ end
+
+ def get_by_user(query, %User{id: user_id} = _user) do
query =
from(
- f in Pleroma.Filter,
+ f in query,
where: f.user_id == ^user_id,
order_by: [desc: :id]
)
@@ -95,4 +103,34 @@ defmodule Pleroma.Filter do
|> validate_required([:phrase, :context])
|> Repo.update()
end
+
+ def compose_regex(user_or_filters, format \\ :postgres)
+
+ def compose_regex(%User{} = user, format) do
+ __MODULE__
+ |> get_active()
+ |> get_irreversible()
+ |> get_by_user(user)
+ |> compose_regex(format)
+ end
+
+ def compose_regex([_ | _] = filters, format) do
+ phrases =
+ filters
+ |> Enum.map(& &1.phrase)
+ |> Enum.join("|")
+
+ case format do
+ :postgres ->
+ "\\y(#{phrases})\\y"
+
+ :re ->
+ ~r/\b#{phrases}\b/i
+
+ _ ->
+ nil
+ end
+ end
+
+ def compose_regex(_, _), do: nil
end
diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex
index 94117202c..31353c866 100644
--- a/lib/pleroma/web/activity_pub/activity_pub.ex
+++ b/lib/pleroma/web/activity_pub/activity_pub.ex
@@ -10,6 +10,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
alias Pleroma.Constants
alias Pleroma.Conversation
alias Pleroma.Conversation.Participation
+ alias Pleroma.Filter
alias Pleroma.Maps
alias Pleroma.Notification
alias Pleroma.Object
@@ -961,6 +962,26 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
defp restrict_instance(query, _), do: query
+ defp restrict_filtered(query, %{user: %User{} = user}) do
+ case Filter.compose_regex(user) do
+ nil ->
+ query
+
+ regex ->
+ from([activity, object] in query,
+ where:
+ fragment("not(?->>'content' ~* ?)", object.data, ^regex) or
+ activity.actor == ^user.ap_id
+ )
+ end
+ end
+
+ defp restrict_filtered(query, %{blocking_user: %User{} = user}) do
+ restrict_filtered(query, %{user: user})
+ end
+
+ defp restrict_filtered(query, _), do: query
+
defp exclude_poll_votes(query, %{include_poll_votes: true}), do: query
defp exclude_poll_votes(query, _) do
@@ -1099,6 +1120,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|> restrict_muted_reblogs(restrict_muted_reblogs_opts)
|> restrict_instance(opts)
|> restrict_announce_object_actor(opts)
+ |> restrict_filtered(opts)
|> Activity.restrict_deactivated_users()
|> exclude_poll_votes(opts)
|> exclude_chat_messages(opts)
diff --git a/lib/pleroma/web/mastodon_api/controllers/filter_controller.ex b/lib/pleroma/web/mastodon_api/controllers/filter_controller.ex
index abbf0ce02..db1ff3189 100644
--- a/lib/pleroma/web/mastodon_api/controllers/filter_controller.ex
+++ b/lib/pleroma/web/mastodon_api/controllers/filter_controller.ex
@@ -22,7 +22,7 @@ defmodule Pleroma.Web.MastodonAPI.FilterController do
@doc "GET /api/v1/filters"
def index(%{assigns: %{user: user}} = conn, _) do
- filters = Filter.get_filters(user)
+ filters = Filter.get_by_user(Filter, user)
render(conn, "index.json", filters: filters)
end
diff --git a/test/filter_test.exs b/test/filter_test.exs
index 63a30c736..061a95ad0 100644
--- a/test/filter_test.exs
+++ b/test/filter_test.exs
@@ -126,7 +126,7 @@ defmodule Pleroma.FilterTest do
{:ok, filter_one} = Pleroma.Filter.create(query_one)
{:ok, filter_two} = Pleroma.Filter.create(query_two)
- filters = Pleroma.Filter.get_filters(user)
+ filters = Pleroma.Filter.get_by_user(Pleroma.Filter, user)
assert filter_one in filters
assert filter_two in filters
end
diff --git a/test/support/factory.ex b/test/support/factory.ex
index af580021c..635d83650 100644
--- a/test/support/factory.ex
+++ b/test/support/factory.ex
@@ -428,4 +428,12 @@ defmodule Pleroma.Factory do
user: build(:user)
}
end
+
+ def filter_factory do
+ %Pleroma.Filter{
+ user: build(:user),
+ filter_id: sequence(:filter_id, & &1),
+ phrase: "cofe"
+ }
+ end
end
diff --git a/test/web/activity_pub/activity_pub_test.exs b/test/web/activity_pub/activity_pub_test.exs
index 575e0c5db..4968403dc 100644
--- a/test/web/activity_pub/activity_pub_test.exs
+++ b/test/web/activity_pub/activity_pub_test.exs
@@ -785,6 +785,75 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
assert activity == expected_activity
end
+ describe "irreversible filters" do
+ setup do
+ user = insert(:user)
+ user_two = insert(:user)
+
+ insert(:filter, user: user_two, phrase: "cofe", hide: true)
+ insert(:filter, user: user_two, phrase: "ok boomer", hide: true)
+ insert(:filter, user: user_two, phrase: "test", hide: false)
+
+ params = %{
+ "type" => ["Create", "Announce"],
+ "user" => user_two
+ }
+
+ {:ok, %{user: user, user_two: user_two, params: params}}
+ end
+
+ test "it returns statuses if they don't contain exact filter words", %{
+ user: user,
+ params: params
+ } do
+ {:ok, _} = CommonAPI.post(user, %{"status" => "hey"})
+ {:ok, _} = CommonAPI.post(user, %{"status" => "got cofefe?"})
+ {:ok, _} = CommonAPI.post(user, %{"status" => "I am not a boomer"})
+ {:ok, _} = CommonAPI.post(user, %{"status" => "ok boomers"})
+ {:ok, _} = CommonAPI.post(user, %{"status" => "ccofee is not a word"})
+ {:ok, _} = CommonAPI.post(user, %{"status" => "this is a test"})
+
+ activities = ActivityPub.fetch_activities([], params)
+
+ assert Enum.count(activities) == 6
+ end
+
+ test "it does not filter user's own statuses", %{user_two: user_two, params: params} do
+ {:ok, _} = CommonAPI.post(user_two, %{"status" => "Give me some cofe!"})
+ {:ok, _} = CommonAPI.post(user_two, %{"status" => "ok boomer"})
+
+ activities = ActivityPub.fetch_activities([], params)
+
+ assert Enum.count(activities) == 2
+ end
+
+ test "it excludes statuses with filter words", %{user: user, params: params} do
+ {:ok, _} = CommonAPI.post(user, %{"status" => "Give me some cofe!"})
+ {:ok, _} = CommonAPI.post(user, %{"status" => "ok boomer"})
+ {:ok, _} = CommonAPI.post(user, %{"status" => "is it a cOfE?"})
+ {:ok, _} = CommonAPI.post(user, %{"status" => "cofe is all I need"})
+ {:ok, _} = CommonAPI.post(user, %{"status" => "— ok BOOMER\n"})
+
+ activities = ActivityPub.fetch_activities([], params)
+
+ assert Enum.empty?(activities)
+ end
+
+ test "it returns all statuses if user does not have any filters" do
+ another_user = insert(:user)
+ {:ok, _} = CommonAPI.post(another_user, %{"status" => "got cofe?"})
+ {:ok, _} = CommonAPI.post(another_user, %{"status" => "test!"})
+
+ activities =
+ ActivityPub.fetch_activities([], %{
+ "type" => ["Create", "Announce"],
+ "user" => another_user
+ })
+
+ assert Enum.count(activities) == 2
+ end
+ end
+
describe "public fetch activities" do
test "doesn't retrieve unlisted activities" do
user = insert(:user)