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.ex61
-rw-r--r--lib/pleroma/web/activity_pub/activity_pub/persisting.ex7
-rw-r--r--lib/pleroma/web/activity_pub/activity_pub/streaming.ex12
-rw-r--r--lib/pleroma/web/activity_pub/activity_pub_controller.ex10
-rw-r--r--lib/pleroma/web/activity_pub/builder.ex3
-rw-r--r--lib/pleroma/web/activity_pub/mrf.ex97
-rw-r--r--lib/pleroma/web/activity_pub/mrf/activity_expiration_policy.ex18
-rw-r--r--lib/pleroma/web/activity_pub/mrf/hellthread_policy.ex27
-rw-r--r--lib/pleroma/web/activity_pub/mrf/keyword_policy.ex42
-rw-r--r--lib/pleroma/web/activity_pub/mrf/media_proxy_warming_policy.ex19
-rw-r--r--lib/pleroma/web/activity_pub/mrf/mention_policy.ex18
-rw-r--r--lib/pleroma/web/activity_pub/mrf/normalize_markup.ex19
-rw-r--r--lib/pleroma/web/activity_pub/mrf/object_age_policy.ex28
-rw-r--r--lib/pleroma/web/activity_pub/mrf/pipeline_filtering.ex7
-rw-r--r--lib/pleroma/web/activity_pub/mrf/reject_non_public.ex23
-rw-r--r--lib/pleroma/web/activity_pub/mrf/simple_policy.ex74
-rw-r--r--lib/pleroma/web/activity_pub/mrf/subchain_policy.ex24
-rw-r--r--lib/pleroma/web/activity_pub/mrf/user_allow_list_policy.ex21
-rw-r--r--lib/pleroma/web/activity_pub/mrf/vocabulary_policy.ex28
-rw-r--r--lib/pleroma/web/activity_pub/object_validator.ex4
-rw-r--r--lib/pleroma/web/activity_pub/object_validator/validating.ex7
-rw-r--r--lib/pleroma/web/activity_pub/object_validators/announce_validator.ex7
-rw-r--r--lib/pleroma/web/activity_pub/object_validators/attachment_validator.ex3
-rw-r--r--lib/pleroma/web/activity_pub/pipeline.ex24
-rw-r--r--lib/pleroma/web/activity_pub/publisher.ex32
-rw-r--r--lib/pleroma/web/activity_pub/side_effects.ex40
-rw-r--r--lib/pleroma/web/activity_pub/side_effects/handling.ex8
-rw-r--r--lib/pleroma/web/activity_pub/transmogrifier.ex5
-rw-r--r--lib/pleroma/web/activity_pub/utils.ex47
-rw-r--r--lib/pleroma/web/activity_pub/views/user_view.ex4
-rw-r--r--lib/pleroma/web/activity_pub/visibility.ex17
31 files changed, 638 insertions, 98 deletions
diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex
index a67a203d0..0923e19e4 100644
--- a/lib/pleroma/web/activity_pub/activity_pub.ex
+++ b/lib/pleroma/web/activity_pub/activity_pub.ex
@@ -32,6 +32,9 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
require Logger
require Pleroma.Constants
+ @behaviour Pleroma.Web.ActivityPub.ActivityPub.Persisting
+ @behaviour Pleroma.Web.ActivityPub.ActivityPub.Streaming
+
defp get_recipients(%{"type" => "Create"} = data) do
to = Map.get(data, "to", [])
cc = Map.get(data, "cc", [])
@@ -85,13 +88,14 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
defp increase_replies_count_if_reply(_create_data), do: :noop
@object_types ~w[ChatMessage Question Answer Audio Video Event Article]
- @spec persist(map(), keyword()) :: {:ok, Activity.t() | Object.t()}
+ @impl true
def persist(%{"type" => type} = object, meta) when type in @object_types do
with {:ok, object} <- Object.create(object) do
{:ok, object, meta}
end
end
+ @impl true
def persist(object, meta) do
with local <- Keyword.fetch!(meta, :local),
{recipients, _, _} <- get_recipients(object),
@@ -123,7 +127,9 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
# Splice in the child object if we have one.
activity = Maps.put_if_present(activity, :object, object)
- BackgroundWorker.enqueue("fetch_data_for_activity", %{"activity_id" => activity.id})
+ ConcurrentLimiter.limit(Pleroma.Web.RichMedia.Helpers, fn ->
+ Task.start(fn -> Pleroma.Web.RichMedia.Helpers.fetch_data_for_activity(activity) end)
+ end)
{:ok, activity}
else
@@ -219,6 +225,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
Streamer.stream("participation", participations)
end
+ @impl true
def stream_out_participations(%Object{data: %{"context" => context}}, user) do
with %Conversation{} = conversation <- Conversation.get_for_ap_id(context) do
conversation = Repo.preload(conversation, :participations)
@@ -235,8 +242,10 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
end
end
+ @impl true
def stream_out_participations(_, _), do: :noop
+ @impl true
def stream_out(%Activity{data: %{"type" => data_type}} = activity)
when data_type in ["Create", "Announce", "Delete"] do
activity
@@ -244,6 +253,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|> Streamer.stream(activity)
end
+ @impl true
def stream_out(_activity) do
:noop
end
@@ -332,15 +342,21 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
end
@spec flag(map()) :: {:ok, Activity.t()} | {:error, any()}
- def flag(
- %{
- actor: actor,
- context: _context,
- account: account,
- statuses: statuses,
- content: content
- } = params
- ) do
+ def flag(params) do
+ with {:ok, result} <- Repo.transaction(fn -> do_flag(params) end) do
+ result
+ end
+ end
+
+ defp do_flag(
+ %{
+ actor: actor,
+ context: _context,
+ account: account,
+ statuses: statuses,
+ content: content
+ } = params
+ ) do
# only accept false as false value
local = !(params[:local] == false)
forward = !(params[:forward] == false)
@@ -358,7 +374,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
{:ok, activity} <- insert(flag_data, local),
{:ok, stripped_activity} <- strip_report_status_data(activity),
_ <- notify_and_stream(activity),
- :ok <- maybe_federate(stripped_activity) do
+ :ok <-
+ maybe_federate(stripped_activity) do
User.all_superusers()
|> Enum.filter(fn user -> not is_nil(user.email) end)
|> Enum.each(fn superuser ->
@@ -368,6 +385,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
end)
{:ok, activity}
+ else
+ {:error, error} -> Repo.rollback(error)
end
end
@@ -590,12 +609,14 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|> Map.put(:muting_user, reading_user)
end
+ pagination_type = Map.get(params, :pagination_type) || :keyset
+
%{
godmode: params[:godmode],
reading_user: reading_user
}
|> user_activities_recipients()
- |> fetch_activities(params)
+ |> fetch_activities(params, pagination_type)
|> Enum.reverse()
end
@@ -1329,12 +1350,10 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
def fetch_follow_information_for_user(user) do
with {:ok, following_data} <-
- Fetcher.fetch_and_contain_remote_object_from_id(user.following_address,
- force_http: true
- ),
+ Fetcher.fetch_and_contain_remote_object_from_id(user.following_address),
{:ok, hide_follows} <- collection_private(following_data),
{:ok, followers_data} <-
- Fetcher.fetch_and_contain_remote_object_from_id(user.follower_address, force_http: true),
+ Fetcher.fetch_and_contain_remote_object_from_id(user.follower_address),
{:ok, hide_followers} <- collection_private(followers_data) do
{:ok,
%{
@@ -1408,8 +1427,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
end
end
- def fetch_and_prepare_user_from_ap_id(ap_id, opts \\ []) do
- with {:ok, data} <- Fetcher.fetch_and_contain_remote_object_from_id(ap_id, opts),
+ def fetch_and_prepare_user_from_ap_id(ap_id) do
+ with {:ok, data} <- Fetcher.fetch_and_contain_remote_object_from_id(ap_id),
{:ok, data} <- user_data_from_user_object(data) do
{:ok, maybe_update_follow_information(data)}
else
@@ -1452,13 +1471,13 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
end
end
- def make_user_from_ap_id(ap_id, opts \\ []) do
+ def make_user_from_ap_id(ap_id) do
user = User.get_cached_by_ap_id(ap_id)
if user && !User.ap_enabled?(user) do
Transmogrifier.upgrade_user_from_ap_id(ap_id)
else
- with {:ok, data} <- fetch_and_prepare_user_from_ap_id(ap_id, opts) do
+ with {:ok, data} <- fetch_and_prepare_user_from_ap_id(ap_id) do
if user do
user
|> User.remote_user_changeset(data)
diff --git a/lib/pleroma/web/activity_pub/activity_pub/persisting.ex b/lib/pleroma/web/activity_pub/activity_pub/persisting.ex
new file mode 100644
index 000000000..3894f48e2
--- /dev/null
+++ b/lib/pleroma/web/activity_pub/activity_pub/persisting.ex
@@ -0,0 +1,7 @@
+# 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.ActivityPub.Persisting do
+ @callback persist(map(), keyword()) :: {:ok, Activity.t() | Object.t()}
+end
diff --git a/lib/pleroma/web/activity_pub/activity_pub/streaming.ex b/lib/pleroma/web/activity_pub/activity_pub/streaming.ex
new file mode 100644
index 000000000..30009f2fb
--- /dev/null
+++ b/lib/pleroma/web/activity_pub/activity_pub/streaming.ex
@@ -0,0 +1,12 @@
+# 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.ActivityPub.Streaming do
+ alias Pleroma.Activity
+ alias Pleroma.Object
+ alias Pleroma.User
+
+ @callback stream_out(Activity.t()) :: any()
+ @callback stream_out_participations(Object.t(), User.t()) :: any()
+end
diff --git a/lib/pleroma/web/activity_pub/activity_pub_controller.ex b/lib/pleroma/web/activity_pub/activity_pub_controller.ex
index 31df80adb..7e5647f8f 100644
--- a/lib/pleroma/web/activity_pub/activity_pub_controller.ex
+++ b/lib/pleroma/web/activity_pub/activity_pub_controller.ex
@@ -82,7 +82,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
def object(conn, _) do
with ap_id <- Endpoint.url() <> conn.request_path,
%Object{} = object <- Object.get_cached_by_ap_id(ap_id),
- {_, true} <- {:public?, Visibility.is_public?(object)} do
+ {_, true} <- {:public?, Visibility.is_public?(object)},
+ {_, false} <- {:local?, Visibility.is_local_public?(object)} do
conn
|> assign(:tracking_fun_data, object.id)
|> set_cache_ttl_for(object)
@@ -92,6 +93,9 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
else
{:public?, false} ->
{:error, :not_found}
+
+ {:local?, true} ->
+ {:error, :not_found}
end
end
@@ -108,7 +112,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
def activity(conn, _params) do
with ap_id <- Endpoint.url() <> conn.request_path,
%Activity{} = activity <- Activity.normalize(ap_id),
- {_, true} <- {:public?, Visibility.is_public?(activity)} do
+ {_, true} <- {:public?, Visibility.is_public?(activity)},
+ {_, false} <- {:local?, Visibility.is_local_public?(activity)} do
conn
|> maybe_set_tracking_data(activity)
|> set_cache_ttl_for(activity)
@@ -117,6 +122,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
|> render("object.json", object: activity)
else
{:public?, false} -> {:error, :not_found}
+ {:local?, true} -> {:error, :not_found}
nil -> {:error, :not_found}
end
end
diff --git a/lib/pleroma/web/activity_pub/builder.ex b/lib/pleroma/web/activity_pub/builder.ex
index 298aff6b7..e99f6fd83 100644
--- a/lib/pleroma/web/activity_pub/builder.ex
+++ b/lib/pleroma/web/activity_pub/builder.ex
@@ -222,6 +222,9 @@ defmodule Pleroma.Web.ActivityPub.Builder do
actor.ap_id == Relay.ap_id() ->
[actor.follower_address]
+ public? and Visibility.is_local_public?(object) ->
+ [actor.follower_address, object.data["actor"], Pleroma.Constants.as_local_public()]
+
public? ->
[actor.follower_address, object.data["actor"], Pleroma.Constants.as_public()]
diff --git a/lib/pleroma/web/activity_pub/mrf.ex b/lib/pleroma/web/activity_pub/mrf.ex
index 5e5361082..02fdee5fc 100644
--- a/lib/pleroma/web/activity_pub/mrf.ex
+++ b/lib/pleroma/web/activity_pub/mrf.ex
@@ -3,7 +3,64 @@
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.MRF do
+ require Logger
+
+ @behaviour Pleroma.Web.ActivityPub.MRF.PipelineFiltering
+
+ @mrf_config_descriptions [
+ %{
+ group: :pleroma,
+ key: :mrf,
+ tab: :mrf,
+ label: "MRF",
+ type: :group,
+ description: "General MRF settings",
+ children: [
+ %{
+ key: :policies,
+ type: [:module, {:list, :module}],
+ description:
+ "A list of MRF policies enabled. Module names are shortened (removed leading `Pleroma.Web.ActivityPub.MRF.` part), but on adding custom module you need to use full name.",
+ suggestions: {:list_behaviour_implementations, Pleroma.Web.ActivityPub.MRF}
+ },
+ %{
+ key: :transparency,
+ label: "MRF transparency",
+ type: :boolean,
+ description:
+ "Make the content of your Message Rewrite Facility settings public (via nodeinfo)"
+ },
+ %{
+ key: :transparency_exclusions,
+ label: "MRF transparency exclusions",
+ type: {:list, :string},
+ description:
+ "Exclude specific instance names from MRF transparency. The use of the exclusions feature will be disclosed in nodeinfo as a boolean value.",
+ suggestions: [
+ "exclusion.com"
+ ]
+ }
+ ]
+ }
+ ]
+
+ @default_description %{
+ label: "",
+ description: ""
+ }
+
+ @required_description_keys [:key, :related_policy]
+
@callback filter(Map.t()) :: {:ok | :reject, Map.t()}
+ @callback describe() :: {:ok | :error, Map.t()}
+ @callback config_description() :: %{
+ optional(:children) => [map()],
+ key: atom(),
+ related_policy: String.t(),
+ label: String.t(),
+ description: String.t()
+ }
+ @optional_callbacks config_description: 0
def filter(policies, %{} = message) do
policies
@@ -15,6 +72,7 @@ defmodule Pleroma.Web.ActivityPub.MRF do
def filter(%{} = object), do: get_policies() |> filter(object)
+ @impl true
def pipeline_filter(%{} = message, meta) do
object = meta[:object_data]
ap_id = message["object"]
@@ -51,8 +109,6 @@ defmodule Pleroma.Web.ActivityPub.MRF do
Enum.any?(domains, fn domain -> Regex.match?(domain, host) end)
end
- @callback describe() :: {:ok | :error, Map.t()}
-
def describe(policies) do
{:ok, policy_configs} =
policies
@@ -82,4 +138,41 @@ defmodule Pleroma.Web.ActivityPub.MRF do
end
def describe, do: get_policies() |> describe()
+
+ def config_descriptions do
+ Pleroma.Web.ActivityPub.MRF
+ |> Pleroma.Docs.Generator.list_behaviour_implementations()
+ |> config_descriptions()
+ end
+
+ def config_descriptions(policies) do
+ Enum.reduce(policies, @mrf_config_descriptions, fn policy, acc ->
+ if function_exported?(policy, :config_description, 0) do
+ description =
+ @default_description
+ |> Map.merge(policy.config_description)
+ |> Map.put(:group, :pleroma)
+ |> Map.put(:tab, :mrf)
+ |> Map.put(:type, :group)
+
+ if Enum.all?(@required_description_keys, &Map.has_key?(description, &1)) do
+ [description | acc]
+ else
+ Logger.warn(
+ "#{policy} config description doesn't have one or all required keys #{
+ inspect(@required_description_keys)
+ }"
+ )
+
+ acc
+ end
+ else
+ Logger.debug(
+ "#{policy} is excluded from config descriptions, because does not implement `config_description/0` method."
+ )
+
+ acc
+ end
+ end)
+ end
end
diff --git a/lib/pleroma/web/activity_pub/mrf/activity_expiration_policy.ex b/lib/pleroma/web/activity_pub/mrf/activity_expiration_policy.ex
index bee47b4ed..655a2ced0 100644
--- a/lib/pleroma/web/activity_pub/mrf/activity_expiration_policy.ex
+++ b/lib/pleroma/web/activity_pub/mrf/activity_expiration_policy.ex
@@ -40,4 +40,22 @@ defmodule Pleroma.Web.ActivityPub.MRF.ActivityExpirationPolicy do
_ -> Map.put(activity, "expires_at", expires_at)
end
end
+
+ @impl true
+ def config_description do
+ %{
+ key: :mrf_activity_expiration,
+ related_policy: "Pleroma.Web.ActivityPub.MRF.ActivityExpirationPolicy",
+ label: "MRF Activity Expiration Policy",
+ description: "Adds automatic expiration to all local activities",
+ children: [
+ %{
+ key: :days,
+ type: :integer,
+ description: "Default global expiration time for all local activities (in days)",
+ suggestions: [90, 365]
+ }
+ ]
+ }
+ end
end
diff --git a/lib/pleroma/web/activity_pub/mrf/hellthread_policy.ex b/lib/pleroma/web/activity_pub/mrf/hellthread_policy.ex
index 9ba07b4e3..3fd5c1e0a 100644
--- a/lib/pleroma/web/activity_pub/mrf/hellthread_policy.ex
+++ b/lib/pleroma/web/activity_pub/mrf/hellthread_policy.ex
@@ -97,4 +97,31 @@ defmodule Pleroma.Web.ActivityPub.MRF.HellthreadPolicy do
@impl true
def describe,
do: {:ok, %{mrf_hellthread: Pleroma.Config.get(:mrf_hellthread) |> Enum.into(%{})}}
+
+ @impl true
+ def config_description do
+ %{
+ key: :mrf_hellthread,
+ related_policy: "Pleroma.Web.ActivityPub.MRF.HellthreadPolicy",
+ label: "MRF Hellthread",
+ description: "Block messages with excessive user mentions",
+ children: [
+ %{
+ key: :delist_threshold,
+ type: :integer,
+ description:
+ "Number of mentioned users after which the message gets removed from timelines and" <>
+ "disables notifications. Set to 0 to disable.",
+ suggestions: [10]
+ },
+ %{
+ key: :reject_threshold,
+ type: :integer,
+ description:
+ "Number of mentioned users after which the messaged gets rejected. Set to 0 to disable.",
+ suggestions: [20]
+ }
+ ]
+ }
+ end
end
diff --git a/lib/pleroma/web/activity_pub/mrf/keyword_policy.ex b/lib/pleroma/web/activity_pub/mrf/keyword_policy.ex
index db66cfa3e..ded0fe7f2 100644
--- a/lib/pleroma/web/activity_pub/mrf/keyword_policy.ex
+++ b/lib/pleroma/web/activity_pub/mrf/keyword_policy.ex
@@ -126,4 +126,46 @@ defmodule Pleroma.Web.ActivityPub.MRF.KeywordPolicy do
{:ok, %{mrf_keyword: mrf_keyword}}
end
+
+ @impl true
+ def config_description do
+ %{
+ key: :mrf_keyword,
+ related_policy: "Pleroma.Web.ActivityPub.MRF.KeywordPolicy",
+ label: "MRF Keyword",
+ description:
+ "Reject or Word-Replace messages matching a keyword or [Regex](https://hexdocs.pm/elixir/Regex.html).",
+ children: [
+ %{
+ key: :reject,
+ type: {:list, :string},
+ description: """
+ A list of patterns which result in message being rejected.
+
+ Each pattern can be a string or [Regex](https://hexdocs.pm/elixir/Regex.html) in the format of `~r/PATTERN/`.
+ """,
+ suggestions: ["foo", ~r/foo/iu]
+ },
+ %{
+ key: :federated_timeline_removal,
+ type: {:list, :string},
+ description: """
+ A list of patterns which result in message being removed from federated timelines (a.k.a unlisted).
+
+ Each pattern can be a string or [Regex](https://hexdocs.pm/elixir/Regex.html) in the format of `~r/PATTERN/`.
+ """,
+ suggestions: ["foo", ~r/foo/iu]
+ },
+ %{
+ key: :replace,
+ type: {:list, :tuple},
+ description: """
+ **Pattern**: a string or [Regex](https://hexdocs.pm/elixir/Regex.html) in the format of `~r/PATTERN/`.
+
+ **Replacement**: a string. Leaving the field empty is permitted.
+ """
+ }
+ ]
+ }
+ end
end
diff --git a/lib/pleroma/web/activity_pub/mrf/media_proxy_warming_policy.ex b/lib/pleroma/web/activity_pub/mrf/media_proxy_warming_policy.ex
index 0fb05d3c4..816cc89bf 100644
--- a/lib/pleroma/web/activity_pub/mrf/media_proxy_warming_policy.ex
+++ b/lib/pleroma/web/activity_pub/mrf/media_proxy_warming_policy.ex
@@ -8,7 +8,6 @@ defmodule Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicy do
alias Pleroma.HTTP
alias Pleroma.Web.MediaProxy
- alias Pleroma.Workers.BackgroundWorker
require Logger
@@ -17,7 +16,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicy do
recv_timeout: 10_000
]
- def perform(:prefetch, url) do
+ defp prefetch(url) do
# Fetching only proxiable resources
if MediaProxy.enabled?() and MediaProxy.url_proxiable?(url) do
# If preview proxy is enabled, it'll also hit media proxy (so we're caching both requests)
@@ -25,17 +24,25 @@ defmodule Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicy do
Logger.debug("Prefetching #{inspect(url)} as #{inspect(prefetch_url)}")
- HTTP.get(prefetch_url, [], @adapter_options)
+ if Pleroma.Config.get(:env) == :test do
+ fetch(prefetch_url)
+ else
+ ConcurrentLimiter.limit(MediaProxy, fn ->
+ Task.start(fn -> fetch(prefetch_url) end)
+ end)
+ end
end
end
- def perform(:preload, %{"object" => %{"attachment" => attachments}} = _message) do
+ defp fetch(url), do: HTTP.get(url, [], @adapter_options)
+
+ defp preload(%{"object" => %{"attachment" => attachments}} = _message) do
Enum.each(attachments, fn
%{"url" => url} when is_list(url) ->
url
|> Enum.each(fn
%{"href" => href} ->
- BackgroundWorker.enqueue("media_proxy_prefetch", %{"url" => href})
+ prefetch(href)
x ->
Logger.debug("Unhandled attachment URL object #{inspect(x)}")
@@ -51,7 +58,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicy do
%{"type" => "Create", "object" => %{"attachment" => attachments} = _object} = message
)
when is_list(attachments) and length(attachments) > 0 do
- BackgroundWorker.enqueue("media_proxy_preload", %{"message" => message})
+ preload(message)
{:ok, message}
end
diff --git a/lib/pleroma/web/activity_pub/mrf/mention_policy.ex b/lib/pleroma/web/activity_pub/mrf/mention_policy.ex
index 7910ca131..9c096712a 100644
--- a/lib/pleroma/web/activity_pub/mrf/mention_policy.ex
+++ b/lib/pleroma/web/activity_pub/mrf/mention_policy.ex
@@ -25,4 +25,22 @@ defmodule Pleroma.Web.ActivityPub.MRF.MentionPolicy do
@impl true
def describe, do: {:ok, %{}}
+
+ @impl true
+ def config_description do
+ %{
+ key: :mrf_mention,
+ related_policy: "Pleroma.Web.ActivityPub.MRF.MentionPolicy",
+ label: "MRF Mention",
+ description: "Block messages which mention a specific user",
+ children: [
+ %{
+ key: :actors,
+ type: {:list, :string},
+ description: "A list of actors for which any post mentioning them will be dropped",
+ suggestions: ["actor1", "actor2"]
+ }
+ ]
+ }
+ end
end
diff --git a/lib/pleroma/web/activity_pub/mrf/normalize_markup.ex b/lib/pleroma/web/activity_pub/mrf/normalize_markup.ex
index 7abae37ae..e00575c2a 100644
--- a/lib/pleroma/web/activity_pub/mrf/normalize_markup.ex
+++ b/lib/pleroma/web/activity_pub/mrf/normalize_markup.ex
@@ -8,6 +8,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.NormalizeMarkup do
@behaviour Pleroma.Web.ActivityPub.MRF
+ @impl true
def filter(%{"type" => "Create", "object" => child_object} = object) do
scrub_policy = Pleroma.Config.get([:mrf_normalize_markup, :scrub_policy])
@@ -22,5 +23,23 @@ defmodule Pleroma.Web.ActivityPub.MRF.NormalizeMarkup do
def filter(object), do: {:ok, object}
+ @impl true
def describe, do: {:ok, %{}}
+
+ @impl true
+ def config_description do
+ %{
+ key: :mrf_normalize_markup,
+ related_policy: "Pleroma.Web.ActivityPub.MRF.NormalizeMarkup",
+ label: "MRF Normalize Markup",
+ description: "MRF NormalizeMarkup settings. Scrub configured hypertext markup.",
+ children: [
+ %{
+ key: :scrub_policy,
+ type: :module,
+ suggestions: [Pleroma.HTML.Scrubber.Default]
+ }
+ ]
+ }
+ end
end
diff --git a/lib/pleroma/web/activity_pub/mrf/object_age_policy.ex b/lib/pleroma/web/activity_pub/mrf/object_age_policy.ex
index d45d2d7e3..eb0481f20 100644
--- a/lib/pleroma/web/activity_pub/mrf/object_age_policy.ex
+++ b/lib/pleroma/web/activity_pub/mrf/object_age_policy.ex
@@ -106,4 +106,32 @@ defmodule Pleroma.Web.ActivityPub.MRF.ObjectAgePolicy do
{:ok, %{mrf_object_age: mrf_object_age}}
end
+
+ @impl true
+ def config_description do
+ %{
+ key: :mrf_object_age,
+ related_policy: "Pleroma.Web.ActivityPub.MRF.ObjectAgePolicy",
+ label: "MRF Object Age",
+ description:
+ "Rejects or delists posts based on their timestamp deviance from your server's clock.",
+ children: [
+ %{
+ key: :threshold,
+ type: :integer,
+ description: "Required age (in seconds) of a post before actions are taken.",
+ suggestions: [172_800]
+ },
+ %{
+ key: :actions,
+ type: {:list, :atom},
+ description:
+ "A list of actions to apply to the post. `:delist` removes the post from public timelines; " <>
+ "`:strip_followers` removes followers from the ActivityPub recipient list ensuring they won't be delivered to home timelines; " <>
+ "`:reject` rejects the message entirely",
+ suggestions: [:delist, :strip_followers, :reject]
+ }
+ ]
+ }
+ end
end
diff --git a/lib/pleroma/web/activity_pub/mrf/pipeline_filtering.ex b/lib/pleroma/web/activity_pub/mrf/pipeline_filtering.ex
new file mode 100644
index 000000000..8e0069bc5
--- /dev/null
+++ b/lib/pleroma/web/activity_pub/mrf/pipeline_filtering.ex
@@ -0,0 +1,7 @@
+# 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.MRF.PipelineFiltering do
+ @callback pipeline_filter(map(), keyword()) :: {:ok, map(), keyword()} | {:error, any()}
+end
diff --git a/lib/pleroma/web/activity_pub/mrf/reject_non_public.ex b/lib/pleroma/web/activity_pub/mrf/reject_non_public.ex
index 0b9ed2224..cd7665e31 100644
--- a/lib/pleroma/web/activity_pub/mrf/reject_non_public.ex
+++ b/lib/pleroma/web/activity_pub/mrf/reject_non_public.ex
@@ -48,4 +48,27 @@ defmodule Pleroma.Web.ActivityPub.MRF.RejectNonPublic do
@impl true
def describe,
do: {:ok, %{mrf_rejectnonpublic: Config.get(:mrf_rejectnonpublic) |> Enum.into(%{})}}
+
+ @impl true
+ def config_description do
+ %{
+ key: :mrf_rejectnonpublic,
+ related_policy: "Pleroma.Web.ActivityPub.MRF.RejectNonPublic",
+ description: "RejectNonPublic drops posts with non-public visibility settings.",
+ label: "MRF Reject Non Public",
+ children: [
+ %{
+ key: :allow_followersonly,
+ label: "Allow followers-only",
+ type: :boolean,
+ description: "Whether to allow followers-only posts"
+ },
+ %{
+ key: :allow_direct,
+ type: :boolean,
+ description: "Whether to allow direct messages"
+ }
+ ]
+ }
+ end
end
diff --git a/lib/pleroma/web/activity_pub/mrf/simple_policy.ex b/lib/pleroma/web/activity_pub/mrf/simple_policy.ex
index 161177727..6cd91826d 100644
--- a/lib/pleroma/web/activity_pub/mrf/simple_policy.ex
+++ b/lib/pleroma/web/activity_pub/mrf/simple_policy.ex
@@ -244,4 +244,78 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do
{:ok, %{mrf_simple: mrf_simple}}
end
+
+ @impl true
+ def config_description do
+ %{
+ key: :mrf_simple,
+ related_policy: "Pleroma.Web.ActivityPub.MRF.SimplePolicy",
+ label: "MRF Simple",
+ description: "Simple ingress policies",
+ children: [
+ %{
+ key: :media_removal,
+ type: {:list, :string},
+ description: "List of instances to strip media attachments from",
+ suggestions: ["example.com", "*.example.com"]
+ },
+ %{
+ key: :media_nsfw,
+ label: "Media NSFW",
+ type: {:list, :string},
+ description: "List of instances to tag all media as NSFW (sensitive) from",
+ suggestions: ["example.com", "*.example.com"]
+ },
+ %{
+ key: :federated_timeline_removal,
+ type: {:list, :string},
+ description:
+ "List of instances to remove from the Federated (aka The Whole Known Network) Timeline",
+ suggestions: ["example.com", "*.example.com"]
+ },
+ %{
+ key: :reject,
+ type: {:list, :string},
+ description: "List of instances to reject activities from (except deletes)",
+ suggestions: ["example.com", "*.example.com"]
+ },
+ %{
+ key: :accept,
+ type: {:list, :string},
+ description: "List of instances to only accept activities from (except deletes)",
+ suggestions: ["example.com", "*.example.com"]
+ },
+ %{
+ key: :followers_only,
+ type: {:list, :string},
+ description: "Force posts from the given instances to be visible by followers only",
+ suggestions: ["example.com", "*.example.com"]
+ },
+ %{
+ key: :report_removal,
+ type: {:list, :string},
+ description: "List of instances to reject reports from",
+ suggestions: ["example.com", "*.example.com"]
+ },
+ %{
+ key: :avatar_removal,
+ type: {:list, :string},
+ description: "List of instances to strip avatars from",
+ suggestions: ["example.com", "*.example.com"]
+ },
+ %{
+ key: :banner_removal,
+ type: {:list, :string},
+ description: "List of instances to strip banners from",
+ suggestions: ["example.com", "*.example.com"]
+ },
+ %{
+ key: :reject_deletes,
+ type: {:list, :string},
+ description: "List of instances to reject deletions from",
+ suggestions: ["example.com", "*.example.com"]
+ }
+ ]
+ }
+ end
end
diff --git a/lib/pleroma/web/activity_pub/mrf/subchain_policy.ex b/lib/pleroma/web/activity_pub/mrf/subchain_policy.ex
index 048052da6..2ec45260a 100644
--- a/lib/pleroma/web/activity_pub/mrf/subchain_policy.ex
+++ b/lib/pleroma/web/activity_pub/mrf/subchain_policy.ex
@@ -39,4 +39,28 @@ defmodule Pleroma.Web.ActivityPub.MRF.SubchainPolicy do
@impl true
def describe, do: {:ok, %{}}
+
+ @impl true
+ def config_description do
+ %{
+ key: :mrf_subchain,
+ related_policy: "Pleroma.Web.ActivityPub.MRF.SubchainPolicy",
+ label: "MRF Subchain",
+ description:
+ "This policy processes messages through an alternate pipeline when a given message matches certain criteria." <>
+ " All criteria are configured as a map of regular expressions to lists of policy modules.",
+ children: [
+ %{
+ key: :match_actor,
+ type: {:map, {:list, :string}},
+ description: "Matches a series of regular expressions against the actor field",
+ suggestions: [
+ %{
+ ~r/https:\/\/example.com/s => [Pleroma.Web.ActivityPub.MRF.DropPolicy]
+ }
+ ]
+ }
+ ]
+ }
+ end
end
diff --git a/lib/pleroma/web/activity_pub/mrf/user_allow_list_policy.ex b/lib/pleroma/web/activity_pub/mrf/user_allow_list_policy.ex
index 1a28f2ba2..e9d0d0503 100644
--- a/lib/pleroma/web/activity_pub/mrf/user_allow_list_policy.ex
+++ b/lib/pleroma/web/activity_pub/mrf/user_allow_list_policy.ex
@@ -41,4 +41,25 @@ defmodule Pleroma.Web.ActivityPub.MRF.UserAllowListPolicy do
{:ok, %{mrf_user_allowlist: mrf_user_allowlist}}
end
+
+ # TODO: change way of getting settings on `lib/pleroma/web/activity_pub/mrf/user_allow_list_policy.ex:18` to use `hosts` subkey
+ # @impl true
+ # def config_description do
+ # %{
+ # key: :mrf_user_allowlist,
+ # related_policy: "Pleroma.Web.ActivityPub.MRF.UserAllowListPolicy",
+ # description: "Accept-list of users from specified instances",
+ # children: [
+ # %{
+ # key: :hosts,
+ # type: :map,
+ # description:
+ # "The keys in this section are the domain names that the policy should apply to." <>
+ # " Each key should be assigned a list of users that should be allowed " <>
+ # "through by their ActivityPub ID",
+ # suggestions: [%{"example.org" => ["https://example.org/users/admin"]}]
+ # }
+ # ]
+ # }
+ # end
end
diff --git a/lib/pleroma/web/activity_pub/mrf/vocabulary_policy.ex b/lib/pleroma/web/activity_pub/mrf/vocabulary_policy.ex
index a6c545570..f325cb680 100644
--- a/lib/pleroma/web/activity_pub/mrf/vocabulary_policy.ex
+++ b/lib/pleroma/web/activity_pub/mrf/vocabulary_policy.ex
@@ -7,6 +7,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.VocabularyPolicy do
@behaviour Pleroma.Web.ActivityPub.MRF
+ @impl true
def filter(%{"type" => "Undo", "object" => child_message} = message) do
with {:ok, _} <- filter(child_message) do
{:ok, message}
@@ -36,6 +37,33 @@ defmodule Pleroma.Web.ActivityPub.MRF.VocabularyPolicy do
def filter(message), do: {:ok, message}
+ @impl true
def describe,
do: {:ok, %{mrf_vocabulary: Pleroma.Config.get(:mrf_vocabulary) |> Enum.into(%{})}}
+
+ @impl true
+ def config_description do
+ %{
+ key: :mrf_vocabulary,
+ related_policy: "Pleroma.Web.ActivityPub.MRF.VocabularyPolicy",
+ label: "MRF Vocabulary",
+ description: "Filter messages which belong to certain activity vocabularies",
+ children: [
+ %{
+ key: :accept,
+ type: {:list, :string},
+ description:
+ "A list of ActivityStreams terms to accept. If empty, all supported messages are accepted.",
+ suggestions: ["Create", "Follow", "Mention", "Announce", "Like"]
+ },
+ %{
+ key: :reject,
+ type: {:list, :string},
+ description:
+ "A list of ActivityStreams terms to reject. If empty, no messages are rejected.",
+ suggestions: ["Create", "Follow", "Mention", "Announce", "Like"]
+ }
+ ]
+ }
+ end
end
diff --git a/lib/pleroma/web/activity_pub/object_validator.ex b/lib/pleroma/web/activity_pub/object_validator.ex
index bd0a2a8dc..ce8e7341b 100644
--- a/lib/pleroma/web/activity_pub/object_validator.ex
+++ b/lib/pleroma/web/activity_pub/object_validator.ex
@@ -9,6 +9,8 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidator do
the system.
"""
+ @behaviour Pleroma.Web.ActivityPub.ObjectValidator.Validating
+
alias Pleroma.Activity
alias Pleroma.EctoType.ActivityPub.ObjectValidators
alias Pleroma.Object
@@ -32,7 +34,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidator do
alias Pleroma.Web.ActivityPub.ObjectValidators.UndoValidator
alias Pleroma.Web.ActivityPub.ObjectValidators.UpdateValidator
- @spec validate(map(), keyword()) :: {:ok, map(), keyword()} | {:error, any()}
+ @impl true
def validate(object, meta)
def validate(%{"type" => type} = object, meta)
diff --git a/lib/pleroma/web/activity_pub/object_validator/validating.ex b/lib/pleroma/web/activity_pub/object_validator/validating.ex
new file mode 100644
index 000000000..64c0c30c5
--- /dev/null
+++ b/lib/pleroma/web/activity_pub/object_validator/validating.ex
@@ -0,0 +1,7 @@
+# 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.ObjectValidator.Validating do
+ @callback validate(map(), keyword()) :: {:ok, map(), keyword()} | {:error, any()}
+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
index 6f757f49c..338957db8 100644
--- a/lib/pleroma/web/activity_pub/object_validators/announce_validator.ex
+++ b/lib/pleroma/web/activity_pub/object_validators/announce_validator.ex
@@ -67,7 +67,12 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.AnnounceValidator do
%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))
+ recipients = get_field(cng, :to) ++ get_field(cng, :cc)
+ local_public = Pleroma.Constants.as_local_public()
+
+ is_public =
+ Enum.member?(recipients, Pleroma.Constants.as_public()) or
+ Enum.member?(recipients, local_public)
cond do
same_actor && is_public ->
diff --git a/lib/pleroma/web/activity_pub/object_validators/attachment_validator.ex b/lib/pleroma/web/activity_pub/object_validators/attachment_validator.ex
index df102a134..f96fd54bf 100644
--- a/lib/pleroma/web/activity_pub/object_validators/attachment_validator.ex
+++ b/lib/pleroma/web/activity_pub/object_validators/attachment_validator.ex
@@ -15,6 +15,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.AttachmentValidator do
field(:type, :string)
field(:mediaType, :string, default: "application/octet-stream")
field(:name, :string)
+ field(:blurhash, :string)
embeds_many :url, UrlObjectValidator, primary_key: false do
field(:type, :string)
@@ -41,7 +42,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.AttachmentValidator do
|> fix_url()
struct
- |> cast(data, [:type, :mediaType, :name])
+ |> cast(data, [:type, :mediaType, :name, :blurhash])
|> cast_embed(:url, with: &url_changeset/2)
|> validate_inclusion(:type, ~w[Link Document Audio Image Video])
|> validate_required([:type, :mediaType, :url])
diff --git a/lib/pleroma/web/activity_pub/pipeline.ex b/lib/pleroma/web/activity_pub/pipeline.ex
index 2db86f116..2715b94d4 100644
--- a/lib/pleroma/web/activity_pub/pipeline.ex
+++ b/lib/pleroma/web/activity_pub/pipeline.ex
@@ -11,14 +11,22 @@ defmodule Pleroma.Web.ActivityPub.Pipeline do
alias Pleroma.Web.ActivityPub.MRF
alias Pleroma.Web.ActivityPub.ObjectValidator
alias Pleroma.Web.ActivityPub.SideEffects
+ alias Pleroma.Web.ActivityPub.Visibility
alias Pleroma.Web.Federator
+ @side_effects Config.get([:pipeline, :side_effects], SideEffects)
+ @federator Config.get([:pipeline, :federator], Federator)
+ @object_validator Config.get([:pipeline, :object_validator], ObjectValidator)
+ @mrf Config.get([:pipeline, :mrf], MRF)
+ @activity_pub Config.get([:pipeline, :activity_pub], ActivityPub)
+ @config Config.get([:pipeline, :config], Config)
+
@spec common_pipeline(map(), keyword()) ::
{:ok, Activity.t() | Object.t(), keyword()} | {:error, any()}
def common_pipeline(object, meta) do
case Repo.transaction(fn -> do_common_pipeline(object, meta) end) do
{:ok, {:ok, activity, meta}} ->
- SideEffects.handle_after_transaction(meta)
+ @side_effects.handle_after_transaction(meta)
{:ok, activity, meta}
{:ok, value} ->
@@ -34,13 +42,13 @@ defmodule Pleroma.Web.ActivityPub.Pipeline do
def do_common_pipeline(object, meta) do
with {_, {:ok, validated_object, meta}} <-
- {:validate_object, ObjectValidator.validate(object, meta)},
+ {:validate_object, @object_validator.validate(object, meta)},
{_, {:ok, mrfd_object, meta}} <-
- {:mrf_object, MRF.pipeline_filter(validated_object, meta)},
+ {:mrf_object, @mrf.pipeline_filter(validated_object, meta)},
{_, {:ok, activity, meta}} <-
- {:persist_object, ActivityPub.persist(mrfd_object, meta)},
+ {:persist_object, @activity_pub.persist(mrfd_object, meta)},
{_, {:ok, activity, meta}} <-
- {:execute_side_effects, SideEffects.handle(activity, meta)},
+ {:execute_side_effects, @side_effects.handle(activity, meta)},
{_, {:ok, _}} <- {:federation, maybe_federate(activity, meta)} do
{:ok, activity, meta}
else
@@ -53,9 +61,9 @@ 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] || !Config.get([:instance, :federating])
+ do_not_federate = meta[:do_not_federate] || !@config.get([:instance, :federating])
- if !do_not_federate && local do
+ if !do_not_federate and local and not Visibility.is_local_public?(activity) do
activity =
if object = Keyword.get(meta, :object_data) do
%{activity | data: Map.put(activity.data, "object", object)}
@@ -63,7 +71,7 @@ defmodule Pleroma.Web.ActivityPub.Pipeline do
activity
end
- Federator.publish(activity)
+ @federator.publish(activity)
{:ok, :federated}
else
{:ok, :not_federated}
diff --git a/lib/pleroma/web/activity_pub/publisher.ex b/lib/pleroma/web/activity_pub/publisher.ex
index a2930c1cd..5ab3562bf 100644
--- a/lib/pleroma/web/activity_pub/publisher.ex
+++ b/lib/pleroma/web/activity_pub/publisher.ex
@@ -13,7 +13,6 @@ defmodule Pleroma.Web.ActivityPub.Publisher do
alias Pleroma.User
alias Pleroma.Web.ActivityPub.Relay
alias Pleroma.Web.ActivityPub.Transmogrifier
- alias Pleroma.Web.FedSockets
require Pleroma.Constants
@@ -50,28 +49,6 @@ defmodule Pleroma.Web.ActivityPub.Publisher do
"""
def publish_one(%{inbox: inbox, json: json, actor: %User{} = actor, id: id} = params) do
Logger.debug("Federating #{id} to #{inbox}")
-
- case FedSockets.get_or_create_fed_socket(inbox) do
- {:ok, fedsocket} ->
- Logger.debug("publishing via fedsockets - #{inspect(inbox)}")
- FedSockets.publish(fedsocket, json)
-
- _ ->
- Logger.debug("publishing via http - #{inspect(inbox)}")
- http_publish(inbox, actor, json, params)
- end
- end
-
- def publish_one(%{actor_id: actor_id} = params) do
- actor = User.get_cached_by_id(actor_id)
-
- params
- |> Map.delete(:actor_id)
- |> Map.put(:actor, actor)
- |> publish_one()
- end
-
- defp http_publish(inbox, actor, json, params) do
uri = %{path: path} = URI.parse(inbox)
digest = "SHA-256=" <> (:crypto.hash(:sha256, json) |> Base.encode64())
@@ -110,6 +87,15 @@ defmodule Pleroma.Web.ActivityPub.Publisher do
end
end
+ def publish_one(%{actor_id: actor_id} = params) do
+ actor = User.get_cached_by_id(actor_id)
+
+ params
+ |> Map.delete(:actor_id)
+ |> Map.put(:actor, actor)
+ |> publish_one()
+ end
+
defp signature_host(%URI{port: port, scheme: scheme, host: host}) do
if port == URI.default_port(scheme) do
host
diff --git a/lib/pleroma/web/activity_pub/side_effects.ex b/lib/pleroma/web/activity_pub/side_effects.ex
index bbff35c36..e37caf6a0 100644
--- a/lib/pleroma/web/activity_pub/side_effects.ex
+++ b/lib/pleroma/web/activity_pub/side_effects.ex
@@ -24,15 +24,22 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do
alias Pleroma.Web.ActivityPub.Utils
alias Pleroma.Web.Push
alias Pleroma.Web.Streamer
- alias Pleroma.Workers.BackgroundWorker
require Logger
+ @cachex Pleroma.Config.get([:cachex, :provider], Cachex)
+ @ap_streamer Pleroma.Config.get([:side_effects, :ap_streamer], ActivityPub)
+ @logger Pleroma.Config.get([:side_effects, :logger], Logger)
+
+ @behaviour Pleroma.Web.ActivityPub.SideEffects.Handling
+
+ @impl true
def handle(object, meta \\ [])
# Task this handles
# - Follows
# - Sends a notification
+ @impl true
def handle(
%{
data: %{
@@ -48,10 +55,9 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do
%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, "accept"),
- {:ok, _relationship} <- FollowingRelationship.update(follower, followed, :follow_accept) do
+ {:ok, _follower, followed} <-
+ FollowingRelationship.update(follower, followed, :follow_accept) do
Notification.update_notification_type(followed, follow_activity)
- User.update_follower_count(followed)
- User.update_following_count(follower)
end
{:ok, object, meta}
@@ -61,6 +67,7 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do
# - Rejects all existing follow activities for this person
# - Updates the follow state
# - Dismisses notification
+ @impl true
def handle(
%{
data: %{
@@ -87,6 +94,7 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do
# - Follows if possible
# - Sends a notification
# - Generates accept or reject if appropriate
+ @impl true
def handle(
%{
data: %{
@@ -100,7 +108,7 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do
) do
with %User{} = follower <- User.get_cached_by_ap_id(following_user),
%User{} = followed <- User.get_cached_by_ap_id(followed_user),
- {_, {:ok, _}, _, _} <-
+ {_, {:ok, _, _}, _, _} <-
{:following, User.follow(follower, followed, :follow_pending), follower, followed} do
if followed.local && !followed.is_locked do
{:ok, accept_data, _} = Builder.accept(followed, object)
@@ -128,6 +136,7 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do
# Tasks this handles:
# - Unfollow and block
+ @impl true
def handle(
%{data: %{"type" => "Block", "object" => blocked_user, "actor" => blocking_user}} =
object,
@@ -146,6 +155,7 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do
#
# For a local user, we also get a changeset with the full information, so we
# can update non-federating, non-activitypub settings as well.
+ @impl true
def handle(%{data: %{"type" => "Update", "object" => updated_object}} = object, meta) do
if changeset = Keyword.get(meta, :user_update_changeset) do
changeset
@@ -164,6 +174,7 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do
# Tasks this handles:
# - Add like to object
# - Set up notification
+ @impl true
def handle(%{data: %{"type" => "Like"}} = object, meta) do
liked_object = Object.get_by_ap_id(object.data["object"])
Utils.add_like_to_object(object, liked_object)
@@ -181,6 +192,7 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do
# - Increase replies count
# - Set up ActivityExpiration
# - Set up notifications
+ @impl true
def handle(%{data: %{"type" => "Create"}} = activity, meta) do
with {:ok, object, meta} <- handle_object_creation(meta[:object_data], meta),
%User{} = user <- User.get_cached_by_ap_id(activity.data["actor"]) do
@@ -191,7 +203,9 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do
Object.increase_replies_count(in_reply_to)
end
- BackgroundWorker.enqueue("fetch_data_for_activity", %{"activity_id" => activity.id})
+ ConcurrentLimiter.limit(Pleroma.Web.RichMedia.Helpers, fn ->
+ Task.start(fn -> Pleroma.Web.RichMedia.Helpers.fetch_data_for_activity(activity) end)
+ end)
meta =
meta
@@ -207,6 +221,7 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do
# - Add announce to object
# - Set up notification
# - Stream out the announce
+ @impl true
def handle(%{data: %{"type" => "Announce"}} = object, meta) do
announced_object = Object.get_by_ap_id(object.data["object"])
user = User.get_cached_by_ap_id(object.data["actor"])
@@ -224,6 +239,7 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do
{:ok, object, meta}
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
@@ -234,6 +250,7 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do
# Tasks this handles:
# - Add reaction to object
# - Set up notification
+ @impl true
def handle(%{data: %{"type" => "EmojiReact"}} = object, meta) do
reacted_object = Object.get_by_ap_id(object.data["object"])
Utils.add_emoji_reaction_to_object(object, reacted_object)
@@ -250,6 +267,7 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do
# - Reduce the user note count
# - Reduce the reply count
# - Stream out the activity
+ @impl true
def handle(%{data: %{"type" => "Delete", "object" => deleted_object}} = object, meta) do
deleted_object =
Object.normalize(deleted_object, false) ||
@@ -271,12 +289,12 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do
MessageReference.delete_for_object(deleted_object)
- ActivityPub.stream_out(object)
- ActivityPub.stream_out_participations(deleted_object, user)
+ @ap_streamer.stream_out(object)
+ @ap_streamer.stream_out_participations(deleted_object, user)
:ok
else
{:actor, _} ->
- Logger.error("The object doesn't have an actor: #{inspect(deleted_object)}")
+ @logger.error("The object doesn't have an actor: #{inspect(deleted_object)}")
:no_object_actor
end
@@ -295,6 +313,7 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do
end
# Nothing to do
+ @impl true
def handle(object, meta) do
{:ok, object, meta}
end
@@ -312,7 +331,7 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do
{:ok, chat} = Chat.bump_or_create(user.id, other_user.ap_id)
{:ok, cm_ref} = MessageReference.create(chat, object, user.ap_id != actor.ap_id)
- Cachex.put(
+ @cachex.put(
:chat_message_id_idempotency_key_cache,
cm_ref.id,
meta[:idempotency_key]
@@ -439,6 +458,7 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do
|> Keyword.put(:notifications, notifications ++ existing)
end
+ @impl true
def handle_after_transaction(meta) do
meta
|> send_notifications()
diff --git a/lib/pleroma/web/activity_pub/side_effects/handling.ex b/lib/pleroma/web/activity_pub/side_effects/handling.ex
new file mode 100644
index 000000000..9d64c0e47
--- /dev/null
+++ b/lib/pleroma/web/activity_pub/side_effects/handling.ex
@@ -0,0 +1,8 @@
+# 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.SideEffects.Handling do
+ @callback handle(map(), keyword()) :: {:ok, map(), keyword()} | {:error, any()}
+ @callback handle_after_transaction(map()) :: map()
+end
diff --git a/lib/pleroma/web/activity_pub/transmogrifier.ex b/lib/pleroma/web/activity_pub/transmogrifier.ex
index 39c8f7e39..5499f8a08 100644
--- a/lib/pleroma/web/activity_pub/transmogrifier.ex
+++ b/lib/pleroma/web/activity_pub/transmogrifier.ex
@@ -252,6 +252,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
}
|> Maps.put_if_present("mediaType", media_type)
|> Maps.put_if_present("name", data["name"])
+ |> Maps.put_if_present("blurhash", data["blurhash"])
else
nil
end
@@ -918,7 +919,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
defp build_emoji_tag({name, url}) do
%{
- "icon" => %{"url" => url, "type" => "Image"},
+ "icon" => %{"url" => "#{URI.encode(url)}", "type" => "Image"},
"name" => ":" <> name <> ":",
"type" => "Emoji",
"updated" => "1970-01-01T00:00:00Z",
@@ -1007,7 +1008,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
def upgrade_user_from_ap_id(ap_id) do
with %User{local: false} = user <- User.get_cached_by_ap_id(ap_id),
- {:ok, data} <- ActivityPub.fetch_and_prepare_user_from_ap_id(ap_id, force_http: true),
+ {:ok, data} <- ActivityPub.fetch_and_prepare_user_from_ap_id(ap_id),
{:ok, user} <- update_user(user, data) do
TransmogrifierWorker.enqueue("user_upgrade", %{"user_id" => user.id})
{:ok, user}
diff --git a/lib/pleroma/web/activity_pub/utils.ex b/lib/pleroma/web/activity_pub/utils.ex
index 713b0ca1f..ea1c3a04a 100644
--- a/lib/pleroma/web/activity_pub/utils.ex
+++ b/lib/pleroma/web/activity_pub/utils.ex
@@ -175,7 +175,8 @@ defmodule Pleroma.Web.ActivityPub.Utils do
outgoing_blocks = Config.get([:activitypub, :outgoing_blocks])
with true <- Config.get!([:instance, :federating]),
- true <- type != "Block" || outgoing_blocks do
+ true <- type != "Block" || outgoing_blocks,
+ false <- Visibility.is_local_public?(activity) do
Pleroma.Web.Federator.publish(activity)
end
@@ -701,14 +702,30 @@ defmodule Pleroma.Web.ActivityPub.Utils do
def make_flag_data(_, _), do: %{}
- defp build_flag_object(%{account: account, statuses: statuses} = _) do
- [account.ap_id] ++ build_flag_object(%{statuses: statuses})
+ defp build_flag_object(%{account: account, statuses: statuses}) do
+ [account.ap_id | build_flag_object(%{statuses: statuses})]
end
defp build_flag_object(%{statuses: statuses}) do
Enum.map(statuses || [], &build_flag_object/1)
end
+ defp build_flag_object(%Activity{data: %{"id" => id}, object: %{data: data}}) do
+ activity_actor = User.get_by_ap_id(data["actor"])
+
+ %{
+ "type" => "Note",
+ "id" => id,
+ "content" => data["content"],
+ "published" => data["published"],
+ "actor" =>
+ AccountView.render(
+ "show.json",
+ %{user: activity_actor, skip_visibility_check: true}
+ )
+ }
+ end
+
defp build_flag_object(act) when is_map(act) or is_binary(act) do
id =
case act do
@@ -719,22 +736,14 @@ defmodule Pleroma.Web.ActivityPub.Utils do
case Activity.get_by_ap_id_with_object(id) do
%Activity{} = activity ->
- activity_actor = User.get_by_ap_id(activity.object.data["actor"])
-
- %{
- "type" => "Note",
- "id" => activity.data["id"],
- "content" => activity.object.data["content"],
- "published" => activity.object.data["published"],
- "actor" =>
- AccountView.render(
- "show.json",
- %{user: activity_actor, skip_visibility_check: true}
- )
- }
-
- _ ->
- %{"id" => id, "deleted" => true}
+ build_flag_object(activity)
+
+ nil ->
+ if activity = Activity.get_by_object_ap_id_with_object(id) do
+ build_flag_object(activity)
+ else
+ %{"id" => id, "deleted" => true}
+ end
end
end
diff --git a/lib/pleroma/web/activity_pub/views/user_view.ex b/lib/pleroma/web/activity_pub/views/user_view.ex
index 4dc45cde3..241224b57 100644
--- a/lib/pleroma/web/activity_pub/views/user_view.ex
+++ b/lib/pleroma/web/activity_pub/views/user_view.ex
@@ -110,8 +110,10 @@ defmodule Pleroma.Web.ActivityPub.UserView do
"endpoints" => endpoints,
"attachment" => fields,
"tag" => emoji_tags,
+ # Note: key name is indeed "discoverable" (not an error)
"discoverable" => user.is_discoverable,
- "capabilities" => capabilities
+ "capabilities" => capabilities,
+ "alsoKnownAs" => user.also_known_as
}
|> Map.merge(maybe_make_image(&User.avatar_url/2, "icon", user))
|> Map.merge(maybe_make_image(&User.banner_url/2, "image", user))
diff --git a/lib/pleroma/web/activity_pub/visibility.ex b/lib/pleroma/web/activity_pub/visibility.ex
index 76bd54a42..2cb5a2bd0 100644
--- a/lib/pleroma/web/activity_pub/visibility.ex
+++ b/lib/pleroma/web/activity_pub/visibility.ex
@@ -17,7 +17,19 @@ defmodule Pleroma.Web.ActivityPub.Visibility do
def is_public?(%Activity{data: %{"type" => "Move"}}), do: true
def is_public?(%Activity{data: data}), do: is_public?(data)
def is_public?(%{"directMessage" => true}), do: false
- def is_public?(data), do: Utils.label_in_message?(Pleroma.Constants.as_public(), data)
+
+ def is_public?(data) do
+ Utils.label_in_message?(Pleroma.Constants.as_public(), data) or
+ Utils.label_in_message?(Pleroma.Constants.as_local_public(), data)
+ end
+
+ def is_local_public?(%Object{data: data}), do: is_local_public?(data)
+ def is_local_public?(%Activity{data: data}), do: is_local_public?(data)
+
+ def is_local_public?(data) do
+ Utils.label_in_message?(Pleroma.Constants.as_local_public(), data) and
+ not Utils.label_in_message?(Pleroma.Constants.as_public(), data)
+ end
def is_private?(activity) do
with false <- is_public?(activity),
@@ -114,6 +126,9 @@ defmodule Pleroma.Web.ActivityPub.Visibility do
Pleroma.Constants.as_public() in cc ->
"unlisted"
+ Pleroma.Constants.as_local_public() in to ->
+ "local"
+
# this should use the sql for the object's activity
Enum.any?(to, &String.contains?(&1, "/followers")) ->
"private"