Move visibility into own module.
authorlain <lain@soykaf.club>
Fri, 22 Feb 2019 12:29:52 +0000 (13:29 +0100)
committerlain <lain@soykaf.club>
Fri, 22 Feb 2019 12:29:52 +0000 (13:29 +0100)
lib/pleroma/gopher/server.ex
lib/pleroma/web/activity_pub/activity_pub.ex
lib/pleroma/web/activity_pub/activity_pub_controller.ex
lib/pleroma/web/activity_pub/transmogrifier.ex
lib/pleroma/web/activity_pub/visibility.ex [new file with mode: 0644]
lib/pleroma/web/federator/federator.ex
lib/pleroma/web/mastodon_api/mastodon_api_controller.ex
lib/pleroma/web/ostatus/ostatus_controller.ex
lib/pleroma/web/streamer.ex
lib/pleroma/web/twitter_api/twitter_api_controller.ex
test/web/activity_pub/visibilty_test.exs [new file with mode: 0644]

index 32cb817d253a639d318ae94f7d0cef494a342428..ba9614029ce510b78c167d12109ecd340e00d88e 100644 (file)
@@ -37,6 +37,7 @@ end
 
 defmodule Pleroma.Gopher.Server.ProtocolHandler do
   alias Pleroma.Web.ActivityPub.ActivityPub
+  alias Pleroma.Web.ActivityPub.Visibility
   alias Pleroma.Activity
   alias Pleroma.HTML
   alias Pleroma.User
@@ -110,7 +111,7 @@ defmodule Pleroma.Gopher.Server.ProtocolHandler do
 
   def response("/notices/" <> id) do
     with %Activity{} = activity <- Repo.get(Activity, id),
-         true <- ActivityPub.is_public?(activity) do
+         true <- Visibility.is_public?(activity) do
       activities =
         ActivityPub.fetch_activities_for_context(activity.data["context"])
         |> render_activities
index fb0b7b68e335132b1d27469b151be6f2ca0601f1..cc255cc9e1a7257e7ebe6855625f2f9f1549fba2 100644 (file)
@@ -18,6 +18,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
 
   import Ecto.Query
   import Pleroma.Web.ActivityPub.Utils
+  import Pleroma.Web.ActivityPub.Visibility
 
   require Logger
 
@@ -912,57 +913,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
     end
   end
 
-  def is_public?(%Object{data: %{"type" => "Tombstone"}}), do: false
-  def is_public?(%Object{data: data}), do: is_public?(data)
-  def is_public?(%Activity{data: data}), do: is_public?(data)
-  def is_public?(%{"directMessage" => true}), do: false
-
-  def is_public?(data) do
-    "https://www.w3.org/ns/activitystreams#Public" in (data["to"] ++ (data["cc"] || []))
-  end
-
-  def is_private?(activity) do
-    unless is_public?(activity) do
-      follower_address = User.get_cached_by_ap_id(activity.data["actor"]).follower_address
-      Enum.any?(activity.data["to"], &(&1 == follower_address))
-    else
-      false
-    end
-  end
-
-  def is_direct?(%Activity{data: %{"directMessage" => true}}), do: true
-  def is_direct?(%Object{data: %{"directMessage" => true}}), do: true
-
-  def is_direct?(activity) do
-    !is_public?(activity) && !is_private?(activity)
-  end
-
-  def visible_for_user?(activity, nil) do
-    is_public?(activity)
-  end
-
-  def visible_for_user?(activity, user) do
-    x = [user.ap_id | user.following]
-    y = [activity.actor] ++ activity.data["to"] ++ (activity.data["cc"] || [])
-    visible_for_user?(activity, nil) || Enum.any?(x, &(&1 in y))
-  end
-
-  # guard
-  def entire_thread_visible_for_user?(nil, _user), do: false
-
-  # child
-  def entire_thread_visible_for_user?(
-        %Activity{data: %{"object" => %{"inReplyTo" => parent_id}}} = tail,
-        user
-      )
-      when is_binary(parent_id) do
-    parent = Activity.get_in_reply_to_activity(tail)
-    visible_for_user?(tail, user) && entire_thread_visible_for_user?(parent, user)
-  end
-
-  # root
-  def entire_thread_visible_for_user?(tail, user), do: visible_for_user?(tail, user)
-
   # filter out broken threads
   def contain_broken_threads(%Activity{} = activity, %User{} = user) do
     entire_thread_visible_for_user?(activity, user)
