2018-12-24 13:34:45 +01:00
|
|
|
# Portions of this file are derived from Pleroma:
|
|
|
|
# Copyright © 2017-2018 Pleroma Authors <https://pleroma.social>
|
|
|
|
# SPDX-License-Identifier: AGPL-3.0-only
|
2018-12-27 11:24:04 +01:00
|
|
|
# Upstream: https://git.pleroma.social/pleroma/pleroma/blob/develop/lib/pleroma/web/web_finger/web_finger.ex
|
2018-12-24 13:34:45 +01:00
|
|
|
|
2020-01-22 22:40:40 +01:00
|
|
|
defmodule Mobilizon.Federation.WebFinger do
|
2018-06-14 18:15:27 +02:00
|
|
|
@moduledoc """
|
2020-01-22 02:14:42 +01:00
|
|
|
Performs the WebFinger requests and responses (JSON only).
|
2018-06-14 18:15:27 +02:00
|
|
|
"""
|
2018-05-17 11:32:23 +02:00
|
|
|
|
2018-10-11 17:37:39 +02:00
|
|
|
alias Mobilizon.Actors
|
2018-11-28 10:49:16 +01:00
|
|
|
alias Mobilizon.Actors.Actor
|
2020-06-15 11:13:20 +02:00
|
|
|
alias Mobilizon.Federation.ActivityPub
|
2020-01-22 22:40:40 +01:00
|
|
|
alias Mobilizon.Federation.WebFinger.XmlBuilder
|
2020-01-28 19:18:33 +01:00
|
|
|
alias Mobilizon.Web.Endpoint
|
2019-12-20 13:04:34 +01:00
|
|
|
alias Mobilizon.Web.Router.Helpers, as: Routes
|
2018-05-17 11:32:23 +02:00
|
|
|
require Jason
|
|
|
|
require Logger
|
|
|
|
|
2020-02-18 08:57:00 +01:00
|
|
|
@http_options [
|
2020-07-30 17:57:20 +02:00
|
|
|
adapter: [
|
|
|
|
follow_redirect: true,
|
|
|
|
ssl: [{:versions, [:"tlsv1.2"]}]
|
|
|
|
]
|
2020-02-18 08:57:00 +01:00
|
|
|
]
|
|
|
|
|
2018-05-17 11:32:23 +02:00
|
|
|
def host_meta do
|
2020-01-28 19:18:33 +01:00
|
|
|
base_url = Endpoint.url()
|
2018-05-17 11:32:23 +02:00
|
|
|
|
|
|
|
{
|
|
|
|
:XRD,
|
|
|
|
%{xmlns: "http://docs.oasis-open.org/ns/xri/xrd-1.0"},
|
|
|
|
{
|
|
|
|
:Link,
|
|
|
|
%{
|
|
|
|
rel: "lrdd",
|
|
|
|
type: "application/xrd+xml",
|
|
|
|
template: "#{base_url}/.well-known/webfinger?resource={uri}"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|> XmlBuilder.to_doc()
|
|
|
|
end
|
|
|
|
|
|
|
|
def webfinger(resource, "JSON") do
|
2020-01-28 19:18:33 +01:00
|
|
|
host = Endpoint.host()
|
2018-05-18 09:56:21 +02:00
|
|
|
regex = ~r/(acct:)?(?<name>\w+)@#{host}/
|
2018-05-17 11:32:23 +02:00
|
|
|
|
2018-11-28 10:49:16 +01:00
|
|
|
with %{"name" => name} <- Regex.named_captures(regex, resource),
|
|
|
|
%Actor{} = actor <- Actors.get_local_actor_by_name(name) do
|
2018-11-27 18:42:56 +01:00
|
|
|
{:ok, represent_actor(actor, "JSON")}
|
2018-05-17 11:32:23 +02:00
|
|
|
else
|
|
|
|
_e ->
|
2020-06-15 11:13:20 +02:00
|
|
|
case ActivityPub.get_or_fetch_actor_by_url(resource) do
|
2019-07-23 18:06:22 +02:00
|
|
|
{:ok, %Actor{} = actor} when not is_nil(actor) ->
|
|
|
|
{:ok, represent_actor(actor, "JSON")}
|
|
|
|
|
2018-05-17 11:32:23 +02:00
|
|
|
_e ->
|
2018-11-27 18:42:56 +01:00
|
|
|
{:error, "Couldn't find actor"}
|
2018-05-17 11:32:23 +02:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2018-11-27 18:42:56 +01:00
|
|
|
@spec represent_actor(Actor.t()) :: struct()
|
|
|
|
def represent_actor(actor), do: represent_actor(actor, "JSON")
|
|
|
|
|
2019-02-22 16:11:57 +01:00
|
|
|
@spec represent_actor(Actor.t(), String.t()) :: struct()
|
2018-11-27 18:42:56 +01:00
|
|
|
def represent_actor(actor, "JSON") do
|
2018-05-17 11:32:23 +02:00
|
|
|
%{
|
2020-01-28 19:18:33 +01:00
|
|
|
"subject" => "acct:#{actor.preferred_username}@#{Endpoint.host()}",
|
2018-11-27 18:42:56 +01:00
|
|
|
"aliases" => [actor.url],
|
2018-05-17 11:32:23 +02:00
|
|
|
"links" => [
|
2018-11-27 18:42:56 +01:00
|
|
|
%{"rel" => "self", "type" => "application/activity+json", "href" => actor.url},
|
2018-11-07 16:45:11 +01:00
|
|
|
%{
|
|
|
|
"rel" => "https://webfinger.net/rel/profile-page/",
|
|
|
|
"type" => "text/html",
|
2018-11-27 18:42:56 +01:00
|
|
|
"href" => actor.url
|
2019-12-20 13:04:34 +01:00
|
|
|
},
|
|
|
|
%{
|
|
|
|
"rel" => "http://ostatus.org/schema/1.0/subscribe",
|
|
|
|
"template" => "#{Routes.page_url(Endpoint, :interact, uri: nil)}{uri}"
|
2018-11-07 16:45:11 +01:00
|
|
|
}
|
2018-05-17 11:32:23 +02:00
|
|
|
]
|
|
|
|
}
|
|
|
|
end
|
|
|
|
|
|
|
|
defp webfinger_from_json(doc) do
|
|
|
|
data =
|
|
|
|
Enum.reduce(doc["links"], %{"subject" => doc["subject"]}, fn link, data ->
|
|
|
|
case {link["type"], link["rel"]} do
|
|
|
|
{"application/activity+json", "self"} ->
|
|
|
|
Map.put(data, "url", link["href"])
|
2018-07-27 10:45:35 +02:00
|
|
|
|
2018-05-17 11:32:23 +02:00
|
|
|
_ ->
|
2018-07-27 10:45:35 +02:00
|
|
|
Logger.debug(fn ->
|
2018-06-14 18:15:27 +02:00
|
|
|
"Unhandled type: #{inspect(link["type"])}"
|
2018-07-27 10:45:35 +02:00
|
|
|
end)
|
|
|
|
|
2018-05-17 11:32:23 +02:00
|
|
|
data
|
|
|
|
end
|
|
|
|
end)
|
|
|
|
|
|
|
|
{:ok, data}
|
|
|
|
end
|
|
|
|
|
2018-05-18 09:56:21 +02:00
|
|
|
def finger(actor) do
|
|
|
|
actor = String.trim_leading(actor, "@")
|
2018-05-17 11:32:23 +02:00
|
|
|
|
|
|
|
domain =
|
2019-07-23 18:06:22 +02:00
|
|
|
case String.split(actor, "@") do
|
|
|
|
[_name, domain] ->
|
|
|
|
domain
|
|
|
|
|
2018-05-17 11:32:23 +02:00
|
|
|
_e ->
|
2018-05-18 09:56:21 +02:00
|
|
|
URI.parse(actor).host
|
2018-05-17 11:32:23 +02:00
|
|
|
end
|
|
|
|
|
2018-07-27 10:45:35 +02:00
|
|
|
address = "http://#{domain}/.well-known/webfinger?resource=acct:#{actor}"
|
|
|
|
|
|
|
|
Logger.debug(inspect(address))
|
2018-05-17 11:32:23 +02:00
|
|
|
|
2018-11-07 16:19:44 +01:00
|
|
|
with false <- is_nil(domain),
|
2020-07-09 17:24:28 +02:00
|
|
|
{:ok, %{} = response} <-
|
|
|
|
Tesla.get(
|
2018-07-27 10:45:35 +02:00
|
|
|
address,
|
2020-07-09 17:24:28 +02:00
|
|
|
headers: [
|
|
|
|
{"accept", "application/json, application/activity+json, application/jrd+json"}
|
|
|
|
],
|
|
|
|
opts: @http_options
|
2018-07-27 10:45:35 +02:00
|
|
|
),
|
2020-07-09 17:24:28 +02:00
|
|
|
%{status: status, body: body} when status in 200..299 <- response,
|
2018-11-07 16:30:55 +01:00
|
|
|
{:ok, doc} <- Jason.decode(body) do
|
2018-07-27 10:45:35 +02:00
|
|
|
webfinger_from_json(doc)
|
2018-05-17 11:32:23 +02:00
|
|
|
else
|
|
|
|
e ->
|
2018-05-18 09:56:21 +02:00
|
|
|
Logger.debug(fn -> "Couldn't finger #{actor}" end)
|
2018-05-17 11:32:23 +02:00
|
|
|
Logger.debug(fn -> inspect(e) end)
|
|
|
|
{:error, e}
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|