From 427bac0966c551eb16eaa6595d99fc5361a32ea9 Mon Sep 17 00:00:00 2001 From: Roger Braun Date: Sat, 29 Apr 2017 19:06:01 +0200 Subject: [PATCH] Rework remote user subscription. --- lib/pleroma/web/ostatus/ostatus.ex | 44 +++++++------------ lib/pleroma/web/web_finger/web_finger.ex | 18 ++++++-- lib/pleroma/web/websub/websub.ex | 10 +++-- test/web/ostatus/ostatus_test.exs | 54 ++++++++++++------------ test/web/websub/websub_test.exs | 11 ++--- 5 files changed, 70 insertions(+), 67 deletions(-) diff --git a/lib/pleroma/web/ostatus/ostatus.ex b/lib/pleroma/web/ostatus/ostatus.ex index 90be86755..3e239179e 100644 --- a/lib/pleroma/web/ostatus/ostatus.ex +++ b/lib/pleroma/web/ostatus/ostatus.ex @@ -37,8 +37,8 @@ def handle_incoming(xml_string) do def handle_note(doc) do content_html = string_from_xpath("/entry/content[1]", doc) - [author] = :xmerl_xpath.string('/entry/author[1]', doc) - {:ok, actor} = find_or_make_user(author) + uri = string_from_xpath("/entry/author/uri[1]", doc) + {:ok, actor} = find_or_make_user(uri) context = string_from_xpath("/entry/ostatus:conversation[1]", doc) |> String.trim context = if String.length(context) > 0 do @@ -78,42 +78,30 @@ def handle_note(doc) do ActivityPub.create(to, actor, context, object, %{}, date) end - def find_or_make_user(author_doc) do - {:xmlObj, :string, uri } = :xmerl_xpath.string('string(/author[1]/uri)', author_doc) - + def find_or_make_user(uri) do query = from user in User, - where: user.local == false and fragment("? @> ?", user.info, ^%{ostatus_uri: to_string(uri)}) + where: user.local == false and fragment("? @> ?", user.info, ^%{uri: uri}) user = Repo.one(query) if is_nil(user) do - make_user(author_doc) + make_user(uri) else {:ok, user} end end - def make_user(author_doc) do - author = string_from_xpath("/author[1]/uri", author_doc) - name = string_from_xpath("/author[1]/name", author_doc) - preferredUsername = string_from_xpath("/author[1]/poco:preferredUsername", author_doc) - displayName = string_from_xpath("/author[1]/poco:displayName", author_doc) - avatar = make_avatar_object(author_doc) - - data = %{ - local: false, - name: preferredUsername || name, - nickname: displayName || name, - ap_id: author, - info: %{ - "ostatus_uri" => author, - "host" => URI.parse(author).host, - "system" => "ostatus" - }, - avatar: avatar - } - - Repo.insert(Ecto.Changeset.change(%User{}, data)) + def make_user(uri) do + with {:ok, info} <- gather_user_info(uri) do + data = %{ + local: false, + name: info.name, + nickname: info.nickname, + ap_id: info.uri, + info: info + } + Repo.insert(Ecto.Changeset.change(%User{}, data)) + end end # TODO: Just takes the first one for now. diff --git a/lib/pleroma/web/web_finger/web_finger.ex b/lib/pleroma/web/web_finger/web_finger.ex index 1d8c4d0c8..49796dab8 100644 --- a/lib/pleroma/web/web_finger/web_finger.ex +++ b/lib/pleroma/web/web_finger/web_finger.ex @@ -42,7 +42,7 @@ def represent_user(user) do # FIXME: Make this call the host-meta to find the actual address. defp webfinger_address(domain) do - "https://#{domain}/.well-known/webfinger" + "//#{domain}/.well-known/webfinger" end defp webfinger_from_xml(doc) do @@ -61,9 +61,21 @@ defp webfinger_from_xml(doc) do end def finger(account, getter \\ &HTTPoison.get/3) do - [name, domain] = String.split(account, "@") + domain = with [_name, domain] <- String.split(account, "@") do + domain + else _e -> + URI.parse(account).host + end address = webfinger_address(domain) - with {:ok, %{status_code: status_code, body: body}} when status_code in 200..299 <- getter.(address, ["Accept": "application/xrd+xml"], [params: [resource: account]]), + + # try https first + response = with {:ok, result} <- getter.("https:" <> address, ["Accept": "application/xrd+xml"], [params: [resource: account]]) do + {:ok, result} + else _ -> + getter.("http:" <> address, ["Accept": "application/xrd+xml"], [params: [resource: account]]) + end + + with {:ok, %{status_code: status_code, body: body}} when status_code in 200..299 <- response, doc <- XML.parse_document(body), {:ok, data} <- webfinger_from_xml(doc) do {:ok, data} diff --git a/lib/pleroma/web/websub/websub.ex b/lib/pleroma/web/websub/websub.ex index c1d48ad7a..8e3e0a54e 100644 --- a/lib/pleroma/web/websub/websub.ex +++ b/lib/pleroma/web/websub/websub.ex @@ -102,19 +102,21 @@ defp valid_topic(%{"hub.topic" => topic}, user) do end end - def subscribe(user, topic, requester \\ &request_subscription/1) do + def subscribe(subscriber, subscribed, requester \\ &request_subscription/1) do + topic = subscribed.info["topic"] # FIXME: Race condition, use transactions {:ok, subscription} = with subscription when not is_nil(subscription) <- Repo.get_by(WebsubClientSubscription, topic: topic) do - subscribers = [user.ap_id, subscription.subcribers] |> Enum.uniq + subscribers = [subscriber.ap_id, subscription.subscribers] |> Enum.uniq change = Ecto.Changeset.change(subscription, %{subscribers: subscribers}) Repo.update(change) else _e -> subscription = %WebsubClientSubscription{ topic: topic, - subscribers: [user.ap_id], + hub: subscribed.info["hub"], + subscribers: [subscriber.ap_id], state: "requested", secret: :crypto.strong_rand_bytes(8) |> Base.url_encode64, - user: user + user: subscribed } Repo.insert(subscription) end diff --git a/test/web/ostatus/ostatus_test.exs b/test/web/ostatus/ostatus_test.exs index 2a5156b31..4f396d940 100644 --- a/test/web/ostatus/ostatus_test.exs +++ b/test/web/ostatus/ostatus_test.exs @@ -25,40 +25,20 @@ test "handle incoming replies" do end describe "new remote user creation" do - test "make new user or find them based on an 'author' xml doc" do - incoming = File.read!("test/fixtures/user_name_only.xml") - doc = XML.parse_document(incoming) - - {:ok, user} = OStatus.find_or_make_user(doc) - - assert user.name == "lambda" - assert user.nickname == "lambda" - assert user.local == false - assert user.info["ostatus_uri"] == "http://gs.example.org:4040/index.php/user/1" - assert user.info["system"] == "ostatus" - assert user.ap_id == "http://gs.example.org:4040/index.php/user/1" - - {:ok, user_again} = OStatus.find_or_make_user(doc) - - assert user == user_again - end - test "tries to use the information in poco fields" do - incoming = File.read!("test/fixtures/user_full.xml") - doc = XML.parse_document(incoming) + # TODO make test local + uri = "https://social.heldscal.la/user/23211" - {:ok, user} = OStatus.find_or_make_user(doc) + {:ok, user} = OStatus.find_or_make_user(uri) + user = Repo.get(Pleroma.User, user.id) assert user.name == "Constance Variable" assert user.nickname == "lambadalambda" assert user.local == false - assert user.info["ostatus_uri"] == "http://gs.example.org:4040/index.php/user/1" - assert user.info["system"] == "ostatus" - assert user.ap_id == "http://gs.example.org:4040/index.php/user/1" + assert user.info["uri"] == uri + assert user.ap_id == uri - assert List.first(user.avatar["url"])["href"] == "http://gs.example.org:4040/theme/neo-gnu/default-avatar-profile.png" - - {:ok, user_again} = OStatus.find_or_make_user(doc) + {:ok, user_again} = OStatus.find_or_make_user(uri) assert user == user_again end @@ -84,5 +64,25 @@ test "it returns user info in a hash" do } assert data == expected end + + test "it works with the uri" do + user = "https://social.heldscal.la/user/29191" + + # TODO: make test local + {:ok, data} = OStatus.gather_user_info(user) + + expected = %{ + hub: "https://social.heldscal.la/main/push/hub", + magic_key: "RSA.wQ3i9UA0qmAxZ0WTIp4a-waZn_17Ez1pEEmqmqoooRsG1_BvpmOvLN0G2tEcWWxl2KOtdQMCiPptmQObeZeuj48mdsDZ4ArQinexY2hCCTcbV8Xpswpkb8K05RcKipdg07pnI7tAgQ0VWSZDImncL6YUGlG5YN8b5TjGOwk2VG8=.AQAB", + name: "shp", + nickname: "shp", + salmon: "https://social.heldscal.la/main/salmon/user/29191", + subject: "https://social.heldscal.la/user/29191", + topic: "https://social.heldscal.la/api/statuses/user_timeline/29191.atom", + uri: "https://social.heldscal.la/user/29191", + fqn: user + } + assert data == expected + end end end diff --git a/test/web/websub/websub_test.exs b/test/web/websub/websub_test.exs index 1b1ef3fa6..25c2b8baa 100644 --- a/test/web/websub/websub_test.exs +++ b/test/web/websub/websub_test.exs @@ -93,12 +93,13 @@ def accepting_verifier(subscription) do end test "initiate a subscription for a given user and topic" do - user = insert(:user) - topic = "http://example.org/some-topic.atom" + subscriber = insert(:user) + user = insert(:user, %{info: %{ "topic" => "some_topic", "hub" => "some_hub"}}) - {:ok, websub} = Websub.subscribe(user, topic, &accepting_verifier/1) - assert websub.subscribers == [user.ap_id] - assert websub.topic == topic + {:ok, websub} = Websub.subscribe(subscriber, user, &accepting_verifier/1) + assert websub.subscribers == [subscriber.ap_id] + assert websub.topic == "some_topic" + assert websub.hub == "some_hub" assert is_binary(websub.secret) assert websub.user == user assert websub.state == "accepted"