aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/pleroma/web/activity_pub/activity_pub.ex30
-rw-r--r--lib/pleroma/web/activity_pub/builder.ex25
-rw-r--r--lib/pleroma/web/activity_pub/object_validator.ex13
-rw-r--r--lib/pleroma/web/activity_pub/object_validators/announce_validator.ex101
-rw-r--r--lib/pleroma/web/activity_pub/pipeline.ex3
-rw-r--r--lib/pleroma/web/activity_pub/relay.ex9
-rw-r--r--lib/pleroma/web/activity_pub/side_effects.ex15
-rw-r--r--lib/pleroma/web/activity_pub/transmogrifier.ex18
-rw-r--r--lib/pleroma/web/common_api/common_api.ex23
-rw-r--r--lib/pleroma/web/mastodon_api/controllers/status_controller.ex2
10 files changed, 175 insertions, 64 deletions
diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex
index d752f4f04..2cea55285 100644
--- a/lib/pleroma/web/activity_pub/activity_pub.ex
+++ b/lib/pleroma/web/activity_pub/activity_pub.ex
@@ -356,36 +356,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
end
end
- @spec announce(User.t(), Object.t(), String.t() | nil, boolean(), boolean()) ::
- {:ok, Activity.t(), Object.t()} | {:error, any()}
- def announce(
- %User{ap_id: _} = user,
- %Object{data: %{"id" => _}} = object,
- activity_id \\ nil,
- local \\ true,
- public \\ true
- ) do
- with {:ok, result} <-
- Repo.transaction(fn -> do_announce(user, object, activity_id, local, public) end) do
- result
- end
- end
-
- defp do_announce(user, object, activity_id, local, public) do
- with true <- is_announceable?(object, user, public),
- object <- Object.get_by_id(object.id),
- announce_data <- make_announce_data(user, object, activity_id, public),
- {:ok, activity} <- insert(announce_data, local),
- {:ok, object} <- add_announce_to_object(activity, object),
- _ <- notify_and_stream(activity),
- :ok <- maybe_federate(activity) do
- {:ok, activity, object}
- else
- false -> {:error, false}
- {: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
diff --git a/lib/pleroma/web/activity_pub/builder.ex b/lib/pleroma/web/activity_pub/builder.ex
index 4a247ad0c..7ece764f5 100644
--- a/lib/pleroma/web/activity_pub/builder.ex
+++ b/lib/pleroma/web/activity_pub/builder.ex
@@ -10,6 +10,8 @@ defmodule Pleroma.Web.ActivityPub.Builder do
alias Pleroma.Web.ActivityPub.Utils
alias Pleroma.Web.ActivityPub.Visibility
+ require Pleroma.Constants
+
@spec emoji_react(User.t(), Object.t(), String.t()) :: {:ok, map(), keyword()}
def emoji_react(actor, object, emoji) do
with {:ok, data, meta} <- object_action(actor, object) do
@@ -83,6 +85,29 @@ defmodule Pleroma.Web.ActivityPub.Builder do
end
end
+ def announce(actor, object, options \\ []) do
+ public? = Keyword.get(options, :public, false)
+ to = [actor.follower_address, object.data["actor"]]
+
+ to =
+ if public? do
+ [Pleroma.Constants.as_public() | to]
+ else
+ to
+ end
+
+ {:ok,
+ %{
+ "id" => Utils.generate_activity_id(),
+ "actor" => actor.ap_id,
+ "object" => object.data["id"],
+ "to" => to,
+ "context" => object.data["context"],
+ "type" => "Announce",
+ "published" => Utils.make_date()
+ }, []}
+ end
+
@spec object_action(User.t(), Object.t()) :: {:ok, map(), keyword()}
defp object_action(actor, object) do
object_actor = User.get_cached_by_ap_id(object.data["actor"])
diff --git a/lib/pleroma/web/activity_pub/object_validator.ex b/lib/pleroma/web/activity_pub/object_validator.ex
index 549e5e761..2599067a8 100644
--- a/lib/pleroma/web/activity_pub/object_validator.ex
+++ b/lib/pleroma/web/activity_pub/object_validator.ex
@@ -11,6 +11,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidator do
alias Pleroma.Object
alias Pleroma.User
+ alias Pleroma.Web.ActivityPub.ObjectValidators.AnnounceValidator
alias Pleroma.Web.ActivityPub.ObjectValidators.DeleteValidator
alias Pleroma.Web.ActivityPub.ObjectValidators.EmojiReactValidator
alias Pleroma.Web.ActivityPub.ObjectValidators.LikeValidator
@@ -58,6 +59,16 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidator do
end
end
+ def validate(%{"type" => "Announce"} = object, meta) do
+ with {:ok, object} <-
+ object
+ |> AnnounceValidator.cast_and_validate()
+ |> Ecto.Changeset.apply_action(:insert) do
+ object = stringify_keys(object |> Map.from_struct())
+ {:ok, object, meta}
+ end
+ end
+
def stringify_keys(%{__struct__: _} = object) do
object
|> Map.from_struct()
@@ -77,7 +88,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidator do
def fetch_actor_and_object(object) do
fetch_actor(object)
- Object.normalize(object["object"])
+ Object.normalize(object["object"], true)
:ok
end
end
diff --git a/lib/pleroma/web/activity_pub/object_validators/announce_validator.ex b/lib/pleroma/web/activity_pub/object_validators/announce_validator.ex
new file mode 100644
index 000000000..40f861f47
--- /dev/null
+++ b/lib/pleroma/web/activity_pub/object_validators/announce_validator.ex
@@ -0,0 +1,101 @@
+# 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.AnnounceValidator do
+ use Ecto.Schema
+
+ alias Pleroma.Object
+ alias Pleroma.User
+ alias Pleroma.Web.ActivityPub.ObjectValidators.Types
+ alias Pleroma.Web.ActivityPub.Utils
+ alias Pleroma.Web.ActivityPub.Visibility
+
+ import Ecto.Changeset
+ import Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations
+
+ require Pleroma.Constants
+
+ @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(:context, :string, autogenerate: {Utils, :generate_context_id, []})
+ field(:to, Types.Recipients, default: [])
+ field(:cc, Types.Recipients, default: [])
+ field(:published, Types.DateTime)
+ 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))
+ |> fix_after_cast()
+ end
+
+ def fix_after_cast(cng) do
+ cng
+ end
+
+ def validate_data(data_cng) do
+ data_cng
+ |> validate_inclusion(:type, ["Announce"])
+ |> validate_required([:id, :type, :object, :actor, :to, :cc])
+ |> validate_actor_presence()
+ |> validate_object_presence()
+ |> validate_existing_announce()
+ |> validate_announcable()
+ end
+
+ def validate_announcable(cng) do
+ with actor when is_binary(actor) <- get_field(cng, :actor),
+ object when is_binary(object) <- get_field(cng, :object),
+ %User{} = actor <- User.get_cached_by_ap_id(actor),
+ %Object{} = object <- Object.get_cached_by_ap_id(object),
+ false <- Visibility.is_public?(object) do
+ same_actor = object.data["actor"] == actor.ap_id
+ is_public = Pleroma.Constants.as_public() in (get_field(cng, :to) ++ get_field(cng, :cc))
+
+ cond do
+ same_actor && is_public ->
+ cng
+ |> add_error(:actor, "can not announce this object publicly")
+
+ !same_actor ->
+ cng
+ |> add_error(:actor, "can not announce this object")
+
+ true ->
+ cng
+ end
+ else
+ _ -> cng
+ end
+ end
+
+ def validate_existing_announce(cng) do
+ actor = get_field(cng, :actor)
+ object = get_field(cng, :object)
+
+ if actor && object && Utils.get_existing_announce(actor, %{data: %{"id" => object}}) do
+ cng
+ |> add_error(:actor, "already announced this object")
+ |> add_error(:object, "already announced by this actor")
+ else
+ cng
+ end
+ end
+end
diff --git a/lib/pleroma/web/activity_pub/pipeline.ex b/lib/pleroma/web/activity_pub/pipeline.ex
index 657cdfdb1..0c54c4b23 100644
--- a/lib/pleroma/web/activity_pub/pipeline.ex
+++ b/lib/pleroma/web/activity_pub/pipeline.ex
@@ -4,6 +4,7 @@
defmodule Pleroma.Web.ActivityPub.Pipeline do
alias Pleroma.Activity
+ alias Pleroma.Config
alias Pleroma.Object
alias Pleroma.Repo
alias Pleroma.Web.ActivityPub.ActivityPub
@@ -44,7 +45,7 @@ defmodule Pleroma.Web.ActivityPub.Pipeline do
defp maybe_federate(%Activity{} = activity, meta) do
with {:ok, local} <- Keyword.fetch(meta, :local) do
- do_not_federate = meta[:do_not_federate]
+ do_not_federate = meta[:do_not_federate] || !Config.get([:instance, :federating])
if !do_not_federate && local do
Federator.publish(activity)
diff --git a/lib/pleroma/web/activity_pub/relay.ex b/lib/pleroma/web/activity_pub/relay.ex
index 729c23af7..484178edd 100644
--- a/lib/pleroma/web/activity_pub/relay.ex
+++ b/lib/pleroma/web/activity_pub/relay.ex
@@ -4,9 +4,10 @@
defmodule Pleroma.Web.ActivityPub.Relay do
alias Pleroma.Activity
- alias Pleroma.Object
alias Pleroma.User
alias Pleroma.Web.ActivityPub.ActivityPub
+ alias Pleroma.Web.ActivityPub.Visibility
+ alias Pleroma.Web.CommonAPI
require Logger
@relay_nickname "relay"
@@ -48,11 +49,11 @@ defmodule Pleroma.Web.ActivityPub.Relay do
end
end
- @spec publish(any()) :: {:ok, Activity.t(), Object.t()} | {:error, any()}
+ @spec publish(any()) :: {:ok, Activity.t()} | {:error, any()}
def publish(%Activity{data: %{"type" => "Create"}} = activity) do
with %User{} = user <- get_actor(),
- %Object{} = object <- Object.normalize(activity) do
- ActivityPub.announce(user, object, nil, true, false)
+ true <- Visibility.is_public?(activity) do
+ CommonAPI.repeat(activity.id, user)
else
error -> format_error(error)
end
diff --git a/lib/pleroma/web/activity_pub/side_effects.ex b/lib/pleroma/web/activity_pub/side_effects.ex
index bfc2ab845..7eae0c52c 100644
--- a/lib/pleroma/web/activity_pub/side_effects.ex
+++ b/lib/pleroma/web/activity_pub/side_effects.ex
@@ -27,6 +27,21 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do
{:ok, object, meta}
end
+ # Tasks this handles:
+ # - Add announce to object
+ # - Set up notification
+ # - Stream out the announce
+ def handle(%{data: %{"type" => "Announce"}} = object, meta) do
+ announced_object = Object.get_by_ap_id(object.data["object"])
+
+ Utils.add_announce_to_object(object, announced_object)
+
+ Notification.create_notifications(object)
+ ActivityPub.stream_out(object)
+
+ {: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
diff --git a/lib/pleroma/web/activity_pub/transmogrifier.ex b/lib/pleroma/web/activity_pub/transmogrifier.ex
index 80701bb63..d594c64f4 100644
--- a/lib/pleroma/web/activity_pub/transmogrifier.ex
+++ b/lib/pleroma/web/activity_pub/transmogrifier.ex
@@ -662,7 +662,8 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|> handle_incoming(options)
end
- def handle_incoming(%{"type" => type} = data, _options) when type in ["Like", "EmojiReact"] do
+ def handle_incoming(%{"type" => type} = data, _options)
+ when type in ["Like", "EmojiReact", "Announce"] do
with :ok <- ObjectValidator.fetch_actor_and_object(data),
{:ok, activity, _meta} <-
Pipeline.common_pipeline(data, local: false) do
@@ -673,21 +674,6 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
end
def handle_incoming(
- %{"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_embedded_obj_helper(object_id, actor),
- public <- Visibility.is_public?(data),
- {:ok, activity, _object} <- ActivityPub.announce(actor, object, id, false, public) do
- {:ok, activity}
- else
- _e -> :error
- end
- end
-
- def handle_incoming(
%{"type" => "Update", "object" => %{"type" => object_type} = object, "actor" => actor_id} =
data,
_options
diff --git a/lib/pleroma/web/common_api/common_api.ex b/lib/pleroma/web/common_api/common_api.ex
index 447dbe4e6..dbb3d7ade 100644
--- a/lib/pleroma/web/common_api/common_api.ex
+++ b/lib/pleroma/web/common_api/common_api.ex
@@ -127,18 +127,19 @@ defmodule Pleroma.Web.CommonAPI do
end
def repeat(id, user, params \\ %{}) do
- with %Activity{data: %{"type" => "Create"}} = activity <- Activity.get_by_id(id) do
- object = Object.normalize(activity)
- announce_activity = Utils.get_existing_announce(user.ap_id, object)
- public = public_announce?(object, params)
-
- if announce_activity do
- {:ok, announce_activity, object}
- else
- ActivityPub.announce(user, object, nil, true, public)
- end
+ with %Activity{data: %{"type" => "Create"}} = activity <- Activity.get_by_id(id),
+ object = %Object{} <- Object.normalize(activity, false),
+ {_, nil} <- {:existing_announce, Utils.get_existing_announce(user.ap_id, object)},
+ public = public_announce?(object, params),
+ {:ok, announce, _} <- Builder.announce(user, object, public: public),
+ {:ok, activity, _} <- Pipeline.common_pipeline(announce, local: true) do
+ {:ok, activity}
else
- _ -> {:error, :not_found}
+ {:existing_announce, %Activity{} = announce} ->
+ {:ok, announce}
+
+ _ ->
+ {:error, :not_found}
end
end
diff --git a/lib/pleroma/web/mastodon_api/controllers/status_controller.ex b/lib/pleroma/web/mastodon_api/controllers/status_controller.ex
index 9dbf4f33c..83d997abd 100644
--- a/lib/pleroma/web/mastodon_api/controllers/status_controller.ex
+++ b/lib/pleroma/web/mastodon_api/controllers/status_controller.ex
@@ -210,7 +210,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do
@doc "POST /api/v1/statuses/:id/reblog"
def reblog(%{assigns: %{user: user}, body_params: params} = conn, %{id: ap_id_or_id}) do
- with {:ok, announce, _activity} <- CommonAPI.repeat(ap_id_or_id, user, params),
+ with {:ok, announce} <- CommonAPI.repeat(ap_id_or_id, user, params),
%Activity{} = announce <- Activity.normalize(announce.data) do
try_render(conn, "show.json", %{activity: announce, for: user, as: :activity})
end