index 2bea5131148330f7a3d87e5236dc648d89815ba6..ff924a5362d6093113cf229bbb2e106bb4e57201 100644 (file)
@@ -11,6 +11,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
   alias Pleroma.Web.ActivityPub.ObjectView
   alias Pleroma.Web.ActivityPub.UserView
   alias Pleroma.Web.ActivityPub.ActivityPub
+  alias Pleroma.Web.ActivityPub.Visibility
   alias Pleroma.Web.ActivityPub.Relay
   alias Pleroma.Web.ActivityPub.Transmogrifier
   alias Pleroma.Web.ActivityPub.Utils
@@ -49,7 +50,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
   def object(conn, %{"uuid" => uuid}) do
     with ap_id <- o_status_url(conn, :object, uuid),
          %Object{} = object <- Object.get_cached_by_ap_id(ap_id),
-         {_, true} <- {:public?, ActivityPub.is_public?(object)} do
+         {_, true} <- {:public?, Visibility.is_public?(object)} do
       conn
       |> put_resp_header("content-type", "application/activity+json")
       |> json(ObjectView.render("object.json", %{object: object}))
@@ -62,7 +63,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
   def object_likes(conn, %{"uuid" => uuid, "page" => page}) do
     with ap_id <- o_status_url(conn, :object, uuid),
          %Object{} = object <- Object.get_cached_by_ap_id(ap_id),
-         {_, true} <- {:public?, ActivityPub.is_public?(object)},
+         {_, true} <- {:public?, Visibility.is_public?(object)},
          likes <- Utils.get_object_likes(object) do
       {page, _} = Integer.parse(page)
 
@@ -78,7 +79,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
   def object_likes(conn, %{"uuid" => uuid}) do
     with ap_id <- o_status_url(conn, :object, uuid),
          %Object{} = object <- Object.get_cached_by_ap_id(ap_id),
-         {_, true} <- {:public?, ActivityPub.is_public?(object)},
+         {_, true} <- {:public?, Visibility.is_public?(object)},
          likes <- Utils.get_object_likes(object) do
       conn
       |> put_resp_header("content-type", "application/activity+json")
@@ -92,7 +93,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
   def activity(conn, %{"uuid" => uuid}) do
     with ap_id <- o_status_url(conn, :activity, uuid),
          %Activity{} = activity <- Activity.normalize(ap_id),
-         {_, true} <- {:public?, ActivityPub.is_public?(activity)} do
+         {_, true} <- {:public?, Visibility.is_public?(activity)} do
       conn
       |> put_resp_header("content-type", "application/activity+json")
       |> json(ObjectView.render("object.json", %{object: activity}))
index 41d89a02b145d6594c7f60558ad643ff9e61beaf..88007aa162b35654738259c787e06335d0640c85 100644 (file)
@@ -12,6 +12,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
   alias Pleroma.Repo
   alias Pleroma.Web.ActivityPub.ActivityPub
   alias Pleroma.Web.ActivityPub.Utils
+  alias Pleroma.Web.ActivityPub.Visibility
 
   import Ecto.Query
 
@@ -489,7 +490,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
     with actor <- get_actor(data),
          %User{} = actor <- User.get_or_fetch_by_ap_id(actor),
          {:ok, object} <- get_obj_helper(object_id) || fetch_obj_helper(object_id),
-         public <- ActivityPub.is_public?(data),
+         public <- Visibility.is_public?(data),
          {:ok, activity, _object} <- ActivityPub.announce(actor, object, id, false, public) do
       {:ok, activity}
     else
