diff options
Diffstat (limited to 'lib/pleroma/web/activity_pub')
37 files changed, 614 insertions, 493 deletions
diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index 18368943d..8324ca22c 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -25,6 +25,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do alias Pleroma.Web.Streamer alias Pleroma.Web.WebFinger alias Pleroma.Workers.BackgroundWorker + alias Pleroma.Workers.PollWorker import Ecto.Query import Pleroma.Web.ActivityPub.Utils @@ -53,15 +54,18 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do {recipients, to, cc} end - defp check_actor_is_active(nil), do: true + defp check_actor_can_insert(%{"type" => "Delete"}), do: true + defp check_actor_can_insert(%{"type" => "Undo"}), do: true - defp check_actor_is_active(actor) when is_binary(actor) do + defp check_actor_can_insert(%{"actor" => actor}) when is_binary(actor) do case User.get_cached_by_ap_id(actor) do %User{is_active: true} -> true _ -> false end end + defp check_actor_can_insert(_), do: true + defp check_remote_limit(%{"object" => %{"content" => content}}) when not is_nil(content) do limit = Config.get([:instance, :remote_limit]) String.length(content) <= limit @@ -88,7 +92,7 @@ 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 Note] + @object_types ~w[ChatMessage Question Answer Audio Video Event Article Note Page] @impl true def persist(%{"type" => type} = object, meta) when type in @object_types do with {:ok, object} <- Object.create(object) do @@ -117,7 +121,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do def insert(map, local \\ true, fake \\ false, bypass_actor_check \\ false) when is_map(map) do with nil <- Activity.normalize(map), map <- lazy_put_activity_defaults(map, fake), - {_, true} <- {:actor_check, bypass_actor_check || check_actor_is_active(map["actor"])}, + {_, true} <- {:actor_check, bypass_actor_check || check_actor_can_insert(map)}, {_, true} <- {:remote_limit_pass, check_remote_limit(map)}, {:ok, map} <- MRF.filter(map), {recipients, _, _} = get_recipients(map), @@ -285,6 +289,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do {:quick_insert, false, activity} <- {:quick_insert, quick_insert?, activity}, {:ok, _actor} <- increase_note_count_if_public(actor, activity), _ <- notify_and_stream(activity), + :ok <- maybe_schedule_poll_notifications(activity), :ok <- maybe_federate(activity) do {:ok, activity} else @@ -299,6 +304,11 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do end end + defp maybe_schedule_poll_notifications(activity) do + PollWorker.schedule_poll_end(activity) + :ok + end + @spec listen(map()) :: {:ok, Activity.t()} | {:error, any()} def listen(%{to: to, actor: actor, context: context, object: object} = params) do additional = params[:additional] || %{} @@ -431,6 +441,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do |> maybe_preload_bookmarks(opts) |> maybe_set_thread_muted_field(opts) |> restrict_blocked(opts) + |> restrict_blockers_visibility(opts) |> restrict_recipients(recipients, opts[:user]) |> restrict_filtered(opts) |> where( @@ -1018,7 +1029,10 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do from( [activity, object: o] in query, + # You don't block the author where: fragment("not (? = ANY(?))", activity.actor, ^blocked_ap_ids), + + # You don't block any recipients, and didn't author the post where: fragment( "((not (? && ?)) or ? = ?)", @@ -1027,12 +1041,18 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do activity.actor, ^user.ap_id ), + + # You don't block the domain of any recipients, and didn't author the post where: fragment( - "recipients_contain_blocked_domains(?, ?) = false", + "(recipients_contain_blocked_domains(?, ?) = false) or ? = ?", activity.recipients, - ^domain_blocks + ^domain_blocks, + activity.actor, + ^user.ap_id ), + + # It's not a boost of a user you block where: fragment( "not (?->>'type' = 'Announce' and ?->'to' \\?| ?)", @@ -1040,6 +1060,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do activity.data, ^blocked_ap_ids ), + + # You don't block the author's domain, and also don't follow the author where: fragment( "(not (split_part(?, '/', 3) = ANY(?))) or ? = ANY(?)", @@ -1048,6 +1070,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do activity.actor, ^following_ap_ids ), + + # Same as above, but checks the Object where: fragment( "(not (split_part(?->>'actor', '/', 3) = ANY(?))) or (?->>'actor') = ANY(?)", @@ -1061,6 +1085,31 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do defp restrict_blocked(query, _), do: query + defp restrict_blockers_visibility(query, %{blocking_user: %User{} = user}) do + if Config.get([:activitypub, :blockers_visible]) == true do + query + else + blocker_ap_ids = User.incoming_relationships_ungrouped_ap_ids(user, [:block]) + + from( + activity in query, + # The author doesn't block you + where: fragment("not (? = ANY(?))", activity.actor, ^blocker_ap_ids), + + # It's not a boost of a user that blocks you + where: + fragment( + "not (?->>'type' = 'Announce' and ?->'to' \\?| ?)", + activity.data, + activity.data, + ^blocker_ap_ids + ) + ) + end + end + + defp restrict_blockers_visibility(query, _), do: query + defp restrict_unlisted(query, %{restrict_unlisted: true}) do from( activity in query, @@ -1287,6 +1336,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do |> restrict_state(opts) |> restrict_favorited_by(opts) |> restrict_blocked(restrict_blocked_opts) + |> restrict_blockers_visibility(opts) |> restrict_muted(restrict_muted_opts) |> restrict_filtered(opts) |> restrict_media(opts) @@ -1587,9 +1637,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do %User{} = old_user <- User.get_by_nickname(nickname), {_, false} <- {:ap_id_comparison, data[:ap_id] == old_user.ap_id} do Logger.info( - "Found an old user for #{nickname}, the old ap id is #{old_user.ap_id}, new one is #{ - data[:ap_id] - }, renaming." + "Found an old user for #{nickname}, the old ap id is #{old_user.ap_id}, new one is #{data[:ap_id]}, renaming." ) old_user diff --git a/lib/pleroma/web/activity_pub/activity_pub_controller.ex b/lib/pleroma/web/activity_pub/activity_pub_controller.ex index 5aa3b281a..4a19938f6 100644 --- a/lib/pleroma/web/activity_pub/activity_pub_controller.ex +++ b/lib/pleroma/web/activity_pub/activity_pub_controller.ex @@ -11,7 +11,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do alias Pleroma.Object.Fetcher alias Pleroma.User alias Pleroma.Web.ActivityPub.ActivityPub - alias Pleroma.Web.ActivityPub.Builder alias Pleroma.Web.ActivityPub.InternalFetchActor alias Pleroma.Web.ActivityPub.ObjectView alias Pleroma.Web.ActivityPub.Pipeline @@ -284,15 +283,29 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do json(conn, "ok") end + def inbox(%{assigns: %{valid_signature: false}} = conn, _params) do + conn + |> put_status(:bad_request) + |> json("Invalid HTTP Signature") + end + # POST /relay/inbox -or- POST /internal/fetch/inbox - def inbox(conn, params) do - if params["type"] == "Create" && FederatingPlug.federating?() do + def inbox(conn, %{"type" => "Create"} = params) do + if FederatingPlug.federating?() do post_inbox_relayed_create(conn, params) else - post_inbox_fallback(conn, params) + conn + |> put_status(:bad_request) + |> json("Not federating") end end + def inbox(conn, _params) do + conn + |> put_status(:bad_request) + |> json("error, missing HTTP Signature") + end + defp post_inbox_relayed_create(conn, params) do Logger.debug( "Signature missing or not from author, relayed Create message, fetching object from source" @@ -303,23 +316,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do json(conn, "ok") end - defp post_inbox_fallback(conn, params) do - headers = Enum.into(conn.req_headers, %{}) - - if headers["signature"] && params["actor"] && - String.contains?(headers["signature"], params["actor"]) do - Logger.debug( - "Signature validation error for: #{params["actor"]}, make sure you are forwarding the HTTP Host header!" - ) - - Logger.debug(inspect(conn.req_headers)) - end - - conn - |> put_status(:bad_request) - |> json(dgettext("errors", "error")) - end - defp represent_service_actor(%User{} = user, conn) do with {:ok, user} <- User.ensure_keys_present(user) do conn @@ -403,83 +399,90 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do |> json(err) end - defp handle_user_activity( - %User{} = user, - %{"type" => "Create", "object" => %{"type" => "Note"} = object} = params - ) do - content = if is_binary(object["content"]), do: object["content"], else: "" - name = if is_binary(object["name"]), do: object["name"], else: "" - summary = if is_binary(object["summary"]), do: object["summary"], else: "" - length = String.length(content <> name <> summary) + defp fix_user_message(%User{ap_id: actor}, %{"type" => "Create", "object" => object} = activity) + when is_map(object) do + length = + [object["content"], object["summary"], object["name"]] + |> Enum.filter(&is_binary(&1)) + |> Enum.join("") + |> String.length() - if length > Pleroma.Config.get([:instance, :limit]) do - {:error, dgettext("errors", "Note is over the character limit")} - else + limit = Pleroma.Config.get([:instance, :limit]) + + if length < limit do object = object - |> Map.merge(Map.take(params, ["to", "cc"])) - |> Map.put("attributedTo", user.ap_id) - |> Transmogrifier.fix_object() - - ActivityPub.create(%{ - to: params["to"], - actor: user, - context: object["context"], - object: object, - additional: Map.take(params, ["cc"]) - }) - end - end + |> Transmogrifier.strip_internal_fields() + |> Map.put("attributedTo", actor) + |> Map.put("actor", actor) + |> Map.put("id", Utils.generate_object_id()) - defp handle_user_activity(%User{} = user, %{"type" => "Delete"} = params) do - with %Object{} = object <- Object.normalize(params["object"], fetch: false), - true <- user.is_moderator || user.ap_id == object.data["actor"], - {:ok, delete_data, _} <- Builder.delete(user, object.data["id"]), - {:ok, delete, _} <- Pipeline.common_pipeline(delete_data, local: true) do - {:ok, delete} + {:ok, Map.put(activity, "object", object)} else - _ -> {:error, dgettext("errors", "Can't delete object")} + {:error, + dgettext( + "errors", + "Character limit (%{limit} characters) exceeded, contains %{length} characters", + limit: limit, + length: length + )} end end - defp handle_user_activity(%User{} = user, %{"type" => "Like"} = params) do - with %Object{} = object <- Object.normalize(params["object"], fetch: false), - {_, {:ok, like_object, meta}} <- {:build_object, Builder.like(user, object)}, - {_, {:ok, %Activity{} = activity, _meta}} <- - {:common_pipeline, - Pipeline.common_pipeline(like_object, Keyword.put(meta, :local, true))} do + defp fix_user_message( + %User{ap_id: actor} = user, + %{"type" => "Delete", "object" => object} = activity + ) do + with {_, %Object{data: object_data}} <- {:normalize, Object.normalize(object, fetch: false)}, + {_, true} <- {:permission, user.is_moderator || actor == object_data["actor"]} do {:ok, activity} else - _ -> {:error, dgettext("errors", "Can't like object")} + {:normalize, _} -> + {:error, "No such object found"} + + {:permission, _} -> + {:forbidden, "You can't delete this object"} end end - defp handle_user_activity(_, _) do - {:error, dgettext("errors", "Unhandled activity type")} + defp fix_user_message(%User{}, activity) do + {:ok, activity} end def update_outbox( - %{assigns: %{user: %User{nickname: nickname} = user}} = conn, + %{assigns: %{user: %User{nickname: nickname, ap_id: actor} = user}} = conn, %{"nickname" => nickname} = params ) do - actor = user.ap_id - params = params - |> Map.drop(["id"]) + |> Map.drop(["nickname"]) + |> Map.put("id", Utils.generate_activity_id()) |> Map.put("actor", actor) - |> Transmogrifier.fix_addressing() - with {:ok, %Activity{} = activity} <- handle_user_activity(user, params) do + with {:ok, params} <- fix_user_message(user, params), + {:ok, activity, _} <- Pipeline.common_pipeline(params, local: true), + %Activity{data: activity_data} <- Activity.normalize(activity) do conn |> put_status(:created) - |> put_resp_header("location", activity.data["id"]) - |> json(activity.data) + |> put_resp_header("location", activity_data["id"]) + |> json(activity_data) else + {:forbidden, message} -> + conn + |> put_status(:forbidden) + |> json(message) + {:error, message} -> conn |> put_status(:bad_request) |> json(message) + + e -> + Logger.warn(fn -> "AP C2S: #{inspect(e)}" end) + + conn + |> put_status(:bad_request) + |> json("Bad Request") end end diff --git a/lib/pleroma/web/activity_pub/builder.ex b/lib/pleroma/web/activity_pub/builder.ex index cde477710..647ccf432 100644 --- a/lib/pleroma/web/activity_pub/builder.ex +++ b/lib/pleroma/web/activity_pub/builder.ex @@ -15,6 +15,7 @@ defmodule Pleroma.Web.ActivityPub.Builder do alias Pleroma.Web.ActivityPub.Relay alias Pleroma.Web.ActivityPub.Utils alias Pleroma.Web.ActivityPub.Visibility + alias Pleroma.Web.CommonAPI.ActivityDraft require Pleroma.Constants @@ -125,6 +126,37 @@ defmodule Pleroma.Web.ActivityPub.Builder do |> Pleroma.Maps.put_if_present("context", context), []} end + @spec note(ActivityDraft.t()) :: {:ok, map(), keyword()} + def note(%ActivityDraft{} = draft) do + data = + %{ + "type" => "Note", + "to" => draft.to, + "cc" => draft.cc, + "content" => draft.content_html, + "summary" => draft.summary, + "sensitive" => draft.sensitive, + "context" => draft.context, + "attachment" => draft.attachments, + "actor" => draft.user.ap_id, + "tag" => Keyword.values(draft.tags) |> Enum.uniq() + } + |> add_in_reply_to(draft.in_reply_to) + |> Map.merge(draft.extra) + + {:ok, data, []} + end + + defp add_in_reply_to(object, nil), do: object + + defp add_in_reply_to(object, in_reply_to) do + with %Object{} = in_reply_to_object <- Object.normalize(in_reply_to, fetch: false) do + Map.put(object, "inReplyTo", in_reply_to_object.data["id"]) + else + _ -> object + end + end + def chat_message(actor, recipient, content, opts \\ []) do basic = %{ "id" => Utils.generate_object_id(), diff --git a/lib/pleroma/web/activity_pub/mrf.ex b/lib/pleroma/web/activity_pub/mrf.ex index ac00fa54b..bd6f6777f 100644 --- a/lib/pleroma/web/activity_pub/mrf.ex +++ b/lib/pleroma/web/activity_pub/mrf.ex @@ -21,7 +21,7 @@ defmodule Pleroma.Web.ActivityPub.MRF do 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} + suggestions: {:list_behaviour_implementations, Pleroma.Web.ActivityPub.MRF.Policy} }, %{ key: :transparency, @@ -33,9 +33,11 @@ defmodule Pleroma.Web.ActivityPub.MRF do %{ key: :transparency_exclusions, label: "MRF transparency exclusions", - type: {:list, :string}, + type: {:list, :tuple}, + key_placeholder: "instance", + value_placeholder: "reason", description: - "Exclude specific instance names from MRF transparency. The use of the exclusions feature will be disclosed in nodeinfo as a boolean value.", + "Exclude specific instance names from MRF transparency. The use of the exclusions feature will be disclosed in nodeinfo as a boolean value. You can also provide a reason for excluding these instance names. The instances and reasons won't be publicly disclosed.", suggestions: [ "exclusion.com" ] @@ -100,6 +102,11 @@ defmodule Pleroma.Web.ActivityPub.MRF do Enum.any?(domains, fn domain -> Regex.match?(domain, host) end) end + @spec instance_list_from_tuples([{String.t(), String.t()}]) :: [String.t()] + def instance_list_from_tuples(list) do + Enum.map(list, fn {instance, _} -> instance end) + end + def describe(policies) do {:ok, policy_configs} = policies @@ -150,9 +157,7 @@ defmodule Pleroma.Web.ActivityPub.MRF do [description | acc] else Logger.warn( - "#{policy} config description doesn't have one or all required keys #{ - inspect(@required_description_keys) - }" + "#{policy} config description doesn't have one or all required keys #{inspect(@required_description_keys)}" ) acc diff --git a/lib/pleroma/web/activity_pub/mrf/keyword_policy.ex b/lib/pleroma/web/activity_pub/mrf/keyword_policy.ex index 646008dd9..1383fa757 100644 --- a/lib/pleroma/web/activity_pub/mrf/keyword_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/keyword_policy.ex @@ -159,6 +159,8 @@ defmodule Pleroma.Web.ActivityPub.MRF.KeywordPolicy do %{ key: :replace, type: {:list, :tuple}, + key_placeholder: "instance", + value_placeholder: "reason", description: """ **Pattern**: a string or [Regex](https://hexdocs.pm/elixir/Regex.html) in the format of `~r/PATTERN/`. 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 9a211fd44..02c9b18ed 100644 --- a/lib/pleroma/web/activity_pub/mrf/object_age_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/object_age_policy.ex @@ -49,6 +49,8 @@ defmodule Pleroma.Web.ActivityPub.MRF.ObjectAgePolicy do message |> Map.put("to", to) |> Map.put("cc", cc) + |> Kernel.put_in(["object", "to"], to) + |> Kernel.put_in(["object", "cc"], cc) {:ok, message} else @@ -70,6 +72,8 @@ defmodule Pleroma.Web.ActivityPub.MRF.ObjectAgePolicy do message |> Map.put("to", to) |> Map.put("cc", cc) + |> Kernel.put_in(["object", "to"], to) + |> Kernel.put_in(["object", "cc"], cc) {:ok, message} else @@ -82,7 +86,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.ObjectAgePolicy do end @impl true - def filter(%{"type" => "Create", "published" => _} = message) do + def filter(%{"type" => "Create", "object" => %{"published" => _}} = message) do with actions <- Config.get([:mrf_object_age, :actions]), {:reject, _} <- check_date(message), {:ok, message} <- check_reject(message, actions), 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 b9d3e52c7..dbb7ca0df 100644 --- a/lib/pleroma/web/activity_pub/mrf/reject_non_public.ex +++ b/lib/pleroma/web/activity_pub/mrf/reject_non_public.ex @@ -47,7 +47,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.RejectNonPublic do @impl true def describe, - do: {:ok, %{mrf_rejectnonpublic: Config.get(:mrf_rejectnonpublic) |> Enum.into(%{})}} + do: {:ok, %{mrf_rejectnonpublic: Config.get(:mrf_rejectnonpublic) |> Map.new()}} @impl true def config_description do diff --git a/lib/pleroma/web/activity_pub/mrf/simple_policy.ex b/lib/pleroma/web/activity_pub/mrf/simple_policy.ex index 30562ac08..c631cc85f 100644 --- a/lib/pleroma/web/activity_pub/mrf/simple_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/simple_policy.ex @@ -15,7 +15,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do defp check_accept(%{host: actor_host} = _actor_info, object) do accepts = - Config.get([:mrf_simple, :accept]) + instance_list(:accept) |> MRF.subdomains_regex() cond do @@ -28,7 +28,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do defp check_reject(%{host: actor_host} = _actor_info, object) do rejects = - Config.get([:mrf_simple, :reject]) + instance_list(:reject) |> MRF.subdomains_regex() if MRF.subdomain_match?(rejects, actor_host) do @@ -44,7 +44,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do ) when length(child_attachment) > 0 do media_removal = - Config.get([:mrf_simple, :media_removal]) + instance_list(:media_removal) |> MRF.subdomains_regex() object = @@ -68,7 +68,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do } = object ) do media_nsfw = - Config.get([:mrf_simple, :media_nsfw]) + instance_list(:media_nsfw) |> MRF.subdomains_regex() object = @@ -85,7 +85,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do defp check_ftl_removal(%{host: actor_host} = _actor_info, object) do timeline_removal = - Config.get([:mrf_simple, :federated_timeline_removal]) + instance_list(:federated_timeline_removal) |> MRF.subdomains_regex() object = @@ -112,7 +112,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do defp check_followers_only(%{host: actor_host} = _actor_info, object) do followers_only = - Config.get([:mrf_simple, :followers_only]) + instance_list(:followers_only) |> MRF.subdomains_regex() object = @@ -137,7 +137,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do defp check_report_removal(%{host: actor_host} = _actor_info, %{"type" => "Flag"} = object) do report_removal = - Config.get([:mrf_simple, :report_removal]) + instance_list(:report_removal) |> MRF.subdomains_regex() if MRF.subdomain_match?(report_removal, actor_host) do @@ -151,7 +151,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do defp check_avatar_removal(%{host: actor_host} = _actor_info, %{"icon" => _icon} = object) do avatar_removal = - Config.get([:mrf_simple, :avatar_removal]) + instance_list(:avatar_removal) |> MRF.subdomains_regex() if MRF.subdomain_match?(avatar_removal, actor_host) do @@ -165,7 +165,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do defp check_banner_removal(%{host: actor_host} = _actor_info, %{"image" => _image} = object) do banner_removal = - Config.get([:mrf_simple, :banner_removal]) + instance_list(:banner_removal) |> MRF.subdomains_regex() if MRF.subdomain_match?(banner_removal, actor_host) do @@ -185,12 +185,17 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do defp check_object(object), do: {:ok, object} + defp instance_list(config_key) do + Config.get([:mrf_simple, config_key]) + |> MRF.instance_list_from_tuples() + end + @impl true def filter(%{"type" => "Delete", "actor" => actor} = object) do %{host: actor_host} = URI.parse(actor) reject_deletes = - Config.get([:mrf_simple, :reject_deletes]) + instance_list(:reject_deletes) |> MRF.subdomains_regex() if MRF.subdomain_match?(reject_deletes, actor_host) do @@ -253,14 +258,42 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do @impl true def describe do - exclusions = Config.get([:mrf, :transparency_exclusions]) + exclusions = Config.get([:mrf, :transparency_exclusions]) |> MRF.instance_list_from_tuples() - mrf_simple = + mrf_simple_excluded = Config.get(:mrf_simple) - |> Enum.map(fn {k, v} -> {k, Enum.reject(v, fn v -> v in exclusions end)} end) - |> Enum.into(%{}) + |> Enum.map(fn {rule, instances} -> + {rule, Enum.reject(instances, fn {host, _} -> host in exclusions end)} + end) - {:ok, %{mrf_simple: mrf_simple}} + mrf_simple = + mrf_simple_excluded + |> Enum.map(fn {rule, instances} -> + {rule, Enum.map(instances, fn {host, _} -> host end)} + end) + |> Map.new() + + # This is for backwards compatibility. We originally didn't sent + # extra info like a reason why an instance was rejected/quarantined/etc. + # Because we didn't want to break backwards compatibility it was decided + # to add an extra "info" key. + mrf_simple_info = + mrf_simple_excluded + |> Enum.map(fn {rule, instances} -> + {rule, Enum.reject(instances, fn {_, reason} -> reason == "" end)} + end) + |> Enum.reject(fn {_, instances} -> instances == [] end) + |> Enum.map(fn {rule, instances} -> + instances = + instances + |> Enum.map(fn {host, reason} -> {host, %{"reason" => reason}} end) + |> Map.new() + + {rule, instances} + end) + |> Map.new() + + {:ok, %{mrf_simple: mrf_simple, mrf_simple_info: mrf_simple_info}} end @impl true @@ -270,70 +303,67 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do 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"] - } - ] + children: + [ + %{ + key: :media_removal, + description: + "List of instances to strip media attachments from and the reason for doing so" + }, + %{ + key: :media_nsfw, + label: "Media NSFW", + description: + "List of instances to tag all media as NSFW (sensitive) from and the reason for doing so" + }, + %{ + key: :federated_timeline_removal, + description: + "List of instances to remove from the Federated (aka The Whole Known Network) Timeline and the reason for doing so" + }, + %{ + key: :reject, + description: + "List of instances to reject activities from (except deletes) and the reason for doing so" + }, + %{ + key: :accept, + description: + "List of instances to only accept activities from (except deletes) and the reason for doing so" + }, + %{ + key: :followers_only, + description: + "Force posts from the given instances to be visible by followers only and the reason for doing so" + }, + %{ + key: :report_removal, + description: "List of instances to reject reports from and the reason for doing so" + }, + %{ + key: :avatar_removal, + description: "List of instances to strip avatars from and the reason for doing so" + }, + %{ + key: :banner_removal, + description: "List of instances to strip banners from and the reason for doing so" + }, + %{ + key: :reject_deletes, + description: "List of instances to reject deletions from and the reason for doing so" + } + ] + |> Enum.map(fn setting -> + Map.merge( + setting, + %{ + type: {:list, :tuple}, + key_placeholder: "instance", + value_placeholder: "reason", + suggestions: [{"example.com", "Some reason"}, {"*.example.com", "Another reason"}] + } + ) + end) } end end diff --git a/lib/pleroma/web/activity_pub/mrf/steal_emoji_policy.ex b/lib/pleroma/web/activity_pub/mrf/steal_emoji_policy.ex index c28f14a41..0dd415732 100644 --- a/lib/pleroma/web/activity_pub/mrf/steal_emoji_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/steal_emoji_policy.ex @@ -38,9 +38,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.StealEmojiPolicy do end else Logger.debug( - "MRF.StealEmojiPolicy: :#{shortcode}: at #{url} (#{byte_size(response.body)} B) over size limit (#{ - size_limit - } B)" + "MRF.StealEmojiPolicy: :#{shortcode}: at #{url} (#{byte_size(response.body)} B) over size limit (#{size_limit} B)" ) nil @@ -93,6 +91,51 @@ defmodule Pleroma.Web.ActivityPub.MRF.StealEmojiPolicy do def filter(message), do: {:ok, message} @impl true + @spec config_description :: %{ + children: [ + %{ + description: <<_::272, _::_*256>>, + key: :hosts | :rejected_shortcodes | :size_limit, + suggestions: [any(), ...], + type: {:list, :string} | {:list, :string} | :integer + }, + ... + ], + description: <<_::448>>, + key: :mrf_steal_emoji, + label: <<_::80>>, + related_policy: <<_::352>> + } + def config_description do + %{ + key: :mrf_steal_emoji, + related_policy: "Pleroma.Web.ActivityPub.MRF.StealEmojiPolicy", + label: "MRF Emojis", + description: "Steals emojis from selected instances when it sees them.", + children: [ + %{ + key: :hosts, + type: {:list, :string}, + description: "List of hosts to steal emojis from", + suggestions: [""] + }, + %{ + key: :rejected_shortcodes, + type: {:list, :string}, + description: "Regex-list of shortcodes to reject", + suggestions: [""] + }, + %{ + key: :size_limit, + type: :integer, + description: "File size limit (in bytes), checked before an emoji is saved to the disk", + suggestions: ["100000"] + } + ] + } + end + + @impl true def describe do {:ok, %{}} end diff --git a/lib/pleroma/web/activity_pub/mrf/subchain_policy.ex b/lib/pleroma/web/activity_pub/mrf/subchain_policy.ex index f84d7cc71..11a36aca1 100644 --- a/lib/pleroma/web/activity_pub/mrf/subchain_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/subchain_policy.ex @@ -23,9 +23,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SubchainPolicy do def filter(%{"actor" => actor} = message) do with {:ok, match, subchain} <- lookup_subchain(actor) do Logger.debug( - "[SubchainPolicy] Matched #{actor} against #{inspect(match)} with subchain #{ - inspect(subchain) - }" + "[SubchainPolicy] Matched #{actor} against #{inspect(match)} with subchain #{inspect(subchain)}" ) MRF.filter(subchain, message) 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 1bcb3688b..52fb02a84 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 @@ -37,7 +37,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.UserAllowListPolicy do def describe do mrf_user_allowlist = Config.get([:mrf_user_allowlist], []) - |> Enum.into(%{}, fn {k, v} -> {k, length(v)} end) + |> Map.new(fn {k, v} -> {k, length(v)} end) {:ok, %{mrf_user_allowlist: mrf_user_allowlist}} end diff --git a/lib/pleroma/web/activity_pub/mrf/vocabulary_policy.ex b/lib/pleroma/web/activity_pub/mrf/vocabulary_policy.ex index 20f57f609..602e10b44 100644 --- a/lib/pleroma/web/activity_pub/mrf/vocabulary_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/vocabulary_policy.ex @@ -39,7 +39,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.VocabularyPolicy do @impl true def describe, - do: {:ok, %{mrf_vocabulary: Pleroma.Config.get(:mrf_vocabulary) |> Enum.into(%{})}} + do: {:ok, %{mrf_vocabulary: Pleroma.Config.get(:mrf_vocabulary) |> Map.new()}} @impl true def config_description do diff --git a/lib/pleroma/web/activity_pub/object_validator.ex b/lib/pleroma/web/activity_pub/object_validator.ex index 248a12a36..187cd0cfd 100644 --- a/lib/pleroma/web/activity_pub/object_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validator.ex @@ -20,7 +20,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidator do alias Pleroma.Web.ActivityPub.ObjectValidators.AddRemoveValidator alias Pleroma.Web.ActivityPub.ObjectValidators.AnnounceValidator alias Pleroma.Web.ActivityPub.ObjectValidators.AnswerValidator - alias Pleroma.Web.ActivityPub.ObjectValidators.ArticleNoteValidator + alias Pleroma.Web.ActivityPub.ObjectValidators.ArticleNotePageValidator alias Pleroma.Web.ActivityPub.ObjectValidators.AudioVideoValidator alias Pleroma.Web.ActivityPub.ObjectValidators.BlockValidator alias Pleroma.Web.ActivityPub.ObjectValidators.ChatMessageValidator @@ -102,7 +102,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidator do %{"type" => "Create", "object" => %{"type" => objtype} = object} = create_activity, meta ) - when objtype in ~w[Question Answer Audio Video Event Article Note] do + when objtype in ~w[Question Answer Audio Video Event Article Note Page] do with {:ok, object_data} <- cast_and_apply(object), meta = Keyword.put(meta, :object_data, object_data |> stringify_keys), {:ok, create_activity} <- @@ -115,15 +115,16 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidator do end def validate(%{"type" => type} = object, meta) - when type in ~w[Event Question Audio Video Article Note] do + when type in ~w[Event Question Audio Video Article Note Page] do validator = case type do "Event" -> EventValidator "Question" -> QuestionValidator "Audio" -> AudioVideoValidator "Video" -> AudioVideoValidator - "Article" -> ArticleNoteValidator - "Note" -> ArticleNoteValidator + "Article" -> ArticleNotePageValidator + "Note" -> ArticleNotePageValidator + "Page" -> ArticleNotePageValidator end with {:ok, object} <- @@ -175,6 +176,8 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidator do end end + def validate(o, m), do: {:error, {:validator_not_set, {o, m}}} + def cast_and_apply(%{"type" => "ChatMessage"} = object) do ChatMessageValidator.cast_and_apply(object) end @@ -195,8 +198,8 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidator do EventValidator.cast_and_apply(object) end - def cast_and_apply(%{"type" => type} = object) when type in ~w[Article Note] do - ArticleNoteValidator.cast_and_apply(object) + def cast_and_apply(%{"type" => type} = object) when type in ~w[Article Note Page] do + ArticleNotePageValidator.cast_and_apply(object) end def cast_and_apply(o), do: {:error, {:validator_not_set, o}} @@ -210,6 +213,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidator do def stringify_keys(object) when is_map(object) do object + |> Enum.filter(fn {_, v} -> v != nil end) |> Map.new(fn {key, val} -> {to_string(key), stringify_keys(val)} end) end diff --git a/lib/pleroma/web/activity_pub/object_validators/accept_reject_validator.ex b/lib/pleroma/web/activity_pub/object_validators/accept_reject_validator.ex index b577a1044..7c3c8d0fa 100644 --- a/lib/pleroma/web/activity_pub/object_validators/accept_reject_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validators/accept_reject_validator.ex @@ -6,7 +6,6 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.AcceptRejectValidator do use Ecto.Schema alias Pleroma.Activity - alias Pleroma.EctoType.ActivityPub.ObjectValidators import Ecto.Changeset import Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations @@ -14,12 +13,13 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.AcceptRejectValidator do @primary_key false embedded_schema do - field(:id, ObjectValidators.ObjectID, primary_key: true) - field(:type, :string) - field(:object, ObjectValidators.ObjectID) - field(:actor, ObjectValidators.ObjectID) - field(:to, ObjectValidators.Recipients, default: []) - field(:cc, ObjectValidators.Recipients, default: []) + quote do + unquote do + import Elixir.Pleroma.Web.ActivityPub.ObjectValidators.CommonFields + message_fields() + activity_fields() + end + end end def cast_data(data) do diff --git a/lib/pleroma/web/activity_pub/object_validators/add_remove_validator.ex b/lib/pleroma/web/activity_pub/object_validators/add_remove_validator.ex index f885aabe4..fc482c9c0 100644 --- a/lib/pleroma/web/activity_pub/object_validators/add_remove_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validators/add_remove_validator.ex @@ -10,19 +10,20 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.AddRemoveValidator do require Pleroma.Constants - alias Pleroma.EctoType.ActivityPub.ObjectValidators alias Pleroma.User @primary_key false embedded_schema do - field(:id, ObjectValidators.ObjectID, primary_key: true) field(:target) - field(:object, ObjectValidators.ObjectID) - field(:actor, ObjectValidators.ObjectID) - field(:type) - field(:to, ObjectValidators.Recipients, default: []) - field(:cc, ObjectValidators.Recipients, default: []) + + quote do + unquote do + import Elixir.Pleroma.Web.ActivityPub.ObjectValidators.CommonFields + message_fields() + activity_fields() + end + end end def cast_and_validate(data) do 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 4db76f387..a7f2f6673 100644 --- a/lib/pleroma/web/activity_pub/object_validators/announce_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validators/announce_validator.ex @@ -20,13 +20,15 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.AnnounceValidator do @primary_key false embedded_schema do - field(:id, ObjectValidators.ObjectID, primary_key: true) - field(:type, :string) - field(:object, ObjectValidators.ObjectID) - field(:actor, ObjectValidators.ObjectID) + quote do + unquote do + import Elixir.Pleroma.Web.ActivityPub.ObjectValidators.CommonFields + message_fields() + activity_fields() + end + end + field(:context, :string) - field(:to, ObjectValidators.Recipients, default: []) - field(:cc, ObjectValidators.Recipients, default: []) field(:published, ObjectValidators.DateTime) end diff --git a/lib/pleroma/web/activity_pub/object_validators/answer_validator.ex b/lib/pleroma/web/activity_pub/object_validators/answer_validator.ex index 3451e1ff8..4325e44f7 100644 --- a/lib/pleroma/web/activity_pub/object_validators/answer_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validators/answer_validator.ex @@ -15,12 +15,13 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.AnswerValidator do @derive Jason.Encoder embedded_schema do - field(:id, ObjectValidators.ObjectID, primary_key: true) - field(:to, ObjectValidators.Recipients, default: []) - field(:cc, ObjectValidators.Recipients, default: []) - field(:bto, ObjectValidators.Recipients, default: []) - field(:bcc, ObjectValidators.Recipients, default: []) - field(:type, :string) + quote do + unquote do + import Elixir.Pleroma.Web.ActivityPub.ObjectValidators.CommonFields + message_fields() + end + end + field(:name, :string) field(:inReplyTo, ObjectValidators.ObjectID) field(:attributedTo, ObjectValidators.ObjectID) diff --git a/lib/pleroma/web/activity_pub/object_validators/article_note_validator.ex b/lib/pleroma/web/activity_pub/object_validators/article_note_page_validator.ex index 193f85f49..0aa249c4c 100644 --- a/lib/pleroma/web/activity_pub/object_validators/article_note_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validators/article_note_page_validator.ex @@ -2,14 +2,12 @@ # Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> # SPDX-License-Identifier: AGPL-3.0-only -defmodule Pleroma.Web.ActivityPub.ObjectValidators.ArticleNoteValidator do +defmodule Pleroma.Web.ActivityPub.ObjectValidators.ArticleNotePageValidator do use Ecto.Schema alias Pleroma.EctoType.ActivityPub.ObjectValidators - alias Pleroma.Web.ActivityPub.ObjectValidators.AttachmentValidator alias Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes alias Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations - alias Pleroma.Web.ActivityPub.ObjectValidators.TagValidator alias Pleroma.Web.ActivityPub.Transmogrifier import Ecto.Changeset @@ -18,38 +16,14 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.ArticleNoteValidator do @derive Jason.Encoder embedded_schema do - field(:id, ObjectValidators.ObjectID, primary_key: true) - field(:to, ObjectValidators.Recipients, default: []) - field(:cc, ObjectValidators.Recipients, default: []) - field(:bto, ObjectValidators.Recipients, default: []) - field(:bcc, ObjectValidators.Recipients, default: []) - embeds_many(:tag, TagValidator) - field(:type, :string) - - field(:name, :string) - field(:summary, :string) - field(:content, :string) - - field(:context, :string) - # short identifier for PleromaFE to group statuses by context - field(:context_id, :integer) - - # TODO: Remove actor on objects - field(:actor, ObjectValidators.ObjectID) - - field(:attributedTo, ObjectValidators.ObjectID) - field(:published, ObjectValidators.DateTime) - field(:emoji, ObjectValidators.Emoji, default: %{}) - field(:sensitive, :boolean, default: false) - embeds_many(:attachment, AttachmentValidator) - field(:replies_count, :integer, default: 0) - field(:like_count, :integer, default: 0) - field(:announcement_count, :integer, default: 0) - field(:inReplyTo, ObjectValidators.ObjectID) - field(:url, ObjectValidators.Uri) - - field(:likes, {:array, ObjectValidators.ObjectID}, default: []) - field(:announcements, {:array, ObjectValidators.ObjectID}, default: []) + quote do + unquote do + import Elixir.Pleroma.Web.ActivityPub.ObjectValidators.CommonFields + message_fields() + object_fields() + status_object_fields() + end + end field(:replies, {:array, ObjectValidators.ObjectID}, default: []) end @@ -113,7 +87,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.ArticleNoteValidator do defp validate_data(data_cng) do data_cng - |> validate_inclusion(:type, ["Article", "Note"]) + |> validate_inclusion(:type, ["Article", "Note", "Page"]) |> validate_required([:id, :actor, :attributedTo, :type, :context, :context_id]) |> CommonValidations.validate_any_presence([:cc, :to]) |> CommonValidations.validate_fields_match([:actor, :attributedTo]) 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 837787b9f..59fef42d6 100644 --- a/lib/pleroma/web/activity_pub/object_validators/attachment_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validators/attachment_validator.ex @@ -68,12 +68,14 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.AttachmentValidator do end end - defp handle_href(href, mediaType) do + defp handle_href(href, mediaType, data) do [ %{ "href" => href, "type" => "Link", - "mediaType" => mediaType + "mediaType" => mediaType, + "width" => data["width"], + "height" => data["height"] } ] end @@ -81,10 +83,10 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.AttachmentValidator do defp fix_url(data) do cond do is_binary(data["url"]) -> - Map.put(data, "url", handle_href(data["url"], data["mediaType"])) + Map.put(data, "url", handle_href(data["url"], data["mediaType"], data)) is_binary(data["href"]) and data["url"] == nil -> - Map.put(data, "url", handle_href(data["href"], data["mediaType"])) + Map.put(data, "url", handle_href(data["href"], data["mediaType"], data)) true -> data diff --git a/lib/pleroma/web/activity_pub/object_validators/audio_video_validator.ex b/lib/pleroma/web/activity_pub/object_validators/audio_video_validator.ex index 572687deb..331ec9050 100644 --- a/lib/pleroma/web/activity_pub/object_validators/audio_video_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validators/audio_video_validator.ex @@ -5,11 +5,8 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.AudioVideoValidator do use Ecto.Schema - alias Pleroma.EctoType.ActivityPub.ObjectValidators - alias Pleroma.Web.ActivityPub.ObjectValidators.AttachmentValidator alias Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes alias Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations - alias Pleroma.Web.ActivityPub.ObjectValidators.TagValidator alias Pleroma.Web.ActivityPub.Transmogrifier import Ecto.Changeset @@ -18,38 +15,14 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.AudioVideoValidator do @derive Jason.Encoder embedded_schema do - field(:id, ObjectValidators.ObjectID, primary_key: true) - field(:to, ObjectValidators.Recipients, default: []) - field(:cc, ObjectValidators.Recipients, default: []) - field(:bto, ObjectValidators.Recipients, default: []) - field(:bcc, ObjectValidators.Recipients, default: []) - embeds_many(:tag, TagValidator) - field(:type, :string) - - field(:name, :string) - field(:summary, :string) - field(:content, :string) - - field(:context, :string) - # short identifier for PleromaFE to group statuses by context - field(:context_id, :integer) - - # TODO: Remove actor on objects - field(:actor, ObjectValidators.ObjectID) - - field(:attributedTo, ObjectValidators.ObjectID) - field(:published, ObjectValidators.DateTime) - field(:emoji, ObjectValidators.Emoji, default: %{}) - field(:sensitive, :boolean, default: false) - embeds_many(:attachment, AttachmentValidator) - field(:replies_count, :integer, default: 0) - field(:like_count, :integer, default: 0) - field(:announcement_count, :integer, default: 0) - field(:inReplyTo, ObjectValidators.ObjectID) - field(:url, ObjectValidators.Uri) - - field(:likes, {:array, ObjectValidators.ObjectID}, default: []) - field(:announcements, {:array, ObjectValidators.ObjectID}, default: []) + quote do + unquote do + import Elixir.Pleroma.Web.ActivityPub.ObjectValidators.CommonFields + message_fields() + object_fields() + status_object_fields() + end + end end def cast_and_apply(data) do diff --git a/lib/pleroma/web/activity_pub/object_validators/block_validator.ex b/lib/pleroma/web/activity_pub/object_validators/block_validator.ex index 88948135f..400e5e278 100644 --- a/lib/pleroma/web/activity_pub/object_validators/block_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validators/block_validator.ex @@ -5,20 +5,21 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.BlockValidator do use Ecto.Schema - alias Pleroma.EctoType.ActivityPub.ObjectValidators + alias Elixir.Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations import Ecto.Changeset - import Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations @primary_key false + @derive Jason.Encoder embedded_schema do - field(:id, ObjectValidators.ObjectID, primary_key: true) - field(:type, :string) - field(:actor, ObjectValidators.ObjectID) - field(:to, ObjectValidators.Recipients, default: []) - field(:cc, ObjectValidators.Recipients, default: []) - field(:object, ObjectValidators.ObjectID) + quote do + unquote do + import Elixir.Pleroma.Web.ActivityPub.ObjectValidators.CommonFields + message_fields() + activity_fields() + end + end end def cast_data(data) do @@ -30,8 +31,8 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.BlockValidator do cng |> validate_required([:id, :type, :actor, :to, :cc, :object]) |> validate_inclusion(:type, ["Block"]) - |> validate_actor_presence() - |> validate_actor_presence(field_name: :object) + |> CommonValidations.validate_actor_presence() + |> CommonValidations.validate_actor_presence(field_name: :object) end def cast_and_validate(data) do diff --git a/lib/pleroma/web/activity_pub/object_validators/common_fields.ex b/lib/pleroma/web/activity_pub/object_validators/common_fields.ex new file mode 100644 index 000000000..872f80ec3 --- /dev/null +++ b/lib/pleroma/web/activity_pub/object_validators/common_fields.ex @@ -0,0 +1,68 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ActivityPub.ObjectValidators.CommonFields do + alias Pleroma.EctoType.ActivityPub.ObjectValidators + alias Pleroma.Web.ActivityPub.ObjectValidators.AttachmentValidator + alias Pleroma.Web.ActivityPub.ObjectValidators.TagValidator + + # Activities and Objects, except (Create)ChatMessage + defmacro message_fields do + quote bind_quoted: binding() do + field(:type, :string) + field(:id, ObjectValidators.ObjectID, primary_key: true) + + field(:to, ObjectValidators.Recipients, default: []) + field(:cc, ObjectValidators.Recipients, default: []) + field(:bto, ObjectValidators.Recipients, default: []) + field(:bcc, ObjectValidators.Recipients, default: []) + end + end + + defmacro activity_fields do + quote bind_quoted: binding() do + field(:object, ObjectValidators.ObjectID) + field(:actor, ObjectValidators.ObjectID) + end + end + + # All objects except Answer and CHatMessage + defmacro object_fields do + quote bind_quoted: binding() do + field(:content, :string) + + field(:published, ObjectValidators.DateTime) + field(:emoji, ObjectValidators.Emoji, default: %{}) + embeds_many(:attachment, AttachmentValidator) + end + end + + # Basically objects that aren't ChatMessage and Answer + defmacro status_object_fields do + quote bind_quoted: binding() do + # TODO: Remove actor on objects + field(:actor, ObjectValidators.ObjectID) + field(:attributedTo, ObjectValidators.ObjectID) + + embeds_many(:tag, TagValidator) + + field(:name, :string) + field(:summary, :string) + + field(:context, :string) + # short identifier for PleromaFE to group statuses by context + field(:context_id, :integer) + + field(:sensitive, :boolean, default: false) + field(:replies_count, :integer, default: 0) + field(:like_count, :integer, default: 0) + field(:announcement_count, :integer, default: 0) + field(:inReplyTo, ObjectValidators.ObjectID) + field(:url, ObjectValidators.Uri) + + field(:likes, {:array, ObjectValidators.ObjectID}, default: []) + field(:announcements, {:array, ObjectValidators.ObjectID}, default: []) + end + end +end diff --git a/lib/pleroma/web/activity_pub/object_validators/create_chat_message_validator.ex b/lib/pleroma/web/activity_pub/object_validators/create_chat_message_validator.ex index 7a31a99bf..6551f64ca 100644 --- a/lib/pleroma/web/activity_pub/object_validators/create_chat_message_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validators/create_chat_message_validator.ex @@ -17,11 +17,16 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.CreateChatMessageValidator do @primary_key false embedded_schema do + quote do + unquote do + import Elixir.Pleroma.Web.ActivityPub.ObjectValidators.CommonFields + activity_fields() + end + end + field(:id, ObjectValidators.ObjectID, primary_key: true) - field(:actor, ObjectValidators.ObjectID) field(:type, :string) field(:to, ObjectValidators.Recipients, default: []) - field(:object, ObjectValidators.ObjectID) end def cast_and_apply(data) do diff --git a/lib/pleroma/web/activity_pub/object_validators/create_generic_validator.ex b/lib/pleroma/web/activity_pub/object_validators/create_generic_validator.ex index d2de53049..803b5d5a1 100644 --- a/lib/pleroma/web/activity_pub/object_validators/create_generic_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validators/create_generic_validator.ex @@ -20,14 +20,14 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.CreateGenericValidator do @primary_key false embedded_schema do - field(:id, ObjectValidators.ObjectID, primary_key: true) - field(:actor, ObjectValidators.ObjectID) - field(:type, :string) - field(:to, ObjectValidators.Recipients, default: []) - field(:cc, ObjectValidators.Recipients, default: []) - field(:bto, ObjectValidators.Recipients, default: []) - field(:bcc, ObjectValidators.Recipients, default: []) - field(:object, ObjectValidators.ObjectID) + quote do + unquote do + import Elixir.Pleroma.Web.ActivityPub.ObjectValidators.CommonFields + message_fields() + activity_fields() + end + end + field(:expires_at, ObjectValidators.DateTime) # Should be moved to object, done for CommonAPI.Utils.make_context diff --git a/lib/pleroma/web/activity_pub/object_validators/delete_validator.ex b/lib/pleroma/web/activity_pub/object_validators/delete_validator.ex index 7da67bf16..f0c99356e 100644 --- a/lib/pleroma/web/activity_pub/object_validators/delete_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validators/delete_validator.ex @@ -7,6 +7,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.DeleteValidator do alias Pleroma.Activity alias Pleroma.EctoType.ActivityPub.ObjectValidators + alias Pleroma.User import Ecto.Changeset import Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations @@ -14,13 +15,15 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.DeleteValidator do @primary_key false embedded_schema do - field(:id, ObjectValidators.ObjectID, primary_key: true) - field(:type, :string) - field(:actor, ObjectValidators.ObjectID) - field(:to, ObjectValidators.Recipients, default: []) - field(:cc, ObjectValidators.Recipients, default: []) + quote do + unquote do + import Elixir.Pleroma.Web.ActivityPub.ObjectValidators.CommonFields + message_fields() + activity_fields() + end + end + field(:deleted_activity_id, ObjectValidators.ObjectID) - field(:object, ObjectValidators.ObjectID) end def cast_data(data) do @@ -57,7 +60,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.DeleteValidator do cng |> validate_required([:id, :type, :actor, :to, :cc, :object]) |> validate_inclusion(:type, ["Delete"]) - |> validate_actor_presence() + |> validate_delete_actor(:actor) |> validate_modification_rights() |> validate_object_or_user_presence(allowed_types: @deletable_types) |> add_deleted_activity_id() @@ -72,4 +75,13 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.DeleteValidator do |> cast_data |> validate_data end + + defp validate_delete_actor(cng, field_name) do + validate_change(cng, field_name, fn field_name, actor -> + case User.get_cached_by_ap_id(actor) do + %User{} -> [] + _ -> [{field_name, "can't find user"}] + end + end) + end end diff --git a/lib/pleroma/web/activity_pub/object_validators/emoji_react_validator.ex b/lib/pleroma/web/activity_pub/object_validators/emoji_react_validator.ex index a18bd7540..9eaaf8319 100644 --- a/lib/pleroma/web/activity_pub/object_validators/emoji_react_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validators/emoji_react_validator.ex @@ -5,7 +5,6 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.EmojiReactValidator do use Ecto.Schema - alias Pleroma.EctoType.ActivityPub.ObjectValidators alias Pleroma.Object alias Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes @@ -15,14 +14,16 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.EmojiReactValidator do @primary_key false embedded_schema do - field(:id, ObjectValidators.ObjectID, primary_key: true) - field(:type, :string) - field(:object, ObjectValidators.ObjectID) - field(:actor, ObjectValidators.ObjectID) + quote do + unquote do + import Elixir.Pleroma.Web.ActivityPub.ObjectValidators.CommonFields + message_fields() + activity_fields() + end + end + field(:context, :string) field(:content, :string) - field(:to, ObjectValidators.Recipients, default: []) - field(:cc, ObjectValidators.Recipients, default: []) end def cast_and_validate(data) do diff --git a/lib/pleroma/web/activity_pub/object_validators/event_validator.ex b/lib/pleroma/web/activity_pub/object_validators/event_validator.ex index fee2e997a..34a3031c3 100644 --- a/lib/pleroma/web/activity_pub/object_validators/event_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validators/event_validator.ex @@ -5,11 +5,8 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.EventValidator do use Ecto.Schema - alias Pleroma.EctoType.ActivityPub.ObjectValidators - alias Pleroma.Web.ActivityPub.ObjectValidators.AttachmentValidator alias Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes alias Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations - alias Pleroma.Web.ActivityPub.ObjectValidators.TagValidator alias Pleroma.Web.ActivityPub.Transmogrifier import Ecto.Changeset @@ -19,38 +16,14 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.EventValidator do # Extends from NoteValidator embedded_schema do - field(:id, ObjectValidators.ObjectID, primary_key: true) - field(:to, ObjectValidators.Recipients, default: []) - field(:cc, ObjectValidators.Recipients, default: []) - field(:bto, ObjectValidators.Recipients, default: []) - field(:bcc, ObjectValidators.Recipients, default: []) - embeds_many(:tag, TagValidator) - field(:type, :string) - - field(:name, :string) - field(:summary, :string) - field(:content, :string) - - field(:context, :string) - # short identifier for PleromaFE to group statuses by context - field(:context_id, :integer) - - # TODO: Remove actor on objects - field(:actor, ObjectValidators.ObjectID) - - field(:attributedTo, ObjectValidators.ObjectID) - field(:published, ObjectValidators.DateTime) - field(:emoji, ObjectValidators.Emoji, default: %{}) - field(:sensitive, :boolean, default: false) - embeds_many(:attachment, AttachmentValidator) - field(:replies_count, :integer, default: 0) - field(:like_count, :integer, default: 0) - field(:announcement_count, :integer, default: 0) - field(:inReplyTo, ObjectValidators.ObjectID) - field(:url, ObjectValidators.Uri) - - field(:likes, {:array, ObjectValidators.ObjectID}, default: []) - field(:announcements, {:array, ObjectValidators.ObjectID}, default: []) + quote do + unquote do + import Elixir.Pleroma.Web.ActivityPub.ObjectValidators.CommonFields + message_fields() + object_fields() + status_object_fields() + end + end end def cast_and_apply(data) do diff --git a/lib/pleroma/web/activity_pub/object_validators/follow_validator.ex b/lib/pleroma/web/activity_pub/object_validators/follow_validator.ex index 239cee5e7..c061ebba9 100644 --- a/lib/pleroma/web/activity_pub/object_validators/follow_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validators/follow_validator.ex @@ -5,20 +5,20 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.FollowValidator do use Ecto.Schema - alias Pleroma.EctoType.ActivityPub.ObjectValidators - import Ecto.Changeset import Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations @primary_key false embedded_schema do - field(:id, ObjectValidators.ObjectID, primary_key: true) - field(:type, :string) - field(:actor, ObjectValidators.ObjectID) - field(:to, ObjectValidators.Recipients, default: []) - field(:cc, ObjectValidators.Recipients, default: []) - field(:object, ObjectValidators.ObjectID) + quote do + unquote do + import Elixir.Pleroma.Web.ActivityPub.ObjectValidators.CommonFields + message_fields() + activity_fields() + end + end + field(:state, :string, default: "pending") end diff --git a/lib/pleroma/web/activity_pub/object_validators/like_validator.ex b/lib/pleroma/web/activity_pub/object_validators/like_validator.ex index 8b99c89b9..35e000d72 100644 --- a/lib/pleroma/web/activity_pub/object_validators/like_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validators/like_validator.ex @@ -5,7 +5,6 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.LikeValidator do use Ecto.Schema - alias Pleroma.EctoType.ActivityPub.ObjectValidators alias Pleroma.Object alias Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes alias Pleroma.Web.ActivityPub.Utils @@ -16,13 +15,15 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.LikeValidator do @primary_key false embedded_schema do - field(:id, ObjectValidators.ObjectID, primary_key: true) - field(:type, :string) - field(:object, ObjectValidators.ObjectID) - field(:actor, ObjectValidators.ObjectID) + quote do + unquote do + import Elixir.Pleroma.Web.ActivityPub.ObjectValidators.CommonFields + message_fields() + activity_fields() + end + end + field(:context, :string) - field(:to, ObjectValidators.Recipients, default: []) - field(:cc, ObjectValidators.Recipients, default: []) end def cast_and_validate(data) do diff --git a/lib/pleroma/web/activity_pub/object_validators/question_validator.ex b/lib/pleroma/web/activity_pub/object_validators/question_validator.ex index 083d08ec4..bdddfdaeb 100644 --- a/lib/pleroma/web/activity_pub/object_validators/question_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validators/question_validator.ex @@ -6,11 +6,9 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.QuestionValidator do use Ecto.Schema alias Pleroma.EctoType.ActivityPub.ObjectValidators - alias Pleroma.Web.ActivityPub.ObjectValidators.AttachmentValidator alias Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes alias Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations alias Pleroma.Web.ActivityPub.ObjectValidators.QuestionOptionsValidator - alias Pleroma.Web.ActivityPub.ObjectValidators.TagValidator alias Pleroma.Web.ActivityPub.Transmogrifier import Ecto.Changeset @@ -20,35 +18,14 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.QuestionValidator do # Extends from NoteValidator embedded_schema do - field(:id, ObjectValidators.ObjectID, primary_key: true) - field(:to, ObjectValidators.Recipients, default: []) - field(:cc, ObjectValidators.Recipients, default: []) - field(:bto, ObjectValidators.Recipients, default: []) - field(:bcc, ObjectValidators.Recipients, default: []) - embeds_many(:tag, TagValidator) - field(:type, :string) - field(:content, :string) - field(:context, :string) - - # TODO: Remove actor on objects - field(:actor, ObjectValidators.ObjectID) - - field(:attributedTo, ObjectValidators.ObjectID) - field(:summary, :string) - field(:published, ObjectValidators.DateTime) - field(:emoji, ObjectValidators.Emoji, default: %{}) - field(:sensitive, :boolean, default: false) - embeds_many(:attachment, AttachmentValidator) - field(:replies_count, :integer, default: 0) - field(:like_count, :integer, default: 0) - field(:announcement_count, :integer, default: 0) - field(:inReplyTo, ObjectValidators.ObjectID) - field(:url, ObjectValidators.Uri) - # short identifier for PleromaFE to group statuses by context - field(:context_id, :integer) - - field(:likes, {:array, ObjectValidators.ObjectID}, default: []) - field(:announcements, {:array, ObjectValidators.ObjectID}, default: []) + quote do + unquote do + import Elixir.Pleroma.Web.ActivityPub.ObjectValidators.CommonFields + message_fields() + object_fields() + status_object_fields() + end + end field(:closed, ObjectValidators.DateTime) field(:voters, {:array, ObjectValidators.ObjectID}, default: []) diff --git a/lib/pleroma/web/activity_pub/object_validators/undo_validator.ex b/lib/pleroma/web/activity_pub/object_validators/undo_validator.ex index e8af60ffa..703643e3f 100644 --- a/lib/pleroma/web/activity_pub/object_validators/undo_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validators/undo_validator.ex @@ -6,7 +6,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.UndoValidator do use Ecto.Schema alias Pleroma.Activity - alias Pleroma.EctoType.ActivityPub.ObjectValidators + alias Pleroma.User import Ecto.Changeset import Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations @@ -14,12 +14,13 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.UndoValidator do @primary_key false embedded_schema do - field(:id, ObjectValidators.ObjectID, primary_key: true) - field(:type, :string) - field(:object, ObjectValidators.ObjectID) - field(:actor, ObjectValidators.ObjectID) - field(:to, ObjectValidators.Recipients, default: []) - field(:cc, ObjectValidators.Recipients, default: []) + quote do + unquote do + import Elixir.Pleroma.Web.ActivityPub.ObjectValidators.CommonFields + message_fields() + activity_fields() + end + end end def cast_and_validate(data) do @@ -42,7 +43,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.UndoValidator do data_cng |> validate_inclusion(:type, ["Undo"]) |> validate_required([:id, :type, :object, :actor, :to, :cc]) - |> validate_actor_presence() + |> validate_undo_actor(:actor) |> validate_object_presence() |> validate_undo_rights() end @@ -59,4 +60,13 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.UndoValidator do _ -> cng end end + + defp validate_undo_actor(cng, field_name) do + validate_change(cng, field_name, fn field_name, actor -> + case User.get_cached_by_ap_id(actor) do + %User{} -> [] + _ -> [{field_name, "can't find user"}] + end + end) + end end diff --git a/lib/pleroma/web/activity_pub/object_validators/update_validator.ex b/lib/pleroma/web/activity_pub/object_validators/update_validator.ex index 6bb1dc7fa..a1fae47f5 100644 --- a/lib/pleroma/web/activity_pub/object_validators/update_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validators/update_validator.ex @@ -13,11 +13,14 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.UpdateValidator do @primary_key false embedded_schema do - field(:id, ObjectValidators.ObjectID, primary_key: true) - field(:type, :string) + quote do + unquote do + import Elixir.Pleroma.Web.ActivityPub.ObjectValidators.CommonFields + message_fields() + end + end + field(:actor, ObjectValidators.ObjectID) - field(:to, ObjectValidators.Recipients, default: []) - field(:cc, ObjectValidators.Recipients, default: []) # In this case, we save the full object in this activity instead of just a # reference, so we can always see what was actually changed by this. field(:object, :map) diff --git a/lib/pleroma/web/activity_pub/publisher.ex b/lib/pleroma/web/activity_pub/publisher.ex index 590beef64..ed99079e2 100644 --- a/lib/pleroma/web/activity_pub/publisher.ex +++ b/lib/pleroma/web/activity_pub/publisher.ex @@ -63,18 +63,17 @@ defmodule Pleroma.Web.ActivityPub.Publisher do date: date }) - with {:ok, %{status: code}} when code in 200..299 <- - result = - HTTP.post( - inbox, - json, - [ - {"Content-Type", "application/activity+json"}, - {"Date", date}, - {"signature", signature}, - {"digest", digest} - ] - ) do + with {:ok, %{status: code}} = result when code in 200..299 <- + HTTP.post( + inbox, + json, + [ + {"Content-Type", "application/activity+json"}, + {"Date", date}, + {"signature", signature}, + {"digest", digest} + ] + ) do if not Map.has_key?(params, :unreachable_since) || params[:unreachable_since] do Instances.set_reachable(inbox) end @@ -112,6 +111,7 @@ defmodule Pleroma.Web.ActivityPub.Publisher do quarantined_instances = Config.get([:instance, :quarantined_instances], []) + |> Pleroma.Web.ActivityPub.MRF.instance_list_from_tuples() |> Pleroma.Web.ActivityPub.MRF.subdomains_regex() !Pleroma.Web.ActivityPub.MRF.subdomain_match?(quarantined_instances, host) diff --git a/lib/pleroma/web/activity_pub/side_effects.ex b/lib/pleroma/web/activity_pub/side_effects.ex index 1eca1cb31..d55a4b340 100644 --- a/lib/pleroma/web/activity_pub/side_effects.ex +++ b/lib/pleroma/web/activity_pub/side_effects.ex @@ -10,7 +10,6 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do collection, and so on. """ alias Pleroma.Activity - alias Pleroma.Activity.Ir.Topics alias Pleroma.Chat alias Pleroma.Chat.MessageReference alias Pleroma.FollowingRelationship @@ -24,6 +23,7 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do alias Pleroma.Web.ActivityPub.Utils alias Pleroma.Web.Push alias Pleroma.Web.Streamer + alias Pleroma.Workers.PollWorker require Logger @@ -195,12 +195,12 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do # - Set up notifications @impl true def handle(%{data: %{"type" => "Create"}} = activity, meta) do - with {:ok, object, meta} <- handle_object_creation(meta[:object_data], meta), + with {:ok, object, meta} <- handle_object_creation(meta[:object_data], activity, meta), %User{} = user <- User.get_cached_by_ap_id(activity.data["actor"]) do {:ok, notifications} = Notification.create_notifications(activity, do_send: false) {:ok, _user} = ActivityPub.increase_note_count_if_public(user, object) - if in_reply_to = object.data["inReplyTo"] && object.data["type"] != "Answer" do + if in_reply_to = object.data["type"] != "Answer" && object.data["inReplyTo"] do Object.increase_replies_count(in_reply_to) end @@ -225,6 +225,8 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do meta |> add_notifications(notifications) + ap_streamer().stream_out(activity) + {:ok, activity, meta} else e -> Repo.rollback(e) @@ -245,9 +247,7 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do if !User.is_internal_user?(user) do Notification.create_notifications(object) - object - |> Topics.get_activity_topics() - |> Streamer.stream(object) + ap_streamer().stream_out(object) end {:ok, object, meta} @@ -389,7 +389,7 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do {:ok, object, meta} end - def handle_object_creation(%{"type" => "ChatMessage"} = object, meta) do + def handle_object_creation(%{"type" => "ChatMessage"} = object, _activity, meta) do with {:ok, object, meta} <- Pipeline.common_pipeline(object, meta) do actor = User.get_cached_by_ap_id(object.data["actor"]) recipient = User.get_cached_by_ap_id(hd(object.data["to"])) @@ -424,7 +424,14 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do end end - def handle_object_creation(%{"type" => "Answer"} = object_map, meta) do + def handle_object_creation(%{"type" => "Question"} = object, activity, meta) do + with {:ok, object, meta} <- Pipeline.common_pipeline(object, meta) do + PollWorker.schedule_poll_end(activity) + {:ok, object, meta} + end + end + + def handle_object_creation(%{"type" => "Answer"} = object_map, _activity, meta) do with {:ok, object, meta} <- Pipeline.common_pipeline(object_map, meta) do Object.increase_vote_count( object.data["inReplyTo"], @@ -436,15 +443,15 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do end end - def handle_object_creation(%{"type" => objtype} = object, meta) - when objtype in ~w[Audio Video Question Event Article Note] do + def handle_object_creation(%{"type" => objtype} = object, _activity, meta) + when objtype in ~w[Audio Video Event Article Note Page] do with {:ok, object, meta} <- Pipeline.common_pipeline(object, meta) do {:ok, object, meta} end end # Nothing to do - def handle_object_creation(object, meta) do + def handle_object_creation(object, _activity, meta) do {:ok, object, meta} end diff --git a/lib/pleroma/web/activity_pub/transmogrifier.ex b/lib/pleroma/web/activity_pub/transmogrifier.ex index 51c0cc860..142af1a13 100644 --- a/lib/pleroma/web/activity_pub/transmogrifier.ex +++ b/lib/pleroma/web/activity_pub/transmogrifier.ex @@ -353,29 +353,6 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do end) end - # Compatibility wrapper for Mastodon votes - defp handle_create(%{"object" => %{"type" => "Answer"}} = data, _user) do - handle_incoming(data) - end - - defp handle_create(%{"object" => object} = data, user) do - %{ - to: data["to"], - object: object, - actor: user, - context: object["context"], - local: false, - published: data["published"], - additional: - Map.take(data, [ - "cc", - "directMessage", - "id" - ]) - } - |> ActivityPub.create() - end - def handle_incoming(data, options \\ []) # Flag objects are placed ahead of the ID check because Mastodon 2.8 and earlier send them @@ -407,43 +384,6 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do def handle_incoming(%{"id" => id}, _options) when is_binary(id) and byte_size(id) < 8, do: :error - # TODO: validate those with a Ecto scheme - # - tags - # - emoji - def handle_incoming( - %{"type" => "Create", "object" => %{"type" => "Page"} = object} = data, - options - ) do - actor = Containment.get_actor(data) - - with nil <- Activity.get_create_by_object_ap_id(object["id"]), - {:ok, %User{} = user} <- User.get_or_fetch_by_ap_id(actor) do - data = - data - |> Map.put("object", fix_object(object, options)) - |> Map.put("actor", actor) - |> fix_addressing() - - with {:ok, created_activity} <- handle_create(data, user) do - reply_depth = (options[:depth] || 0) + 1 - - if Federator.allowed_thread_distance?(reply_depth) do - for reply_id <- replies(object) do - Pleroma.Workers.RemoteFetcherWorker.enqueue("fetch_remote", %{ - "id" => reply_id, - "depth" => reply_depth - }) - end - end - - {:ok, created_activity} - end - else - %Activity{} = activity -> {:ok, activity} - _e -> :error - end - end - def handle_incoming( %{"type" => "Listen", "object" => %{"type" => "Audio"} = object} = data, options @@ -507,7 +447,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do %{"type" => "Create", "object" => %{"type" => objtype, "id" => obj_id}} = data, options ) - when objtype in ~w{Question Answer ChatMessage Audio Video Event Article Note} do + when objtype in ~w{Question Answer ChatMessage Audio Video Event Article Note Page} do fetch_options = Keyword.put(options, :depth, (options[:depth] || 0) + 1) object = diff --git a/lib/pleroma/web/activity_pub/utils.ex b/lib/pleroma/web/activity_pub/utils.ex index 1df53f79a..c1f6b2b49 100644 --- a/lib/pleroma/web/activity_pub/utils.ex +++ b/lib/pleroma/web/activity_pub/utils.ex @@ -446,7 +446,7 @@ defmodule Pleroma.Web.ActivityPub.Utils do |> Activity.Queries.by_type() |> Activity.Queries.by_actor(actor) |> Activity.Queries.by_object_id(object) - |> where(fragment("data->>'state' = 'pending'")) + |> where(fragment("data->>'state' = 'pending'") or fragment("data->>'state' = 'accept'")) |> update(set: [data: fragment("jsonb_set(data, '{state}', ?)", ^state)]) |> Repo.update_all([]) diff --git a/lib/pleroma/web/activity_pub/visibility.ex b/lib/pleroma/web/activity_pub/visibility.ex index 2be59144d..986fa3a08 100644 --- a/lib/pleroma/web/activity_pub/visibility.ex +++ b/lib/pleroma/web/activity_pub/visibility.ex @@ -57,6 +57,7 @@ defmodule Pleroma.Web.ActivityPub.Visibility do def is_list?(_), do: false @spec visible_for_user?(Object.t() | Activity.t() | nil, User.t() | nil) :: boolean() + def visible_for_user?(%Object{data: %{"type" => "Tombstone"}}, _), do: false def visible_for_user?(%Activity{actor: ap_id}, %User{ap_id: ap_id}), do: true def visible_for_user?(%Object{data: %{"actor" => ap_id}}, %User{ap_id: ap_id}), do: true def visible_for_user?(nil, _), do: false |