summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHaelwenn (lanodan) Monnier <contact@hacktivis.me>2021-06-17 18:43:46 +0200
committerHaelwenn (lanodan) Monnier <contact@hacktivis.me>2021-07-21 09:19:28 +0200
commitbd977a3c3f4ede45c8f911cf26fd81561ecd8b7f (patch)
treee35b82ae641b922cdd8a94e8f355a6f5d969cc4b
parent173e977e283789a814278c63bc81f40a13942e21 (diff)
Ingestion Pipeline: Undo-Follow aka Unfollowfeatures/ingestion-unfollow
-rw-r--r--lib/pleroma/user.ex12
-rw-r--r--lib/pleroma/web/activity_pub/activity_pub.ex22
-rw-r--r--lib/pleroma/web/activity_pub/side_effects.ex47
-rw-r--r--lib/pleroma/web/activity_pub/transmogrifier.ex44
-rw-r--r--lib/pleroma/web/activity_pub/utils.ex31
-rw-r--r--test/pleroma/web/activity_pub/transmogrifier/undo_handling_test.exs4
-rw-r--r--test/pleroma/web/common_api_test.exs17
7 files changed, 66 insertions, 111 deletions
diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex
index 62506f37a..5c0efb38d 100644
--- a/lib/pleroma/user.ex
+++ b/lib/pleroma/user.ex
@@ -983,7 +983,7 @@ defmodule Pleroma.User do
end
def unfollow(%User{ap_id: ap_id}, %User{ap_id: ap_id}) do
- {:error, "Not subscribed!"}
+ {:error, "Can't unfollow yourself!"}
end
@spec unfollow(User.t(), User.t()) :: {:ok, User.t(), Activity.t()} | {:error, String.t()}
@@ -1771,17 +1771,11 @@ defmodule Pleroma.User do
# Remove all relationships
user
|> get_followers()
- |> Enum.each(fn follower ->
- ActivityPub.unfollow(follower, user)
- unfollow(follower, user)
- end)
+ |> Enum.each(fn follower -> ActivityPub.unfollow(follower, user) end)
user
|> get_friends()
- |> Enum.each(fn followed ->
- ActivityPub.unfollow(user, followed)
- unfollow(user, followed)
- end)
+ |> Enum.each(fn followed -> ActivityPub.unfollow(user, followed) end)
delete_user_activities(user)
delete_notifications_from_user_activities(user)
diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex
index 4c29dda35..446355c1e 100644
--- a/lib/pleroma/web/activity_pub/activity_pub.ex
+++ b/lib/pleroma/web/activity_pub/activity_pub.ex
@@ -20,8 +20,11 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
alias Pleroma.Repo
alias Pleroma.Upload
alias Pleroma.User
+ alias Pleroma.Web.ActivityPub.Builder
alias Pleroma.Web.ActivityPub.MRF
+ alias Pleroma.Web.ActivityPub.Pipeline
alias Pleroma.Web.ActivityPub.Transmogrifier
+ alias Pleroma.Web.ActivityPub.Utils
alias Pleroma.Web.Streamer
alias Pleroma.Web.WebFinger
alias Pleroma.Workers.BackgroundWorker
@@ -322,22 +325,17 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
end
end
- @spec unfollow(User.t(), User.t(), String.t() | nil, boolean()) ::
- {:ok, Activity.t()} | nil | {:error, any()}
- def unfollow(follower, followed, activity_id \\ nil, local \\ true) do
- with {:ok, result} <-
- Repo.transaction(fn -> do_unfollow(follower, followed, activity_id, local) end) do
+ @spec unfollow(User.t(), User.t()) :: {:ok, Activity.t()} | nil | {:error, any()}
+ def unfollow(follower, followed) do
+ with {:ok, result} <- Repo.transaction(fn -> do_unfollow(follower, followed) end) do
result
end
end
- defp do_unfollow(follower, followed, activity_id, local) do
- with %Activity{} = follow_activity <- fetch_latest_follow(follower, followed),
- {:ok, follow_activity} <- update_follow_state(follow_activity, "cancelled"),
- unfollow_data <- make_unfollow_data(follower, followed, follow_activity, activity_id),
- {:ok, activity} <- insert(unfollow_data, local),
- _ <- notify_and_stream(activity),
- :ok <- maybe_federate(activity) do
+ defp do_unfollow(follower, followed) do
+ with %Activity{} = follow_activity <- Utils.fetch_latest_follow(follower, followed),
+ {:ok, unfollow_data, _meta} <- Builder.undo(follower, follow_activity),
+ {:ok, activity, _meta} <- Pipeline.common_pipeline(unfollow_data, local: true) do
{:ok, activity}
else
nil -> nil
diff --git a/lib/pleroma/web/activity_pub/side_effects.ex b/lib/pleroma/web/activity_pub/side_effects.ex
index b0ec84ade..b9ec33a55 100644
--- a/lib/pleroma/web/activity_pub/side_effects.ex
+++ b/lib/pleroma/web/activity_pub/side_effects.ex
@@ -254,10 +254,12 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do
end
@impl true
- def handle(%{data: %{"type" => "Undo", "object" => undone_object}} = object, meta) do
- with undone_object <- Activity.get_by_ap_id(undone_object),
- :ok <- handle_undoing(undone_object) do
+ def handle(%{data: %{"type" => "Undo", "object" => undone_activity}} = object, meta) do
+ with undone_activity <- Activity.get_by_ap_id(undone_activity),
+ :ok <- handle_undoing(undone_activity) do
{:ok, object, meta}
+ else
+ e -> {:error, e}
end
end
@@ -456,35 +458,48 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do
end
end
- def handle_undoing(%{data: %{"type" => "Like"}} = object) do
- object.data["object"]
+ def handle_undoing(%{data: %{"type" => "Like"}} = activity) do
+ activity.data["object"]
|> Object.get_by_ap_id()
- |> undo_like(object)
+ |> undo_like(activity)
end
- def handle_undoing(%{data: %{"type" => "EmojiReact"}} = object) do
- with %Object{} = reacted_object <- Object.get_by_ap_id(object.data["object"]),
- {:ok, _} <- Utils.remove_emoji_reaction_from_object(object, reacted_object),
- {:ok, _} <- Repo.delete(object) do
+ def handle_undoing(%{data: %{"type" => "EmojiReact"}} = activity) do
+ with %Object{} = reacted_object <- Object.get_by_ap_id(activity.data["object"]),
+ {:ok, _} <- Utils.remove_emoji_reaction_from_object(activity, reacted_object),
+ {:ok, _} <- Repo.delete(activity) do
:ok
end
end
- def handle_undoing(%{data: %{"type" => "Announce"}} = object) do
- with %Object{} = liked_object <- Object.get_by_ap_id(object.data["object"]),
- {:ok, _} <- Utils.remove_announce_from_object(object, liked_object),
- {:ok, _} <- Repo.delete(object) do
+ def handle_undoing(%{data: %{"type" => "Announce"}} = activity) do
+ with %Object{} = liked_object <- Object.get_by_ap_id(activity.data["object"]),
+ {:ok, _} <- Utils.remove_announce_from_object(activity, liked_object),
+ {:ok, _} <- Repo.delete(activity) do
:ok
end
end
def handle_undoing(
- %{data: %{"type" => "Block", "actor" => blocker, "object" => blocked}} = object
+ %{data: %{"type" => "Block", "actor" => blocker, "object" => blocked}} = activity
) do
with %User{} = blocker <- User.get_cached_by_ap_id(blocker),
%User{} = blocked <- User.get_cached_by_ap_id(blocked),
{:ok, _} <- User.unblock(blocker, blocked),
- {:ok, _} <- Repo.delete(object) do
+ {:ok, _} <- Repo.delete(activity) do
+ :ok
+ end
+ end
+
+ def handle_undoing(
+ %{data: %{"type" => "Follow", "object" => followed, "actor" => follower}} = activity
+ ) do
+ with %User{} = follower <- User.get_cached_by_ap_id(follower),
+ %User{} = followed <- User.get_cached_by_ap_id(followed),
+ {:ok, _activity} <- Utils.update_follow_state_for_all(activity, "reject"),
+ {:ok, nil} <- FollowingRelationship.update(follower, followed, :follow_reject) do
+ Notification.dismiss(activity)
+
:ok
end
end
diff --git a/lib/pleroma/web/activity_pub/transmogrifier.ex b/lib/pleroma/web/activity_pub/transmogrifier.ex
index 142af1a13..6d49f0141 100644
--- a/lib/pleroma/web/activity_pub/transmogrifier.ex
+++ b/lib/pleroma/web/activity_pub/transmogrifier.ex
@@ -516,52 +516,30 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
end
def handle_incoming(
- %{
- "type" => "Undo",
- "object" => %{"type" => "Follow", "object" => followed},
- "actor" => follower,
- "id" => id
- } = _data,
- _options
- ) do
- with %User{local: true} = followed <- User.get_cached_by_ap_id(followed),
- {:ok, %User{} = follower} <- User.get_or_fetch_by_ap_id(follower),
- {:ok, activity} <- ActivityPub.unfollow(follower, followed, id, false) do
- User.unfollow(follower, followed)
- {:ok, activity}
- else
- _e -> :error
- end
- end
-
- def handle_incoming(
- %{
- "type" => "Undo",
- "object" => %{"type" => type}
- } = data,
+ %{"type" => "Undo", "object" => %{"type" => objtype, "id" => object_id}} = data,
_options
)
- when type in ["Like", "EmojiReact", "Announce", "Block"] do
- with {:ok, activity, _} <- Pipeline.common_pipeline(data, local: false) do
+ when objtype in ~w[Like EmojiReact Announce Block Follow] do
+ with {:ok, %User{}} <- ObjectValidator.fetch_actor(data),
+ {_, %Activity{}} <- {:exists, Activity.get_by_ap_id(object_id)},
+ {:ok, activity, _} <- Pipeline.common_pipeline(data, local: false) do
{:ok, activity}
+ else
+ {:error, _} = e -> e
+ e -> {:error, e}
end
end
# For Undos that don't have the complete object attached, try to find it in our database.
- def handle_incoming(
- %{
- "type" => "Undo",
- "object" => object
- } = activity,
- options
- )
+ def handle_incoming(%{"type" => "Undo", "object" => object} = activity, options)
when is_binary(object) do
with %Activity{data: data} <- Activity.get_by_ap_id(object) do
activity
|> Map.put("object", data)
|> handle_incoming(options)
else
- _e -> :error
+ {:error, _} = e -> e
+ e -> {:error, e}
end
end
diff --git a/lib/pleroma/web/activity_pub/utils.ex b/lib/pleroma/web/activity_pub/utils.ex
index 1df53f79a..6bed3dffa 100644
--- a/lib/pleroma/web/activity_pub/utils.ex
+++ b/lib/pleroma/web/activity_pub/utils.ex
@@ -572,25 +572,6 @@ defmodule Pleroma.Web.ActivityPub.Utils do
|> Maps.put_if_present("id", activity_id)
end
- def make_undo_data(
- %User{ap_id: actor, follower_address: follower_address},
- %Activity{
- data: %{"id" => undone_activity_id, "context" => context},
- actor: undone_activity_actor
- },
- activity_id \\ nil
- ) do
- %{
- "type" => "Undo",
- "actor" => actor,
- "object" => undone_activity_id,
- "to" => [follower_address, undone_activity_actor],
- "cc" => [Pleroma.Constants.as_public()],
- "context" => context
- }
- |> Maps.put_if_present("id", activity_id)
- end
-
@spec add_announce_to_object(Activity.t(), Object.t()) ::
{:ok, Object.t()} | {:error, Ecto.Changeset.t()}
def add_announce_to_object(
@@ -624,18 +605,6 @@ defmodule Pleroma.Web.ActivityPub.Utils do
defp take_announcements(_), do: []
- #### Unfollow-related helpers
-
- def make_unfollow_data(follower, followed, follow_activity, activity_id) do
- %{
- "type" => "Undo",
- "actor" => follower.ap_id,
- "to" => [followed.ap_id],
- "object" => follow_activity.data
- }
- |> Maps.put_if_present("id", activity_id)
- end
-
#### Block-related helpers
@spec fetch_latest_block(User.t(), User.t()) :: Activity.t() | nil
def fetch_latest_block(%User{ap_id: blocker_id}, %User{ap_id: blocked_id}) do
diff --git a/test/pleroma/web/activity_pub/transmogrifier/undo_handling_test.exs b/test/pleroma/web/activity_pub/transmogrifier/undo_handling_test.exs
index f6e40722c..29b1cc027 100644
--- a/test/pleroma/web/activity_pub/transmogrifier/undo_handling_test.exs
+++ b/test/pleroma/web/activity_pub/transmogrifier/undo_handling_test.exs
@@ -32,7 +32,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier.UndoHandlingTest do
assert activity.data["type"] == "Undo"
end
- test "it returns an error for incoming unlikes wihout a like activity" do
+ test "it returns an error for incoming unlikes without a like activity" do
user = insert(:user)
{:ok, activity} = CommonAPI.post(user, %{status: "leave a like pls"})
@@ -41,7 +41,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier.UndoHandlingTest do
|> Jason.decode!()
|> Map.put("object", activity.data["object"])
- assert Transmogrifier.handle_incoming(data) == :error
+ assert Transmogrifier.handle_incoming(data) == {:error, nil}
end
test "it works for incoming unlikes with an existing like activity" do
diff --git a/test/pleroma/web/common_api_test.exs b/test/pleroma/web/common_api_test.exs
index a5dfd3934..9ec8677dc 100644
--- a/test/pleroma/web/common_api_test.exs
+++ b/test/pleroma/web/common_api_test.exs
@@ -15,6 +15,7 @@ defmodule Pleroma.Web.CommonAPITest do
alias Pleroma.User
alias Pleroma.Web.ActivityPub.ActivityPub
alias Pleroma.Web.ActivityPub.Transmogrifier
+ alias Pleroma.Web.ActivityPub.Utils
alias Pleroma.Web.ActivityPub.Visibility
alias Pleroma.Web.AdminAPI.AccountView
alias Pleroma.Web.CommonAPI
@@ -1213,15 +1214,15 @@ defmodule Pleroma.Web.CommonAPITest do
assert {:ok, follower} = CommonAPI.unfollow(follower, followed)
assert User.get_follow_state(follower, followed) == nil
- assert %{id: ^activity_id, data: %{"state" => "cancelled"}} =
- Pleroma.Web.ActivityPub.Utils.fetch_latest_follow(follower, followed)
+ assert %{id: ^activity_id, data: %{"state" => "reject"}} =
+ Utils.fetch_latest_follow(follower, followed)
assert %{
data: %{
"type" => "Undo",
- "object" => %{"type" => "Follow", "state" => "cancelled"}
+ "object" => %{"type" => "Follow", "state" => "reject"}
}
- } = Pleroma.Web.ActivityPub.Utils.fetch_latest_undo(follower)
+ } = Utils.fetch_latest_undo(follower)
end
test "cancels a pending follow for a remote user" do
@@ -1235,15 +1236,15 @@ defmodule Pleroma.Web.CommonAPITest do
assert {:ok, follower} = CommonAPI.unfollow(follower, followed)
assert User.get_follow_state(follower, followed) == nil
- assert %{id: ^activity_id, data: %{"state" => "cancelled"}} =
- Pleroma.Web.ActivityPub.Utils.fetch_latest_follow(follower, followed)
+ assert %{id: ^activity_id, data: %{"state" => "reject"}} =
+ Utils.fetch_latest_follow(follower, followed)
assert %{
data: %{
"type" => "Undo",
- "object" => %{"type" => "Follow", "state" => "cancelled"}
+ "object" => %{"type" => "Follow", "state" => "reject"}
}
- } = Pleroma.Web.ActivityPub.Utils.fetch_latest_undo(follower)
+ } = Utils.fetch_latest_undo(follower)
end
end