From 7224bf309ef38a80898d7e560e96fbc2895737be Mon Sep 17 00:00:00 2001 From: lain Date: Wed, 12 Aug 2020 14:48:51 +0200 Subject: [PATCH] Transmogrifier: Move Rejects to the Pipeline --- lib/pleroma/web/activity_pub/builder.ex | 19 +++++-- .../web/activity_pub/object_validator.ex | 7 ++- ...alidator.ex => accept_reject_validator.ex} | 10 ++-- lib/pleroma/web/activity_pub/side_effects.ex | 24 ++++++++ .../web/activity_pub/transmogrifier.ex | 38 +------------ .../reject_validation_test.exs | 56 +++++++++++++++++++ .../transmogrifier/reject_handling_test.exs | 2 +- 7 files changed, 105 insertions(+), 51 deletions(-) rename lib/pleroma/web/activity_pub/object_validators/{accept_validator.ex => accept_reject_validator.ex} (82%) create mode 100644 test/web/activity_pub/object_validators/reject_validation_test.exs diff --git a/lib/pleroma/web/activity_pub/builder.ex b/lib/pleroma/web/activity_pub/builder.ex index e1f88e6cc..f2392ce79 100644 --- a/lib/pleroma/web/activity_pub/builder.ex +++ b/lib/pleroma/web/activity_pub/builder.ex @@ -14,19 +14,28 @@ defmodule Pleroma.Web.ActivityPub.Builder do require Pleroma.Constants - @spec accept(User.t(), Activity.t()) :: {:ok, map(), keyword()} - def accept(actor, accepted_activity) do + def accept_or_reject(actor, activity, type) do data = %{ "id" => Utils.generate_activity_id(), "actor" => actor.ap_id, - "type" => "Accept", - "object" => accepted_activity.data["id"], - "to" => [accepted_activity.actor] + "type" => type, + "object" => activity.data["id"], + "to" => [activity.actor] } {:ok, data, []} end + @spec reject(User.t(), Activity.t()) :: {:ok, map(), keyword()} + def reject(actor, rejected_activity) do + accept_or_reject(actor, rejected_activity, "Reject") + end + + @spec accept(User.t(), Activity.t()) :: {:ok, map(), keyword()} + def accept(actor, accepted_activity) do + accept_or_reject(actor, accepted_activity, "Accept") + end + @spec follow(User.t(), User.t()) :: {:ok, map(), keyword()} def follow(follower, followed) do data = %{ diff --git a/lib/pleroma/web/activity_pub/object_validator.ex b/lib/pleroma/web/activity_pub/object_validator.ex index d9dd2bc30..3f1dffe2b 100644 --- a/lib/pleroma/web/activity_pub/object_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validator.ex @@ -13,7 +13,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidator do alias Pleroma.EctoType.ActivityPub.ObjectValidators alias Pleroma.Object alias Pleroma.User - alias Pleroma.Web.ActivityPub.ObjectValidators.AcceptValidator + alias Pleroma.Web.ActivityPub.ObjectValidators.AcceptRejectValidator alias Pleroma.Web.ActivityPub.ObjectValidators.AnnounceValidator alias Pleroma.Web.ActivityPub.ObjectValidators.AnswerValidator alias Pleroma.Web.ActivityPub.ObjectValidators.BlockValidator @@ -31,10 +31,11 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidator do @spec validate(map(), keyword()) :: {:ok, map(), keyword()} | {:error, any()} def validate(object, meta) - def validate(%{"type" => "Accept"} = object, meta) do + def validate(%{"type" => type} = object, meta) + when type in ~w[Accept Reject] do with {:ok, object} <- object - |> AcceptValidator.cast_and_validate() + |> AcceptRejectValidator.cast_and_validate() |> Ecto.Changeset.apply_action(:insert) do object = stringify_keys(object) {:ok, object, meta} diff --git a/lib/pleroma/web/activity_pub/object_validators/accept_validator.ex b/lib/pleroma/web/activity_pub/object_validators/accept_reject_validator.ex similarity index 82% rename from lib/pleroma/web/activity_pub/object_validators/accept_validator.ex rename to lib/pleroma/web/activity_pub/object_validators/accept_reject_validator.ex index fd75f4b6e..179beda58 100644 --- a/lib/pleroma/web/activity_pub/object_validators/accept_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validators/accept_reject_validator.ex @@ -2,7 +2,7 @@ # Copyright © 2017-2020 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only -defmodule Pleroma.Web.ActivityPub.ObjectValidators.AcceptValidator do +defmodule Pleroma.Web.ActivityPub.ObjectValidators.AcceptRejectValidator do use Ecto.Schema alias Pleroma.Activity @@ -30,10 +30,10 @@ def cast_data(data) do def validate_data(cng) do cng |> validate_required([:id, :type, :actor, :to, :cc, :object]) - |> validate_inclusion(:type, ["Accept"]) + |> validate_inclusion(:type, ["Accept", "Reject"]) |> validate_actor_presence() |> validate_object_presence(allowed_types: ["Follow"]) - |> validate_accept_rights() + |> validate_accept_reject_rights() end def cast_and_validate(data) do @@ -42,7 +42,7 @@ def cast_and_validate(data) do |> validate_data end - def validate_accept_rights(cng) do + def validate_accept_reject_rights(cng) do with object_id when is_binary(object_id) <- get_field(cng, :object), %Activity{data: %{"object" => followed_actor}} <- Activity.get_by_ap_id(object_id), true <- followed_actor == get_field(cng, :actor) do @@ -50,7 +50,7 @@ def validate_accept_rights(cng) do else _e -> cng - |> add_error(:actor, "can't accept the given activity") + |> add_error(:actor, "can't accept or reject the given activity") end end end diff --git a/lib/pleroma/web/activity_pub/side_effects.ex b/lib/pleroma/web/activity_pub/side_effects.ex index e1fa75e1c..a4ad12d53 100644 --- a/lib/pleroma/web/activity_pub/side_effects.ex +++ b/lib/pleroma/web/activity_pub/side_effects.ex @@ -52,6 +52,30 @@ def handle( {:ok, object, meta} end + # Task this handles + # - Rejects all existing follow activities for this person + # - Updates the follow state + def handle( + %{ + data: %{ + "actor" => actor, + "type" => "Reject", + "object" => follow_activity_id + } + } = object, + meta + ) do + with %Activity{actor: follower_id} = follow_activity <- + Activity.get_by_ap_id(follow_activity_id), + %User{} = followed <- User.get_cached_by_ap_id(actor), + %User{} = follower <- User.get_cached_by_ap_id(follower_id), + {:ok, _follow_activity} <- Utils.update_follow_state_for_all(follow_activity, "reject") do + FollowingRelationship.update(follower, followed, :follow_reject) + end + + {:ok, object, meta} + end + # Tasks this handle # - Follows if possible # - Sends a notification diff --git a/lib/pleroma/web/activity_pub/transmogrifier.ex b/lib/pleroma/web/activity_pub/transmogrifier.ex index 24da1ef9c..544f3f3b6 100644 --- a/lib/pleroma/web/activity_pub/transmogrifier.ex +++ b/lib/pleroma/web/activity_pub/transmogrifier.ex @@ -9,7 +9,6 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do alias Pleroma.Activity alias Pleroma.EarmarkRenderer alias Pleroma.EctoType.ActivityPub.ObjectValidators - alias Pleroma.FollowingRelationship alias Pleroma.Maps alias Pleroma.Object alias Pleroma.Object.Containment @@ -390,16 +389,6 @@ defp fix_content(%{"mediaType" => "text/markdown", "content" => content} = objec defp fix_content(object), do: object - defp get_follow_activity(follow_object, _followed) do - with object_id when not is_nil(object_id) <- Utils.get_ap_id(follow_object), - {_, %Activity{} = activity} <- {:activity, Activity.get_by_ap_id(object_id)} do - {:ok, activity} - else - _ -> - {:error, nil} - end - end - # Reduce the object list to find the reported user. defp get_reported(objects) do Enum.reduce_while(objects, nil, fn ap_id, _ -> @@ -534,31 +523,6 @@ def handle_incoming( end end - def handle_incoming( - %{"type" => "Reject", "object" => follow_object, "actor" => _actor, "id" => id} = data, - _options - ) do - with actor <- Containment.get_actor(data), - {:ok, %User{} = followed} <- User.get_or_fetch_by_ap_id(actor), - {:ok, follow_activity} <- get_follow_activity(follow_object, followed), - {:ok, follow_activity} <- Utils.update_follow_state_for_all(follow_activity, "reject"), - %User{local: true} = follower <- User.get_cached_by_ap_id(follow_activity.data["actor"]), - {:ok, _relationship} <- FollowingRelationship.update(follower, followed, :follow_reject), - {:ok, activity} <- - ActivityPub.reject(%{ - to: follow_activity.data["to"], - type: "Reject", - actor: followed, - object: follow_activity.data["id"], - local: false, - activity_id: id - }) do - {:ok, activity} - else - _e -> :error - end - end - @misskey_reactions %{ "like" => "👍", "love" => "❤️", @@ -613,7 +577,7 @@ def handle_incoming( %{"type" => type} = data, _options ) - when type in ~w{Update Block Follow Accept} do + when type in ~w{Update Block Follow Accept Reject} do with {:ok, %User{}} <- ObjectValidator.fetch_actor(data), {:ok, activity, _} <- Pipeline.common_pipeline(data, local: false) do diff --git a/test/web/activity_pub/object_validators/reject_validation_test.exs b/test/web/activity_pub/object_validators/reject_validation_test.exs new file mode 100644 index 000000000..370bb6e5c --- /dev/null +++ b/test/web/activity_pub/object_validators/reject_validation_test.exs @@ -0,0 +1,56 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ActivityPub.ObjectValidators.RejectValidationTest do + use Pleroma.DataCase + + alias Pleroma.Web.ActivityPub.Builder + alias Pleroma.Web.ActivityPub.ObjectValidator + alias Pleroma.Web.ActivityPub.Pipeline + + import Pleroma.Factory + + setup do + follower = insert(:user) + followed = insert(:user, local: false) + + {:ok, follow_data, _} = Builder.follow(follower, followed) + {:ok, follow_activity, _} = Pipeline.common_pipeline(follow_data, local: true) + + {:ok, reject_data, _} = Builder.reject(followed, follow_activity) + + %{reject_data: reject_data, followed: followed} + end + + test "it validates a basic 'reject'", %{reject_data: reject_data} do + assert {:ok, _, _} = ObjectValidator.validate(reject_data, []) + end + + test "it fails when the actor doesn't exist", %{reject_data: reject_data} do + reject_data = + reject_data + |> Map.put("actor", "https://gensokyo.2hu/users/raymoo") + + assert {:error, _} = ObjectValidator.validate(reject_data, []) + end + + test "it fails when the rejected activity doesn't exist", %{reject_data: reject_data} do + reject_data = + reject_data + |> Map.put("object", "https://gensokyo.2hu/users/raymoo/follows/1") + + assert {:error, _} = ObjectValidator.validate(reject_data, []) + end + + test "for an rejected follow, it only validates if the actor of the reject is the followed actor", + %{reject_data: reject_data} do + stranger = insert(:user) + + reject_data = + reject_data + |> Map.put("actor", stranger.ap_id) + + assert {:error, _} = ObjectValidator.validate(reject_data, []) + end +end diff --git a/test/web/activity_pub/transmogrifier/reject_handling_test.exs b/test/web/activity_pub/transmogrifier/reject_handling_test.exs index 5e5248641..7592fbe1c 100644 --- a/test/web/activity_pub/transmogrifier/reject_handling_test.exs +++ b/test/web/activity_pub/transmogrifier/reject_handling_test.exs @@ -24,7 +24,7 @@ test "it fails for incoming rejects which cannot be correlated" do accept_data = Map.put(accept_data, "object", Map.put(accept_data["object"], "actor", follower.ap_id)) - :error = Transmogrifier.handle_incoming(accept_data) + {:error, _} = Transmogrifier.handle_incoming(accept_data) follower = User.get_cached_by_id(follower.id)