feat(activitypub): implement FEP-2677 to identify the application actor used for federation

Instead of always assuming it will be @relay@host.tld

Closes #1367

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
This commit is contained in:
Thomas Citharel 2023-12-14 16:31:58 +01:00
parent 049ffd61b7
commit f10977a99a
No known key found for this signature in database
GPG key ID: A061B9DDE0CA0773
4 changed files with 56 additions and 5 deletions

View file

@ -13,7 +13,7 @@ defmodule Mobilizon.Federation.ActivityPub.Relay do
alias Mobilizon.Federation.ActivityPub.{Actions, Activity, Transmogrifier}
alias Mobilizon.Federation.ActivityPub.Actor, as: ActivityPubActor
alias Mobilizon.Federation.WebFinger
alias Mobilizon.Federation.{NodeInfo, WebFinger}
alias Mobilizon.GraphQL.API.Follows
alias Mobilizon.Service.Workers.Background
import Mobilizon.Federation.ActivityPub.Utils, only: [create_full_domain_string: 1]
@ -192,10 +192,10 @@ defmodule Mobilizon.Federation.ActivityPub.Relay do
check_actor(address)
!is_nil(host) ->
uri
|> create_full_domain_string()
|> then(&Kernel.<>("relay@", &1))
|> check_actor()
case NodeInfo.application_actor(host) do
nil -> check_actor("relay@#{host}")
actor_url when is_binary(actor_url) -> {:ok, actor_url}
end
true ->
{:error, :bad_url}

View file

@ -0,0 +1,33 @@
defmodule Mobilizon.Federation.NodeInfo do
@moduledoc """
Performs NodeInfo requests
"""
alias Mobilizon.Service.HTTP.WebfingerClient
require Logger
@application_uri "https://www.w3.org/ns/activitystreams#Application"
@env Application.compile_env(:mobilizon, :env)
@spec application_actor(String.t()) :: String.t() | nil
def application_actor(host) do
prefix = if @env !== :dev, do: "https", else: "http"
case WebfingerClient.get("#{prefix}://#{host}/.well-known/nodeinfo") do
{:ok, %{body: body, status: code}} when code in 200..299 ->
extract_application_actor(body)
err ->
Logger.debug("Failed to fetch NodeInfo data #{inspect(err)}")
nil
end
end
defp extract_application_actor(body) do
body
|> Enum.find(%{rel: @application_uri, href: nil}, fn %{rel: rel, href: href} ->
rel == @application_uri and is_binary(href)
end)
|> Map.get(:href)
end
end

View file

@ -7,13 +7,17 @@ defmodule Mobilizon.Web.NodeInfoController do
use Mobilizon.Web, :controller
alias Mobilizon.Config
alias Mobilizon.Federation.ActivityPub.Relay
alias Mobilizon.Service.Statistics
@node_info_supported_versions ["2.0", "2.1"]
@node_info_schema_uri "http://nodeinfo.diaspora.software/ns/schema/"
@application_uri "https://www.w3.org/ns/activitystreams#Application"
@spec schemas(Plug.Conn.t(), any) :: Plug.Conn.t()
def schemas(conn, _params) do
relay = Relay.get_actor()
links =
@node_info_supported_versions
|> Enum.map(fn version ->
@ -22,6 +26,12 @@ defmodule Mobilizon.Web.NodeInfoController do
href: url(~p"/.well-known/nodeinfo/#{version}")
}
end)
|> Kernel.++([
%{
rel: @application_uri,
href: relay.url
}
])
json(conn, %{
links: links

View file

@ -2,12 +2,16 @@ defmodule Mobilizon.Web.NodeInfoControllerTest do
use Mobilizon.Web.ConnCase
alias Mobilizon.Config
alias Mobilizon.Federation.ActivityPub.Relay
use Mobilizon.Web, :verified_routes
test "Get node info schemas", %{conn: conn} do
conn = get(conn, url(~p"/.well-known/nodeinfo"))
relay = Relay.get_actor()
relay_url = relay.url
assert json_response(conn, 200) == %{
"links" => [
%{
@ -17,6 +21,10 @@ defmodule Mobilizon.Web.NodeInfoControllerTest do
%{
"href" => url(~p"/.well-known/nodeinfo/2.1"),
"rel" => "http://nodeinfo.diaspora.software/ns/schema/2.1"
},
%{
"href" => relay_url,
"rel" => "https://www.w3.org/ns/activitystreams#Application"
}
]
}