summaryrefslogtreecommitdiff
path: root/lib/pleroma/web/activity_pub
diff options
context:
space:
mode:
Diffstat (limited to 'lib/pleroma/web/activity_pub')
-rw-r--r--lib/pleroma/web/activity_pub/activity_pub.ex101
-rw-r--r--lib/pleroma/web/activity_pub/builder.ex13
-rw-r--r--lib/pleroma/web/activity_pub/object_validator.ex11
-rw-r--r--lib/pleroma/web/activity_pub/object_validators/common_validations.ex3
-rw-r--r--lib/pleroma/web/activity_pub/object_validators/undo_validator.ex62
-rw-r--r--lib/pleroma/web/activity_pub/side_effects.ex46
-rw-r--r--lib/pleroma/web/activity_pub/transmogrifier.ex94
-rw-r--r--lib/pleroma/web/activity_pub/utils.ex49
8 files changed, 148 insertions, 231 deletions
diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex
index fcc3ce728..4955243ab 100644
--- a/lib/pleroma/web/activity_pub/activity_pub.ex
+++ b/lib/pleroma/web/activity_pub/activity_pub.ex
@@ -356,56 +356,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
end
end
- @spec unreact_with_emoji(User.t(), String.t(), keyword()) ::
- {:ok, Activity.t(), Object.t()} | {:error, any()}
- def unreact_with_emoji(user, reaction_id, options \\ []) do
- with {:ok, result} <-
- Repo.transaction(fn -> do_unreact_with_emoji(user, reaction_id, options) end) do
- result
- end
- end
-
- defp do_unreact_with_emoji(user, reaction_id, options) do
- with local <- Keyword.get(options, :local, true),
- activity_id <- Keyword.get(options, :activity_id, nil),
- user_ap_id <- user.ap_id,
- %Activity{actor: ^user_ap_id} = reaction_activity <- Activity.get_by_ap_id(reaction_id),
- object <- Object.normalize(reaction_activity),
- unreact_data <- make_undo_data(user, reaction_activity, activity_id),
- {:ok, activity} <- insert(unreact_data, local),
- {:ok, object} <- remove_emoji_reaction_from_object(reaction_activity, object),
- _ <- notify_and_stream(activity),
- :ok <- maybe_federate(activity) do
- {:ok, activity, object}
- else
- {:error, error} -> Repo.rollback(error)
- end
- end
-
- @spec unlike(User.t(), Object.t(), String.t() | nil, boolean()) ::
- {:ok, Activity.t(), Activity.t(), Object.t()} | {:ok, Object.t()} | {:error, any()}
- def unlike(%User{} = actor, %Object{} = object, activity_id \\ nil, local \\ true) do
- with {:ok, result} <-
- Repo.transaction(fn -> do_unlike(actor, object, activity_id, local) end) do
- result
- end
- end
-
- defp do_unlike(actor, object, activity_id, local) do
- with %Activity{} = like_activity <- get_existing_like(actor.ap_id, object),
- unlike_data <- make_unlike_data(actor, like_activity, activity_id),
- {:ok, unlike_activity} <- insert(unlike_data, local),
- {:ok, _activity} <- Repo.delete(like_activity),
- {:ok, object} <- remove_like_from_object(like_activity, object),
- _ <- notify_and_stream(unlike_activity),
- :ok <- maybe_federate(unlike_activity) do
- {:ok, unlike_activity, like_activity, object}
- else
- nil -> {:ok, object}
- {:error, error} -> Repo.rollback(error)
- end
- end
-
@spec announce(User.t(), Object.t(), String.t() | nil, boolean(), boolean()) ::
{:ok, Activity.t(), Object.t()} | {:error, any()}
def announce(
@@ -436,35 +386,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
end
end
- @spec unannounce(User.t(), Object.t(), String.t() | nil, boolean()) ::
- {:ok, Activity.t(), Object.t()} | {:ok, Object.t()} | {:error, any()}
- def unannounce(
- %User{} = actor,
- %Object{} = object,
- activity_id \\ nil,
- local \\ true
- ) do
- with {:ok, result} <-
- Repo.transaction(fn -> do_unannounce(actor, object, activity_id, local) end) do
- result
- end
- end
-
- defp do_unannounce(actor, object, activity_id, local) do
- with %Activity{} = announce_activity <- get_existing_announce(actor.ap_id, object),
- unannounce_data <- make_unannounce_data(actor, announce_activity, activity_id),
- {:ok, unannounce_activity} <- insert(unannounce_data, local),
- _ <- notify_and_stream(unannounce_activity),
- :ok <- maybe_federate(unannounce_activity),
- {:ok, _activity} <- Repo.delete(announce_activity),
- {:ok, object} <- remove_announce_from_object(announce_activity, object) do
- {:ok, unannounce_activity, object}
- else
- nil -> {:ok, object}
- {:error, error} -> Repo.rollback(error)
- end
- end
-
@spec follow(User.t(), User.t(), String.t() | nil, boolean()) ::
{:ok, Activity.t()} | {:error, any()}
def follow(follower, followed, activity_id \\ nil, local \\ true) do
@@ -537,28 +458,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
end
end
- @spec unblock(User.t(), User.t(), String.t() | nil, boolean()) ::
- {:ok, Activity.t()} | {:error, any()} | nil
- def unblock(blocker, blocked, activity_id \\ nil, local \\ true) do
- with {:ok, result} <-
- Repo.transaction(fn -> do_unblock(blocker, blocked, activity_id, local) end) do
- result
- end
- end
-
- defp do_unblock(blocker, blocked, activity_id, local) do
- with %Activity{} = block_activity <- fetch_latest_block(blocker, blocked),
- unblock_data <- make_unblock_data(blocker, blocked, block_activity, activity_id),
- {:ok, activity} <- insert(unblock_data, local),
- _ <- notify_and_stream(activity),
- :ok <- maybe_federate(activity) do
- {:ok, activity}
- else
- nil -> nil
- {:error, error} -> Repo.rollback(error)
- end
- end
-
@spec flag(map()) :: {:ok, Activity.t()} | {:error, any()}
def flag(
%{
diff --git a/lib/pleroma/web/activity_pub/builder.ex b/lib/pleroma/web/activity_pub/builder.ex
index d130176cf..f6544d3f5 100644
--- a/lib/pleroma/web/activity_pub/builder.ex
+++ b/lib/pleroma/web/activity_pub/builder.ex
@@ -22,6 +22,19 @@ defmodule Pleroma.Web.ActivityPub.Builder do
end
end
+ @spec undo(User.t(), Activity.t()) :: {:ok, map(), keyword()}
+ def undo(actor, object) do
+ {:ok,
+ %{
+ "id" => Utils.generate_activity_id(),
+ "actor" => actor.ap_id,
+ "type" => "Undo",
+ "object" => object.data["id"],
+ "to" => object.data["to"] || [],
+ "cc" => object.data["cc"] || []
+ }, []}
+ end
+
@spec delete(User.t(), String.t()) :: {:ok, map(), keyword()}
def delete(actor, object_id) do
object = Object.normalize(object_id, false)
diff --git a/lib/pleroma/web/activity_pub/object_validator.ex b/lib/pleroma/web/activity_pub/object_validator.ex
index e51a8e0a8..549e5e761 100644
--- a/lib/pleroma/web/activity_pub/object_validator.ex
+++ b/lib/pleroma/web/activity_pub/object_validator.ex
@@ -15,10 +15,21 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidator do
alias Pleroma.Web.ActivityPub.ObjectValidators.EmojiReactValidator
alias Pleroma.Web.ActivityPub.ObjectValidators.LikeValidator
alias Pleroma.Web.ActivityPub.ObjectValidators.Types
+ alias Pleroma.Web.ActivityPub.ObjectValidators.UndoValidator
@spec validate(map(), keyword()) :: {:ok, map(), keyword()} | {:error, any()}
def validate(object, meta)
+ def validate(%{"type" => "Undo"} = object, meta) do
+ with {:ok, object} <-
+ object
+ |> UndoValidator.cast_and_validate()
+ |> Ecto.Changeset.apply_action(:insert) do
+ object = stringify_keys(object)
+ {:ok, object, meta}
+ end
+ end
+
def validate(%{"type" => "Delete"} = object, meta) do
with cng <- DeleteValidator.cast_and_validate(object),
do_not_federate <- DeleteValidator.do_not_federate?(cng),
diff --git a/lib/pleroma/web/activity_pub/object_validators/common_validations.ex b/lib/pleroma/web/activity_pub/object_validators/common_validations.ex
index 4e6ee2034..aeef31945 100644
--- a/lib/pleroma/web/activity_pub/object_validators/common_validations.ex
+++ b/lib/pleroma/web/activity_pub/object_validators/common_validations.ex
@@ -5,6 +5,7 @@
defmodule Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations do
import Ecto.Changeset
+ alias Pleroma.Activity
alias Pleroma.Object
alias Pleroma.User
@@ -47,7 +48,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations do
cng
|> validate_change(field_name, fn field_name, object_id ->
- object = Object.get_cached_by_ap_id(object_id)
+ object = Object.get_cached_by_ap_id(object_id) || Activity.get_by_ap_id(object_id)
cond do
!object ->
diff --git a/lib/pleroma/web/activity_pub/object_validators/undo_validator.ex b/lib/pleroma/web/activity_pub/object_validators/undo_validator.ex
new file mode 100644
index 000000000..d0ba418e8
--- /dev/null
+++ b/lib/pleroma/web/activity_pub/object_validators/undo_validator.ex
@@ -0,0 +1,62 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.ActivityPub.ObjectValidators.UndoValidator do
+ use Ecto.Schema
+
+ alias Pleroma.Activity
+ alias Pleroma.Web.ActivityPub.ObjectValidators.Types
+
+ import Ecto.Changeset
+ import Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations
+
+ @primary_key false
+
+ embedded_schema do
+ field(:id, Types.ObjectID, primary_key: true)
+ field(:type, :string)
+ field(:object, Types.ObjectID)
+ field(:actor, Types.ObjectID)
+ field(:to, {:array, :string}, default: [])
+ field(:cc, {:array, :string}, default: [])
+ end
+
+ def cast_and_validate(data) do
+ data
+ |> cast_data()
+ |> validate_data()
+ end
+
+ def cast_data(data) do
+ %__MODULE__{}
+ |> changeset(data)
+ end
+
+ def changeset(struct, data) do
+ struct
+ |> cast(data, __schema__(:fields))
+ end
+
+ def validate_data(data_cng) do
+ data_cng
+ |> validate_inclusion(:type, ["Undo"])
+ |> validate_required([:id, :type, :object, :actor, :to, :cc])
+ |> validate_actor_presence()
+ |> validate_object_presence()
+ |> validate_undo_rights()
+ end
+
+ def validate_undo_rights(cng) do
+ actor = get_field(cng, :actor)
+ object = get_field(cng, :object)
+
+ with %Activity{data: %{"actor" => object_actor}} <- Activity.get_by_ap_id(object),
+ true <- object_actor != actor do
+ cng
+ |> add_error(:actor, "not the same as object actor")
+ else
+ _ -> cng
+ end
+ end
+end
diff --git a/lib/pleroma/web/activity_pub/side_effects.ex b/lib/pleroma/web/activity_pub/side_effects.ex
index 8e5586e88..bfc2ab845 100644
--- a/lib/pleroma/web/activity_pub/side_effects.ex
+++ b/lib/pleroma/web/activity_pub/side_effects.ex
@@ -5,8 +5,10 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do
liked object, a `Follow` activity will add the user to the follower
collection, and so on.
"""
+ alias Pleroma.Activity
alias Pleroma.Notification
alias Pleroma.Object
+ alias Pleroma.Repo
alias Pleroma.User
alias Pleroma.Web.ActivityPub.ActivityPub
alias Pleroma.Web.ActivityPub.Utils
@@ -25,6 +27,13 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do
{:ok, object, meta}
end
+ 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
+ {:ok, object, meta}
+ end
+ end
+
# Tasks this handles:
# - Add reaction to object
# - Set up notification
@@ -84,4 +93,41 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do
def handle(object, meta) do
{:ok, object, meta}
end
+
+ def handle_undoing(%{data: %{"type" => "Like"}} = object) do
+ with %Object{} = liked_object <- Object.get_by_ap_id(object.data["object"]),
+ {:ok, _} <- Utils.remove_like_from_object(object, liked_object),
+ {:ok, _} <- Repo.delete(object) do
+ :ok
+ end
+ 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
+ :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
+ :ok
+ end
+ end
+
+ def handle_undoing(
+ %{data: %{"type" => "Block", "actor" => blocker, "object" => blocked}} = object
+ ) 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
+ end
+ end
+
+ def handle_undoing(object), do: {:error, ["don't know how to handle", object]}
end
diff --git a/lib/pleroma/web/activity_pub/transmogrifier.ex b/lib/pleroma/web/activity_pub/transmogrifier.ex
index ee6fc31ce..be7b57f13 100644
--- a/lib/pleroma/web/activity_pub/transmogrifier.ex
+++ b/lib/pleroma/web/activity_pub/transmogrifier.ex
@@ -726,25 +726,6 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
def handle_incoming(
%{
"type" => "Undo",
- "object" => %{"type" => "Announce", "object" => object_id},
- "actor" => _actor,
- "id" => id
- } = data,
- _options
- ) do
- with actor <- Containment.get_actor(data),
- {:ok, %User{} = actor} <- User.get_or_fetch_by_ap_id(actor),
- {:ok, object} <- get_obj_helper(object_id),
- {:ok, activity, _} <- ActivityPub.unannounce(actor, object, id, false) do
- {:ok, activity}
- else
- _e -> :error
- end
- end
-
- def handle_incoming(
- %{
- "type" => "Undo",
"object" => %{"type" => "Follow", "object" => followed},
"actor" => follower,
"id" => id
@@ -764,39 +745,29 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
def handle_incoming(
%{
"type" => "Undo",
- "object" => %{"type" => "EmojiReact", "id" => reaction_activity_id},
- "actor" => _actor,
- "id" => id
+ "object" => %{"type" => type}
} = data,
_options
- ) do
- with actor <- Containment.get_actor(data),
- {:ok, %User{} = actor} <- User.get_or_fetch_by_ap_id(actor),
- {:ok, activity, _} <-
- ActivityPub.unreact_with_emoji(actor, reaction_activity_id,
- activity_id: id,
- local: false
- ) do
+ )
+ when type in ["Like", "EmojiReact", "Announce", "Block"] do
+ with {:ok, activity, _} <- Pipeline.common_pipeline(data, local: false) do
{:ok, activity}
- else
- _e -> :error
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" => %{"type" => "Block", "object" => blocked},
- "actor" => blocker,
- "id" => id
- } = _data,
- _options
- ) do
- with %User{local: true} = blocked <- User.get_cached_by_ap_id(blocked),
- {:ok, %User{} = blocker} <- User.get_or_fetch_by_ap_id(blocker),
- {:ok, activity} <- ActivityPub.unblock(blocker, blocked, id, false) do
- User.unblock(blocker, blocked)
- {:ok, activity}
+ "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
end
@@ -819,43 +790,6 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
def handle_incoming(
%{
- "type" => "Undo",
- "object" => %{"type" => "Like", "object" => object_id},
- "actor" => _actor,
- "id" => id
- } = data,
- _options
- ) do
- with actor <- Containment.get_actor(data),
- {:ok, %User{} = actor} <- User.get_or_fetch_by_ap_id(actor),
- {:ok, object} <- get_obj_helper(object_id),
- {:ok, activity, _, _} <- ActivityPub.unlike(actor, object, id, false) do
- {:ok, activity}
- else
- _e -> :error
- 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
- )
- 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
- end
- end
-
- def handle_incoming(
- %{
"type" => "Move",
"actor" => origin_actor,
"object" => origin_actor,
diff --git a/lib/pleroma/web/activity_pub/utils.ex b/lib/pleroma/web/activity_pub/utils.ex
index 1a3b0b3c1..09b80fa57 100644
--- a/lib/pleroma/web/activity_pub/utils.ex
+++ b/lib/pleroma/web/activity_pub/utils.ex
@@ -562,45 +562,6 @@ defmodule Pleroma.Web.ActivityPub.Utils do
|> maybe_put("id", activity_id)
end
- @doc """
- Make unannounce activity data for the given actor and object
- """
- def make_unannounce_data(
- %User{ap_id: ap_id} = user,
- %Activity{data: %{"context" => context, "object" => object}} = activity,
- activity_id
- ) do
- object = Object.normalize(object)
-
- %{
- "type" => "Undo",
- "actor" => ap_id,
- "object" => activity.data,
- "to" => [user.follower_address, object.data["actor"]],
- "cc" => [Pleroma.Constants.as_public()],
- "context" => context
- }
- |> maybe_put("id", activity_id)
- end
-
- def make_unlike_data(
- %User{ap_id: ap_id} = user,
- %Activity{data: %{"context" => context, "object" => object}} = activity,
- activity_id
- ) do
- object = Object.normalize(object)
-
- %{
- "type" => "Undo",
- "actor" => ap_id,
- "object" => activity.data,
- "to" => [user.follower_address, object.data["actor"]],
- "cc" => [Pleroma.Constants.as_public()],
- "context" => context
- }
- |> maybe_put("id", activity_id)
- end
-
def make_undo_data(
%User{ap_id: actor, follower_address: follower_address},
%Activity{
@@ -688,16 +649,6 @@ defmodule Pleroma.Web.ActivityPub.Utils do
|> maybe_put("id", activity_id)
end
- def make_unblock_data(blocker, blocked, block_activity, activity_id) do
- %{
- "type" => "Undo",
- "actor" => blocker.ap_id,
- "to" => [blocked.ap_id],
- "object" => block_activity.data
- }
- |> maybe_put("id", activity_id)
- end
-
#### Create-related helpers
def make_create_data(params, additional) do