diff --git a/lib/pleroma/web/activity_pub/visibility.ex b/lib/pleroma/web/activity_pub/visibility.ex
new file mode 100644 (file)
index 0000000..db52fe9
--- /dev/null
@@ -0,0 +1,56 @@
+defmodule Pleroma.Web.ActivityPub.Visibility do
+  alias Pleroma.Activity
+  alias Pleroma.Object
+  alias Pleroma.User
+
+  def is_public?(%Object{data: %{"type" => "Tombstone"}}), do: false
+  def is_public?(%Object{data: data}), do: is_public?(data)
+  def is_public?(%Activity{data: data}), do: is_public?(data)
+  def is_public?(%{"directMessage" => true}), do: false
+
+  def is_public?(data) do
+    "https://www.w3.org/ns/activitystreams#Public" in (data["to"] ++ (data["cc"] || []))
+  end
+
+  def is_private?(activity) do
+    unless is_public?(activity) do
+      follower_address = User.get_cached_by_ap_id(activity.data["actor"]).follower_address
+      Enum.any?(activity.data["to"], &(&1 == follower_address))
+    else
+      false
+    end
+  end
+
+  def is_direct?(%Activity{data: %{"directMessage" => true}}), do: true
+  def is_direct?(%Object{data: %{"directMessage" => true}}), do: true
+
+  def is_direct?(activity) do
+    !is_public?(activity) && !is_private?(activity)
+  end
+
+  def visible_for_user?(activity, nil) do
+    is_public?(activity)
+  end
+
+  def visible_for_user?(activity, user) do
+    x = [user.ap_id | user.following]
+    y = [activity.actor] ++ activity.data["to"] ++ (activity.data["cc"] || [])
+    visible_for_user?(activity, nil) || Enum.any?(x, &(&1 in y))
+  end
+
+  # guard
+  def entire_thread_visible_for_user?(nil, _user), do: false
+
+  # child
+  def entire_thread_visible_for_user?(
+        %Activity{data: %{"object" => %{"inReplyTo" => parent_id}}} = tail,
+        user
+      )
+      when is_binary(parent_id) do
+    parent = Activity.get_in_reply_to_activity(tail)
+    visible_for_user?(tail, user) && entire_thread_visible_for_user?(parent, user)
+  end
+
+  # root
+  def entire_thread_visible_for_user?(tail, user), do: visible_for_user?(tail, user)
+end
index d4e2a97422e59dd0a680be3bbef31e494d688c8a..fbfe97dbc7a79640071cfd2de5e6496b6fe4c762 100644 (file)
@@ -9,6 +9,7 @@ defmodule Pleroma.Web.Federator do
   alias Pleroma.Web.Websub
   alias Pleroma.Web.Salmon
   alias Pleroma.Web.ActivityPub.ActivityPub
+  alias Pleroma.Web.ActivityPub.Visibility
   alias Pleroma.Web.ActivityPub.Relay
   alias Pleroma.Web.ActivityPub.Transmogrifier
   alias Pleroma.Web.ActivityPub.Utils
@@ -94,7 +95,7 @@ defmodule Pleroma.Web.Federator do
     with actor when not is_nil(actor) <- User.get_cached_by_ap_id(activity.data["actor"]) do
       {:ok, actor} = WebFinger.ensure_keys_present(actor)
 
-      if ActivityPub.is_public?(activity) do
+      if Visibility.is_public?(activity) do
         if OStatus.is_representable?(activity) do
           Logger.info(fn -> "Sending #{activity.data["id"]} out via WebSub" end)
           Websub.publish(Pleroma.Web.OStatus.feed_path(actor), actor, activity)
index cf7458d5fe4f5c2927288c7535b0af390ccb97a7..12987442a0c487f1695d961d184643f506e1da94 100644 (file)
@@ -27,6 +27,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
   alias Pleroma.Web.MastodonAPI.ReportView
   alias Pleroma.Web.ActivityPub.ActivityPub
   alias Pleroma.Web.ActivityPub.Utils
