Merge branch 'webfinger-validation' into 'develop'
Fix validate_webfinger when running a different domain for Webfinger See merge request pleroma/pleroma!4116
This commit is contained in:
commit
a8e1fc0f6a
|
@ -0,0 +1 @@
|
||||||
|
Fix validate_webfinger when running a different domain for Webfinger
|
|
@ -162,7 +162,8 @@ defp cachex_children do
|
||||||
expiration: chat_message_id_idempotency_key_expiration(),
|
expiration: chat_message_id_idempotency_key_expiration(),
|
||||||
limit: 500_000
|
limit: 500_000
|
||||||
),
|
),
|
||||||
build_cachex("rel_me", limit: 2500)
|
build_cachex("rel_me", limit: 2500),
|
||||||
|
build_cachex("host_meta", default_ttl: :timer.minutes(120), limit: 5000)
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -155,7 +155,16 @@ def get_template_from_xml(body) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@cachex Pleroma.Config.get([:cachex, :provider], Cachex)
|
||||||
def find_lrdd_template(domain) do
|
def find_lrdd_template(domain) do
|
||||||
|
@cachex.fetch!(:host_meta_cache, domain, fn _ ->
|
||||||
|
{:commit, fetch_lrdd_template(domain)}
|
||||||
|
end)
|
||||||
|
rescue
|
||||||
|
e -> {:error, "Cachex error: #{inspect(e)}"}
|
||||||
|
end
|
||||||
|
|
||||||
|
defp fetch_lrdd_template(domain) do
|
||||||
# WebFinger is restricted to HTTPS - https://tools.ietf.org/html/rfc7033#section-9.1
|
# WebFinger is restricted to HTTPS - https://tools.ietf.org/html/rfc7033#section-9.1
|
||||||
meta_url = "https://#{domain}/.well-known/host-meta"
|
meta_url = "https://#{domain}/.well-known/host-meta"
|
||||||
|
|
||||||
|
@ -168,7 +177,7 @@ def find_lrdd_template(domain) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp get_address_from_domain(domain, encoded_account) when is_binary(domain) do
|
defp get_address_from_domain(domain, "acct:" <> _ = encoded_account) when is_binary(domain) do
|
||||||
case find_lrdd_template(domain) do
|
case find_lrdd_template(domain) do
|
||||||
{:ok, template} ->
|
{:ok, template} ->
|
||||||
String.replace(template, "{uri}", encoded_account)
|
String.replace(template, "{uri}", encoded_account)
|
||||||
|
@ -178,6 +187,11 @@ defp get_address_from_domain(domain, encoded_account) when is_binary(domain) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp get_address_from_domain(domain, account) when is_binary(domain) do
|
||||||
|
encoded_account = URI.encode("acct:#{account}")
|
||||||
|
get_address_from_domain(domain, encoded_account)
|
||||||
|
end
|
||||||
|
|
||||||
defp get_address_from_domain(_, _), do: {:error, :webfinger_no_domain}
|
defp get_address_from_domain(_, _), do: {:error, :webfinger_no_domain}
|
||||||
|
|
||||||
@spec finger(String.t()) :: {:ok, map()} | {:error, any()}
|
@spec finger(String.t()) :: {:ok, map()} | {:error, any()}
|
||||||
|
@ -192,9 +206,7 @@ def finger(account) do
|
||||||
URI.parse(account).host
|
URI.parse(account).host
|
||||||
end
|
end
|
||||||
|
|
||||||
encoded_account = URI.encode("acct:#{account}")
|
with address when is_binary(address) <- get_address_from_domain(domain, account),
|
||||||
|
|
||||||
with address when is_binary(address) <- get_address_from_domain(domain, encoded_account),
|
|
||||||
{:ok, %{status: status, body: body, headers: headers}} when status in 200..299 <-
|
{:ok, %{status: status, body: body, headers: headers}} when status in 200..299 <-
|
||||||
HTTP.get(
|
HTTP.get(
|
||||||
address,
|
address,
|
||||||
|
@ -227,13 +239,15 @@ def finger(account) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp validate_webfinger(url, %{"subject" => "acct:" <> acct} = data) do
|
defp validate_webfinger(request_url, %{"subject" => "acct:" <> acct = subject} = data) do
|
||||||
with %URI{host: request_host} <- URI.parse(url),
|
with [_name, acct_host] <- String.split(acct, "@"),
|
||||||
[_name, acct_host] <- String.split(acct, "@"),
|
{_, url} <- {:address, get_address_from_domain(acct_host, subject)},
|
||||||
{_, true} <- {:hosts_match_or_subdomain, String.ends_with?(request_host, acct_host)} do
|
%URI{host: request_host} <- URI.parse(request_url),
|
||||||
|
%URI{host: acct_host} <- URI.parse(url),
|
||||||
|
{_, true} <- {:hosts_match, acct_host == request_host} do
|
||||||
{:ok, data}
|
{:ok, data}
|
||||||
else
|
else
|
||||||
_ -> {:error, {:webfinger_invalid, url, data}}
|
_ -> {:error, {:webfinger_invalid, request_url, data}}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -877,109 +877,19 @@ test "gets an existing user by nickname starting with http" do
|
||||||
setup do: clear_config([Pleroma.Web.WebFinger, :update_nickname_on_user_fetch], true)
|
setup do: clear_config([Pleroma.Web.WebFinger, :update_nickname_on_user_fetch], true)
|
||||||
|
|
||||||
test "for mastodon" do
|
test "for mastodon" do
|
||||||
Tesla.Mock.mock(fn
|
ap_id = "a@mastodon.example"
|
||||||
%{url: "https://example.com/.well-known/host-meta"} ->
|
|
||||||
%Tesla.Env{
|
|
||||||
status: 302,
|
|
||||||
headers: [{"location", "https://sub.example.com/.well-known/host-meta"}]
|
|
||||||
}
|
|
||||||
|
|
||||||
%{url: "https://sub.example.com/.well-known/host-meta"} ->
|
|
||||||
%Tesla.Env{
|
|
||||||
status: 200,
|
|
||||||
body:
|
|
||||||
"test/fixtures/webfinger/masto-host-meta.xml"
|
|
||||||
|> File.read!()
|
|
||||||
|> String.replace("{{domain}}", "sub.example.com")
|
|
||||||
}
|
|
||||||
|
|
||||||
%{url: "https://sub.example.com/.well-known/webfinger?resource=acct:a@example.com"} ->
|
|
||||||
%Tesla.Env{
|
|
||||||
status: 200,
|
|
||||||
body:
|
|
||||||
"test/fixtures/webfinger/masto-webfinger.json"
|
|
||||||
|> File.read!()
|
|
||||||
|> String.replace("{{nickname}}", "a")
|
|
||||||
|> String.replace("{{domain}}", "example.com")
|
|
||||||
|> String.replace("{{subdomain}}", "sub.example.com"),
|
|
||||||
headers: [{"content-type", "application/jrd+json"}]
|
|
||||||
}
|
|
||||||
|
|
||||||
%{url: "https://sub.example.com/users/a"} ->
|
|
||||||
%Tesla.Env{
|
|
||||||
status: 200,
|
|
||||||
body:
|
|
||||||
"test/fixtures/webfinger/masto-user.json"
|
|
||||||
|> File.read!()
|
|
||||||
|> String.replace("{{nickname}}", "a")
|
|
||||||
|> String.replace("{{domain}}", "sub.example.com"),
|
|
||||||
headers: [{"content-type", "application/activity+json"}]
|
|
||||||
}
|
|
||||||
|
|
||||||
%{url: "https://sub.example.com/users/a/collections/featured"} ->
|
|
||||||
%Tesla.Env{
|
|
||||||
status: 200,
|
|
||||||
body:
|
|
||||||
File.read!("test/fixtures/users_mock/masto_featured.json")
|
|
||||||
|> String.replace("{{domain}}", "sub.example.com")
|
|
||||||
|> String.replace("{{nickname}}", "a"),
|
|
||||||
headers: [{"content-type", "application/activity+json"}]
|
|
||||||
}
|
|
||||||
end)
|
|
||||||
|
|
||||||
ap_id = "a@example.com"
|
|
||||||
{:ok, fetched_user} = User.get_or_fetch(ap_id)
|
{:ok, fetched_user} = User.get_or_fetch(ap_id)
|
||||||
|
|
||||||
assert fetched_user.ap_id == "https://sub.example.com/users/a"
|
assert fetched_user.ap_id == "https://sub.mastodon.example/users/a"
|
||||||
assert fetched_user.nickname == "a@example.com"
|
assert fetched_user.nickname == "a@mastodon.example"
|
||||||
end
|
end
|
||||||
|
|
||||||
test "for pleroma" do
|
test "for pleroma" do
|
||||||
Tesla.Mock.mock(fn
|
ap_id = "a@pleroma.example"
|
||||||
%{url: "https://example.com/.well-known/host-meta"} ->
|
|
||||||
%Tesla.Env{
|
|
||||||
status: 302,
|
|
||||||
headers: [{"location", "https://sub.example.com/.well-known/host-meta"}]
|
|
||||||
}
|
|
||||||
|
|
||||||
%{url: "https://sub.example.com/.well-known/host-meta"} ->
|
|
||||||
%Tesla.Env{
|
|
||||||
status: 200,
|
|
||||||
body:
|
|
||||||
"test/fixtures/webfinger/pleroma-host-meta.xml"
|
|
||||||
|> File.read!()
|
|
||||||
|> String.replace("{{domain}}", "sub.example.com")
|
|
||||||
}
|
|
||||||
|
|
||||||
%{url: "https://sub.example.com/.well-known/webfinger?resource=acct:a@example.com"} ->
|
|
||||||
%Tesla.Env{
|
|
||||||
status: 200,
|
|
||||||
body:
|
|
||||||
"test/fixtures/webfinger/pleroma-webfinger.json"
|
|
||||||
|> File.read!()
|
|
||||||
|> String.replace("{{nickname}}", "a")
|
|
||||||
|> String.replace("{{domain}}", "example.com")
|
|
||||||
|> String.replace("{{subdomain}}", "sub.example.com"),
|
|
||||||
headers: [{"content-type", "application/jrd+json"}]
|
|
||||||
}
|
|
||||||
|
|
||||||
%{url: "https://sub.example.com/users/a"} ->
|
|
||||||
%Tesla.Env{
|
|
||||||
status: 200,
|
|
||||||
body:
|
|
||||||
"test/fixtures/webfinger/pleroma-user.json"
|
|
||||||
|> File.read!()
|
|
||||||
|> String.replace("{{nickname}}", "a")
|
|
||||||
|> String.replace("{{domain}}", "sub.example.com"),
|
|
||||||
headers: [{"content-type", "application/activity+json"}]
|
|
||||||
}
|
|
||||||
end)
|
|
||||||
|
|
||||||
ap_id = "a@example.com"
|
|
||||||
{:ok, fetched_user} = User.get_or_fetch(ap_id)
|
{:ok, fetched_user} = User.get_or_fetch(ap_id)
|
||||||
|
|
||||||
assert fetched_user.ap_id == "https://sub.example.com/users/a"
|
assert fetched_user.ap_id == "https://sub.pleroma.example/users/a"
|
||||||
assert fetched_user.nickname == "a@example.com"
|
assert fetched_user.nickname == "a@pleroma.example"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -1521,6 +1521,120 @@ def get("https://friends.grishka.me/users/1", _, _, _) do
|
||||||
}}
|
}}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def get("https://mastodon.example/.well-known/host-meta", _, _, _) do
|
||||||
|
{:ok,
|
||||||
|
%Tesla.Env{
|
||||||
|
status: 302,
|
||||||
|
headers: [{"location", "https://sub.mastodon.example/.well-known/host-meta"}]
|
||||||
|
}}
|
||||||
|
end
|
||||||
|
|
||||||
|
def get("https://sub.mastodon.example/.well-known/host-meta", _, _, _) do
|
||||||
|
{:ok,
|
||||||
|
%Tesla.Env{
|
||||||
|
status: 200,
|
||||||
|
body:
|
||||||
|
"test/fixtures/webfinger/masto-host-meta.xml"
|
||||||
|
|> File.read!()
|
||||||
|
|> String.replace("{{domain}}", "sub.mastodon.example")
|
||||||
|
}}
|
||||||
|
end
|
||||||
|
|
||||||
|
def get(
|
||||||
|
"https://sub.mastodon.example/.well-known/webfinger?resource=acct:a@mastodon.example",
|
||||||
|
_,
|
||||||
|
_,
|
||||||
|
_
|
||||||
|
) do
|
||||||
|
{:ok,
|
||||||
|
%Tesla.Env{
|
||||||
|
status: 200,
|
||||||
|
body:
|
||||||
|
"test/fixtures/webfinger/masto-webfinger.json"
|
||||||
|
|> File.read!()
|
||||||
|
|> String.replace("{{nickname}}", "a")
|
||||||
|
|> String.replace("{{domain}}", "mastodon.example")
|
||||||
|
|> String.replace("{{subdomain}}", "sub.mastodon.example"),
|
||||||
|
headers: [{"content-type", "application/jrd+json"}]
|
||||||
|
}}
|
||||||
|
end
|
||||||
|
|
||||||
|
def get("https://sub.mastodon.example/users/a", _, _, _) do
|
||||||
|
{:ok,
|
||||||
|
%Tesla.Env{
|
||||||
|
status: 200,
|
||||||
|
body:
|
||||||
|
"test/fixtures/webfinger/masto-user.json"
|
||||||
|
|> File.read!()
|
||||||
|
|> String.replace("{{nickname}}", "a")
|
||||||
|
|> String.replace("{{domain}}", "sub.mastodon.example"),
|
||||||
|
headers: [{"content-type", "application/activity+json"}]
|
||||||
|
}}
|
||||||
|
end
|
||||||
|
|
||||||
|
def get("https://sub.mastodon.example/users/a/collections/featured", _, _, _) do
|
||||||
|
{:ok,
|
||||||
|
%Tesla.Env{
|
||||||
|
status: 200,
|
||||||
|
body:
|
||||||
|
File.read!("test/fixtures/users_mock/masto_featured.json")
|
||||||
|
|> String.replace("{{domain}}", "sub.mastodon.example")
|
||||||
|
|> String.replace("{{nickname}}", "a"),
|
||||||
|
headers: [{"content-type", "application/activity+json"}]
|
||||||
|
}}
|
||||||
|
end
|
||||||
|
|
||||||
|
def get("https://pleroma.example/.well-known/host-meta", _, _, _) do
|
||||||
|
{:ok,
|
||||||
|
%Tesla.Env{
|
||||||
|
status: 302,
|
||||||
|
headers: [{"location", "https://sub.pleroma.example/.well-known/host-meta"}]
|
||||||
|
}}
|
||||||
|
end
|
||||||
|
|
||||||
|
def get("https://sub.pleroma.example/.well-known/host-meta", _, _, _) do
|
||||||
|
{:ok,
|
||||||
|
%Tesla.Env{
|
||||||
|
status: 200,
|
||||||
|
body:
|
||||||
|
"test/fixtures/webfinger/pleroma-host-meta.xml"
|
||||||
|
|> File.read!()
|
||||||
|
|> String.replace("{{domain}}", "sub.pleroma.example")
|
||||||
|
}}
|
||||||
|
end
|
||||||
|
|
||||||
|
def get(
|
||||||
|
"https://sub.pleroma.example/.well-known/webfinger?resource=acct:a@pleroma.example",
|
||||||
|
_,
|
||||||
|
_,
|
||||||
|
_
|
||||||
|
) do
|
||||||
|
{:ok,
|
||||||
|
%Tesla.Env{
|
||||||
|
status: 200,
|
||||||
|
body:
|
||||||
|
"test/fixtures/webfinger/pleroma-webfinger.json"
|
||||||
|
|> File.read!()
|
||||||
|
|> String.replace("{{nickname}}", "a")
|
||||||
|
|> String.replace("{{domain}}", "pleroma.example")
|
||||||
|
|> String.replace("{{subdomain}}", "sub.pleroma.example"),
|
||||||
|
headers: [{"content-type", "application/jrd+json"}]
|
||||||
|
}}
|
||||||
|
end
|
||||||
|
|
||||||
|
def get("https://sub.pleroma.example/users/a", _, _, _) do
|
||||||
|
{:ok,
|
||||||
|
%Tesla.Env{
|
||||||
|
status: 200,
|
||||||
|
body:
|
||||||
|
"test/fixtures/webfinger/pleroma-user.json"
|
||||||
|
|> File.read!()
|
||||||
|
|> String.replace("{{nickname}}", "a")
|
||||||
|
|> String.replace("{{domain}}", "sub.pleroma.example"),
|
||||||
|
headers: [{"content-type", "application/activity+json"}]
|
||||||
|
}}
|
||||||
|
end
|
||||||
|
|
||||||
def get(url, query, body, headers) do
|
def get(url, query, body, headers) do
|
||||||
{:error,
|
{:error,
|
||||||
"Mock response not implemented for GET #{inspect(url)}, #{query}, #{inspect(body)}, #{inspect(headers)}"}
|
"Mock response not implemented for GET #{inspect(url)}, #{query}, #{inspect(body)}, #{inspect(headers)}"}
|
||||||
|
|
Loading…
Reference in New Issue