From e7d292f80ee03d6eabf30792640a7a40e041a796 Mon Sep 17 00:00:00 2001 From: William Pitcock Date: Sun, 12 May 2019 02:41:34 +0000 Subject: [PATCH 01/19] federator: add publisher module defining a contract for publishing behaviours --- lib/pleroma/web/federator/publisher.ex | 38 ++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 lib/pleroma/web/federator/publisher.ex diff --git a/lib/pleroma/web/federator/publisher.ex b/lib/pleroma/web/federator/publisher.ex new file mode 100644 index 000000000..36277fd7e --- /dev/null +++ b/lib/pleroma/web/federator/publisher.ex @@ -0,0 +1,38 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.Federator.Publisher do + @moduledoc """ + Defines the contract used by federation implementations to publish messages to + their peers. + """ + + @doc """ + Determine whether an activity can be relayed using the federation module. + """ + @callback is_representable?(Pleroma.Activity.t()) :: boolean() + + @doc """ + Relays an activity to a specified peer, determined by the parameters. The + parameters used are controlled by the federation module. + """ + @callback publish_one(Map.t()) :: {:ok, Map.t()} | {:error, any()} + + @doc """ + Relays an activity to all specified peers. + """ + @callback publish(Pleroma.User.t(), Pleroma.Activity.t()) :: :ok | {:error, any()} + + @doc """ + Enqueues work generated by the federation module. + """ + @spec enqueue(module(), keyword()) :: :ok + def enqueue(module, args), do: PleromaJobQueue.enqueue(:federation_outgoing, module, args) + + @doc """ + Enqueue publishing a single activity. + """ + @spec enqueue_one(module(), Map.t()) :: :ok + def enqueue_one(module, %{} = args), do: enqueue(module, [:publish_one, args]) +end From ef1f9e8d4e4bd2bae0483523c7d31a20c086207e Mon Sep 17 00:00:00 2001 From: William Pitcock Date: Sun, 12 May 2019 03:09:05 +0000 Subject: [PATCH 02/19] activitypub: split out outgoing federation into a federation module --- lib/pleroma/web/activity_pub/activity_pub.ex | 87 ------------ lib/pleroma/web/activity_pub/publisher.ex | 131 +++++++++++++++++++ 2 files changed, 131 insertions(+), 87 deletions(-) create mode 100644 lib/pleroma/web/activity_pub/publisher.ex diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index 8f8c23a9b..11777c220 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -5,7 +5,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do alias Pleroma.Activity alias Pleroma.Conversation - alias Pleroma.Instances alias Pleroma.Notification alias Pleroma.Object alias Pleroma.Object.Fetcher @@ -15,7 +14,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do alias Pleroma.User alias Pleroma.Web.ActivityPub.MRF alias Pleroma.Web.ActivityPub.Transmogrifier - alias Pleroma.Web.Federator alias Pleroma.Web.WebFinger import Ecto.Query @@ -24,8 +22,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do require Logger - @httpoison Application.get_env(:pleroma, :httpoison) - # For Announce activities, we filter the recipients based on following status for any actors # that match actual users. See issue #164 for more information about why this is necessary. defp get_recipients(%{"type" => "Announce"} = data) do @@ -961,89 +957,6 @@ def make_user_from_nickname(nickname) do end end - def should_federate?(inbox, public) do - if public do - true - else - inbox_info = URI.parse(inbox) - !Enum.member?(Pleroma.Config.get([:instance, :quarantined_instances], []), inbox_info.host) - end - end - - def publish(actor, activity) do - remote_followers = - if actor.follower_address in activity.recipients do - {:ok, followers} = User.get_followers(actor) - followers |> Enum.filter(&(!&1.local)) - else - [] - end - - public = is_public?(activity) - - {:ok, data} = Transmogrifier.prepare_outgoing(activity.data) - json = Jason.encode!(data) - - (Pleroma.Web.Salmon.remote_users(activity) ++ remote_followers) - |> Enum.filter(fn user -> User.ap_enabled?(user) end) - |> Enum.map(fn %{info: %{source_data: data}} -> - (is_map(data["endpoints"]) && Map.get(data["endpoints"], "sharedInbox")) || data["inbox"] - end) - |> Enum.uniq() - |> Enum.filter(fn inbox -> should_federate?(inbox, public) end) - |> Instances.filter_reachable() - |> Enum.each(fn {inbox, unreachable_since} -> - Federator.publish_single_ap(%{ - inbox: inbox, - json: json, - actor: actor, - id: activity.data["id"], - unreachable_since: unreachable_since - }) - end) - end - - def publish_one(%{inbox: inbox, json: json, actor: actor, id: id} = params) do - Logger.info("Federating #{id} to #{inbox}") - host = URI.parse(inbox).host - - digest = "SHA-256=" <> (:crypto.hash(:sha256, json) |> Base.encode64()) - - date = - NaiveDateTime.utc_now() - |> Timex.format!("{WDshort}, {0D} {Mshort} {YYYY} {h24}:{m}:{s} GMT") - - signature = - Pleroma.Web.HTTPSignatures.sign(actor, %{ - host: host, - "content-length": byte_size(json), - digest: digest, - date: date - }) - - with {:ok, %{status: code}} when code in 200..299 <- - result = - @httpoison.post( - inbox, - json, - [ - {"Content-Type", "application/activity+json"}, - {"Date", date}, - {"signature", signature}, - {"digest", digest} - ] - ) do - if !Map.has_key?(params, :unreachable_since) || params[:unreachable_since], - do: Instances.set_reachable(inbox) - - result - else - {_post_result, response} -> - unless params[:unreachable_since], do: Instances.set_unreachable(inbox) - {:error, response} - end - end - # filter out broken threads def contain_broken_threads(%Activity{} = activity, %User{} = user) do entire_thread_visible_for_user?(activity, user) diff --git a/lib/pleroma/web/activity_pub/publisher.ex b/lib/pleroma/web/activity_pub/publisher.ex new file mode 100644 index 000000000..ee9f0fdd3 --- /dev/null +++ b/lib/pleroma/web/activity_pub/publisher.ex @@ -0,0 +1,131 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ActivityPub.Publisher do + alias Pleroma.Activity + alias Pleroma.Instances + alias Pleroma.User + alias Pleroma.Web.ActivityPub.Transmogrifier + + import Pleroma.Web.ActivityPub.Visibility + + @behaviour Pleroma.Web.Federator.Publisher + + require Logger + + @httpoison Application.get_env(:pleroma, :httpoison) + + @moduledoc """ + ActivityPub outgoing federation module. + """ + + @doc """ + Determine if an activity can be represented by running it through Transmogrifier. + """ + def is_representable?(%Activity{} = activity) do + with %{} = _data <- Transmogrifier.prepare_outgoing(activity.data) do + true + else + _e -> false + end + end + + @doc """ + Publish a single message to a peer. Takes a struct with the following + parameters set: + + * `inbox`: the inbox to publish to + * `json`: the JSON message body representing the ActivityPub message + * `actor`: the actor which is signing the message + * `id`: the ActivityStreams URI of the message + """ + def publish_one(%{inbox: inbox, json: json, actor: %User{} = actor, id: id} = params) do + Logger.info("Federating #{id} to #{inbox}") + host = URI.parse(inbox).host + + digest = "SHA-256=" <> (:crypto.hash(:sha256, json) |> Base.encode64()) + + date = + NaiveDateTime.utc_now() + |> Timex.format!("{WDshort}, {0D} {Mshort} {YYYY} {h24}:{m}:{s} GMT") + + signature = + Pleroma.Web.HTTPSignatures.sign(actor, %{ + host: host, + "content-length": byte_size(json), + digest: digest, + date: date + }) + + with {:ok, %{status: code}} when code in 200..299 <- + result = + @httpoison.post( + inbox, + json, + [ + {"Content-Type", "application/activity+json"}, + {"Date", date}, + {"signature", signature}, + {"digest", digest} + ] + ) do + if !Map.has_key?(params, :unreachable_since) || params[:unreachable_since], + do: Instances.set_reachable(inbox) + + result + else + {_post_result, response} -> + unless params[:unreachable_since], do: Instances.set_unreachable(inbox) + {:error, response} + end + end + + defp should_federate?(inbox, public) do + if public do + true + else + inbox_info = URI.parse(inbox) + !Enum.member?(Pleroma.Config.get([:instance, :quarantined_instances], []), inbox_info.host) + end + end + + @doc """ + Publishes an activity to all relevant peers. + """ + def publish(%User{} = actor, %Activity{} = activity) do + remote_followers = + if actor.follower_address in activity.recipients do + {:ok, followers} = User.get_followers(actor) + followers |> Enum.filter(&(!&1.local)) + else + [] + end + + public = is_public?(activity) + + {:ok, data} = Transmogrifier.prepare_outgoing(activity.data) + json = Jason.encode!(data) + + (Pleroma.Web.Salmon.remote_users(activity) ++ remote_followers) + |> Enum.filter(fn user -> User.ap_enabled?(user) end) + |> Enum.map(fn %{info: %{source_data: data}} -> + (is_map(data["endpoints"]) && Map.get(data["endpoints"], "sharedInbox")) || data["inbox"] + end) + |> Enum.uniq() + |> Enum.filter(fn inbox -> should_federate?(inbox, public) end) + |> Instances.filter_reachable() + |> Enum.each(fn {inbox, unreachable_since} -> + Pleroma.Web.Federator.Publisher.enqueue_one( + __MODULE__, + %{ + inbox: inbox, + json: json, + actor: actor, + id: activity.data["id"], + unreachable_since: unreachable_since + } + ) + end) + end +end From 0afc8d7856c9fe37de338d1e9365563d986c9319 Mon Sep 17 00:00:00 2001 From: William Pitcock Date: Sun, 12 May 2019 03:43:53 +0000 Subject: [PATCH 03/19] federator: publisher: integrate job queue, simplify publish_one logic --- lib/pleroma/web/federator/publisher.ex | 29 +++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/lib/pleroma/web/federator/publisher.ex b/lib/pleroma/web/federator/publisher.ex index 36277fd7e..2e533ae94 100644 --- a/lib/pleroma/web/federator/publisher.ex +++ b/lib/pleroma/web/federator/publisher.ex @@ -3,6 +3,10 @@ # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Federator.Publisher do + alias Pleroma.Web.Federator.RetryQueue + + require Logger + @moduledoc """ Defines the contract used by federation implementations to publish messages to their peers. @@ -24,15 +28,26 @@ defmodule Pleroma.Web.Federator.Publisher do """ @callback publish(Pleroma.User.t(), Pleroma.Activity.t()) :: :ok | {:error, any()} - @doc """ - Enqueues work generated by the federation module. - """ - @spec enqueue(module(), keyword()) :: :ok - def enqueue(module, args), do: PleromaJobQueue.enqueue(:federation_outgoing, module, args) - @doc """ Enqueue publishing a single activity. """ @spec enqueue_one(module(), Map.t()) :: :ok - def enqueue_one(module, %{} = args), do: enqueue(module, [:publish_one, args]) + def enqueue_one(module, %{} = params), + do: PleromaJobQueue.enqueue(:federation_outgoing, __MODULE__, [:publish_one, module, params]) + + @spec perform(atom(), module(), any()) :: {:ok, any()} | {:error, any()} + def perform(:publish_one, module, params) do + case apply(module, :publish_one, [params]) do + {:ok, _} -> + :ok + + {:error, _} -> + RetryQueue.enqueue(params, module) + end + end + + def perform(type, _, _) do + Logger.debug("Unknown task: #{type}") + {:error, "Don't know what to do with this"} + end end From 10695a28d28d74f4b6b9bba20af66b506b662c07 Mon Sep 17 00:00:00 2001 From: William Pitcock Date: Sun, 12 May 2019 03:55:17 +0000 Subject: [PATCH 04/19] federator: publisher: add publish() wrapper --- config/config.exs | 3 +++ lib/pleroma/web/federator/publisher.ex | 24 +++++++++++++++++++----- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/config/config.exs b/config/config.exs index 1e64b79a7..37803383a 100644 --- a/config/config.exs +++ b/config/config.exs @@ -212,6 +212,9 @@ registrations_open: true, federating: true, federation_reachability_timeout_days: 7, + federation_publisher_modules: [ + Pleroma.Web.ActivityPub.Publisher + ], allow_relay: true, rewrite_policy: Pleroma.Web.ActivityPub.MRF.NoOpPolicy, public: true, diff --git a/lib/pleroma/web/federator/publisher.ex b/lib/pleroma/web/federator/publisher.ex index 2e533ae94..8777a3deb 100644 --- a/lib/pleroma/web/federator/publisher.ex +++ b/lib/pleroma/web/federator/publisher.ex @@ -3,6 +3,9 @@ # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Federator.Publisher do + alias Pleroma.Activity + alias Pleroma.Config + alias Pleroma.User alias Pleroma.Web.Federator.RetryQueue require Logger @@ -23,11 +26,6 @@ defmodule Pleroma.Web.Federator.Publisher do """ @callback publish_one(Map.t()) :: {:ok, Map.t()} | {:error, any()} - @doc """ - Relays an activity to all specified peers. - """ - @callback publish(Pleroma.User.t(), Pleroma.Activity.t()) :: :ok | {:error, any()} - @doc """ Enqueue publishing a single activity. """ @@ -50,4 +48,20 @@ def perform(type, _, _) do Logger.debug("Unknown task: #{type}") {:error, "Don't know what to do with this"} end + + @doc """ + Relays an activity to all specified peers. + """ + @callback publish(Pleroma.User.t(), Pleroma.Activity.t()) :: :ok | {:error, any()} + + @spec publish(Pleroma.User.t(), Pleroma.Activity.t()) :: :ok + def publish(%User{} = user, %Activity{} = activity) do + Config.get([:instance, :federation_publisher_modules]) + |> Enum.each(fn module -> + Logger.info("Publishing #{activity.data["id"]} using #{inspect(module)}") + module.publish(user, activity) + end) + + :ok + end end From f7a6a37c4eb98e354fbcd98ea19c9207d891e993 Mon Sep 17 00:00:00 2001 From: William Pitcock Date: Sun, 12 May 2019 03:56:49 +0000 Subject: [PATCH 05/19] federator: remove no longer used :publish_single_ap --- lib/pleroma/web/federator/federator.ex | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/lib/pleroma/web/federator/federator.ex b/lib/pleroma/web/federator/federator.ex index 29e178ba9..d8534b365 100644 --- a/lib/pleroma/web/federator/federator.ex +++ b/lib/pleroma/web/federator/federator.ex @@ -42,10 +42,6 @@ def publish(activity, priority \\ 1) do PleromaJobQueue.enqueue(:federator_outgoing, __MODULE__, [:publish, activity], priority) end - def publish_single_ap(params) do - PleromaJobQueue.enqueue(:federator_outgoing, __MODULE__, [:publish_single_ap, params]) - end - def publish_single_websub(websub) do PleromaJobQueue.enqueue(:federator_outgoing, __MODULE__, [:publish_single_websub, websub]) end @@ -157,16 +153,6 @@ def perform(:publish_single_salmon, params) do Salmon.send_to_user(params) end - def perform(:publish_single_ap, params) do - case ActivityPub.publish_one(params) do - {:ok, _} -> - :ok - - {:error, _} -> - RetryQueue.enqueue(params, ActivityPub) - end - end - def perform( :publish_single_websub, %{xml: _xml, topic: _topic, callback: _callback, secret: _secret} = params From 676752bb8367ec6b5831c7dbd2aad993b1fe45aa Mon Sep 17 00:00:00 2001 From: William Pitcock Date: Sun, 12 May 2019 03:57:10 +0000 Subject: [PATCH 06/19] federator: hook up Publisher.publish() --- lib/pleroma/web/federator/federator.ex | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/pleroma/web/federator/federator.ex b/lib/pleroma/web/federator/federator.ex index d8534b365..ef2708c07 100644 --- a/lib/pleroma/web/federator/federator.ex +++ b/lib/pleroma/web/federator/federator.ex @@ -11,6 +11,7 @@ defmodule Pleroma.Web.Federator do alias Pleroma.Web.ActivityPub.Transmogrifier alias Pleroma.Web.ActivityPub.Utils alias Pleroma.Web.ActivityPub.Visibility + alias Pleroma.Web.Federator.Publisher alias Pleroma.Web.Federator.RetryQueue alias Pleroma.Web.OStatus alias Pleroma.Web.Salmon @@ -106,8 +107,7 @@ def perform(:publish, activity) do end end - Logger.info(fn -> "Sending #{activity.data["id"]} out via AP" end) - Pleroma.Web.ActivityPub.ActivityPub.publish(actor, activity) + Publisher.publish(actor, activity) end end From 85377c0b67cf82fdf7e49608b94aec80ace74c4d Mon Sep 17 00:00:00 2001 From: William Pitcock Date: Sun, 12 May 2019 04:00:55 +0000 Subject: [PATCH 07/19] federator: move activitypub relaying to the AP publisher module --- lib/pleroma/web/activity_pub/publisher.ex | 7 +++++++ lib/pleroma/web/federator/federator.ex | 6 ------ 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/lib/pleroma/web/activity_pub/publisher.ex b/lib/pleroma/web/activity_pub/publisher.ex index ee9f0fdd3..11e54b77d 100644 --- a/lib/pleroma/web/activity_pub/publisher.ex +++ b/lib/pleroma/web/activity_pub/publisher.ex @@ -4,8 +4,10 @@ defmodule Pleroma.Web.ActivityPub.Publisher do alias Pleroma.Activity + alias Pleroma.Config alias Pleroma.Instances alias Pleroma.User + alias Pleroma.Web.ActivityPub.Relay alias Pleroma.Web.ActivityPub.Transmogrifier import Pleroma.Web.ActivityPub.Visibility @@ -104,6 +106,11 @@ def publish(%User{} = actor, %Activity{} = activity) do public = is_public?(activity) + if public && Config.get([:instance, :allow_relay]) do + Logger.info(fn -> "Relaying #{activity.data["id"]} out" end) + Relay.publish(activity) + end + {:ok, data} = Transmogrifier.prepare_outgoing(activity.data) json = Jason.encode!(data) diff --git a/lib/pleroma/web/federator/federator.ex b/lib/pleroma/web/federator/federator.ex index ef2708c07..252d3b009 100644 --- a/lib/pleroma/web/federator/federator.ex +++ b/lib/pleroma/web/federator/federator.ex @@ -7,7 +7,6 @@ defmodule Pleroma.Web.Federator do alias Pleroma.Object.Containment alias Pleroma.User alias Pleroma.Web.ActivityPub.ActivityPub - alias Pleroma.Web.ActivityPub.Relay alias Pleroma.Web.ActivityPub.Transmogrifier alias Pleroma.Web.ActivityPub.Utils alias Pleroma.Web.ActivityPub.Visibility @@ -100,11 +99,6 @@ def perform(:publish, activity) do Logger.info(fn -> "Sending #{activity.data["id"]} out via Salmon" end) Pleroma.Web.Salmon.publish(actor, activity) end - - if Keyword.get(Application.get_env(:pleroma, :instance), :allow_relay) do - Logger.info(fn -> "Relaying #{activity.data["id"]} out" end) - Relay.publish(activity) - end end Publisher.publish(actor, activity) From 69158f10652e735e3300335dba3856a0233da89f Mon Sep 17 00:00:00 2001 From: William Pitcock Date: Sun, 12 May 2019 04:04:50 +0000 Subject: [PATCH 08/19] ostatus: only as:Public activities are representable --- lib/pleroma/web/ostatus/ostatus.ex | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/pleroma/web/ostatus/ostatus.ex b/lib/pleroma/web/ostatus/ostatus.ex index 4744c6d83..61515b31e 100644 --- a/lib/pleroma/web/ostatus/ostatus.ex +++ b/lib/pleroma/web/ostatus/ostatus.ex @@ -16,6 +16,7 @@ defmodule Pleroma.Web.OStatus do alias Pleroma.Web alias Pleroma.Web.ActivityPub.ActivityPub alias Pleroma.Web.ActivityPub.Transmogrifier + alias Pleroma.Web.ActivityPub.Visibility alias Pleroma.Web.OStatus.DeleteHandler alias Pleroma.Web.OStatus.FollowHandler alias Pleroma.Web.OStatus.NoteHandler @@ -30,7 +31,7 @@ def is_representable?(%Activity{} = activity) do is_nil(object) -> false - object.data["type"] == "Note" -> + Visibility.is_public?(activity) && object.data["type"] == "Note" -> true true -> From 2aee62a4565d36983484ead9dda187e9e8188971 Mon Sep 17 00:00:00 2001 From: William Pitcock Date: Sun, 12 May 2019 04:17:17 +0000 Subject: [PATCH 09/19] federator: publisher: only attempt publishing if we know the activity is representable --- lib/pleroma/web/federator/publisher.ex | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/pleroma/web/federator/publisher.ex b/lib/pleroma/web/federator/publisher.ex index 8777a3deb..67f4b7ba7 100644 --- a/lib/pleroma/web/federator/publisher.ex +++ b/lib/pleroma/web/federator/publisher.ex @@ -39,7 +39,7 @@ def perform(:publish_one, module, params) do {:ok, _} -> :ok - {:error, _} -> + {:error, _e} -> RetryQueue.enqueue(params, module) end end @@ -58,8 +58,10 @@ def perform(type, _, _) do def publish(%User{} = user, %Activity{} = activity) do Config.get([:instance, :federation_publisher_modules]) |> Enum.each(fn module -> - Logger.info("Publishing #{activity.data["id"]} using #{inspect(module)}") - module.publish(user, activity) + if module.is_representable?(activity) do + Logger.info("Publishing #{activity.data["id"]} using #{inspect(module)}") + module.publish(user, activity) + end end) :ok From 179293e51c2e381fdc15c0a291b735750f9cd656 Mon Sep 17 00:00:00 2001 From: William Pitcock Date: Sun, 12 May 2019 04:27:01 +0000 Subject: [PATCH 10/19] salmon: refactor to work as a federator publishing module --- config/config.exs | 3 ++- lib/pleroma/web/federator/federator.ex | 12 --------- lib/pleroma/web/salmon/salmon.ex | 34 +++++++++++++++++--------- 3 files changed, 24 insertions(+), 25 deletions(-) diff --git a/config/config.exs b/config/config.exs index 37803383a..3dcff3c46 100644 --- a/config/config.exs +++ b/config/config.exs @@ -213,7 +213,8 @@ federating: true, federation_reachability_timeout_days: 7, federation_publisher_modules: [ - Pleroma.Web.ActivityPub.Publisher + Pleroma.Web.ActivityPub.Publisher, + Pleroma.Web.Salmon ], allow_relay: true, rewrite_policy: Pleroma.Web.ActivityPub.MRF.NoOpPolicy, diff --git a/lib/pleroma/web/federator/federator.ex b/lib/pleroma/web/federator/federator.ex index 252d3b009..c9b245933 100644 --- a/lib/pleroma/web/federator/federator.ex +++ b/lib/pleroma/web/federator/federator.ex @@ -13,7 +13,6 @@ defmodule Pleroma.Web.Federator do alias Pleroma.Web.Federator.Publisher alias Pleroma.Web.Federator.RetryQueue alias Pleroma.Web.OStatus - alias Pleroma.Web.Salmon alias Pleroma.Web.WebFinger alias Pleroma.Web.Websub @@ -58,10 +57,6 @@ def refresh_subscriptions do PleromaJobQueue.enqueue(:federator_outgoing, __MODULE__, [:refresh_subscriptions]) end - def publish_single_salmon(params) do - PleromaJobQueue.enqueue(:federator_outgoing, __MODULE__, [:publish_single_salmon, params]) - end - # Job Worker Callbacks def perform(:refresh_subscriptions) do @@ -95,9 +90,6 @@ def perform(:publish, activity) do if OStatus.is_representable?(activity) do Logger.info(fn -> "Sending #{activity.data["id"]} out via WebSub" end) Websub.publish(Pleroma.Web.OStatus.feed_path(actor), actor, activity) - - Logger.info(fn -> "Sending #{activity.data["id"]} out via Salmon" end) - Pleroma.Web.Salmon.publish(actor, activity) end end @@ -143,10 +135,6 @@ def perform(:incoming_ap_doc, params) do end end - def perform(:publish_single_salmon, params) do - Salmon.send_to_user(params) - end - def perform( :publish_single_websub, %{xml: _xml, topic: _topic, callback: _callback, secret: _secret} = params diff --git a/lib/pleroma/web/salmon/salmon.ex b/lib/pleroma/web/salmon/salmon.ex index 0a9e51656..7b59609c0 100644 --- a/lib/pleroma/web/salmon/salmon.ex +++ b/lib/pleroma/web/salmon/salmon.ex @@ -3,12 +3,17 @@ # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Salmon do + @behaviour Pleroma.Web.Federator.Publisher + @httpoison Application.get_env(:pleroma, :httpoison) use Bitwise + alias Pleroma.Activity alias Pleroma.Instances alias Pleroma.User + alias Pleroma.Web.ActivityPub.Visibility + alias Pleroma.Web.Federator.Publisher alias Pleroma.Web.OStatus.ActivityRepresenter alias Pleroma.Web.XML @@ -165,12 +170,12 @@ def remote_users(%{data: %{"to" => to} = data}) do end @doc "Pushes an activity to remote account." - def send_to_user(%{recipient: %{info: %{salmon: salmon}}} = params), - do: send_to_user(Map.put(params, :recipient, salmon)) + def publish_one(%{recipient: %{info: %{salmon: salmon}}} = params), + do: publish_one(Map.put(params, :recipient, salmon)) - def send_to_user(%{recipient: url, feed: feed, poster: poster} = params) when is_binary(url) do + def publish_one(%{recipient: url, feed: feed} = params) when is_binary(url) do with {:ok, %{status: code}} when code in 200..299 <- - poster.( + @httpoison.post( url, feed, [{"Content-Type", "application/magic-envelope+xml"}] @@ -184,11 +189,11 @@ def send_to_user(%{recipient: url, feed: feed, poster: poster} = params) when is e -> unless params[:unreachable_since], do: Instances.set_reachable(url) Logger.debug(fn -> "Pushing Salmon to #{url} failed, #{inspect(e)}" end) - :error + {:error, "Unreachable instance"} end end - def send_to_user(_), do: :noop + def publish_one(_), do: :noop @supported_activities [ "Create", @@ -199,13 +204,19 @@ def send_to_user(_), do: :noop "Delete" ] + def is_representable?(%Activity{data: %{"type" => type}} = activity) + when type in @supported_activities, + do: Visibility.is_public?(activity) + + def is_representable?(_), do: false + @doc """ Publishes an activity to remote accounts """ - @spec publish(User.t(), Pleroma.Activity.t(), Pleroma.HTTP.t()) :: none - def publish(user, activity, poster \\ &@httpoison.post/3) + @spec publish(User.t(), Pleroma.Activity.t()) :: none + def publish(user, activity) - def publish(%{info: %{keys: keys}} = user, %{data: %{"type" => type}} = activity, poster) + def publish(%{info: %{keys: keys}} = user, %{data: %{"type" => type}} = activity) when type in @supported_activities do feed = ActivityRepresenter.to_simple_form(activity, user, true) @@ -229,15 +240,14 @@ def publish(%{info: %{keys: keys}} = user, %{data: %{"type" => type}} = activity |> Enum.each(fn remote_user -> Logger.debug(fn -> "Sending Salmon to #{remote_user.ap_id}" end) - Pleroma.Web.Federator.publish_single_salmon(%{ + Publisher.enqueue_one(__MODULE__, %{ recipient: remote_user, feed: feed, - poster: poster, unreachable_since: reachable_urls_metadata[remote_user.info.salmon] }) end) end end - def publish(%{id: id}, _, _), do: Logger.debug(fn -> "Keys missing for user #{id}" end) + def publish(%{id: id}, _), do: Logger.debug(fn -> "Keys missing for user #{id}" end) end From fedaca15a3932897d86f73d836b6b6c2f7860f59 Mon Sep 17 00:00:00 2001 From: William Pitcock Date: Sun, 12 May 2019 04:34:55 +0000 Subject: [PATCH 11/19] websub: adapt to work as a federator publishing module --- config/config.exs | 1 + lib/pleroma/web/federator/federator.ex | 4 ---- lib/pleroma/web/websub/websub.ex | 16 +++++++++++++++- 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/config/config.exs b/config/config.exs index 3dcff3c46..6f2aaf297 100644 --- a/config/config.exs +++ b/config/config.exs @@ -214,6 +214,7 @@ federation_reachability_timeout_days: 7, federation_publisher_modules: [ Pleroma.Web.ActivityPub.Publisher, + Pleroma.Web.Websub, Pleroma.Web.Salmon ], allow_relay: true, diff --git a/lib/pleroma/web/federator/federator.ex b/lib/pleroma/web/federator/federator.ex index c9b245933..9a377da68 100644 --- a/lib/pleroma/web/federator/federator.ex +++ b/lib/pleroma/web/federator/federator.ex @@ -41,10 +41,6 @@ def publish(activity, priority \\ 1) do PleromaJobQueue.enqueue(:federator_outgoing, __MODULE__, [:publish, activity], priority) end - def publish_single_websub(websub) do - PleromaJobQueue.enqueue(:federator_outgoing, __MODULE__, [:publish_single_websub, websub]) - end - def verify_websub(websub) do PleromaJobQueue.enqueue(:federator_outgoing, __MODULE__, [:verify_websub, websub]) end diff --git a/lib/pleroma/web/websub/websub.ex b/lib/pleroma/web/websub/websub.ex index 3ffa6b416..1fb993282 100644 --- a/lib/pleroma/web/websub/websub.ex +++ b/lib/pleroma/web/websub/websub.ex @@ -4,10 +4,13 @@ defmodule Pleroma.Web.Websub do alias Ecto.Changeset + alias Pleroma.Activity alias Pleroma.Instances alias Pleroma.Repo + alias Pleroma.Web.ActivityPub.Visibility alias Pleroma.Web.Endpoint alias Pleroma.Web.Federator + alias Pleroma.Web.Federator.Publisher alias Pleroma.Web.OStatus alias Pleroma.Web.OStatus.FeedRepresenter alias Pleroma.Web.Router.Helpers @@ -18,6 +21,8 @@ defmodule Pleroma.Web.Websub do import Ecto.Query + @behaviour Pleroma.Web.Federator.Publisher + @httpoison Application.get_env(:pleroma, :httpoison) def verify(subscription, getter \\ &@httpoison.get/3) do @@ -56,6 +61,13 @@ def verify(subscription, getter \\ &@httpoison.get/3) do "Undo", "Delete" ] + + def is_representable?(%Activity{data: %{"type" => type}} = activity) + when type in @supported_activities, + do: Visibility.is_public?(activity) + + def is_representable?(_), do: false + def publish(topic, user, %{data: %{"type" => type}} = activity) when type in @supported_activities do response = @@ -88,12 +100,14 @@ def publish(topic, user, %{data: %{"type" => type}} = activity) unreachable_since: reachable_callbacks_metadata[sub.callback] } - Federator.publish_single_websub(data) + Publisher.enqueue_one(__MODULE__, data) end) end def publish(_, _, _), do: "" + def publish(actor, activity), do: publish(Pleroma.Web.OStatus.feed_path(actor), actor, activity) + def sign(secret, doc) do :crypto.hmac(:sha, secret, to_string(doc)) |> Base.encode16() |> String.downcase() end From c23276a59aa57a89e27c2e2f46d701392917b9a0 Mon Sep 17 00:00:00 2001 From: William Pitcock Date: Sun, 12 May 2019 05:01:00 +0000 Subject: [PATCH 12/19] activitypub: publisher: fixups --- lib/pleroma/web/activity_pub/publisher.ex | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/pleroma/web/activity_pub/publisher.ex b/lib/pleroma/web/activity_pub/publisher.ex index 11e54b77d..5d72299a5 100644 --- a/lib/pleroma/web/activity_pub/publisher.ex +++ b/lib/pleroma/web/activity_pub/publisher.ex @@ -26,10 +26,11 @@ defmodule Pleroma.Web.ActivityPub.Publisher do Determine if an activity can be represented by running it through Transmogrifier. """ def is_representable?(%Activity{} = activity) do - with %{} = _data <- Transmogrifier.prepare_outgoing(activity.data) do + with {:ok, _data} <- Transmogrifier.prepare_outgoing(activity.data) do true else - _e -> false + _e -> + false end end From 55fa4b812a30feddc0c60d7ade03d50193871d22 Mon Sep 17 00:00:00 2001 From: William Pitcock Date: Sun, 12 May 2019 05:01:15 +0000 Subject: [PATCH 13/19] federator: websub removal --- lib/pleroma/web/federator/federator.ex | 9 --------- 1 file changed, 9 deletions(-) diff --git a/lib/pleroma/web/federator/federator.ex b/lib/pleroma/web/federator/federator.ex index 9a377da68..8621eda95 100644 --- a/lib/pleroma/web/federator/federator.ex +++ b/lib/pleroma/web/federator/federator.ex @@ -9,10 +9,8 @@ defmodule Pleroma.Web.Federator do alias Pleroma.Web.ActivityPub.ActivityPub alias Pleroma.Web.ActivityPub.Transmogrifier alias Pleroma.Web.ActivityPub.Utils - alias Pleroma.Web.ActivityPub.Visibility alias Pleroma.Web.Federator.Publisher alias Pleroma.Web.Federator.RetryQueue - alias Pleroma.Web.OStatus alias Pleroma.Web.WebFinger alias Pleroma.Web.Websub @@ -82,13 +80,6 @@ def perform(:publish, activity) do with actor when not is_nil(actor) <- User.get_cached_by_ap_id(activity.data["actor"]) do {:ok, actor} = WebFinger.ensure_keys_present(actor) - if Visibility.is_public?(activity) do - if OStatus.is_representable?(activity) do - Logger.info(fn -> "Sending #{activity.data["id"]} out via WebSub" end) - Websub.publish(Pleroma.Web.OStatus.feed_path(actor), actor, activity) - end - end - Publisher.publish(actor, activity) end end From 2b847d4addc5d018fdb23c38e5b5afa66c8ac772 Mon Sep 17 00:00:00 2001 From: William Pitcock Date: Sun, 12 May 2019 04:08:41 +0000 Subject: [PATCH 14/19] tests: chase federator publish_single_foo removals --- test/web/federator_test.exs | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/test/web/federator_test.exs b/test/web/federator_test.exs index 52729eb50..0f43bc8f2 100644 --- a/test/web/federator_test.exs +++ b/test/web/federator_test.exs @@ -58,7 +58,7 @@ test "with relays deactivated, it does not publish to the relay", %{ describe "Targets reachability filtering in `publish`" do test_with_mock "it federates only to reachable instances via AP", - Federator, + Pleroma.Web.ActivityPub.Publisher, [:passthrough], [] do user = insert(:user) @@ -88,13 +88,18 @@ test "with relays deactivated, it does not publish to the relay", %{ {:ok, _activity} = CommonAPI.post(user, %{"status" => "HI @nick1@domain.com, @nick2@domain2.com!"}) - assert called(Federator.publish_single_ap(%{inbox: inbox1, unreachable_since: dt})) + assert called( + Pleroma.Web.ActivityPub.Publisher.publish_one(%{ + inbox: inbox1, + unreachable_since: dt + }) + ) - refute called(Federator.publish_single_ap(%{inbox: inbox2})) + refute called(Pleroma.Web.ActivityPub.Publisher.publish_one(%{inbox: inbox2})) end test_with_mock "it federates only to reachable instances via Websub", - Federator, + Pleroma.Web.Websub, [:passthrough], [] do user = insert(:user) @@ -122,17 +127,17 @@ test "with relays deactivated, it does not publish to the relay", %{ {:ok, _activity} = CommonAPI.post(user, %{"status" => "HI"}) assert called( - Federator.publish_single_websub(%{ + Pleroma.Web.Websub.publish_one(%{ callback: sub2.callback, unreachable_since: dt }) ) - refute called(Federator.publish_single_websub(%{callback: sub1.callback})) + refute called(Pleroma.Web.Websub.publish_one(%{callback: sub1.callback})) end test_with_mock "it federates only to reachable instances via Salmon", - Federator, + Pleroma.Web.Salmon, [:passthrough], [] do user = insert(:user) @@ -162,13 +167,13 @@ test "with relays deactivated, it does not publish to the relay", %{ CommonAPI.post(user, %{"status" => "HI @nick1@domain.com, @nick2@domain2.com!"}) assert called( - Federator.publish_single_salmon(%{ + Pleroma.Web.Salmon.publish_one(%{ recipient: remote_user2, unreachable_since: dt }) ) - refute called(Federator.publish_single_websub(%{recipient: remote_user1})) + refute called(Pleroma.Web.Salmon.publish_one(%{recipient: remote_user1})) end end From d823eb921b6710affe5e30a683155b645a5bd1d2 Mon Sep 17 00:00:00 2001 From: William Pitcock Date: Sun, 12 May 2019 14:35:38 +0000 Subject: [PATCH 15/19] tests: fix salmon tests --- test/web/salmon/salmon_test.exs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/test/web/salmon/salmon_test.exs b/test/web/salmon/salmon_test.exs index 7532578ca..232082779 100644 --- a/test/web/salmon/salmon_test.exs +++ b/test/web/salmon/salmon_test.exs @@ -7,7 +7,9 @@ defmodule Pleroma.Web.Salmon.SalmonTest do alias Pleroma.Activity alias Pleroma.Repo alias Pleroma.User + alias Pleroma.Web.Federator.Publisher alias Pleroma.Web.Salmon + import Mock import Pleroma.Factory @magickey "RSA.pu0s-halox4tu7wmES1FVSx6u-4wc0YrUFXcqWXZG4-27UmbCOpMQftRCldNRfyA-qLbz-eqiwQhh-1EwUvjsD4cYbAHNGHwTvDOyx5AKthQUP44ykPv7kjKGh3DWKySJvcs9tlUG87hlo7AvnMo9pwRS_Zz2CacQ-MKaXyDepk=.AQAB" @@ -77,7 +79,10 @@ test "it gets a magic key" do "RSA.uzg6r1peZU0vXGADWxGJ0PE34WvmhjUmydbX5YYdOiXfODVLwCMi1umGoqUDm-mRu4vNEdFBVJU1CpFA7dKzWgIsqsa501i2XqElmEveXRLvNRWFB6nG03Q5OUY2as8eE54BJm0p20GkMfIJGwP6TSFb-ICp3QjzbatuSPJ6xCE=.AQAB" end - test "it pushes an activity to remote accounts it's addressed to" do + test_with_mock "it pushes an activity to remote accounts it's addressed to", + Publisher, + [:passthrough], + [] do user_data = %{ info: %{ salmon: "http://test-example.org/salmon" @@ -102,10 +107,8 @@ test "it pushes an activity to remote accounts it's addressed to" do user = User.get_cached_by_ap_id(activity.data["actor"]) {:ok, user} = Pleroma.Web.WebFinger.ensure_keys_present(user) - poster = fn url, _data, _headers -> - assert url == "http://test-example.org/salmon" - end + Salmon.publish(user, activity) - Salmon.publish(user, activity, poster) + assert called(Publisher.enqueue_one(Salmon, %{recipient: mentioned_user})) end end From 582bd9d170446d3ef5312f3266d960b1e22a9d18 Mon Sep 17 00:00:00 2001 From: William Pitcock Date: Sun, 12 May 2019 14:38:40 +0000 Subject: [PATCH 16/19] tests: fix AP test failures --- test/web/activity_pub/activity_pub_test.exs | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/test/web/activity_pub/activity_pub_test.exs b/test/web/activity_pub/activity_pub_test.exs index 1e056b7ee..0f90aa1ac 100644 --- a/test/web/activity_pub/activity_pub_test.exs +++ b/test/web/activity_pub/activity_pub_test.exs @@ -10,6 +10,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do alias Pleroma.Object alias Pleroma.User alias Pleroma.Web.ActivityPub.ActivityPub + alias Pleroma.Web.ActivityPub.Publisher alias Pleroma.Web.ActivityPub.Utils alias Pleroma.Web.CommonAPI @@ -963,8 +964,7 @@ test "it filters broken threads" do private_activity_1 = Activity.get_by_ap_id_with_object(private_activity_1.data["id"]) - assert [public_activity, private_activity_1, private_activity_3] == - activities + assert [public_activity, private_activity_1, private_activity_3] == activities assert length(activities) == 3 @@ -1057,7 +1057,7 @@ test "it can create a Flag activity" do actor = insert(:user) inbox = "http://200.site/users/nick1/inbox" - assert {:ok, _} = ActivityPub.publish_one(%{inbox: inbox, json: "{}", actor: actor, id: 1}) + assert {:ok, _} = Publisher.publish_one(%{inbox: inbox, json: "{}", actor: actor, id: 1}) assert called(Instances.set_reachable(inbox)) end @@ -1070,7 +1070,7 @@ test "it can create a Flag activity" do inbox = "http://200.site/users/nick1/inbox" assert {:ok, _} = - ActivityPub.publish_one(%{ + Publisher.publish_one(%{ inbox: inbox, json: "{}", actor: actor, @@ -1089,7 +1089,7 @@ test "it can create a Flag activity" do inbox = "http://200.site/users/nick1/inbox" assert {:ok, _} = - ActivityPub.publish_one(%{ + Publisher.publish_one(%{ inbox: inbox, json: "{}", actor: actor, @@ -1107,8 +1107,7 @@ test "it can create a Flag activity" do actor = insert(:user) inbox = "http://404.site/users/nick1/inbox" - assert {:error, _} = - ActivityPub.publish_one(%{inbox: inbox, json: "{}", actor: actor, id: 1}) + assert {:error, _} = Publisher.publish_one(%{inbox: inbox, json: "{}", actor: actor, id: 1}) assert called(Instances.set_unreachable(inbox)) end @@ -1120,8 +1119,7 @@ test "it can create a Flag activity" do actor = insert(:user) inbox = "http://connrefused.site/users/nick1/inbox" - assert {:error, _} = - ActivityPub.publish_one(%{inbox: inbox, json: "{}", actor: actor, id: 1}) + assert {:error, _} = Publisher.publish_one(%{inbox: inbox, json: "{}", actor: actor, id: 1}) assert called(Instances.set_unreachable(inbox)) end @@ -1133,7 +1131,7 @@ test "it can create a Flag activity" do actor = insert(:user) inbox = "http://200.site/users/nick1/inbox" - assert {:ok, _} = ActivityPub.publish_one(%{inbox: inbox, json: "{}", actor: actor, id: 1}) + assert {:ok, _} = Publisher.publish_one(%{inbox: inbox, json: "{}", actor: actor, id: 1}) refute called(Instances.set_unreachable(inbox)) end @@ -1146,7 +1144,7 @@ test "it can create a Flag activity" do inbox = "http://connrefused.site/users/nick1/inbox" assert {:error, _} = - ActivityPub.publish_one(%{ + Publisher.publish_one(%{ inbox: inbox, json: "{}", actor: actor, From 80759f012eb2183bc24f84c4a1f2a5dbe94762ce Mon Sep 17 00:00:00 2001 From: William Pitcock Date: Sun, 12 May 2019 19:04:37 +0000 Subject: [PATCH 17/19] xml builder: properly escape quotes --- lib/xml_builder.ex | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/xml_builder.ex b/lib/xml_builder.ex index 88f8ce2a3..b58602c7b 100644 --- a/lib/xml_builder.ex +++ b/lib/xml_builder.ex @@ -35,6 +35,7 @@ def to_doc(content), do: ~s() <> to_xml(co defp make_open_tag(tag, attributes) do attributes_string = for {attribute, value} <- attributes do + value = String.replace(value, "\"", """) "#{attribute}=\"#{value}\"" end |> Enum.join(" ") From 28f7f4c6dec681ae292767623cbad553b2a0f5b5 Mon Sep 17 00:00:00 2001 From: William Pitcock Date: Sun, 12 May 2019 19:05:03 +0000 Subject: [PATCH 18/19] webfinger: build the response based on enabled federation modules --- lib/pleroma/web/activity_pub/publisher.ex | 11 ++++ lib/pleroma/web/federator/publisher.ex | 13 +++++ lib/pleroma/web/salmon/salmon.ex | 14 +++++ lib/pleroma/web/web_finger/web_finger.ex | 66 +++++++---------------- lib/pleroma/web/websub/websub.ex | 15 ++++++ 5 files changed, 71 insertions(+), 48 deletions(-) diff --git a/lib/pleroma/web/activity_pub/publisher.ex b/lib/pleroma/web/activity_pub/publisher.ex index 5d72299a5..5c97485c8 100644 --- a/lib/pleroma/web/activity_pub/publisher.ex +++ b/lib/pleroma/web/activity_pub/publisher.ex @@ -136,4 +136,15 @@ def publish(%User{} = actor, %Activity{} = activity) do ) end) end + + def gather_webfinger_links(%User{} = user) do + [ + %{"rel" => "self", "type" => "application/activity+json", "href" => user.ap_id}, + %{ + "rel" => "self", + "type" => "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"", + "href" => user.ap_id + } + ] + end end diff --git a/lib/pleroma/web/federator/publisher.ex b/lib/pleroma/web/federator/publisher.ex index 67f4b7ba7..112a0574f 100644 --- a/lib/pleroma/web/federator/publisher.ex +++ b/lib/pleroma/web/federator/publisher.ex @@ -66,4 +66,17 @@ def publish(%User{} = user, %Activity{} = activity) do :ok end + + @doc """ + Gathers links used by an outgoing federation module for WebFinger output. + """ + @callback gather_webfinger_links(Pleroma.User.t()) :: list() + + @spec gather_webfinger_links(Pleroma.User.t()) :: list() + def gather_webfinger_links(%User{} = user) do + Config.get([:instance, :federation_publisher_modules]) + |> Enum.reduce([], fn module, links -> + links ++ module.gather_webfinger_links(user) + end) + end end diff --git a/lib/pleroma/web/salmon/salmon.ex b/lib/pleroma/web/salmon/salmon.ex index 7b59609c0..92e85b5e9 100644 --- a/lib/pleroma/web/salmon/salmon.ex +++ b/lib/pleroma/web/salmon/salmon.ex @@ -14,6 +14,7 @@ defmodule Pleroma.Web.Salmon do alias Pleroma.User alias Pleroma.Web.ActivityPub.Visibility alias Pleroma.Web.Federator.Publisher + alias Pleroma.Web.OStatus alias Pleroma.Web.OStatus.ActivityRepresenter alias Pleroma.Web.XML @@ -250,4 +251,17 @@ def publish(%{info: %{keys: keys}} = user, %{data: %{"type" => type}} = activity end def publish(%{id: id}, _), do: Logger.debug(fn -> "Keys missing for user #{id}" end) + + def gather_webfinger_links(%User{} = user) do + {:ok, _private, public} = keys_from_pem(user.info.keys) + magic_key = encode_key(public) + + [ + %{"rel" => "salmon", "href" => OStatus.salmon_path(user)}, + %{ + "rel" => "magic-public-key", + "href" => "data:application/magic-public-key,#{magic_key}" + } + ] + end end diff --git a/lib/pleroma/web/web_finger/web_finger.ex b/lib/pleroma/web/web_finger/web_finger.ex index a3b0bf999..3a3b98a10 100644 --- a/lib/pleroma/web/web_finger/web_finger.ex +++ b/lib/pleroma/web/web_finger/web_finger.ex @@ -7,7 +7,7 @@ defmodule Pleroma.Web.WebFinger do alias Pleroma.User alias Pleroma.Web - alias Pleroma.Web.OStatus + alias Pleroma.Web.Federator.Publisher alias Pleroma.Web.Salmon alias Pleroma.Web.XML alias Pleroma.XmlBuilder @@ -50,70 +50,40 @@ def webfinger(resource, fmt) when fmt in ["XML", "JSON"] do end end + defp gather_links(%User{} = user) do + [ + %{ + "rel" => "http://webfinger.net/rel/profile-page", + "type" => "text/html", + "href" => user.ap_id + } + ] ++ Publisher.gather_webfinger_links(user) + end + def represent_user(user, "JSON") do {:ok, user} = ensure_keys_present(user) - {:ok, _private, public} = Salmon.keys_from_pem(user.info.keys) - magic_key = Salmon.encode_key(public) %{ "subject" => "acct:#{user.nickname}@#{Pleroma.Web.Endpoint.host()}", "aliases" => [user.ap_id], - "links" => [ - %{ - "rel" => "http://schemas.google.com/g/2010#updates-from", - "type" => "application/atom+xml", - "href" => OStatus.feed_path(user) - }, - %{ - "rel" => "http://webfinger.net/rel/profile-page", - "type" => "text/html", - "href" => user.ap_id - }, - %{"rel" => "salmon", "href" => OStatus.salmon_path(user)}, - %{ - "rel" => "magic-public-key", - "href" => "data:application/magic-public-key,#{magic_key}" - }, - %{"rel" => "self", "type" => "application/activity+json", "href" => user.ap_id}, - %{ - "rel" => "self", - "type" => "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"", - "href" => user.ap_id - }, - %{ - "rel" => "http://ostatus.org/schema/1.0/subscribe", - "template" => OStatus.remote_follow_path() - } - ] + "links" => gather_links(user) } end def represent_user(user, "XML") do {:ok, user} = ensure_keys_present(user) - {:ok, _private, public} = Salmon.keys_from_pem(user.info.keys) - magic_key = Salmon.encode_key(public) + + links = + gather_links(user) + |> Enum.map(fn link -> {:Link, link} end) { :XRD, %{xmlns: "http://docs.oasis-open.org/ns/xri/xrd-1.0"}, [ {:Subject, "acct:#{user.nickname}@#{Pleroma.Web.Endpoint.host()}"}, - {:Alias, user.ap_id}, - {:Link, - %{ - rel: "http://schemas.google.com/g/2010#updates-from", - type: "application/atom+xml", - href: OStatus.feed_path(user) - }}, - {:Link, - %{rel: "http://webfinger.net/rel/profile-page", type: "text/html", href: user.ap_id}}, - {:Link, %{rel: "salmon", href: OStatus.salmon_path(user)}}, - {:Link, - %{rel: "magic-public-key", href: "data:application/magic-public-key,#{magic_key}"}}, - {:Link, %{rel: "self", type: "application/activity+json", href: user.ap_id}}, - {:Link, - %{rel: "http://ostatus.org/schema/1.0/subscribe", template: OStatus.remote_follow_path()}} - ] + {:Alias, user.ap_id} + ] ++ links } |> XmlBuilder.to_doc() end diff --git a/lib/pleroma/web/websub/websub.ex b/lib/pleroma/web/websub/websub.ex index 1fb993282..2ce6dcc19 100644 --- a/lib/pleroma/web/websub/websub.ex +++ b/lib/pleroma/web/websub/websub.ex @@ -7,6 +7,7 @@ defmodule Pleroma.Web.Websub do alias Pleroma.Activity alias Pleroma.Instances alias Pleroma.Repo + alias Pleroma.User alias Pleroma.Web.ActivityPub.Visibility alias Pleroma.Web.Endpoint alias Pleroma.Web.Federator @@ -313,4 +314,18 @@ def publish_one(%{xml: xml, topic: topic, callback: callback, secret: secret} = {:error, response} end end + + def gather_webfinger_links(%User{} = user) do + [ + %{ + "rel" => "http://schemas.google.com/g/2010#updates-from", + "type" => "application/atom+xml", + "href" => OStatus.feed_path(user) + }, + %{ + "rel" => "http://ostatus.org/schema/1.0/subscribe", + "template" => OStatus.remote_follow_path() + } + ] + end end From 44b182732efe2d8571aa54e6062637e7e42021ce Mon Sep 17 00:00:00 2001 From: William Pitcock Date: Sun, 12 May 2019 19:15:29 +0000 Subject: [PATCH 19/19] nodeinfo: gather supported protocol names from federation modules --- lib/pleroma/web/activity_pub/publisher.ex | 2 ++ lib/pleroma/web/federator/publisher.ex | 13 +++++++++++++ lib/pleroma/web/nodeinfo/nodeinfo_controller.ex | 3 ++- lib/pleroma/web/salmon/salmon.ex | 2 ++ lib/pleroma/web/websub/websub.ex | 2 ++ 5 files changed, 21 insertions(+), 1 deletion(-) diff --git a/lib/pleroma/web/activity_pub/publisher.ex b/lib/pleroma/web/activity_pub/publisher.ex index 5c97485c8..8e3af0a81 100644 --- a/lib/pleroma/web/activity_pub/publisher.ex +++ b/lib/pleroma/web/activity_pub/publisher.ex @@ -147,4 +147,6 @@ def gather_webfinger_links(%User{} = user) do } ] end + + def gather_nodeinfo_protocol_names, do: ["activitypub"] end diff --git a/lib/pleroma/web/federator/publisher.ex b/lib/pleroma/web/federator/publisher.ex index 112a0574f..916bcdcba 100644 --- a/lib/pleroma/web/federator/publisher.ex +++ b/lib/pleroma/web/federator/publisher.ex @@ -79,4 +79,17 @@ def gather_webfinger_links(%User{} = user) do links ++ module.gather_webfinger_links(user) end) end + + @doc """ + Gathers nodeinfo protocol names supported by the federation module. + """ + @callback gather_nodeinfo_protocol_names() :: list() + + @spec gather_nodeinfo_protocol_names() :: list() + def gather_nodeinfo_protocol_names do + Config.get([:instance, :federation_publisher_modules]) + |> Enum.reduce([], fn module, links -> + links ++ module.gather_nodeinfo_protocol_names() + end) + end end diff --git a/lib/pleroma/web/nodeinfo/nodeinfo_controller.ex b/lib/pleroma/web/nodeinfo/nodeinfo_controller.ex index 216a962bd..3bf2a0fbc 100644 --- a/lib/pleroma/web/nodeinfo/nodeinfo_controller.ex +++ b/lib/pleroma/web/nodeinfo/nodeinfo_controller.ex @@ -10,6 +10,7 @@ defmodule Pleroma.Web.Nodeinfo.NodeinfoController do alias Pleroma.User alias Pleroma.Web alias Pleroma.Web.ActivityPub.MRF + alias Pleroma.Web.Federator.Publisher plug(Pleroma.Web.FederatingPlug) @@ -137,7 +138,7 @@ def raw_nodeinfo do name: Pleroma.Application.name() |> String.downcase(), version: Pleroma.Application.version() }, - protocols: ["ostatus", "activitypub"], + protocols: Publisher.gather_nodeinfo_protocol_names(), services: %{ inbound: [], outbound: [] diff --git a/lib/pleroma/web/salmon/salmon.ex b/lib/pleroma/web/salmon/salmon.ex index 92e85b5e9..42709ab47 100644 --- a/lib/pleroma/web/salmon/salmon.ex +++ b/lib/pleroma/web/salmon/salmon.ex @@ -264,4 +264,6 @@ def gather_webfinger_links(%User{} = user) do } ] end + + def gather_nodeinfo_protocol_names, do: [] end diff --git a/lib/pleroma/web/websub/websub.ex b/lib/pleroma/web/websub/websub.ex index 2ce6dcc19..7ad0414ab 100644 --- a/lib/pleroma/web/websub/websub.ex +++ b/lib/pleroma/web/websub/websub.ex @@ -328,4 +328,6 @@ def gather_webfinger_links(%User{} = user) do } ] end + + def gather_nodeinfo_protocol_names, do: ["ostatus"] end