From 93397fce3de54985bde3c3f260660a63157077be Mon Sep 17 00:00:00 2001 From: Egor Kislitsyn Date: Fri, 31 May 2019 16:22:13 +0700 Subject: [PATCH] Contain search for unauthenticated users --- lib/pleroma/activity.ex | 2 + lib/pleroma/activity/search.ex | 75 +++++++++++++++++++ .../mastodon_api/mastodon_api_controller.ex | 61 +-------------- test/activity_test.exs | 38 ++++++++++ 4 files changed, 117 insertions(+), 59 deletions(-) create mode 100644 lib/pleroma/activity/search.ex diff --git a/lib/pleroma/activity.ex b/lib/pleroma/activity.ex index 99589590c..6db41fe6e 100644 --- a/lib/pleroma/activity.ex +++ b/lib/pleroma/activity.ex @@ -343,4 +343,6 @@ def restrict_deactivated_users(query) do ) ) end + + defdelegate search(user, query), to: Pleroma.Activity.Search end diff --git a/lib/pleroma/activity/search.ex b/lib/pleroma/activity/search.ex new file mode 100644 index 000000000..f2fdfffe1 --- /dev/null +++ b/lib/pleroma/activity/search.ex @@ -0,0 +1,75 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Activity.Search do + alias Pleroma.Activity + alias Pleroma.Object.Fetcher + alias Pleroma.Repo + alias Pleroma.User + alias Pleroma.Web.ActivityPub.Visibility + + import Ecto.Query + + def search(user, search_query) do + index_type = if Pleroma.Config.get([:database, :rum_enabled]), do: :rum, else: :gin + + Activity + |> Activity.with_preloaded_object() + |> Activity.restrict_deactivated_users() + |> restrict_public() + |> query_with(index_type, search_query) + |> maybe_restrict_local(user) + |> Repo.all() + |> maybe_fetch(user, search_query) + end + + defp restrict_public(q) do + from([a, o] in q, + where: fragment("?->>'type' = 'Create'", a.data), + where: "https://www.w3.org/ns/activitystreams#Public" in a.recipients, + limit: 40 + ) + end + + defp query_with(q, :gin, search_query) do + from([a, o] in q, + where: + fragment( + "to_tsvector('english', ?->>'content') @@ plainto_tsquery('english', ?)", + o.data, + ^search_query + ), + order_by: [desc: :id] + ) + end + + defp query_with(q, :rum, search_query) do + from([a, o] in q, + where: + fragment( + "? @@ plainto_tsquery('english', ?)", + o.fts_content, + ^search_query + ), + order_by: [fragment("? <=> now()::date", o.inserted_at)] + ) + end + + # users can search everything + defp maybe_restrict_local(q, %User{}), do: q + + # unauthenticated users can only search local activities + defp maybe_restrict_local(q, _), do: where(q, local: true) + + defp maybe_fetch(activities, user, search_query) do + with true <- Regex.match?(~r/https?:/, search_query), + {:ok, object} <- Fetcher.fetch_object_from_id(search_query), + %Activity{} = activity <- Activity.get_create_by_object_ap_id(object.data["id"]), + true <- Visibility.visible_for_user?(activity, user) do + activities ++ [activity] + else + _ -> activities + end + end +end diff --git a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex index d825555c6..92cd77f62 100644 --- a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex +++ b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex @@ -14,7 +14,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do alias Pleroma.HTTP alias Pleroma.Notification alias Pleroma.Object - alias Pleroma.Object.Fetcher alias Pleroma.Pagination alias Pleroma.Repo alias Pleroma.ScheduledActivity @@ -1125,64 +1124,9 @@ def unsubscribe(%{assigns: %{user: user}} = conn, %{"id" => id}) do end end - def status_search_query_with_gin(q, query) do - from([a, o] in q, - where: - fragment( - "to_tsvector('english', ?->>'content') @@ plainto_tsquery('english', ?)", - o.data, - ^query - ), - order_by: [desc: :id] - ) - end - - def status_search_query_with_rum(q, query) do - from([a, o] in q, - where: - fragment( - "? @@ plainto_tsquery('english', ?)", - o.fts_content, - ^query - ), - order_by: [fragment("? <=> now()::date", o.inserted_at)] - ) - end - - def status_search(user, query) do - fetched = - if Regex.match?(~r/https?:/, query) do - with {:ok, object} <- Fetcher.fetch_object_from_id(query), - %Activity{} = activity <- Activity.get_create_by_object_ap_id(object.data["id"]), - true <- Visibility.visible_for_user?(activity, user) do - [activity] - else - _e -> [] - end - end || [] - - q = - from([a, o] in Activity.with_preloaded_object(Activity), - where: fragment("?->>'type' = 'Create'", a.data), - where: "https://www.w3.org/ns/activitystreams#Public" in a.recipients, - limit: 40 - ) - - q = - if Pleroma.Config.get([:database, :rum_enabled]) do - status_search_query_with_rum(q, query) - else - status_search_query_with_gin(q, query) - end - - Repo.all(q) ++ fetched - end - def search2(%{assigns: %{user: user}} = conn, %{"q" => query} = params) do accounts = User.search(query, resolve: params["resolve"] == "true", for_user: user) - - statuses = status_search(user, query) - + statuses = Activity.search(user, query) tags_path = Web.base_url() <> "/tag/" tags = @@ -1205,8 +1149,7 @@ def search2(%{assigns: %{user: user}} = conn, %{"q" => query} = params) do def search(%{assigns: %{user: user}} = conn, %{"q" => query} = params) do accounts = User.search(query, resolve: params["resolve"] == "true", for_user: user) - - statuses = status_search(user, query) + statuses = Activity.search(user, query) tags = query diff --git a/test/activity_test.exs b/test/activity_test.exs index 15c95502a..5260ebb9e 100644 --- a/test/activity_test.exs +++ b/test/activity_test.exs @@ -99,4 +99,42 @@ test "when association is not loaded" do assert Activity.get_bookmark(queried_activity, user) == bookmark end end + + describe "search" do + setup do + Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end) + + user = insert(:user) + + params = %{ + "@context" => "https://www.w3.org/ns/activitystreams", + "actor" => "http://mastodon.example.org/users/admin", + "type" => "Create", + "id" => "http://mastodon.example.org/users/admin/activities/1", + "object" => %{ + "type" => "Note", + "content" => "find me!", + "id" => "http://mastodon.example.org/users/admin/objects/1", + "attributedTo" => "http://mastodon.example.org/users/admin" + }, + "to" => ["https://www.w3.org/ns/activitystreams#Public"] + } + + {:ok, local_activity} = Pleroma.Web.CommonAPI.post(user, %{"status" => "find me!"}) + {:ok, remote_activity} = Pleroma.Web.Federator.incoming_ap_doc(params) + %{local_activity: local_activity, remote_activity: remote_activity, user: user} + end + + test "find local and remote statuses for authenticated users", %{ + local_activity: local_activity, + remote_activity: remote_activity, + user: user + } do + assert [^remote_activity, ^local_activity] = Activity.search(user, "find me") + end + + test "find only local statuses for unauthenticated users", %{local_activity: local_activity} do + assert [^local_activity] = Activity.search(nil, "find me") + end + end end