aboutsummaryrefslogtreecommitdiff
path: root/lib/pleroma/object
diff options
context:
space:
mode:
Diffstat (limited to 'lib/pleroma/object')
-rw-r--r--[-rwxr-xr-x]lib/pleroma/object/containment.ex0
-rw-r--r--[-rwxr-xr-x]lib/pleroma/object/fetcher.ex85
-rw-r--r--lib/pleroma/object/fetcher.ex.orig238
-rw-r--r--lib/pleroma/object/fetcher.ex.rej13
-rw-r--r--[-rwxr-xr-x]lib/pleroma/object/updater.ex0
5 files changed, 301 insertions, 35 deletions
diff --git a/lib/pleroma/object/containment.ex b/lib/pleroma/object/containment.ex
index f6106cb..f6106cb 100755..100644
--- a/lib/pleroma/object/containment.ex
+++ b/lib/pleroma/object/containment.ex
diff --git a/lib/pleroma/object/fetcher.ex b/lib/pleroma/object/fetcher.ex
index cc37725..2350664 100755..100644
--- a/lib/pleroma/object/fetcher.ex
+++ b/lib/pleroma/object/fetcher.ex
@@ -3,9 +3,11 @@
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Object.Fetcher do
+ @behaviour HTTPSignatures.Adapter
alias Pleroma.HTTP
alias Pleroma.Instances
alias Pleroma.Maps
+ alias Pleroma.Keys
alias Pleroma.Object
alias Pleroma.Object.Containment
alias Pleroma.Signature
@@ -72,20 +74,25 @@ defmodule Pleroma.Object.Fetcher do
{:object, data, Object.normalize(activity, fetch: false)} do
{:ok, object}
else
- {:allowed_depth, false} ->
- {:error, "Max thread distance exceeded."}
+ {:allowed_depth, false} = e ->
+ log_fetch_error(id, e)
+ {:error, :allowed_depth}
- {:containment, _} ->
- {:error, "Object containment failed."}
+ {:containment, reason} = e ->
+ log_fetch_error(id, e)
+ {:error, reason}
- {:transmogrifier, {:error, {:reject, e}}} ->
- {:reject, e}
+ {:transmogrifier, {:error, {:reject, reason}}} = e ->
+ log_fetch_error(id, e)
+ {:reject, reason}
- {:transmogrifier, {:reject, e}} ->
- {:reject, e}
+ {:transmogrifier, {:reject, reason}} = e ->
+ log_fetch_error(id, e)
+ {:reject, reason}
- {:transmogrifier, _} = e ->
- {:error, e}
+ {:transmogrifier, reason} = e ->
+ log_fetch_error(id, e)
+ {:error, reason}
{:object, data, nil} ->
reinject_object(%Object{}, data)
@@ -96,14 +103,21 @@ defmodule Pleroma.Object.Fetcher do
{:fetch_object, %Object{} = object} ->
{:ok, object}
- {:fetch, {:error, error}} ->
- {:error, error}
+ {:fetch, {:error, reason}} = e ->
+ log_fetch_error(id, e)
+ {:error, reason}
e ->
- e
+ log_fetch_error(id, e)
+ {:error, e}
end
end
+ defp log_fetch_error(id, error) do
+ Logger.metadata(object: id)
+ Logger.error("Object rejected while fetching #{id} #{inspect(error)}")
+ end
+
defp prepare_activity_params(data) do
%{
"type" => "Create",
@@ -117,36 +131,34 @@ defmodule Pleroma.Object.Fetcher do
|> Maps.put_if_present("bcc", data["bcc"])
end
- def fetch_object_from_id!(id, options \\ []) do
- with {:ok, object} <- fetch_object_from_id(id, options) do
- object
- else
- {:error, %Tesla.Mock.Error{}} ->
- nil
-
- {:error, "Object has been deleted"} ->
- nil
-
- {:reject, reason} ->
- Logger.info("Rejected #{id} while fetching: #{inspect(reason)}")
- nil
-
- e ->
- Logger.error("Error while fetching #{id}: #{inspect(e)}")
- nil
- end
- end
-
defp make_signature(id, date) do
uri = URI.parse(id)
- signature =
+ spoofed_pem = Pleroma.Config.get([:activitypub, :spoofed_key])
+ # workaround for syntax shite disallowing me from defining signature in "if" block
+ spoofed_key = if Pleroma.Config.get([:activitypub, :spoof_object_fetch_signatures]) do
+ with {:ok, private_key, _} <- Keys.keys_from_pem(spoofed_pem) do
+ private_key
+ end
+ else
+ ""
+ end
+ spoofed_instance = Pleroma.Config.get([:activitypub, :spoofed_instance])
+
+ signature = if Pleroma.Config.get([:activitypub, :spoof_object_fetch_signatures]) do
+ HTTPSignatures.sign(spoofed_key, spoofed_instance <> "/internal/fetch#main-key", %{
+ "(request-target)": "get #{uri.path}",
+ host: uri.host,
+ date: date
+ })
+ else
InternalFetchActor.get_actor()
|> Signature.sign(%{
"(request-target)": "get #{uri.path}",
host: uri.host,
date: date
})
+ end
{"signature", signature}
end
@@ -227,8 +239,11 @@ defmodule Pleroma.Object.Fetcher do
{:error, {:content_type, nil}}
end
+ {:ok, %{status: code}} when code in [401, 403] ->
+ {:error, :forbidden}
+
{:ok, %{status: code}} when code in [404, 410] ->
- {:error, "Object has been deleted"}
+ {:error, :not_found}
{:error, e} ->
{:error, e}
diff --git a/lib/pleroma/object/fetcher.ex.orig b/lib/pleroma/object/fetcher.ex.orig
new file mode 100644
index 0000000..af5642a
--- /dev/null
+++ b/lib/pleroma/object/fetcher.ex.orig
@@ -0,0 +1,238 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Object.Fetcher do
+ alias Pleroma.HTTP
+ alias Pleroma.Instances
+ alias Pleroma.Maps
+ alias Pleroma.Object
+ alias Pleroma.Object.Containment
+ alias Pleroma.Signature
+ alias Pleroma.Web.ActivityPub.InternalFetchActor
+ alias Pleroma.Web.ActivityPub.MRF
+ alias Pleroma.Web.ActivityPub.ObjectValidator
+ alias Pleroma.Web.ActivityPub.Pipeline
+ alias Pleroma.Web.ActivityPub.Transmogrifier
+ alias Pleroma.Web.Federator
+
+ require Logger
+ require Pleroma.Constants
+
+ @spec reinject_object(struct(), map()) :: {:ok, Object.t()} | {:error, any()}
+ defp reinject_object(%Object{data: %{}} = object, new_data) do
+ Logger.debug("Reinjecting object #{new_data["id"]}")
+
+ with {:ok, new_data, _} <- ObjectValidator.validate(new_data, %{}),
+ {:ok, new_data} <- MRF.filter(new_data),
+ {:ok, new_object, _} <-
+ Object.Updater.do_update_and_invalidate_cache(
+ object,
+ new_data,
+ _touch_changeset? = true
+ ) do
+ {:ok, new_object}
+ else
+ e ->
+ Logger.error("Error while processing object: #{inspect(e)}")
+ {:error, e}
+ end
+ end
+
+ defp reinject_object(_, new_data) do
+ with {:ok, object, _} <- Pipeline.common_pipeline(new_data, local: false) do
+ {:ok, object}
+ else
+ e -> e
+ end
+ end
+
+ def refetch_object(%Object{data: %{"id" => id}} = object) do
+ with {:local, false} <- {:local, Object.local?(object)},
+ {:ok, new_data} <- fetch_and_contain_remote_object_from_id(id),
+ {:ok, object} <- reinject_object(object, new_data) do
+ {:ok, object}
+ else
+ {:local, true} -> {:ok, object}
+ e -> {:error, e}
+ end
+ end
+
+ # Note: will create a Create activity, which we need internally at the moment.
+ def fetch_object_from_id(id, options \\ []) do
+ with {_, nil} <- {:fetch_object, Object.get_cached_by_ap_id(id)},
+ {_, true} <- {:allowed_depth, Federator.allowed_thread_distance?(options[:depth])},
+ {_, {:ok, data}} <- {:fetch, fetch_and_contain_remote_object_from_id(id)},
+ {_, nil} <- {:normalize, Object.normalize(data, fetch: false)},
+ params <- prepare_activity_params(data),
+ {_, :ok} <- {:containment, Containment.contain_origin(id, params)},
+ {_, {:ok, activity}} <-
+ {:transmogrifier, Transmogrifier.handle_incoming(params, options)},
+ {_, _data, %Object{} = object} <-
+ {:object, data, Object.normalize(activity, fetch: false)} do
+ {:ok, object}
+ else
+ {:allowed_depth, false} = e ->
+ log_fetch_error(id, e)
+ {:error, :allowed_depth}
+
+ {:containment, reason} = e ->
+ log_fetch_error(id, e)
+ {:error, reason}
+
+ {:transmogrifier, {:error, {:reject, reason}}} = e ->
+ log_fetch_error(id, e)
+ {:reject, reason}
+
+ {:transmogrifier, {:reject, reason}} = e ->
+ log_fetch_error(id, e)
+ {:reject, reason}
+
+ {:transmogrifier, reason} = e ->
+ log_fetch_error(id, e)
+ {:error, reason}
+
+ {:object, data, nil} ->
+ reinject_object(%Object{}, data)
+
+ {:normalize, object = %Object{}} ->
+ {:ok, object}
+
+ {:fetch_object, %Object{} = object} ->
+ {:ok, object}
+
+ {:fetch, {:error, reason}} = e ->
+ log_fetch_error(id, e)
+ {:error, reason}
+
+ e ->
+ log_fetch_error(id, e)
+ {:error, e}
+ end
+ end
+
+ defp log_fetch_error(id, error) do
+ Logger.metadata(object: id)
+ Logger.error("Object rejected while fetching #{id} #{inspect(error)}")
+ end
+
+ defp prepare_activity_params(data) do
+ %{
+ "type" => "Create",
+ # Should we seriously keep this attributedTo thing?
+ "actor" => data["actor"] || data["attributedTo"],
+ "object" => data
+ }
+ |> Maps.put_if_present("to", data["to"])
+ |> Maps.put_if_present("cc", data["cc"])
+ |> Maps.put_if_present("bto", data["bto"])
+ |> Maps.put_if_present("bcc", data["bcc"])
+ end
+
+ defp make_signature(id, date) do
+ uri = URI.parse(id)
+
+ signature =
+ InternalFetchActor.get_actor()
+ |> Signature.sign(%{
+ "(request-target)": "get #{uri.path}",
+ host: uri.host,
+ date: date
+ })
+
+ {"signature", signature}
+ end
+
+ defp sign_fetch(headers, id, date) do
+ if Pleroma.Config.get([:activitypub, :sign_object_fetches]) do
+ [make_signature(id, date) | headers]
+ else
+ headers
+ end
+ end
+
+ defp maybe_date_fetch(headers, date) do
+ if Pleroma.Config.get([:activitypub, :sign_object_fetches]) do
+ [{"date", date} | headers]
+ else
+ headers
+ end
+ end
+
+ def fetch_and_contain_remote_object_from_id(id)
+
+ def fetch_and_contain_remote_object_from_id(%{"id" => id}),
+ do: fetch_and_contain_remote_object_from_id(id)
+
+ def fetch_and_contain_remote_object_from_id(id) when is_binary(id) do
+ Logger.debug("Fetching object #{id} via AP")
+
+ with {:scheme, true} <- {:scheme, String.starts_with?(id, "http")},
+ {:ok, body} <- get_object(id),
+ {:ok, data} <- safe_json_decode(body),
+ :ok <- Containment.contain_origin_from_id(id, data) do
+ if not Instances.reachable?(id) do
+ Instances.set_reachable(id)
+ end
+
+ {:ok, data}
+ else
+ {:scheme, _} ->
+ {:error, "Unsupported URI scheme"}
+
+ {:error, e} ->
+ {:error, e}
+
+ e ->
+ {:error, e}
+ end
+ end
+
+ def fetch_and_contain_remote_object_from_id(_id),
+ do: {:error, "id must be a string"}
+
+ defp get_object(id) do
+ date = Pleroma.Signature.signed_date()
+
+ headers =
+ [{"accept", "application/activity+json"}]
+ |> maybe_date_fetch(date)
+ |> sign_fetch(id, date)
+
+ case HTTP.get(id, headers) do
+ {:ok, %{body: body, status: code, headers: headers}} when code in 200..299 ->
+ case List.keyfind(headers, "content-type", 0) do
+ {_, content_type} ->
+ case Plug.Conn.Utils.media_type(content_type) do
+ {:ok, "application", "activity+json", _} ->
+ {:ok, body}
+
+ {:ok, "application", "ld+json",
+ %{"profile" => "https://www.w3.org/ns/activitystreams"}} ->
+ {:ok, body}
+
+ _ ->
+ {:error, {:content_type, content_type}}
+ end
+
+ _ ->
+ {:error, {:content_type, nil}}
+ end
+
+ {:ok, %{status: code}} when code in [401, 403] ->
+ {:error, :forbidden}
+
+ {:ok, %{status: code}} when code in [404, 410] ->
+ {:error, :not_found}
+
+ {:error, e} ->
+ {:error, e}
+
+ e ->
+ {:error, e}
+ end
+ end
+
+ defp safe_json_decode(nil), do: {:ok, nil}
+ defp safe_json_decode(json), do: Jason.decode(json)
+end
diff --git a/lib/pleroma/object/fetcher.ex.rej b/lib/pleroma/object/fetcher.ex.rej
new file mode 100644
index 0000000..25b13f5
--- /dev/null
+++ b/lib/pleroma/object/fetcher.ex.rej
@@ -0,0 +1,13 @@
+--- lib/pleroma/object/fetcher.ex
++++ lib/pleroma/object/fetcher.ex
+@@ -3,7 +3,10 @@
+ # SPDX-License-Identifier: AGPL-3.0-only
+
+ defmodule Pleroma.Object.Fetcher do
++ @behaviour HTTPSignatures.Adapter
++
+ alias Pleroma.HTTP
++ alias Pleroma.Keys
+ alias Pleroma.Maps
+ alias Pleroma.Object
+ alias Pleroma.Object.Containment
diff --git a/lib/pleroma/object/updater.ex b/lib/pleroma/object/updater.ex
index b1e4870..b1e4870 100755..100644
--- a/lib/pleroma/object/updater.ex
+++ b/lib/pleroma/object/updater.ex