From 8b9a0dd4a7e60f610e3aa1db92e62bc0fbe54521 Mon Sep 17 00:00:00 2001 From: lain Date: Wed, 5 Jun 2019 12:06:45 +0200 Subject: [PATCH] User: Don't error out when following a user that's already followed. This leads to a few situations where it is impossible to follow a user. --- lib/pleroma/user.ex | 4 +- .../transmogrifier/follow_handling_test.exs | 115 ++++++++++++++++++ test/web/activity_pub/transmogrifier_test.exs | 54 -------- test/web/twitter_api/twitter_api_test.exs | 6 +- 4 files changed, 119 insertions(+), 60 deletions(-) create mode 100644 test/web/activity_pub/transmogrifier/follow_handling_test.exs diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index dc534b05c..48b9f1d7d 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -370,8 +370,8 @@ def follow(%User{} = follower, %User{info: info} = followed) do ap_followers = followed.follower_address cond do - following?(follower, followed) or info.deactivated -> - {:error, "Could not follow user: #{followed.nickname} is already on your list."} + info.deactivated -> + {:error, "Could not follow user: You are deactivatedt."} deny_follow_blocked and blocks?(followed, follower) -> {:error, "Could not follow user: #{followed.nickname} blocked you."} diff --git a/test/web/activity_pub/transmogrifier/follow_handling_test.exs b/test/web/activity_pub/transmogrifier/follow_handling_test.exs new file mode 100644 index 000000000..9f89e876b --- /dev/null +++ b/test/web/activity_pub/transmogrifier/follow_handling_test.exs @@ -0,0 +1,115 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2018 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ActivityPub.Transmogrifier.FollowHandlingTest do + use Pleroma.DataCase + alias Pleroma.Activity + alias Pleroma.Repo + alias Pleroma.User + alias Pleroma.Web.ActivityPub.Transmogrifier + alias Pleroma.Web.ActivityPub.Utils + + import Pleroma.Factory + import Ecto.Query + + setup_all do + Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end) + :ok + end + + describe "handle_incoming" do + test "it works for incoming follow requests" do + user = insert(:user) + + data = + File.read!("test/fixtures/mastodon-follow-activity.json") + |> Poison.decode!() + |> Map.put("object", user.ap_id) + + {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) + + assert data["actor"] == "http://mastodon.example.org/users/admin" + assert data["type"] == "Follow" + assert data["id"] == "http://mastodon.example.org/users/admin#follows/2" + assert User.following?(User.get_cached_by_ap_id(data["actor"]), user) + end + + test "it works for follow requests when you are already followed, creating a new accept activity" do + # This is important because the remote might have the wrong idea about the current follow status. + # This can lead to instance A thinking that x@A is followed by y@B, but B thinks they are not. In + # this case, the follow can never go through again because it will never get an Accept. + user = insert(:user) + + data = + File.read!("test/fixtures/mastodon-follow-activity.json") + |> Poison.decode!() + |> Map.put("object", user.ap_id) + + {:ok, %Activity{local: false}} = Transmogrifier.handle_incoming(data) + + accepts = + from( + a in Activity, + where: fragment("?->>'type' = ?", a.data, "Accept") + ) + |> Repo.all() + + assert length(accepts) == 1 + + data = + File.read!("test/fixtures/mastodon-follow-activity.json") + |> Poison.decode!() + |> Map.put("id", String.replace(data["id"], "2", "3")) + |> Map.put("object", user.ap_id) + + {:ok, %Activity{local: false}} = Transmogrifier.handle_incoming(data) + + accepts = + from( + a in Activity, + where: fragment("?->>'type' = ?", a.data, "Accept") + ) + |> Repo.all() + + assert length(accepts) == 2 + end + + test "it rejects incoming follow requests from blocked users when deny_follow_blocked is enabled" do + Pleroma.Config.put([:user, :deny_follow_blocked], true) + + user = insert(:user) + {:ok, target} = User.get_or_fetch("http://mastodon.example.org/users/admin") + + {:ok, user} = User.block(user, target) + + data = + File.read!("test/fixtures/mastodon-follow-activity.json") + |> Poison.decode!() + |> Map.put("object", user.ap_id) + + {:ok, %Activity{data: %{"id" => id}}} = Transmogrifier.handle_incoming(data) + + %Activity{} = activity = Activity.get_by_ap_id(id) + + assert activity.data["state"] == "reject" + end + + test "it works for incoming follow requests from hubzilla" do + user = insert(:user) + + data = + File.read!("test/fixtures/hubzilla-follow-activity.json") + |> Poison.decode!() + |> Map.put("object", user.ap_id) + |> Utils.normalize_params() + + {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) + + assert data["actor"] == "https://hubzilla.example.org/channel/kaniini" + assert data["type"] == "Follow" + assert data["id"] == "https://hubzilla.example.org/channel/kaniini#follows/2" + assert User.following?(User.get_cached_by_ap_id(data["actor"]), user) + end + end +end diff --git a/test/web/activity_pub/transmogrifier_test.exs b/test/web/activity_pub/transmogrifier_test.exs index 89c8f79c9..28971ae45 100644 --- a/test/web/activity_pub/transmogrifier_test.exs +++ b/test/web/activity_pub/transmogrifier_test.exs @@ -11,7 +11,6 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do alias Pleroma.User alias Pleroma.Web.ActivityPub.ActivityPub alias Pleroma.Web.ActivityPub.Transmogrifier - alias Pleroma.Web.ActivityPub.Utils alias Pleroma.Web.OStatus alias Pleroma.Web.Websub.WebsubClientSubscription @@ -248,59 +247,6 @@ test "it cleans up incoming notices which are not really DMs" do assert object_data["cc"] == to end - test "it works for incoming follow requests" do - user = insert(:user) - - data = - File.read!("test/fixtures/mastodon-follow-activity.json") - |> Poison.decode!() - |> Map.put("object", user.ap_id) - - {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) - - assert data["actor"] == "http://mastodon.example.org/users/admin" - assert data["type"] == "Follow" - assert data["id"] == "http://mastodon.example.org/users/admin#follows/2" - assert User.following?(User.get_cached_by_ap_id(data["actor"]), user) - end - - test "it rejects incoming follow requests from blocked users when deny_follow_blocked is enabled" do - Pleroma.Config.put([:user, :deny_follow_blocked], true) - - user = insert(:user) - {:ok, target} = User.get_or_fetch("http://mastodon.example.org/users/admin") - - {:ok, user} = User.block(user, target) - - data = - File.read!("test/fixtures/mastodon-follow-activity.json") - |> Poison.decode!() - |> Map.put("object", user.ap_id) - - {:ok, %Activity{data: %{"id" => id}}} = Transmogrifier.handle_incoming(data) - - %Activity{} = activity = Activity.get_by_ap_id(id) - - assert activity.data["state"] == "reject" - end - - test "it works for incoming follow requests from hubzilla" do - user = insert(:user) - - data = - File.read!("test/fixtures/hubzilla-follow-activity.json") - |> Poison.decode!() - |> Map.put("object", user.ap_id) - |> Utils.normalize_params() - - {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) - - assert data["actor"] == "https://hubzilla.example.org/channel/kaniini" - assert data["type"] == "Follow" - assert data["id"] == "https://hubzilla.example.org/channel/kaniini#follows/2" - assert User.following?(User.get_cached_by_ap_id(data["actor"]), user) - end - test "it works for incoming likes" do user = insert(:user) {:ok, activity} = CommonAPI.post(user, %{"status" => "hello"}) diff --git a/test/web/twitter_api/twitter_api_test.exs b/test/web/twitter_api/twitter_api_test.exs index d601c8f1f..475531a09 100644 --- a/test/web/twitter_api/twitter_api_test.exs +++ b/test/web/twitter_api/twitter_api_test.exs @@ -116,8 +116,7 @@ test "Follow another user using user_id" do {:ok, user, followed, _activity} = TwitterAPI.follow(user, %{"user_id" => followed.id}) assert User.ap_followers(followed) in user.following - {:error, msg} = TwitterAPI.follow(user, %{"user_id" => followed.id}) - assert msg == "Could not follow user: #{followed.nickname} is already on your list." + {:ok, _, _, _} = TwitterAPI.follow(user, %{"user_id" => followed.id}) end test "Follow another user using screen_name" do @@ -132,8 +131,7 @@ test "Follow another user using screen_name" do followed = User.get_cached_by_ap_id(followed.ap_id) assert followed.info.follower_count == 1 - {:error, msg} = TwitterAPI.follow(user, %{"screen_name" => followed.nickname}) - assert msg == "Could not follow user: #{followed.nickname} is already on your list." + {:ok, _, _, _} = TwitterAPI.follow(user, %{"screen_name" => followed.nickname}) end test "Unfollow another user using user_id" do