respect content-type header in finger request
This commit is contained in:
parent
a9bc652ab9
commit
d7e51206a2
5 changed files with 141 additions and 69 deletions
|
@ -94,52 +94,56 @@ def represent_user(user, "XML") do
|
||||||
|> XmlBuilder.to_doc()
|
|> XmlBuilder.to_doc()
|
||||||
end
|
end
|
||||||
|
|
||||||
defp webfinger_from_xml(doc) do
|
defp webfinger_from_xml(body) do
|
||||||
subject = XML.string_from_xpath("//Subject", doc)
|
with {:ok, doc} <- XML.parse_document(body) do
|
||||||
|
subject = XML.string_from_xpath("//Subject", doc)
|
||||||
|
|
||||||
subscribe_address =
|
subscribe_address =
|
||||||
~s{//Link[@rel="http://ostatus.org/schema/1.0/subscribe"]/@template}
|
~s{//Link[@rel="http://ostatus.org/schema/1.0/subscribe"]/@template}
|
||||||
|> XML.string_from_xpath(doc)
|
|> XML.string_from_xpath(doc)
|
||||||
|
|
||||||
ap_id =
|
ap_id =
|
||||||
~s{//Link[@rel="self" and @type="application/activity+json"]/@href}
|
~s{//Link[@rel="self" and @type="application/activity+json"]/@href}
|
||||||
|> XML.string_from_xpath(doc)
|
|> XML.string_from_xpath(doc)
|
||||||
|
|
||||||
data = %{
|
data = %{
|
||||||
"subject" => subject,
|
"subject" => subject,
|
||||||
"subscribe_address" => subscribe_address,
|
"subscribe_address" => subscribe_address,
|
||||||
"ap_id" => ap_id
|
"ap_id" => ap_id
|
||||||
}
|
}
|
||||||
|
|
||||||
{:ok, data}
|
{:ok, data}
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp webfinger_from_json(doc) do
|
defp webfinger_from_json(body) do
|
||||||
data =
|
with {:ok, doc} <- Jason.decode(body) do
|
||||||
Enum.reduce(doc["links"], %{"subject" => doc["subject"]}, fn link, data ->
|
data =
|
||||||
case {link["type"], link["rel"]} do
|
Enum.reduce(doc["links"], %{"subject" => doc["subject"]}, fn link, data ->
|
||||||
{"application/activity+json", "self"} ->
|
case {link["type"], link["rel"]} do
|
||||||
Map.put(data, "ap_id", link["href"])
|
{"application/activity+json", "self"} ->
|
||||||
|
Map.put(data, "ap_id", link["href"])
|
||||||
|
|
||||||
{"application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"", "self"} ->
|
{"application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"", "self"} ->
|
||||||
Map.put(data, "ap_id", link["href"])
|
Map.put(data, "ap_id", link["href"])
|
||||||
|
|
||||||
{nil, "http://ostatus.org/schema/1.0/subscribe"} ->
|
{nil, "http://ostatus.org/schema/1.0/subscribe"} ->
|
||||||
Map.put(data, "subscribe_address", link["template"])
|
Map.put(data, "subscribe_address", link["template"])
|
||||||
|
|
||||||
_ ->
|
_ ->
|
||||||
Logger.debug("Unhandled type: #{inspect(link["type"])}")
|
Logger.debug("Unhandled type: #{inspect(link["type"])}")
|
||||||
data
|
data
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
{:ok, data}
|
{:ok, data}
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def get_template_from_xml(body) do
|
def get_template_from_xml(body) do
|
||||||
xpath = "//Link[@rel='lrdd']/@template"
|
xpath = "//Link[@rel='lrdd']/@template"
|
||||||
|
|
||||||
with doc when doc != :error <- XML.parse_document(body),
|
with {:ok, doc} <- XML.parse_document(body),
|
||||||
template when template != nil <- XML.string_from_xpath(xpath, doc) do
|
template when template != nil <- XML.string_from_xpath(xpath, doc) do
|
||||||
{:ok, template}
|
{:ok, template}
|
||||||
end
|
end
|
||||||
|
@ -192,15 +196,23 @@ def finger(account) do
|
||||||
address,
|
address,
|
||||||
[{"accept", "application/xrd+xml,application/jrd+json"}]
|
[{"accept", "application/xrd+xml,application/jrd+json"}]
|
||||||
),
|
),
|
||||||
{:ok, %{status: status, body: body}} when status in 200..299 <- response do
|
{:ok, %{status: status, body: body, headers: headers}} when status in 200..299 <-
|
||||||
doc = XML.parse_document(body)
|
response do
|
||||||
|
case List.keyfind(headers, "content-type", 0) do
|
||||||
|
{_, content_type} ->
|
||||||
|
case Plug.Conn.Utils.media_type(content_type) do
|
||||||
|
{:ok, "application", subtype, _} when subtype in ~w(xrd+xml xml) ->
|
||||||
|
webfinger_from_xml(body)
|
||||||
|
|
||||||
if doc != :error do
|
{:ok, "application", subtype, _} when subtype in ~w(jrd+json json) ->
|
||||||
webfinger_from_xml(doc)
|
webfinger_from_json(body)
|
||||||
else
|
|
||||||
with {:ok, doc} <- Jason.decode(body) do
|
_ ->
|
||||||
webfinger_from_json(doc)
|
{:error, {:content_type, content_type}}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
{:error, {:content_type, nil}}
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
e ->
|
e ->
|
||||||
|
|
|
@ -31,7 +31,7 @@ def parse_document(text) do
|
||||||
|> :binary.bin_to_list()
|
|> :binary.bin_to_list()
|
||||||
|> :xmerl_scan.string(quiet: true)
|
|> :xmerl_scan.string(quiet: true)
|
||||||
|
|
||||||
doc
|
{:ok, doc}
|
||||||
rescue
|
rescue
|
||||||
_e ->
|
_e ->
|
||||||
Logger.debug("Couldn't parse XML: #{inspect(text)}")
|
Logger.debug("Couldn't parse XML: #{inspect(text)}")
|
||||||
|
|
|
@ -1,4 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<XRD xmlns="http://docs.oasis-open.org/ns/xri/xrd-1.0">
|
|
||||||
<Link rel="lrdd" template="https://zetsubou.xn--q9jyb4c/.well-known/webfinger?resource={uri}" type="application/xrd+xml" />
|
|
||||||
</XRD>
|
|
|
@ -45,6 +45,26 @@ test "returns error for nonsensical input" do
|
||||||
assert {:error, _} = WebFinger.finger("pleroma.social")
|
assert {:error, _} = WebFinger.finger("pleroma.social")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "returns error when there is no content-type header" do
|
||||||
|
Tesla.Mock.mock(fn
|
||||||
|
%{url: "http://social.heldscal.la/.well-known/host-meta"} ->
|
||||||
|
{:ok,
|
||||||
|
%Tesla.Env{
|
||||||
|
status: 200,
|
||||||
|
body: File.read!("test/fixtures/tesla_mock/social.heldscal.la_host_meta")
|
||||||
|
}}
|
||||||
|
|
||||||
|
%{
|
||||||
|
url:
|
||||||
|
"https://social.heldscal.la/.well-known/webfinger?resource=acct:invalid_content@social.heldscal.la"
|
||||||
|
} ->
|
||||||
|
{:ok, %Tesla.Env{status: 200, body: ""}}
|
||||||
|
end)
|
||||||
|
|
||||||
|
user = "invalid_content@social.heldscal.la"
|
||||||
|
assert {:error, {:content_type, nil}} = WebFinger.finger(user)
|
||||||
|
end
|
||||||
|
|
||||||
test "returns error when fails parse xml or json" do
|
test "returns error when fails parse xml or json" do
|
||||||
user = "invalid_content@social.heldscal.la"
|
user = "invalid_content@social.heldscal.la"
|
||||||
assert {:error, %Jason.DecodeError{}} = WebFinger.finger(user)
|
assert {:error, %Jason.DecodeError{}} = WebFinger.finger(user)
|
||||||
|
@ -113,5 +133,52 @@ test "it works with idna domains as link" do
|
||||||
ap_id = "https://" <> to_string(:idna.encode("zetsubou.みんな")) <> "/users/lain"
|
ap_id = "https://" <> to_string(:idna.encode("zetsubou.みんな")) <> "/users/lain"
|
||||||
{:ok, _data} = WebFinger.finger(ap_id)
|
{:ok, _data} = WebFinger.finger(ap_id)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "respects json content-type" do
|
||||||
|
Tesla.Mock.mock(fn
|
||||||
|
%{
|
||||||
|
url:
|
||||||
|
"https://mastodon.social/.well-known/webfinger?resource=acct:emelie@mastodon.social"
|
||||||
|
} ->
|
||||||
|
{:ok,
|
||||||
|
%Tesla.Env{
|
||||||
|
status: 200,
|
||||||
|
body: File.read!("test/fixtures/tesla_mock/webfinger_emelie.json"),
|
||||||
|
headers: [{"content-type", "application/jrd+json"}]
|
||||||
|
}}
|
||||||
|
|
||||||
|
%{url: "http://mastodon.social/.well-known/host-meta"} ->
|
||||||
|
{:ok,
|
||||||
|
%Tesla.Env{
|
||||||
|
status: 200,
|
||||||
|
body: File.read!("test/fixtures/tesla_mock/mastodon.social_host_meta")
|
||||||
|
}}
|
||||||
|
end)
|
||||||
|
|
||||||
|
{:ok, _data} = WebFinger.finger("emelie@mastodon.social")
|
||||||
|
end
|
||||||
|
|
||||||
|
test "respects xml content-type" do
|
||||||
|
Tesla.Mock.mock(fn
|
||||||
|
%{
|
||||||
|
url: "https://pawoo.net/.well-known/webfinger?resource=acct:pekorino@pawoo.net"
|
||||||
|
} ->
|
||||||
|
{:ok,
|
||||||
|
%Tesla.Env{
|
||||||
|
status: 200,
|
||||||
|
body: File.read!("test/fixtures/tesla_mock/https___pawoo.net_users_pekorino.xml"),
|
||||||
|
headers: [{"content-type", "application/xrd+xml"}]
|
||||||
|
}}
|
||||||
|
|
||||||
|
%{url: "http://pawoo.net/.well-known/host-meta"} ->
|
||||||
|
{:ok,
|
||||||
|
%Tesla.Env{
|
||||||
|
status: 200,
|
||||||
|
body: File.read!("test/fixtures/tesla_mock/pawoo.net_host_meta")
|
||||||
|
}}
|
||||||
|
end)
|
||||||
|
|
||||||
|
{:ok, _data} = WebFinger.finger("pekorino@pawoo.net")
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -122,7 +122,7 @@ def get(
|
||||||
%Tesla.Env{
|
%Tesla.Env{
|
||||||
status: 200,
|
status: 200,
|
||||||
body: File.read!("test/fixtures/tesla_mock/mike@osada.macgirvin.com.json"),
|
body: File.read!("test/fixtures/tesla_mock/mike@osada.macgirvin.com.json"),
|
||||||
headers: activitypub_object_headers()
|
headers: [{"content-type", "application/jrd+json"}]
|
||||||
}}
|
}}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -187,7 +187,8 @@ def get(
|
||||||
{:ok,
|
{:ok,
|
||||||
%Tesla.Env{
|
%Tesla.Env{
|
||||||
status: 200,
|
status: 200,
|
||||||
body: File.read!("test/fixtures/tesla_mock/lain_squeet.me_webfinger.xml")
|
body: File.read!("test/fixtures/tesla_mock/lain_squeet.me_webfinger.xml"),
|
||||||
|
headers: [{"content-type", "application/xrd+xml"}]
|
||||||
}}
|
}}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -526,22 +527,6 @@ def get(
|
||||||
}}
|
}}
|
||||||
end
|
end
|
||||||
|
|
||||||
def get("http://zetsubou.xn--q9jyb4c/.well-known/host-meta", _, _, _) do
|
|
||||||
{:ok,
|
|
||||||
%Tesla.Env{
|
|
||||||
status: 200,
|
|
||||||
body: File.read!("test/fixtures/tesla_mock/xn--q9jyb4c_host_meta")
|
|
||||||
}}
|
|
||||||
end
|
|
||||||
|
|
||||||
def get("https://zetsubou.xn--q9jyb4c/.well-known/host-meta", _, _, _) do
|
|
||||||
{:ok,
|
|
||||||
%Tesla.Env{
|
|
||||||
status: 200,
|
|
||||||
body: File.read!("test/fixtures/tesla_mock/xn--q9jyb4c_host_meta")
|
|
||||||
}}
|
|
||||||
end
|
|
||||||
|
|
||||||
def get("http://pleroma.soykaf.com/.well-known/host-meta", _, _, _) do
|
def get("http://pleroma.soykaf.com/.well-known/host-meta", _, _, _) do
|
||||||
{:ok,
|
{:ok,
|
||||||
%Tesla.Env{
|
%Tesla.Env{
|
||||||
|
@ -786,7 +771,8 @@ def get(
|
||||||
{:ok,
|
{:ok,
|
||||||
%Tesla.Env{
|
%Tesla.Env{
|
||||||
status: 200,
|
status: 200,
|
||||||
body: File.read!("test/fixtures/tesla_mock/shp@social.heldscal.la.xml")
|
body: File.read!("test/fixtures/tesla_mock/shp@social.heldscal.la.xml"),
|
||||||
|
headers: [{"content-type", "application/xrd+xml"}]
|
||||||
}}
|
}}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -796,7 +782,7 @@ def get(
|
||||||
_,
|
_,
|
||||||
[{"accept", "application/xrd+xml,application/jrd+json"}]
|
[{"accept", "application/xrd+xml,application/jrd+json"}]
|
||||||
) do
|
) do
|
||||||
{:ok, %Tesla.Env{status: 200, body: ""}}
|
{:ok, %Tesla.Env{status: 200, body: "", headers: [{"content-type", "application/jrd+json"}]}}
|
||||||
end
|
end
|
||||||
|
|
||||||
def get("http://framatube.org/.well-known/host-meta", _, _, _) do
|
def get("http://framatube.org/.well-known/host-meta", _, _, _) do
|
||||||
|
@ -816,7 +802,7 @@ def get(
|
||||||
{:ok,
|
{:ok,
|
||||||
%Tesla.Env{
|
%Tesla.Env{
|
||||||
status: 200,
|
status: 200,
|
||||||
headers: [{"content-type", "application/json"}],
|
headers: [{"content-type", "application/jrd+json"}],
|
||||||
body: File.read!("test/fixtures/tesla_mock/framasoft@framatube.org.json")
|
body: File.read!("test/fixtures/tesla_mock/framasoft@framatube.org.json")
|
||||||
}}
|
}}
|
||||||
end
|
end
|
||||||
|
@ -876,7 +862,7 @@ def get(
|
||||||
{:ok,
|
{:ok,
|
||||||
%Tesla.Env{
|
%Tesla.Env{
|
||||||
status: 200,
|
status: 200,
|
||||||
headers: [{"content-type", "application/json"}],
|
headers: [{"content-type", "application/jrd+json"}],
|
||||||
body: File.read!("test/fixtures/tesla_mock/kaniini@gerzilla.de.json")
|
body: File.read!("test/fixtures/tesla_mock/kaniini@gerzilla.de.json")
|
||||||
}}
|
}}
|
||||||
end
|
end
|
||||||
|
@ -1074,7 +1060,8 @@ def get(
|
||||||
{:ok,
|
{:ok,
|
||||||
%Tesla.Env{
|
%Tesla.Env{
|
||||||
status: 200,
|
status: 200,
|
||||||
body: File.read!("test/fixtures/lain.xml")
|
body: File.read!("test/fixtures/lain.xml"),
|
||||||
|
headers: [{"content-type", "application/xrd+xml"}]
|
||||||
}}
|
}}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -1087,7 +1074,16 @@ def get(
|
||||||
{:ok,
|
{:ok,
|
||||||
%Tesla.Env{
|
%Tesla.Env{
|
||||||
status: 200,
|
status: 200,
|
||||||
body: File.read!("test/fixtures/lain.xml")
|
body: File.read!("test/fixtures/lain.xml"),
|
||||||
|
headers: [{"content-type", "application/xrd+xml"}]
|
||||||
|
}}
|
||||||
|
end
|
||||||
|
|
||||||
|
def get("http://zetsubou.xn--q9jyb4c/.well-known/host-meta", _, _, _) do
|
||||||
|
{:ok,
|
||||||
|
%Tesla.Env{
|
||||||
|
status: 200,
|
||||||
|
body: File.read!("test/fixtures/host-meta-zetsubou.xn--q9jyb4c.xml")
|
||||||
}}
|
}}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -1153,7 +1149,8 @@ def get("https://mstdn.jp/.well-known/webfinger?resource=acct:kpherox@mstdn.jp",
|
||||||
{:ok,
|
{:ok,
|
||||||
%Tesla.Env{
|
%Tesla.Env{
|
||||||
status: 200,
|
status: 200,
|
||||||
body: File.read!("test/fixtures/tesla_mock/kpherox@mstdn.jp.xml")
|
body: File.read!("test/fixtures/tesla_mock/kpherox@mstdn.jp.xml"),
|
||||||
|
headers: [{"content-type", "application/xrd+xml"}]
|
||||||
}}
|
}}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue