activitypub: visibility: refactor is_public?() to use JSON-LD safe accessors
authorAriadne Conill <ariadne@dereferenced.org>
Wed, 23 Oct 2019 01:50:25 +0000 (20:50 -0500)
committerAriadne Conill <ariadne@dereferenced.org>
Wed, 23 Oct 2019 01:50:46 +0000 (20:50 -0500)
lib/pleroma/web/activity_pub/utils.ex
lib/pleroma/web/activity_pub/visibility.ex
test/web/activity_pub/activity_pub_controller_test.exs

index 6b28df92c2a6ec753f233dce80cf2b8ca9b9804e..699d5162f282a168510ad9a0ae6908be56a3982a 100644 (file)
@@ -49,26 +49,28 @@ defmodule Pleroma.Web.ActivityPub.Utils do
 
   def determine_explicit_mentions(_), do: []
 
-  @spec recipient_in_collection(any(), any()) :: boolean()
-  defp recipient_in_collection(ap_id, coll) when is_binary(coll), do: ap_id == coll
-  defp recipient_in_collection(ap_id, coll) when is_list(coll), do: ap_id in coll
-  defp recipient_in_collection(_, _), do: false
+  @spec label_in_collection?(any(), any()) :: boolean()
+  defp label_in_collection?(ap_id, coll) when is_binary(coll), do: ap_id == coll
+  defp label_in_collection?(ap_id, coll) when is_list(coll), do: ap_id in coll
+  defp label_in_collection?(_, _), do: false
+
+  @spec label_in_message?(String.t(), map()) :: boolean()
+  def label_in_message?(label, params),
+    do:
+      [params["to"], params["cc"], params["bto"], params["bcc"]]
+      |> Enum.any?(&label_in_collection?(label, &1))
+
+  @spec unaddressed_message?(map()) :: boolean()
+  def unaddressed_message?(params),
+    do:
+      [params["to"], params["cc"], params["bto"], params["bcc"]]
+      |> Enum.all?(&is_nil(&1))
 
   @spec recipient_in_message(User.t(), User.t(), map()) :: boolean()
-  def recipient_in_message(%User{ap_id: ap_id} = recipient, %User{} = actor, params) do
-    addresses = [params["to"], params["cc"], params["bto"], params["bcc"]]
-
-    cond do
-      Enum.any?(addresses, &recipient_in_collection(ap_id, &1)) -> true
-      # if the message is unaddressed at all, then assume it is directly addressed
-      # to the recipient
-      Enum.all?(addresses, &is_nil(&1)) -> true
-      # if the message is sent from somebody the user is following, then assume it
-      # is addressed to the recipient
-      User.following?(recipient, actor) -> true
-      true -> false
-    end
-  end
+  def recipient_in_message(%User{ap_id: ap_id} = recipient, %User{} = actor, params),
+    do:
+      label_in_message?(ap_id, params) || unaddressed_message?(params) ||
+        User.following?(recipient, actor)
 
   defp extract_list(target) when is_binary(target), do: [target]
   defp extract_list(lst) when is_list(lst), do: lst
@@ -76,8 +78,8 @@ defmodule Pleroma.Web.ActivityPub.Utils do
 
   def maybe_splice_recipient(ap_id, params) do
     need_splice? =
-      !recipient_in_collection(ap_id, params["to"]) &&
-        !recipient_in_collection(ap_id, params["cc"])
+      !label_in_collection?(ap_id, params["to"]) &&
+        !label_in_collection?(ap_id, params["cc"])
 
     if need_splice? do
       cc_list = extract_list(params["cc"])
index 270d0fa0211f87146408ae5fead8476c2fb5eb9f..f3ab48f7cf8fb1854b1b0aa3c81d0200752c56a5 100644 (file)
@@ -7,6 +7,7 @@ defmodule Pleroma.Web.ActivityPub.Visibility do
   alias Pleroma.Object
   alias Pleroma.Repo
   alias Pleroma.User
+  alias Pleroma.Web.ActivityPub.Utils
 
   require Pleroma.Constants
 
@@ -15,7 +16,7 @@ defmodule Pleroma.Web.ActivityPub.Visibility do
   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: Pleroma.Constants.as_public() in (data["to"] ++ (data["cc"] || []))
+  def is_public?(data), do: Utils.label_in_message?(Pleroma.Constants.as_public(), data)
 
   def is_private?(activity) do
     with false <- is_public?(activity),
index 6a3e48b5e7c93f622b98f59854130b102b84ffb1..fc0444c32f217153a4e61824f52f03b52b45aa2d 100644 (file)
@@ -354,6 +354,87 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
       assert Activity.get_by_ap_id(data["id"])
     end
 
+    test "it accepts messages with to as string instead of array", %{conn: conn, data: data} do
+      user = insert(:user)
+
+      data =
+        Map.put(data, "to", user.ap_id)
+        |> Map.delete("cc")
+
+      conn =
+        conn
+        |> assign(:valid_signature, true)
+        |> put_req_header("content-type", "application/activity+json")
+        |> post("/users/#{user.nickname}/inbox", data)
+
+      assert "ok" == json_response(conn, 200)
+      ObanHelpers.perform(all_enqueued(worker: ReceiverWorker))
+      assert Activity.get_by_ap_id(data["id"])
+    end
+
+    test "it accepts messages with cc as string instead of array", %{conn: conn, data: data} do
+      user = insert(:user)
+
+      data =
+        Map.put(data, "cc", user.ap_id)
+        |> Map.delete("to")
+
+      conn =
+        conn
+        |> assign(:valid_signature, true)
+        |> put_req_header("content-type", "application/activity+json")
+        |> post("/users/#{user.nickname}/inbox", data)
+
+      assert "ok" == json_response(conn, 200)
+      ObanHelpers.perform(all_enqueued(worker: ReceiverWorker))
+      %Activity{} = activity = Activity.get_by_ap_id(data["id"])
+      assert user.ap_id in activity.recipients
+    end
+
+    test "it accepts messages with bcc as string instead of array", %{conn: conn, data: data} do
+      user = insert(:user)
+
+      data =
+        Map.put(data, "bcc", user.ap_id)
+        |> Map.delete("to")
+        |> Map.delete("cc")
+
+      conn =
+        conn
+        |> assign(:valid_signature, true)
+        |> put_req_header("content-type", "application/activity+json")
+        |> post("/users/#{user.nickname}/inbox", data)
+
+      assert "ok" == json_response(conn, 200)
+      ObanHelpers.perform(all_enqueued(worker: ReceiverWorker))
+      assert Activity.get_by_ap_id(data["id"])
+    end
+
+    test "it accepts announces with to as string instead of array", %{conn: conn} do
+      user = insert(:user)
+
+      data = %{
+        "@context" => "https://www.w3.org/ns/activitystreams",
+        "actor" => "http://mastodon.example.org/users/admin",
+        "id" => "http://mastodon.example.org/users/admin/statuses/19512778738411822/activity",
+        "object" => "https://mastodon.social/users/emelie/statuses/101849165031453009",
+        "to" => "https://www.w3.org/ns/activitystreams#Public",
+        "cc" => [user.ap_id],
+        "type" => "Announce"
+      }
+
+      conn =
+        conn
+        |> assign(:valid_signature, true)
+        |> put_req_header("content-type", "application/activity+json")
+        |> post("/users/#{user.nickname}/inbox", data)
+
+      assert "ok" == json_response(conn, 200)
+      ObanHelpers.perform(all_enqueued(worker: ReceiverWorker))
+      %Activity{} = activity = Activity.get_by_ap_id(data["id"])
+      assert "https://www.w3.org/ns/activitystreams#Public" in activity.recipients
+    end
+
     test "it accepts messages from actors that are followed by the user", %{
       conn: conn,
       data: data