+  alias Pleroma.Web.ActivityPub.Visibility
   alias Pleroma.Web.OAuth.App
   alias Pleroma.Web.OAuth.Authorization
   alias Pleroma.Web.OAuth.Token
@@ -307,7 +308,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
 
   def get_status(%{assigns: %{user: user}} = conn, %{"id" => id}) do
     with %Activity{} = activity <- Repo.get(Activity, id),
-         true <- ActivityPub.visible_for_user?(activity, user) do
+         true <- Visibility.visible_for_user?(activity, user) do
       conn
       |> put_view(StatusView)
       |> try_render("status.json", %{activity: activity, for: user})
@@ -449,7 +450,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
   def bookmark_status(%{assigns: %{user: user}} = conn, %{"id" => id}) do
     with %Activity{} = activity <- Repo.get(Activity, id),
          %User{} = user <- User.get_by_nickname(user.nickname),
-         true <- ActivityPub.visible_for_user?(activity, user),
+         true <- Visibility.visible_for_user?(activity, user),
          {:ok, user} <- User.bookmark(user, activity.data["object"]["id"]) do
       conn
       |> put_view(StatusView)
@@ -460,7 +461,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
   def unbookmark_status(%{assigns: %{user: user}} = conn, %{"id" => id}) do
     with %Activity{} = activity <- Repo.get(Activity, id),
          %User{} = user <- User.get_by_nickname(user.nickname),
-         true <- ActivityPub.visible_for_user?(activity, user),
+         true <- Visibility.visible_for_user?(activity, user),
          {:ok, user} <- User.unbookmark(user, activity.data["object"]["id"]) do
       conn
       |> put_view(StatusView)
@@ -867,7 +868,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
       if Regex.match?(~r/https?:/, query) do
         with {:ok, object} <- ActivityPub.fetch_object_from_id(query),
              %Activity{} = activity <- Activity.get_create_by_object_ap_id(object.data["id"]),
-             true <- ActivityPub.visible_for_user?(activity, user) do
+             true <- Visibility.visible_for_user?(activity, user) do
           [activity]
         else
           _e -> []
@@ -1520,7 +1521,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
 
   def status_card(%{assigns: %{user: user}} = conn, %{"id" => status_id}) do
     with %Activity{} = activity <- Repo.get(Activity, status_id),
