Add activity visibility index.
authorlain <lain@soykaf.club>
Wed, 9 Jan 2019 15:45:09 +0000 (16:45 +0100)
committerlain <lain@soykaf.club>
Wed, 9 Jan 2019 15:45:09 +0000 (16:45 +0100)
lib/pleroma/web/activity_pub/activity_pub.ex
priv/repo/migrations/20190109152453_add_visibility_function.exs [new file with mode: 0644]
test/web/activity_pub/activity_pub_test.exs

index 4685f6d95ad796f6e8cca791c6426361b60b90ee..b8141146fa5372a3c275e99cf66267de9a533533 100644 (file)
@@ -364,21 +364,18 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub 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 (file)
index 0000000..6c94f1b
--- /dev/null
@@ -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
index 2453998ad8a54f4ed59c0dfce92c55335aa6bf48..47aa5a56f3b3ff9cc82c18fe66b4ffd755b00120 100644 (file)
@@ -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"