From 5027f82cdef52391e408428ecc8013b1c4847b6b Mon Sep 17 00:00:00 2001
From: lain <lain@soykaf.club>
Date: Wed, 9 Jan 2019 16:45:09 +0100
Subject: [PATCH 1/2] Add activity visibility index.

---
 lib/pleroma/web/activity_pub/activity_pub.ex  | 25 +++++-----
 ...20190109152453_add_visibility_function.exs | 46 +++++++++++++++++++
 test/web/activity_pub/activity_pub_test.exs   | 36 +++++++++++++++
 3 files changed, 93 insertions(+), 14 deletions(-)
 create mode 100644 priv/repo/migrations/20190109152453_add_visibility_function.exs

diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex
index 4685f6d95..b8141146f 100644
--- a/lib/pleroma/web/activity_pub/activity_pub.ex
+++ b/lib/pleroma/web/activity_pub/activity_pub.ex
@@ -364,21 +364,18 @@ def fetch_public_activities(opts \\ %{}) do
 
   @valid_visibilities ~w[direct unlisted public private]
 
-  defp restrict_visibility(query, %{visibility: "direct"}) do
-    public = "https://www.w3.org/ns/activitystreams#Public"
+  defp restrict_visibility(query, %{visibility: visibility})
+       when visibility in @valid_visibilities do
+    query =
+      from(
+        a in query,
+        where:
+          fragment("activity_visibility(?, ?, ?) = ?", a.actor, a.recipients, a.data, ^visibility)
+      )
 
-    from(
-      activity in query,
-      join: sender in User,
-      on: sender.ap_id == activity.actor,
-      # Are non-direct statuses with no to/cc possible?
-      where:
-        fragment(
-          "not (? && ?)",
-          [^public, sender.follower_address],
-          activity.recipients
-        )
-    )
+    Ecto.Adapters.SQL.to_sql(:all, Repo, query)
+
+    query
   end
 
   defp restrict_visibility(_query, %{visibility: visibility})
diff --git a/priv/repo/migrations/20190109152453_add_visibility_function.exs b/priv/repo/migrations/20190109152453_add_visibility_function.exs
new file mode 100644
index 000000000..6c94f1bb3
--- /dev/null
+++ b/priv/repo/migrations/20190109152453_add_visibility_function.exs
@@ -0,0 +1,46 @@
+defmodule Pleroma.Repo.Migrations.AddVisibilityFunction do
+  use Ecto.Migration
+
+  def up do
+    definition = """
+    create or replace function activity_visibility(actor varchar, recipients varchar[], data jsonb) returns varchar as $$
+    DECLARE
+      fa varchar;
+      public varchar := 'https://www.w3.org/ns/activitystreams#Public';
+    BEGIN
+      SELECT COALESCE(users.follower_address, '') into fa from users where users.ap_id = actor;
+
+      IF data->'to' ? public THEN
+        RETURN 'public';
+      ELSIF data->'cc' ? public THEN
+        RETURN 'unlisted';
+      ELSIF ARRAY[fa] && recipients THEN
+        RETURN 'private';
+      ELSIF not(ARRAY[fa, public] && recipients) THEN
+        RETURN 'direct';
+      ELSE
+        RETURN 'unknown';
+      END IF;
+    END;
+    $$ LANGUAGE plpgsql IMMUTABLE;
+    """
+
+    execute(definition)
+
+    create(
+      index(:activities, ["activity_visibility(actor, recipients, data)"],
+        name: :activities_visibility_index
+      )
+    )
+  end
+
+  def down do
+    drop(
+      index(:activities, ["activity_visibility(actor, recipients, data)"],
+        name: :activities_visibility_index
+      )
+    )
+
+    execute("drop function activity_visibility(actor varchar, recipients varchar[], data jsonb)")
+  end
+end
diff --git a/test/web/activity_pub/activity_pub_test.exs b/test/web/activity_pub/activity_pub_test.exs
index 2453998ad..47aa5a56f 100644
--- a/test/web/activity_pub/activity_pub_test.exs
+++ b/test/web/activity_pub/activity_pub_test.exs
@@ -18,6 +18,42 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
     :ok
   end
 
+  describe "fetching restricted by visibility" do
+    test "it restricts by the appropriate visibility" do
+      user = insert(:user)
+
+      {:ok, public_activity} = CommonAPI.post(user, %{"status" => ".", "visibility" => "public"})
+
+      {:ok, direct_activity} = CommonAPI.post(user, %{"status" => ".", "visibility" => "direct"})
+
+      {:ok, unlisted_activity} =
+        CommonAPI.post(user, %{"status" => ".", "visibility" => "unlisted"})
+
+      {:ok, private_activity} =
+        CommonAPI.post(user, %{"status" => ".", "visibility" => "private"})
+
+      activities =
+        ActivityPub.fetch_activities([], %{:visibility => "direct", "actor_id" => user.ap_id})
+
+      assert activities == [direct_activity]
+
+      activities =
+        ActivityPub.fetch_activities([], %{:visibility => "unlisted", "actor_id" => user.ap_id})
+
+      assert activities == [unlisted_activity]
+
+      activities =
+        ActivityPub.fetch_activities([], %{:visibility => "private", "actor_id" => user.ap_id})
+
+      assert activities == [private_activity]
+
+      activities =
+        ActivityPub.fetch_activities([], %{:visibility => "public", "actor_id" => user.ap_id})
+
+      assert activities == [public_activity]
+    end
+  end
+
   describe "building a user from his ap id" do
     test "it returns a user" do
       user_id = "http://mastodon.example.org/users/admin"

From 04735db193978f05d2731878896fa70172f2fb11 Mon Sep 17 00:00:00 2001
From: lain <lain@soykaf.club>
Date: Wed, 9 Jan 2019 16:55:05 +0100
Subject: [PATCH 2/2] Build the index concurrently.

---
 .../migrations/20190109152453_add_visibility_function.exs     | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/priv/repo/migrations/20190109152453_add_visibility_function.exs b/priv/repo/migrations/20190109152453_add_visibility_function.exs
index 6c94f1bb3..3aadabcd7 100644
--- a/priv/repo/migrations/20190109152453_add_visibility_function.exs
+++ b/priv/repo/migrations/20190109152453_add_visibility_function.exs
@@ -1,5 +1,6 @@
 defmodule Pleroma.Repo.Migrations.AddVisibilityFunction do
   use Ecto.Migration
+  @disable_ddl_transaction true
 
   def up do
     definition = """
@@ -29,7 +30,8 @@ def up do
 
     create(
       index(:activities, ["activity_visibility(actor, recipients, data)"],
-        name: :activities_visibility_index
+        name: :activities_visibility_index,
+        concurrently: true
       )
     )
   end