From 4f7a468659626700fff9fea97d1506aa3525dee0 Mon Sep 17 00:00:00 2001 From: William Pitcock Date: Fri, 5 Oct 2018 22:53:59 +0000 Subject: [PATCH 1/7] user: only pre-create follow relationships on OStatus closes #306 --- lib/pleroma/user.ex | 27 +-------------------------- 1 file changed, 1 insertion(+), 26 deletions(-) diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index 487bfce32..228f12498 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -185,32 +185,7 @@ def needs_update?(%User{local: false} = user) do def needs_update?(_), do: true def maybe_direct_follow(%User{} = follower, %User{info: info} = followed) do - user_config = Application.get_env(:pleroma, :user) - deny_follow_blocked = Keyword.get(user_config, :deny_follow_blocked) - - user_info = user_info(followed) - - should_direct_follow = - cond do - # if the account is locked, don't pre-create the relationship - user_info[:locked] == true -> - false - - # if the users are blocking each other, we shouldn't even be here, but check for it anyway - deny_follow_blocked and - (User.blocks?(follower, followed) or User.blocks?(followed, follower)) -> - false - - # if OStatus, then there is no three-way handshake to follow - User.ap_enabled?(followed) != true -> - true - - # if there are no other reasons not to, just pre-create the relationship - true -> - true - end - - if should_direct_follow do + if !User.ap_enabled?(followed) do follow(follower, followed) else {:ok, follower} From 8ce217776df12df6f0e8445980cc6a62ba156648 Mon Sep 17 00:00:00 2001 From: William Pitcock Date: Fri, 5 Oct 2018 23:30:34 +0000 Subject: [PATCH 2/7] activitypub transmogrifier: better manage follow state --- lib/pleroma/web/activity_pub/transmogrifier.ex | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/pleroma/web/activity_pub/transmogrifier.ex b/lib/pleroma/web/activity_pub/transmogrifier.ex index aece77a54..65ac07845 100644 --- a/lib/pleroma/web/activity_pub/transmogrifier.ex +++ b/lib/pleroma/web/activity_pub/transmogrifier.ex @@ -326,6 +326,7 @@ def handle_incoming( with actor <- get_actor(data), %User{} = followed <- User.get_or_fetch_by_ap_id(actor), {:ok, follow_activity} <- get_follow_activity(follow_object, followed), + {:ok, follow_activity} <- Utils.update_follow_state(follow_activity, "accept"), %User{local: true} = follower <- User.get_cached_by_ap_id(follow_activity.data["actor"]), {:ok, activity} <- ActivityPub.accept(%{ @@ -351,6 +352,7 @@ def handle_incoming( with actor <- get_actor(data), %User{} = followed <- User.get_or_fetch_by_ap_id(actor), {:ok, follow_activity} <- get_follow_activity(follow_object, followed), + {:ok, follow_activity} <- Utils.update_follow_state(follow_activity, "reject"), %User{local: true} = follower <- User.get_cached_by_ap_id(follow_activity.data["actor"]), {:ok, activity} <- ActivityPub.accept(%{ From a71b82201365f92ad78ccec7f6f1ceda456eca4f Mon Sep 17 00:00:00 2001 From: William Pitcock Date: Fri, 5 Oct 2018 23:31:00 +0000 Subject: [PATCH 3/7] activitypub: always track following state for async reasons --- lib/pleroma/web/activity_pub/utils.ex | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/pleroma/web/activity_pub/utils.ex b/lib/pleroma/web/activity_pub/utils.ex index 0664b5a2e..43a1f432d 100644 --- a/lib/pleroma/web/activity_pub/utils.ex +++ b/lib/pleroma/web/activity_pub/utils.ex @@ -247,11 +247,11 @@ def make_follow_data( "actor" => follower_id, "to" => [followed_id], "cc" => ["https://www.w3.org/ns/activitystreams#Public"], - "object" => followed_id + "object" => followed_id, + "state" => "pending" } data = if activity_id, do: Map.put(data, "id", activity_id), else: data - data = if User.locked?(followed), do: Map.put(data, "state", "pending"), else: data data end From 3e751496e3d5f8c90d5e73d356bebb607d0edb44 Mon Sep 17 00:00:00 2001 From: William Pitcock Date: Fri, 5 Oct 2018 23:31:49 +0000 Subject: [PATCH 4/7] mastodon api: account view: fetch follow state and use it to populate `requested` field --- lib/pleroma/web/mastodon_api/views/account_view.ex | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/pleroma/web/mastodon_api/views/account_view.ex b/lib/pleroma/web/mastodon_api/views/account_view.ex index 96795c420..d4d8ee2a5 100644 --- a/lib/pleroma/web/mastodon_api/views/account_view.ex +++ b/lib/pleroma/web/mastodon_api/views/account_view.ex @@ -72,6 +72,9 @@ def render("mention.json", %{user: user}) do end def render("relationship.json", %{user: user, target: target}) do + follow_activity = Pleroma.Web.ActivityPub.Utils.fetch_latest_follow(user, target) + requested = follow_activity.data["state"] == "pending" + %{ id: to_string(target.id), following: User.following?(user, target), @@ -79,7 +82,7 @@ def render("relationship.json", %{user: user, target: target}) do blocking: User.blocks?(user, target), muting: false, muting_notifications: false, - requested: false, + requested: requested, domain_blocking: false, showing_reblogs: false, endorsed: false From e69faf550cd14cfee8f56f050a2a544b7450367c Mon Sep 17 00:00:00 2001 From: William Pitcock Date: Fri, 5 Oct 2018 23:40:49 +0000 Subject: [PATCH 5/7] user: add wait_and_refresh() for async three-way handshake case --- lib/pleroma/user.ex | 24 +++++++++++++++++++ .../mastodon_api/mastodon_api_controller.ex | 3 ++- lib/pleroma/web/twitter_api/twitter_api.ex | 3 ++- 3 files changed, 28 insertions(+), 2 deletions(-) diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index 228f12498..02f13eb2c 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -738,4 +738,28 @@ def get_or_fetch(uri_or_nickname) do get_or_fetch_by_nickname(uri_or_nickname) end end + + # wait a period of time and return newest version of the User structs + # this is because we have synchronous follow APIs and need to simulate them + # with an async handshake + def wait_and_refresh(_, %User{local: true} = a, %User{local: true} = b) do + with %User{} = a <- Repo.get(User, a.id), + %User{} = b <- Repo.get(User, b.id) do + {:ok, a, b} + else + _e -> + :error + end + end + + def wait_and_refresh(timeout, %User{} = a, %User{} = b) do + with :ok <- :timer.sleep(timeout), + %User{} = a <- Repo.get(User, a.id), + %User{} = b <- Repo.get(User, b.id) do + {:ok, a, b} + else + _e -> + :error + end + end end diff --git a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex index 391a79885..9a470c364 100644 --- a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex +++ b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex @@ -574,7 +574,8 @@ def reject_follow_request(%{assigns: %{user: followed}} = conn, %{"id" => id}) d def follow(%{assigns: %{user: follower}} = conn, %{"id" => id}) do with %User{} = followed <- Repo.get(User, id), {:ok, follower} <- User.maybe_direct_follow(follower, followed), - {:ok, _activity} <- ActivityPub.follow(follower, followed) do + {:ok, _activity} <- ActivityPub.follow(follower, followed), + {:ok, follower, followed} <- User.wait_and_refresh(500, follower, followed) do render(conn, AccountView, "relationship.json", %{user: follower, target: followed}) else {:error, message} -> diff --git a/lib/pleroma/web/twitter_api/twitter_api.ex b/lib/pleroma/web/twitter_api/twitter_api.ex index dbad08e66..3f0e2425c 100644 --- a/lib/pleroma/web/twitter_api/twitter_api.ex +++ b/lib/pleroma/web/twitter_api/twitter_api.ex @@ -23,7 +23,8 @@ def delete(%User{} = user, id) do def follow(%User{} = follower, params) do with {:ok, %User{} = followed} <- get_user(params), {:ok, follower} <- User.maybe_direct_follow(follower, followed), - {:ok, activity} <- ActivityPub.follow(follower, followed) do + {:ok, activity} <- ActivityPub.follow(follower, followed), + {:ok, follower, followed} <- User.wait_and_refresh(500, follower, followed) do {:ok, follower, followed, activity} else err -> err From 7f530f6f8084b899b2fff40c074602e90d5fa35f Mon Sep 17 00:00:00 2001 From: William Pitcock Date: Fri, 5 Oct 2018 23:50:13 +0000 Subject: [PATCH 6/7] mastodon api: relationship view: better handle no pre-existing follow activity --- lib/pleroma/web/mastodon_api/views/account_view.ex | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/pleroma/web/mastodon_api/views/account_view.ex b/lib/pleroma/web/mastodon_api/views/account_view.ex index d4d8ee2a5..b68845e16 100644 --- a/lib/pleroma/web/mastodon_api/views/account_view.ex +++ b/lib/pleroma/web/mastodon_api/views/account_view.ex @@ -73,7 +73,13 @@ def render("mention.json", %{user: user}) do def render("relationship.json", %{user: user, target: target}) do follow_activity = Pleroma.Web.ActivityPub.Utils.fetch_latest_follow(user, target) - requested = follow_activity.data["state"] == "pending" + + requested = + if follow_activity do + follow_activity.data["state"] == "pending" + else + false + end %{ id: to_string(target.id), From 7b3fff9af87bbf8e6b0cc824b7ebf681e4a614f1 Mon Sep 17 00:00:00 2001 From: William Pitcock Date: Sun, 7 Oct 2018 01:05:59 +0000 Subject: [PATCH 7/7] {mastodon api, twitter api}: make the follow handshake timeout configurable --- config/config.exs | 3 ++- lib/pleroma/web/mastodon_api/mastodon_api_controller.ex | 6 +++++- lib/pleroma/web/twitter_api/twitter_api.ex | 6 +++++- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/config/config.exs b/config/config.exs index 608c035b0..c32ac9da2 100644 --- a/config/config.exs +++ b/config/config.exs @@ -109,7 +109,8 @@ config :pleroma, :activitypub, accept_blocks: true, unfollow_blocked: true, - outgoing_blocks: true + outgoing_blocks: true, + follow_handshake_timeout: 500 config :pleroma, :user, deny_follow_blocked: true diff --git a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex index 9a470c364..499635a9d 100644 --- a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex +++ b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex @@ -571,11 +571,15 @@ def reject_follow_request(%{assigns: %{user: followed}} = conn, %{"id" => id}) d end end + @activitypub Application.get_env(:pleroma, :activitypub) + @follow_handshake_timeout Keyword.get(@activitypub, :follow_handshake_timeout) + def follow(%{assigns: %{user: follower}} = conn, %{"id" => id}) do with %User{} = followed <- Repo.get(User, id), {:ok, follower} <- User.maybe_direct_follow(follower, followed), {:ok, _activity} <- ActivityPub.follow(follower, followed), - {:ok, follower, followed} <- User.wait_and_refresh(500, follower, followed) do + {:ok, follower, followed} <- + User.wait_and_refresh(@follow_handshake_timeout, follower, followed) do render(conn, AccountView, "relationship.json", %{user: follower, target: followed}) else {:error, message} -> diff --git a/lib/pleroma/web/twitter_api/twitter_api.ex b/lib/pleroma/web/twitter_api/twitter_api.ex index 3f0e2425c..3747285da 100644 --- a/lib/pleroma/web/twitter_api/twitter_api.ex +++ b/lib/pleroma/web/twitter_api/twitter_api.ex @@ -20,11 +20,15 @@ def delete(%User{} = user, id) do end end + @activitypub Application.get_env(:pleroma, :activitypub) + @follow_handshake_timeout Keyword.get(@activitypub, :follow_handshake_timeout) + def follow(%User{} = follower, params) do with {:ok, %User{} = followed} <- get_user(params), {:ok, follower} <- User.maybe_direct_follow(follower, followed), {:ok, activity} <- ActivityPub.follow(follower, followed), - {:ok, follower, followed} <- User.wait_and_refresh(500, follower, followed) do + {:ok, follower, followed} <- + User.wait_and_refresh(@follow_handshake_timeout, follower, followed) do {:ok, follower, followed, activity} else err -> err