-         true <- ActivityPub.visible_for_user?(activity, user) do
+         true <- Visibility.visible_for_user?(activity, user) do
       data =
         StatusView.render(
           "card.json",
index df723f63889de94c11f66dfa137d3b29de2f86c7..4e963774a9eb1e9226c4d2561f03fc63b5fa8e35 100644 (file)
@@ -9,6 +9,7 @@ defmodule Pleroma.Web.OStatus.OStatusController do
   alias Pleroma.Object
   alias Pleroma.User
   alias Pleroma.Web.ActivityPub.ActivityPub
+  alias Pleroma.Web.ActivityPub.Visibility
   alias Pleroma.Web.ActivityPub.ActivityPubController
   alias Pleroma.Web.ActivityPub.ObjectView
   alias Pleroma.Web.OStatus.ActivityRepresenter
@@ -102,7 +103,7 @@ defmodule Pleroma.Web.OStatus.OStatusController do
     else
       with id <- o_status_url(conn, :object, uuid),
            {_, %Activity{} = activity} <- {:activity, Activity.get_create_by_object_ap_id(id)},
-           {_, true} <- {:public?, ActivityPub.is_public?(activity)},
+           {_, true} <- {:public?, Visibility.is_public?(activity)},
            %User{} = user <- User.get_cached_by_ap_id(activity.data["actor"]) do
         case get_format(conn) do
           "html" -> redirect(conn, to: "/notice/#{activity.id}")
@@ -127,7 +128,7 @@ defmodule Pleroma.Web.OStatus.OStatusController do
     else
       with id <- o_status_url(conn, :activity, uuid),
            {_, %Activity{} = activity} <- {:activity, Activity.normalize(id)},
-           {_, true} <- {:public?, ActivityPub.is_public?(activity)},
+           {_, true} <- {:public?, Visibility.is_public?(activity)},
            %User{} = user <- User.get_cached_by_ap_id(activity.data["actor"]) do
         case format = get_format(conn) do
           "html" -> redirect(conn, to: "/notice/#{activity.id}")
@@ -148,7 +149,7 @@ defmodule Pleroma.Web.OStatus.OStatusController do
 
   def notice(conn, %{"id" => id}) do
     with {_, %Activity{} = activity} <- {:activity, Activity.get_by_id(id)},
-         {_, true} <- {:public?, ActivityPub.is_public?(activity)},
+         {_, true} <- {:public?, Visibility.is_public?(activity)},
          %User{} = user <- User.get_cached_by_ap_id(activity.data["actor"]) do
       case format = get_format(conn) do
         "html" ->
@@ -191,7 +192,7 @@ defmodule Pleroma.Web.OStatus.OStatusController do
   # Returns an HTML embedded <audio> or <video> player suitable for embed iframes.
   def notice_player(conn, %{"id" => id}) do
     with %Activity{data: %{"type" => "Create"}} = activity <- Activity.get_by_id(id),
-         true <- ActivityPub.is_public?(activity),
+         true <- Visibility.is_public?(activity),
          %Object{} = object <- Object.normalize(activity.data["object"]),
          %{data: %{"attachment" => [%{"url" => [url | _]} | _]}} <- object,
          true <- String.starts_with?(url["mediaType"], ["audio", "video"]) do
index 4de7608e4c683d789cbf80779101136739770c4d..477481bb9cd55bd3643b6199df186d998ef940e9 100644 (file)
@@ -10,7 +10,7 @@ defmodule Pleroma.Web.Streamer do
   alias Pleroma.Activity
   alias Pleroma.Object
   alias Pleroma.Repo
-  alias Pleroma.Web.ActivityPub.ActivityPub
+  alias Pleroma.Web.ActivityPub.Visibility
 
   @keepalive_interval :timer.seconds(30)
 
@@ -73,7 +73,7 @@ defmodule Pleroma.Web.Streamer do
   def handle_cast(%{action: :stream, topic: "list", item: item}, topics) do
     # filter the recipient list if the activity is not public, see #270.
     recipient_lists =
-      case ActivityPub.is_public?(item) do
+      case Visibility.is_public?(item) do
         true ->
           Pleroma.List.get_lists_from_activity(item)
 
@@ -82,7 +82,7 @@ defmodule Pleroma.Web.Streamer do
           |> Enum.filter(fn list ->
             owner = Repo.get(User, list.user_id)
 
-            ActivityPub.visible_for_user?(item, owner)
+            Visibility.visible_for_user?(item, owner)
           end)
       end
 
index b815379fdae614ec57972249cf22ab471c77100a..0d74c30c36266b92b545b02c2d8e46d47d35133c 100644 (file)
@@ -13,6 +13,7 @@ defmodule Pleroma.Web.TwitterAPI.Controller do
   alias Pleroma.{Repo, Activity, Object, User, Notification}
   alias Pleroma.Web.OAuth.Token
   alias Pleroma.Web.ActivityPub.ActivityPub
+  alias Pleroma.Web.ActivityPub.Visibility
   alias Pleroma.Web.ActivityPub.Utils
   alias Pleroma.Web.CommonAPI
   alias Pleroma.Web.TwitterAPI.ActivityView
@@ -268,7 +269,7 @@ defmodule Pleroma.Web.TwitterAPI.Controller do
 
   def fetch_status(%{assigns: %{user: user}} = conn, %{"id" => id}) do
     with %Activity{} = activity <- Repo.get(Activity, id),
-         true <- ActivityPub.visible_for_user?(activity, user) do
+         true <- Visibility.visible_for_user?(activity, user) do
       conn
       |> put_view(ActivityView)
       |> render("activity.json", %{activity: activity, for: user})
diff --git a/test/web/activity_pub/visibilty_test.exs b/test/web/activity_pub/visibilty_test.exs
new file mode 100644 (file)
index 0000000..1172b74
--- /dev/null
@@ -0,0 +1,98 @@
+defmodule Pleroma.Web.ActivityPub.VisibilityTest do
+  use Pleroma.DataCase
+
+  alias Pleroma.Web.CommonAPI
+  alias Pleroma.Web.ActivityPub.Visibility
+  import Pleroma.Factory
+
+  setup do
+    user = insert(:user)
+    mentioned = insert(:user)
+    following = insert(:user)
+    unrelated = insert(:user)
+    {:ok, following} = Pleroma.User.follow(following, user)
+
+    {:ok, public} =
+      CommonAPI.post(user, %{"status" => "@#{mentioned.nickname}", "visibility" => "public"})
+
+    {:ok, private} =
+      CommonAPI.post(user, %{"status" => "@#{mentioned.nickname}", "visibility" => "private"})
+
+    {:ok, direct} =
+      CommonAPI.post(user, %{"status" => "@#{mentioned.nickname}", "visibility" => "direct"})
+
+    {:ok, unlisted} =
+      CommonAPI.post(user, %{"status" => "@#{mentioned.nickname}", "visibility" => "unlisted"})
+
+    %{
+      public: public,
+      private: private,
+      direct: direct,
+      unlisted: unlisted,
+      user: user,
+      mentioned: mentioned,
+      following: following,
+      unrelated: unrelated
+    }
+  end
+
+  test "is_direct?", %{public: public, private: private, direct: direct, unlisted: unlisted} do
+    assert Visibility.is_direct?(direct)
+    refute Visibility.is_direct?(public)
+    refute Visibility.is_direct?(private)
+    refute Visibility.is_direct?(unlisted)
+  end
+
+  test "is_public?", %{public: public, private: private, direct: direct, unlisted: unlisted} do
+    refute Visibility.is_public?(direct)
+    assert Visibility.is_public?(public)
+    refute Visibility.is_public?(private)
+    assert Visibility.is_public?(unlisted)
+  end
+
+  test "is_private?", %{public: public, private: private, direct: direct, unlisted: unlisted} do
+    refute Visibility.is_private?(direct)
+    refute Visibility.is_private?(public)
+    assert Visibility.is_private?(private)
+    refute Visibility.is_private?(unlisted)
+  end
+
+  test "visible_for_user?", %{
+    public: public,
+    private: private,
+    direct: direct,
+    unlisted: unlisted,
+    user: user,
+    mentioned: mentioned,
+    following: following,
+    unrelated: unrelated
+  } do
+    # All visible to author
+
+    assert Visibility.visible_for_user?(public, user)
+    assert Visibility.visible_for_user?(private, user)
+    assert Visibility.visible_for_user?(unlisted, user)
+    assert Visibility.visible_for_user?(direct, user)
+
+    # All visible to a mentioned user
+
+    assert Visibility.visible_for_user?(public, mentioned)
+    assert Visibility.visible_for_user?(private, mentioned)
+    assert Visibility.visible_for_user?(unlisted, mentioned)
+    assert Visibility.visible_for_user?(direct, mentioned)
+
+    # DM not visible for just follower
+
+    assert Visibility.visible_for_user?(public, following)
+    assert Visibility.visible_for_user?(private, following)
+    assert Visibility.visible_for_user?(unlisted, following)
+    refute Visibility.visible_for_user?(direct, following)
+
+    # Public and unlisted visible for unrelated user
+
+    assert Visibility.visible_for_user?(public, unrelated)
+    assert Visibility.visible_for_user?(unlisted, unrelated)
+    refute Visibility.visible_for_user?(private, unrelated)
+    refute Visibility.visible_for_user?(direct, unrelated)
+  end
+end