diff options
Diffstat (limited to 'test/web')
57 files changed, 2082 insertions, 1329 deletions
diff --git a/test/web/activity_pub/activity_pub_controller_test.exs b/test/web/activity_pub/activity_pub_controller_test.exs index e722f7c04..ed900d8f8 100644 --- a/test/web/activity_pub/activity_pub_controller_test.exs +++ b/test/web/activity_pub/activity_pub_controller_test.exs @@ -1082,6 +1082,45 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do assert object = Object.get_by_ap_id(note_object.data["id"]) assert object.data["like_count"] == 1 end + + test "it doesn't spreads faulty attributedTo or actor fields", %{ + conn: conn, + activity: activity + } do + reimu = insert(:user, nickname: "reimu") + cirno = insert(:user, nickname: "cirno") + + assert reimu.ap_id + assert cirno.ap_id + + activity = + activity + |> put_in(["object", "actor"], reimu.ap_id) + |> put_in(["object", "attributedTo"], reimu.ap_id) + |> put_in(["actor"], reimu.ap_id) + |> put_in(["attributedTo"], reimu.ap_id) + + _reimu_outbox = + conn + |> assign(:user, cirno) + |> put_req_header("content-type", "application/activity+json") + |> post("/users/#{reimu.nickname}/outbox", activity) + |> json_response(403) + + cirno_outbox = + conn + |> assign(:user, cirno) + |> put_req_header("content-type", "application/activity+json") + |> post("/users/#{cirno.nickname}/outbox", activity) + |> json_response(201) + + assert cirno_outbox["attributedTo"] == nil + assert cirno_outbox["actor"] == cirno.ap_id + + assert cirno_object = Object.normalize(cirno_outbox["object"]) + assert cirno_object.data["actor"] == cirno.ap_id + assert cirno_object.data["attributedTo"] == cirno.ap_id + end end describe "/relay/followers" do diff --git a/test/web/activity_pub/activity_pub_test.exs b/test/web/activity_pub/activity_pub_test.exs index be7ab2ae4..f3951462f 100644 --- a/test/web/activity_pub/activity_pub_test.exs +++ b/test/web/activity_pub/activity_pub_test.exs @@ -184,36 +184,43 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do assert User.invisible?(user) end - test "it fetches the appropriate tag-restricted posts" do - user = insert(:user) + test "it returns a user that accepts chat messages" do + user_id = "http://mastodon.example.org/users/admin" + {:ok, user} = ActivityPub.make_user_from_ap_id(user_id) - {:ok, status_one} = CommonAPI.post(user, %{status: ". #test"}) - {:ok, status_two} = CommonAPI.post(user, %{status: ". #essais"}) - {:ok, status_three} = CommonAPI.post(user, %{status: ". #test #reject"}) + assert user.accepts_chat_messages + end + end - fetch_one = ActivityPub.fetch_activities([], %{type: "Create", tag: "test"}) + test "it fetches the appropriate tag-restricted posts" do + user = insert(:user) - fetch_two = ActivityPub.fetch_activities([], %{type: "Create", tag: ["test", "essais"]}) + {:ok, status_one} = CommonAPI.post(user, %{status: ". #test"}) + {:ok, status_two} = CommonAPI.post(user, %{status: ". #essais"}) + {:ok, status_three} = CommonAPI.post(user, %{status: ". #test #reject"}) - fetch_three = - ActivityPub.fetch_activities([], %{ - type: "Create", - tag: ["test", "essais"], - tag_reject: ["reject"] - }) + fetch_one = ActivityPub.fetch_activities([], %{type: "Create", tag: "test"}) - fetch_four = - ActivityPub.fetch_activities([], %{ - type: "Create", - tag: ["test"], - tag_all: ["test", "reject"] - }) + fetch_two = ActivityPub.fetch_activities([], %{type: "Create", tag: ["test", "essais"]}) - assert fetch_one == [status_one, status_three] - assert fetch_two == [status_one, status_two, status_three] - assert fetch_three == [status_one, status_two] - assert fetch_four == [status_three] - end + fetch_three = + ActivityPub.fetch_activities([], %{ + type: "Create", + tag: ["test", "essais"], + tag_reject: ["reject"] + }) + + fetch_four = + ActivityPub.fetch_activities([], %{ + type: "Create", + tag: ["test"], + tag_all: ["test", "reject"] + }) + + assert fetch_one == [status_one, status_three] + assert fetch_two == [status_one, status_two, status_three] + assert fetch_three == [status_one, status_two] + assert fetch_four == [status_three] end describe "insertion" do @@ -507,6 +514,33 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do activities = ActivityPub.fetch_activities_for_context("2hu", %{blocking_user: user}) assert activities == [activity_two, activity] end + + test "doesn't return activities with filtered words" do + user = insert(:user) + user_two = insert(:user) + insert(:filter, user: user, phrase: "test", hide: true) + + {:ok, %{id: id1, data: %{"context" => context}}} = CommonAPI.post(user, %{status: "1"}) + + {:ok, %{id: id2}} = CommonAPI.post(user_two, %{status: "2", in_reply_to_status_id: id1}) + + {:ok, %{id: id3} = user_activity} = + CommonAPI.post(user, %{status: "3 test?", in_reply_to_status_id: id2}) + + {:ok, %{id: id4} = filtered_activity} = + CommonAPI.post(user_two, %{status: "4 test!", in_reply_to_status_id: id3}) + + {:ok, _} = CommonAPI.post(user, %{status: "5", in_reply_to_status_id: id4}) + + activities = + context + |> ActivityPub.fetch_activities_for_context(%{user: user}) + |> Enum.map(& &1.id) + + assert length(activities) == 4 + assert user_activity.id in activities + refute filtered_activity.id in activities + end end test "doesn't return blocked activities" do @@ -642,7 +676,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do refute activity in activities followed_user = insert(:user) - ActivityPub.follow(user, followed_user) + CommonAPI.follow(user, followed_user) {:ok, repeat_activity} = CommonAPI.repeat(activity.id, followed_user) activities = ActivityPub.fetch_activities([], %{blocking_user: user, skip_preload: true}) @@ -785,6 +819,75 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do assert activity == expected_activity end + describe "irreversible filters" do + setup do + user = insert(:user) + user_two = insert(:user) + + insert(:filter, user: user_two, phrase: "cofe", hide: true) + insert(:filter, user: user_two, phrase: "ok boomer", hide: true) + insert(:filter, user: user_two, phrase: "test", hide: false) + + params = %{ + type: ["Create", "Announce"], + user: user_two + } + + {:ok, %{user: user, user_two: user_two, params: params}} + end + + test "it returns statuses if they don't contain exact filter words", %{ + user: user, + params: params + } do + {:ok, _} = CommonAPI.post(user, %{status: "hey"}) + {:ok, _} = CommonAPI.post(user, %{status: "got cofefe?"}) + {:ok, _} = CommonAPI.post(user, %{status: "I am not a boomer"}) + {:ok, _} = CommonAPI.post(user, %{status: "ok boomers"}) + {:ok, _} = CommonAPI.post(user, %{status: "ccofee is not a word"}) + {:ok, _} = CommonAPI.post(user, %{status: "this is a test"}) + + activities = ActivityPub.fetch_activities([], params) + + assert Enum.count(activities) == 6 + end + + test "it does not filter user's own statuses", %{user_two: user_two, params: params} do + {:ok, _} = CommonAPI.post(user_two, %{status: "Give me some cofe!"}) + {:ok, _} = CommonAPI.post(user_two, %{status: "ok boomer"}) + + activities = ActivityPub.fetch_activities([], params) + + assert Enum.count(activities) == 2 + end + + test "it excludes statuses with filter words", %{user: user, params: params} do + {:ok, _} = CommonAPI.post(user, %{status: "Give me some cofe!"}) + {:ok, _} = CommonAPI.post(user, %{status: "ok boomer"}) + {:ok, _} = CommonAPI.post(user, %{status: "is it a cOfE?"}) + {:ok, _} = CommonAPI.post(user, %{status: "cofe is all I need"}) + {:ok, _} = CommonAPI.post(user, %{status: "— ok BOOMER\n"}) + + activities = ActivityPub.fetch_activities([], params) + + assert Enum.empty?(activities) + end + + test "it returns all statuses if user does not have any filters" do + another_user = insert(:user) + {:ok, _} = CommonAPI.post(another_user, %{status: "got cofe?"}) + {:ok, _} = CommonAPI.post(another_user, %{status: "test!"}) + + activities = + ActivityPub.fetch_activities([], %{ + type: ["Create", "Announce"], + user: another_user + }) + + assert Enum.count(activities) == 2 + end + end + describe "public fetch activities" do test "doesn't retrieve unlisted activities" do user = insert(:user) @@ -917,24 +1020,12 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do end end - describe "following / unfollowing" do - test "it reverts follow activity" do - follower = insert(:user) - followed = insert(:user) - - with_mock(Utils, [:passthrough], maybe_federate: fn _ -> {:error, :reverted} end) do - assert {:error, :reverted} = ActivityPub.follow(follower, followed) - end - - assert Repo.aggregate(Activity, :count, :id) == 0 - assert Repo.aggregate(Object, :count, :id) == 0 - end - + describe "unfollowing" do test "it reverts unfollow activity" do follower = insert(:user) followed = insert(:user) - {:ok, follow_activity} = ActivityPub.follow(follower, followed) + {:ok, _, _, follow_activity} = CommonAPI.follow(follower, followed) with_mock(Utils, [:passthrough], maybe_federate: fn _ -> {:error, :reverted} end) do assert {:error, :reverted} = ActivityPub.unfollow(follower, followed) @@ -947,21 +1038,11 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do assert activity.data["object"] == followed.ap_id end - test "creates a follow activity" do - follower = insert(:user) - followed = insert(:user) - - {:ok, activity} = ActivityPub.follow(follower, followed) - assert activity.data["type"] == "Follow" - assert activity.data["actor"] == follower.ap_id - assert activity.data["object"] == followed.ap_id - end - test "creates an undo activity for the last follow" do follower = insert(:user) followed = insert(:user) - {:ok, follow_activity} = ActivityPub.follow(follower, followed) + {:ok, _, _, follow_activity} = CommonAPI.follow(follower, followed) {:ok, activity} = ActivityPub.unfollow(follower, followed) assert activity.data["type"] == "Undo" @@ -978,7 +1059,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do follower = insert(:user) followed = insert(:user, %{locked: true}) - {:ok, follow_activity} = ActivityPub.follow(follower, followed) + {:ok, _, _, follow_activity} = CommonAPI.follow(follower, followed) {:ok, activity} = ActivityPub.unfollow(follower, followed) assert activity.data["type"] == "Undo" @@ -992,54 +1073,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do end end - describe "blocking" do - test "reverts block activity on error" do - [blocker, blocked] = insert_list(2, :user) - - with_mock(Utils, [:passthrough], maybe_federate: fn _ -> {:error, :reverted} end) do - assert {:error, :reverted} = ActivityPub.block(blocker, blocked) - end - - assert Repo.aggregate(Activity, :count, :id) == 0 - assert Repo.aggregate(Object, :count, :id) == 0 - end - - test "creates a block activity" do - clear_config([:instance, :federating], true) - blocker = insert(:user) - blocked = insert(:user) - - with_mock Pleroma.Web.Federator, - publish: fn _ -> nil end do - {:ok, activity} = ActivityPub.block(blocker, blocked) - - assert activity.data["type"] == "Block" - assert activity.data["actor"] == blocker.ap_id - assert activity.data["object"] == blocked.ap_id - - assert called(Pleroma.Web.Federator.publish(activity)) - end - end - - test "works with outgoing blocks disabled, but doesn't federate" do - clear_config([:instance, :federating], true) - clear_config([:activitypub, :outgoing_blocks], false) - blocker = insert(:user) - blocked = insert(:user) - - with_mock Pleroma.Web.Federator, - publish: fn _ -> nil end do - {:ok, activity} = ActivityPub.block(blocker, blocked) - - assert activity.data["type"] == "Block" - assert activity.data["actor"] == blocker.ap_id - assert activity.data["object"] == blocked.ap_id - - refute called(Pleroma.Web.Federator.publish(:_)) - end - end - end - describe "timeline post-processing" do test "it filters broken threads" do user1 = insert(:user) @@ -1411,7 +1444,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do assert_enqueued(worker: Pleroma.Workers.BackgroundWorker, args: params) - Pleroma.Workers.BackgroundWorker.perform(params, nil) + Pleroma.Workers.BackgroundWorker.perform(%Oban.Job{args: params}) refute User.following?(follower, old_user) assert User.following?(follower, new_user) @@ -2023,4 +2056,46 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do assert [%{activity_id: ^id_create}] = Pleroma.ActivityExpiration |> Repo.all() end end + + describe "handling of clashing nicknames" do + test "renames an existing user with a clashing nickname and a different ap id" do + orig_user = + insert( + :user, + local: false, + nickname: "admin@mastodon.example.org", + ap_id: "http://mastodon.example.org/users/harinezumigari" + ) + + %{ + nickname: orig_user.nickname, + ap_id: orig_user.ap_id <> "part_2" + } + |> ActivityPub.maybe_handle_clashing_nickname() + + user = User.get_by_id(orig_user.id) + + assert user.nickname == "#{orig_user.id}.admin@mastodon.example.org" + end + + test "does nothing with a clashing nickname and the same ap id" do + orig_user = + insert( + :user, + local: false, + nickname: "admin@mastodon.example.org", + ap_id: "http://mastodon.example.org/users/harinezumigari" + ) + + %{ + nickname: orig_user.nickname, + ap_id: orig_user.ap_id + } + |> ActivityPub.maybe_handle_clashing_nickname() + + user = User.get_by_id(orig_user.id) + + assert user.nickname == orig_user.nickname + end + end end diff --git a/test/web/activity_pub/mrf/anti_followbot_policy_test.exs b/test/web/activity_pub/mrf/anti_followbot_policy_test.exs index fca0de7c6..3c795f5ac 100644 --- a/test/web/activity_pub/mrf/anti_followbot_policy_test.exs +++ b/test/web/activity_pub/mrf/anti_followbot_policy_test.exs @@ -21,7 +21,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.AntiFollowbotPolicyTest do "id" => "https://example.com/activities/1234" } - {:reject, nil} = AntiFollowbotPolicy.filter(message) + assert {:reject, "[AntiFollowbotPolicy]" <> _} = AntiFollowbotPolicy.filter(message) end test "matches followbots by display name" do @@ -36,7 +36,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.AntiFollowbotPolicyTest do "id" => "https://example.com/activities/1234" } - {:reject, nil} = AntiFollowbotPolicy.filter(message) + assert {:reject, "[AntiFollowbotPolicy]" <> _} = AntiFollowbotPolicy.filter(message) end end diff --git a/test/web/activity_pub/mrf/anti_link_spam_policy_test.exs b/test/web/activity_pub/mrf/anti_link_spam_policy_test.exs index 1a13699be..6867c9853 100644 --- a/test/web/activity_pub/mrf/anti_link_spam_policy_test.exs +++ b/test/web/activity_pub/mrf/anti_link_spam_policy_test.exs @@ -33,7 +33,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.AntiLinkSpamPolicyTest do describe "with new user" do test "it allows posts without links" do - user = insert(:user) + user = insert(:user, local: false) assert user.note_count == 0 @@ -45,7 +45,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.AntiLinkSpamPolicyTest do end test "it disallows posts with links" do - user = insert(:user) + user = insert(:user, local: false) assert user.note_count == 0 @@ -55,6 +55,18 @@ defmodule Pleroma.Web.ActivityPub.MRF.AntiLinkSpamPolicyTest do {:reject, _} = AntiLinkSpamPolicy.filter(message) end + + test "it allows posts with links for local users" do + user = insert(:user) + + assert user.note_count == 0 + + message = + @linkful_message + |> Map.put("actor", user.ap_id) + + {:ok, _message} = AntiLinkSpamPolicy.filter(message) + end end describe "with old user" do diff --git a/test/web/activity_pub/mrf/hellthread_policy_test.exs b/test/web/activity_pub/mrf/hellthread_policy_test.exs index 6e9daa7f9..26f5bcdaa 100644 --- a/test/web/activity_pub/mrf/hellthread_policy_test.exs +++ b/test/web/activity_pub/mrf/hellthread_policy_test.exs @@ -50,7 +50,8 @@ defmodule Pleroma.Web.ActivityPub.MRF.HellthreadPolicyTest do } do Pleroma.Config.put([:mrf_hellthread], %{delist_threshold: 0, reject_threshold: 2}) - {:reject, nil} = filter(message) + assert {:reject, "[HellthreadPolicy] 3 recipients is over the limit of 2"} == + filter(message) end test "does not reject the message if the recipient count is below reject_threshold", %{ diff --git a/test/web/activity_pub/mrf/keyword_policy_test.exs b/test/web/activity_pub/mrf/keyword_policy_test.exs index fd1f7aec8..b3d0f3d90 100644 --- a/test/web/activity_pub/mrf/keyword_policy_test.exs +++ b/test/web/activity_pub/mrf/keyword_policy_test.exs @@ -25,7 +25,8 @@ defmodule Pleroma.Web.ActivityPub.MRF.KeywordPolicyTest do } } - assert {:reject, nil} == KeywordPolicy.filter(message) + assert {:reject, "[KeywordPolicy] Matches with rejected keyword"} = + KeywordPolicy.filter(message) end test "rejects if string matches in summary" do @@ -39,7 +40,8 @@ defmodule Pleroma.Web.ActivityPub.MRF.KeywordPolicyTest do } } - assert {:reject, nil} == KeywordPolicy.filter(message) + assert {:reject, "[KeywordPolicy] Matches with rejected keyword"} = + KeywordPolicy.filter(message) end test "rejects if regex matches in content" do @@ -55,7 +57,8 @@ defmodule Pleroma.Web.ActivityPub.MRF.KeywordPolicyTest do } } - {:reject, nil} == KeywordPolicy.filter(message) + {:reject, "[KeywordPolicy] Matches with rejected keyword"} == + KeywordPolicy.filter(message) end) end @@ -72,7 +75,8 @@ defmodule Pleroma.Web.ActivityPub.MRF.KeywordPolicyTest do } } - {:reject, nil} == KeywordPolicy.filter(message) + {:reject, "[KeywordPolicy] Matches with rejected keyword"} == + KeywordPolicy.filter(message) end) end end diff --git a/test/web/activity_pub/mrf/mention_policy_test.exs b/test/web/activity_pub/mrf/mention_policy_test.exs index aa003bef5..220309cc9 100644 --- a/test/web/activity_pub/mrf/mention_policy_test.exs +++ b/test/web/activity_pub/mrf/mention_policy_test.exs @@ -76,7 +76,8 @@ defmodule Pleroma.Web.ActivityPub.MRF.MentionPolicyTest do "to" => ["https://example.com/blocked"] } - assert MentionPolicy.filter(message) == {:reject, nil} + assert MentionPolicy.filter(message) == + {:reject, "[MentionPolicy] Rejected for mention of https://example.com/blocked"} end test "cc" do @@ -88,7 +89,8 @@ defmodule Pleroma.Web.ActivityPub.MRF.MentionPolicyTest do "cc" => ["https://example.com/blocked"] } - assert MentionPolicy.filter(message) == {:reject, nil} + assert MentionPolicy.filter(message) == + {:reject, "[MentionPolicy] Rejected for mention of https://example.com/blocked"} end end end diff --git a/test/web/activity_pub/mrf/reject_non_public_test.exs b/test/web/activity_pub/mrf/reject_non_public_test.exs index f36299b86..58b46b9a2 100644 --- a/test/web/activity_pub/mrf/reject_non_public_test.exs +++ b/test/web/activity_pub/mrf/reject_non_public_test.exs @@ -64,7 +64,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.RejectNonPublicTest do } Pleroma.Config.put([:mrf_rejectnonpublic, :allow_followersonly], false) - assert {:reject, nil} = RejectNonPublic.filter(message) + assert {:reject, _} = RejectNonPublic.filter(message) end end @@ -94,7 +94,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.RejectNonPublicTest do } Pleroma.Config.put([:mrf_rejectnonpublic, :allow_direct], false) - assert {:reject, nil} = RejectNonPublic.filter(message) + assert {:reject, _} = RejectNonPublic.filter(message) end end end diff --git a/test/web/activity_pub/mrf/simple_policy_test.exs b/test/web/activity_pub/mrf/simple_policy_test.exs index b7b9bc6a2..e842d8d8d 100644 --- a/test/web/activity_pub/mrf/simple_policy_test.exs +++ b/test/web/activity_pub/mrf/simple_policy_test.exs @@ -124,7 +124,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do report_message = build_report_message() local_message = build_local_message() - assert SimplePolicy.filter(report_message) == {:reject, nil} + assert {:reject, _} = SimplePolicy.filter(report_message) assert SimplePolicy.filter(local_message) == {:ok, local_message} end @@ -133,7 +133,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do report_message = build_report_message() local_message = build_local_message() - assert SimplePolicy.filter(report_message) == {:reject, nil} + assert {:reject, _} = SimplePolicy.filter(report_message) assert SimplePolicy.filter(local_message) == {:ok, local_message} end end @@ -241,7 +241,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do remote_message = build_remote_message() - assert SimplePolicy.filter(remote_message) == {:reject, nil} + assert {:reject, _} = SimplePolicy.filter(remote_message) end test "activity matches with wildcard domain" do @@ -249,7 +249,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do remote_message = build_remote_message() - assert SimplePolicy.filter(remote_message) == {:reject, nil} + assert {:reject, _} = SimplePolicy.filter(remote_message) end test "actor has a matching host" do @@ -257,7 +257,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do remote_user = build_remote_user() - assert SimplePolicy.filter(remote_user) == {:reject, nil} + assert {:reject, _} = SimplePolicy.filter(remote_user) end end @@ -279,7 +279,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do remote_message = build_remote_message() assert SimplePolicy.filter(local_message) == {:ok, local_message} - assert SimplePolicy.filter(remote_message) == {:reject, nil} + assert {:reject, _} = SimplePolicy.filter(remote_message) end test "activity has a matching host" do @@ -429,7 +429,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do test "it rejects the deletion" do deletion_message = build_remote_deletion_message() - assert SimplePolicy.filter(deletion_message) == {:reject, nil} + assert {:reject, _} = SimplePolicy.filter(deletion_message) end end @@ -439,7 +439,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicyTest do test "it rejects the deletion" do deletion_message = build_remote_deletion_message() - assert SimplePolicy.filter(deletion_message) == {:reject, nil} + assert {:reject, _} = SimplePolicy.filter(deletion_message) end end diff --git a/test/web/activity_pub/mrf/tag_policy_test.exs b/test/web/activity_pub/mrf/tag_policy_test.exs index e7793641a..6ff71d640 100644 --- a/test/web/activity_pub/mrf/tag_policy_test.exs +++ b/test/web/activity_pub/mrf/tag_policy_test.exs @@ -12,8 +12,8 @@ defmodule Pleroma.Web.ActivityPub.MRF.TagPolicyTest do describe "mrf_tag:disable-any-subscription" do test "rejects message" do actor = insert(:user, tags: ["mrf_tag:disable-any-subscription"]) - message = %{"object" => actor.ap_id, "type" => "Follow"} - assert {:reject, nil} = TagPolicy.filter(message) + message = %{"object" => actor.ap_id, "type" => "Follow", "actor" => actor.ap_id} + assert {:reject, _} = TagPolicy.filter(message) end end @@ -22,7 +22,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.TagPolicyTest do actor = insert(:user, tags: ["mrf_tag:disable-remote-subscription"]) follower = insert(:user, tags: ["mrf_tag:disable-remote-subscription"], local: false) message = %{"object" => actor.ap_id, "type" => "Follow", "actor" => follower.ap_id} - assert {:reject, nil} = TagPolicy.filter(message) + assert {:reject, _} = TagPolicy.filter(message) end test "allows non-local follow requests" do diff --git a/test/web/activity_pub/mrf/user_allowlist_policy_test.exs b/test/web/activity_pub/mrf/user_allowlist_policy_test.exs index ba1b69658..8e1ad5bc8 100644 --- a/test/web/activity_pub/mrf/user_allowlist_policy_test.exs +++ b/test/web/activity_pub/mrf/user_allowlist_policy_test.exs @@ -26,6 +26,6 @@ defmodule Pleroma.Web.ActivityPub.MRF.UserAllowListPolicyTest do actor = insert(:user) Pleroma.Config.put([:mrf_user_allowlist], %{"localhost" => ["test-ap-id"]}) message = %{"actor" => actor.ap_id} - assert UserAllowListPolicy.filter(message) == {:reject, nil} + assert {:reject, _} = UserAllowListPolicy.filter(message) end end diff --git a/test/web/activity_pub/mrf/vocabulary_policy_test.exs b/test/web/activity_pub/mrf/vocabulary_policy_test.exs index 69f22bb77..2bceb67ee 100644 --- a/test/web/activity_pub/mrf/vocabulary_policy_test.exs +++ b/test/web/activity_pub/mrf/vocabulary_policy_test.exs @@ -46,7 +46,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.VocabularyPolicyTest do } } - {:reject, nil} = VocabularyPolicy.filter(message) + {:reject, _} = VocabularyPolicy.filter(message) end test "it does not accept disallowed parent types" do @@ -60,7 +60,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.VocabularyPolicyTest do } } - {:reject, nil} = VocabularyPolicy.filter(message) + {:reject, _} = VocabularyPolicy.filter(message) end end @@ -75,7 +75,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.VocabularyPolicyTest do "object" => "whatever" } - {:reject, nil} = VocabularyPolicy.filter(message) + {:reject, _} = VocabularyPolicy.filter(message) end test "it rejects based on child object type" do @@ -89,7 +89,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.VocabularyPolicyTest do } } - {:reject, nil} = VocabularyPolicy.filter(message) + {:reject, _} = VocabularyPolicy.filter(message) end test "it passes through objects that aren't disallowed" do diff --git a/test/web/activity_pub/object_validator_test.exs b/test/web/activity_pub/object_validator_test.exs deleted file mode 100644 index 770a8dcf8..000000000 --- a/test/web/activity_pub/object_validator_test.exs +++ /dev/null @@ -1,657 +0,0 @@ -defmodule Pleroma.Web.ActivityPub.ObjectValidatorTest do - use Pleroma.DataCase - - alias Pleroma.Object - alias Pleroma.Web.ActivityPub.ActivityPub - alias Pleroma.Web.ActivityPub.Builder - alias Pleroma.Web.ActivityPub.ObjectValidator - alias Pleroma.Web.ActivityPub.ObjectValidators.AttachmentValidator - alias Pleroma.Web.ActivityPub.ObjectValidators.LikeValidator - alias Pleroma.Web.ActivityPub.Utils - alias Pleroma.Web.CommonAPI - - import Pleroma.Factory - - describe "attachments" do - test "works with honkerific attachments" do - attachment = %{ - "mediaType" => "", - "name" => "", - "summary" => "298p3RG7j27tfsZ9RQ.jpg", - "type" => "Document", - "url" => "https://honk.tedunangst.com/d/298p3RG7j27tfsZ9RQ.jpg" - } - - assert {:ok, attachment} = - AttachmentValidator.cast_and_validate(attachment) - |> Ecto.Changeset.apply_action(:insert) - - assert attachment.mediaType == "application/octet-stream" - end - - test "it turns mastodon attachments into our attachments" do - attachment = %{ - "url" => - "http://mastodon.example.org/system/media_attachments/files/000/000/002/original/334ce029e7bfb920.jpg", - "type" => "Document", - "name" => nil, - "mediaType" => "image/jpeg" - } - - {:ok, attachment} = - AttachmentValidator.cast_and_validate(attachment) - |> Ecto.Changeset.apply_action(:insert) - - assert [ - %{ - href: - "http://mastodon.example.org/system/media_attachments/files/000/000/002/original/334ce029e7bfb920.jpg", - type: "Link", - mediaType: "image/jpeg" - } - ] = attachment.url - - assert attachment.mediaType == "image/jpeg" - end - - test "it handles our own uploads" do - user = insert(:user) - - file = %Plug.Upload{ - content_type: "image/jpg", - path: Path.absname("test/fixtures/image.jpg"), - filename: "an_image.jpg" - } - - {:ok, attachment} = ActivityPub.upload(file, actor: user.ap_id) - - {:ok, attachment} = - attachment.data - |> AttachmentValidator.cast_and_validate() - |> Ecto.Changeset.apply_action(:insert) - - assert attachment.mediaType == "image/jpeg" - end - end - - describe "chat message create activities" do - test "it is invalid if the object already exists" do - user = insert(:user) - recipient = insert(:user) - {:ok, activity} = CommonAPI.post_chat_message(user, recipient, "hey") - object = Object.normalize(activity, false) - - {:ok, create_data, _} = Builder.create(user, object.data, [recipient.ap_id]) - - {:error, cng} = ObjectValidator.validate(create_data, []) - - assert {:object, {"The object to create already exists", []}} in cng.errors - end - - test "it is invalid if the object data has a different `to` or `actor` field" do - user = insert(:user) - recipient = insert(:user) - {:ok, object_data, _} = Builder.chat_message(recipient, user.ap_id, "Hey") - - {:ok, create_data, _} = Builder.create(user, object_data, [recipient.ap_id]) - - {:error, cng} = ObjectValidator.validate(create_data, []) - - assert {:to, {"Recipients don't match with object recipients", []}} in cng.errors - assert {:actor, {"Actor doesn't match with object actor", []}} in cng.errors - end - end - - describe "chat messages" do - setup do - clear_config([:instance, :remote_limit]) - user = insert(:user) - recipient = insert(:user, local: false) - - {:ok, valid_chat_message, _} = Builder.chat_message(user, recipient.ap_id, "hey :firefox:") - - %{user: user, recipient: recipient, valid_chat_message: valid_chat_message} - end - - test "let's through some basic html", %{user: user, recipient: recipient} do - {:ok, valid_chat_message, _} = - Builder.chat_message( - user, - recipient.ap_id, - "hey <a href='https://example.org'>example</a> <script>alert('uguu')</script>" - ) - - assert {:ok, object, _meta} = ObjectValidator.validate(valid_chat_message, []) - - assert object["content"] == - "hey <a href=\"https://example.org\">example</a> alert('uguu')" - end - - test "validates for a basic object we build", %{valid_chat_message: valid_chat_message} do - assert {:ok, object, _meta} = ObjectValidator.validate(valid_chat_message, []) - - assert Map.put(valid_chat_message, "attachment", nil) == object - end - - test "validates for a basic object with an attachment", %{ - valid_chat_message: valid_chat_message, - user: user - } do - file = %Plug.Upload{ - content_type: "image/jpg", - path: Path.absname("test/fixtures/image.jpg"), - filename: "an_image.jpg" - } - - {:ok, attachment} = ActivityPub.upload(file, actor: user.ap_id) - - valid_chat_message = - valid_chat_message - |> Map.put("attachment", attachment.data) - - assert {:ok, object, _meta} = ObjectValidator.validate(valid_chat_message, []) - - assert object["attachment"] - end - - test "validates for a basic object with an attachment in an array", %{ - valid_chat_message: valid_chat_message, - user: user - } do - file = %Plug.Upload{ - content_type: "image/jpg", - path: Path.absname("test/fixtures/image.jpg"), - filename: "an_image.jpg" - } - - {:ok, attachment} = ActivityPub.upload(file, actor: user.ap_id) - - valid_chat_message = - valid_chat_message - |> Map.put("attachment", [attachment.data]) - - assert {:ok, object, _meta} = ObjectValidator.validate(valid_chat_message, []) - - assert object["attachment"] - end - - test "validates for a basic object with an attachment but without content", %{ - valid_chat_message: valid_chat_message, - user: user - } do - file = %Plug.Upload{ - content_type: "image/jpg", - path: Path.absname("test/fixtures/image.jpg"), - filename: "an_image.jpg" - } - - {:ok, attachment} = ActivityPub.upload(file, actor: user.ap_id) - - valid_chat_message = - valid_chat_message - |> Map.put("attachment", attachment.data) - |> Map.delete("content") - - assert {:ok, object, _meta} = ObjectValidator.validate(valid_chat_message, []) - - assert object["attachment"] - end - - test "does not validate if the message has no content", %{ - valid_chat_message: valid_chat_message - } do - contentless = - valid_chat_message - |> Map.delete("content") - - refute match?({:ok, _object, _meta}, ObjectValidator.validate(contentless, [])) - end - - test "does not validate if the message is longer than the remote_limit", %{ - valid_chat_message: valid_chat_message - } do - Pleroma.Config.put([:instance, :remote_limit], 2) - refute match?({:ok, _object, _meta}, ObjectValidator.validate(valid_chat_message, [])) - end - - test "does not validate if the recipient is blocking the actor", %{ - valid_chat_message: valid_chat_message, - user: user, - recipient: recipient - } do - Pleroma.User.block(recipient, user) - refute match?({:ok, _object, _meta}, ObjectValidator.validate(valid_chat_message, [])) - end - - test "does not validate if the actor or the recipient is not in our system", %{ - valid_chat_message: valid_chat_message - } do - chat_message = - valid_chat_message - |> Map.put("actor", "https://raymoo.com/raymoo") - - {:error, _} = ObjectValidator.validate(chat_message, []) - - chat_message = - valid_chat_message - |> Map.put("to", ["https://raymoo.com/raymoo"]) - - {:error, _} = ObjectValidator.validate(chat_message, []) - end - - test "does not validate for a message with multiple recipients", %{ - valid_chat_message: valid_chat_message, - user: user, - recipient: recipient - } do - chat_message = - valid_chat_message - |> Map.put("to", [user.ap_id, recipient.ap_id]) - - assert {:error, _} = ObjectValidator.validate(chat_message, []) - end - - test "does not validate if it doesn't concern local users" do - user = insert(:user, local: false) - recipient = insert(:user, local: false) - - {:ok, valid_chat_message, _} = Builder.chat_message(user, recipient.ap_id, "hey") - assert {:error, _} = ObjectValidator.validate(valid_chat_message, []) - end - end - - describe "EmojiReacts" do - setup do - user = insert(:user) - {:ok, post_activity} = CommonAPI.post(user, %{status: "uguu"}) - - object = Pleroma.Object.get_by_ap_id(post_activity.data["object"]) - - {:ok, valid_emoji_react, []} = Builder.emoji_react(user, object, "👌") - - %{user: user, post_activity: post_activity, valid_emoji_react: valid_emoji_react} - end - - test "it validates a valid EmojiReact", %{valid_emoji_react: valid_emoji_react} do - assert {:ok, _, _} = ObjectValidator.validate(valid_emoji_react, []) - end - - test "it is not valid without a 'content' field", %{valid_emoji_react: valid_emoji_react} do - without_content = - valid_emoji_react - |> Map.delete("content") - - {:error, cng} = ObjectValidator.validate(without_content, []) - - refute cng.valid? - assert {:content, {"can't be blank", [validation: :required]}} in cng.errors - end - - test "it is not valid with a non-emoji content field", %{valid_emoji_react: valid_emoji_react} do - without_emoji_content = - valid_emoji_react - |> Map.put("content", "x") - - {:error, cng} = ObjectValidator.validate(without_emoji_content, []) - - refute cng.valid? - - assert {:content, {"must be a single character emoji", []}} in cng.errors - end - end - - describe "Undos" do - setup do - user = insert(:user) - {:ok, post_activity} = CommonAPI.post(user, %{status: "uguu"}) - {:ok, like} = CommonAPI.favorite(user, post_activity.id) - {:ok, valid_like_undo, []} = Builder.undo(user, like) - - %{user: user, like: like, valid_like_undo: valid_like_undo} - end - - test "it validates a basic like undo", %{valid_like_undo: valid_like_undo} do - assert {:ok, _, _} = ObjectValidator.validate(valid_like_undo, []) - end - - test "it does not validate if the actor of the undo is not the actor of the object", %{ - valid_like_undo: valid_like_undo - } do - other_user = insert(:user, ap_id: "https://gensokyo.2hu/users/raymoo") - - bad_actor = - valid_like_undo - |> Map.put("actor", other_user.ap_id) - - {:error, cng} = ObjectValidator.validate(bad_actor, []) - - assert {:actor, {"not the same as object actor", []}} in cng.errors - end - - test "it does not validate if the object is missing", %{valid_like_undo: valid_like_undo} do - missing_object = - valid_like_undo - |> Map.put("object", "https://gensokyo.2hu/objects/1") - - {:error, cng} = ObjectValidator.validate(missing_object, []) - - assert {:object, {"can't find object", []}} in cng.errors - assert length(cng.errors) == 1 - end - end - - describe "deletes" do - setup do - user = insert(:user) - {:ok, post_activity} = CommonAPI.post(user, %{status: "cancel me daddy"}) - - {:ok, valid_post_delete, _} = Builder.delete(user, post_activity.data["object"]) - {:ok, valid_user_delete, _} = Builder.delete(user, user.ap_id) - - %{user: user, valid_post_delete: valid_post_delete, valid_user_delete: valid_user_delete} - end - - test "it is valid for a post deletion", %{valid_post_delete: valid_post_delete} do - {:ok, valid_post_delete, _} = ObjectValidator.validate(valid_post_delete, []) - - assert valid_post_delete["deleted_activity_id"] - end - - test "it is invalid if the object isn't in a list of certain types", %{ - valid_post_delete: valid_post_delete - } do - object = Object.get_by_ap_id(valid_post_delete["object"]) - - data = - object.data - |> Map.put("type", "Like") - - {:ok, _object} = - object - |> Ecto.Changeset.change(%{data: data}) - |> Object.update_and_set_cache() - - {:error, cng} = ObjectValidator.validate(valid_post_delete, []) - assert {:object, {"object not in allowed types", []}} in cng.errors - end - - test "it is valid for a user deletion", %{valid_user_delete: valid_user_delete} do - assert match?({:ok, _, _}, ObjectValidator.validate(valid_user_delete, [])) - end - - test "it's invalid if the id is missing", %{valid_post_delete: valid_post_delete} do - no_id = - valid_post_delete - |> Map.delete("id") - - {:error, cng} = ObjectValidator.validate(no_id, []) - - assert {:id, {"can't be blank", [validation: :required]}} in cng.errors - end - - test "it's invalid if the object doesn't exist", %{valid_post_delete: valid_post_delete} do - missing_object = - valid_post_delete - |> Map.put("object", "http://does.not/exist") - - {:error, cng} = ObjectValidator.validate(missing_object, []) - - assert {:object, {"can't find object", []}} in cng.errors - end - - test "it's invalid if the actor of the object and the actor of delete are from different domains", - %{valid_post_delete: valid_post_delete} do - valid_user = insert(:user) - - valid_other_actor = - valid_post_delete - |> Map.put("actor", valid_user.ap_id) - - assert match?({:ok, _, _}, ObjectValidator.validate(valid_other_actor, [])) - - invalid_other_actor = - valid_post_delete - |> Map.put("actor", "https://gensokyo.2hu/users/raymoo") - - {:error, cng} = ObjectValidator.validate(invalid_other_actor, []) - - assert {:actor, {"is not allowed to delete object", []}} in cng.errors - end - - test "it's valid if the actor of the object is a local superuser", - %{valid_post_delete: valid_post_delete} do - user = - insert(:user, local: true, is_moderator: true, ap_id: "https://gensokyo.2hu/users/raymoo") - - valid_other_actor = - valid_post_delete - |> Map.put("actor", user.ap_id) - - {:ok, _, meta} = ObjectValidator.validate(valid_other_actor, []) - assert meta[:do_not_federate] - end - end - - describe "likes" do - setup do - user = insert(:user) - {:ok, post_activity} = CommonAPI.post(user, %{status: "uguu"}) - - valid_like = %{ - "to" => [user.ap_id], - "cc" => [], - "type" => "Like", - "id" => Utils.generate_activity_id(), - "object" => post_activity.data["object"], - "actor" => user.ap_id, - "context" => "a context" - } - - %{valid_like: valid_like, user: user, post_activity: post_activity} - end - - test "returns ok when called in the ObjectValidator", %{valid_like: valid_like} do - {:ok, object, _meta} = ObjectValidator.validate(valid_like, []) - - assert "id" in Map.keys(object) - end - - test "is valid for a valid object", %{valid_like: valid_like} do - assert LikeValidator.cast_and_validate(valid_like).valid? - end - - test "sets the 'to' field to the object actor if no recipients are given", %{ - valid_like: valid_like, - user: user - } do - without_recipients = - valid_like - |> Map.delete("to") - - {:ok, object, _meta} = ObjectValidator.validate(without_recipients, []) - - assert object["to"] == [user.ap_id] - end - - test "sets the context field to the context of the object if no context is given", %{ - valid_like: valid_like, - post_activity: post_activity - } do - without_context = - valid_like - |> Map.delete("context") - - {:ok, object, _meta} = ObjectValidator.validate(without_context, []) - - assert object["context"] == post_activity.data["context"] - end - - test "it errors when the actor is missing or not known", %{valid_like: valid_like} do - without_actor = Map.delete(valid_like, "actor") - - refute LikeValidator.cast_and_validate(without_actor).valid? - - with_invalid_actor = Map.put(valid_like, "actor", "invalidactor") - - refute LikeValidator.cast_and_validate(with_invalid_actor).valid? - end - - test "it errors when the object is missing or not known", %{valid_like: valid_like} do - without_object = Map.delete(valid_like, "object") - - refute LikeValidator.cast_and_validate(without_object).valid? - - with_invalid_object = Map.put(valid_like, "object", "invalidobject") - - refute LikeValidator.cast_and_validate(with_invalid_object).valid? - end - - test "it errors when the actor has already like the object", %{ - valid_like: valid_like, - user: user, - post_activity: post_activity - } do - _like = CommonAPI.favorite(user, post_activity.id) - - refute LikeValidator.cast_and_validate(valid_like).valid? - end - - test "it works when actor or object are wrapped in maps", %{valid_like: valid_like} do - wrapped_like = - valid_like - |> Map.put("actor", %{"id" => valid_like["actor"]}) - |> Map.put("object", %{"id" => valid_like["object"]}) - - validated = LikeValidator.cast_and_validate(wrapped_like) - - assert validated.valid? - - assert {:actor, valid_like["actor"]} in validated.changes - assert {:object, valid_like["object"]} in validated.changes - end - end - - describe "announces" do - setup do - user = insert(:user) - announcer = insert(:user) - {:ok, post_activity} = CommonAPI.post(user, %{status: "uguu"}) - - object = Object.normalize(post_activity, false) - {:ok, valid_announce, []} = Builder.announce(announcer, object) - - %{ - valid_announce: valid_announce, - user: user, - post_activity: post_activity, - announcer: announcer - } - end - - test "returns ok for a valid announce", %{valid_announce: valid_announce} do - assert {:ok, _object, _meta} = ObjectValidator.validate(valid_announce, []) - end - - test "returns an error if the object can't be found", %{valid_announce: valid_announce} do - without_object = - valid_announce - |> Map.delete("object") - - {:error, cng} = ObjectValidator.validate(without_object, []) - - assert {:object, {"can't be blank", [validation: :required]}} in cng.errors - - nonexisting_object = - valid_announce - |> Map.put("object", "https://gensokyo.2hu/objects/99999999") - - {:error, cng} = ObjectValidator.validate(nonexisting_object, []) - - assert {:object, {"can't find object", []}} in cng.errors - end - - test "returns an error if we don't have the actor", %{valid_announce: valid_announce} do - nonexisting_actor = - valid_announce - |> Map.put("actor", "https://gensokyo.2hu/users/raymoo") - - {:error, cng} = ObjectValidator.validate(nonexisting_actor, []) - - assert {:actor, {"can't find user", []}} in cng.errors - end - - test "returns an error if the actor already announced the object", %{ - valid_announce: valid_announce, - announcer: announcer, - post_activity: post_activity - } do - _announce = CommonAPI.repeat(post_activity.id, announcer) - - {:error, cng} = ObjectValidator.validate(valid_announce, []) - - assert {:actor, {"already announced this object", []}} in cng.errors - assert {:object, {"already announced by this actor", []}} in cng.errors - end - - test "returns an error if the actor can't announce the object", %{ - announcer: announcer, - user: user - } do - {:ok, post_activity} = - CommonAPI.post(user, %{status: "a secret post", visibility: "private"}) - - object = Object.normalize(post_activity, false) - - # Another user can't announce it - {:ok, announce, []} = Builder.announce(announcer, object, public: false) - - {:error, cng} = ObjectValidator.validate(announce, []) - - assert {:actor, {"can not announce this object", []}} in cng.errors - - # The actor of the object can announce it - {:ok, announce, []} = Builder.announce(user, object, public: false) - - assert {:ok, _, _} = ObjectValidator.validate(announce, []) - - # The actor of the object can not announce it publicly - {:ok, announce, []} = Builder.announce(user, object, public: true) - - {:error, cng} = ObjectValidator.validate(announce, []) - - assert {:actor, {"can not announce this object publicly", []}} in cng.errors - end - end - - describe "updates" do - setup do - user = insert(:user) - - object = %{ - "id" => user.ap_id, - "name" => "A new name", - "summary" => "A new bio" - } - - {:ok, valid_update, []} = Builder.update(user, object) - - %{user: user, valid_update: valid_update} - end - - test "validates a basic object", %{valid_update: valid_update} do - assert {:ok, _update, []} = ObjectValidator.validate(valid_update, []) - end - - test "returns an error if the object can't be updated by the actor", %{ - valid_update: valid_update - } do - other_user = insert(:user) - - update = - valid_update - |> Map.put("actor", other_user.ap_id) - - assert {:error, _cng} = ObjectValidator.validate(update, []) - end - end -end diff --git a/test/web/activity_pub/object_validators/announce_validation_test.exs b/test/web/activity_pub/object_validators/announce_validation_test.exs new file mode 100644 index 000000000..623342f76 --- /dev/null +++ b/test/web/activity_pub/object_validators/announce_validation_test.exs @@ -0,0 +1,106 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ActivityPub.ObjectValidators.AnnouncValidationTest do + use Pleroma.DataCase + + alias Pleroma.Object + alias Pleroma.Web.ActivityPub.Builder + alias Pleroma.Web.ActivityPub.ObjectValidator + alias Pleroma.Web.CommonAPI + + import Pleroma.Factory + + describe "announces" do + setup do + user = insert(:user) + announcer = insert(:user) + {:ok, post_activity} = CommonAPI.post(user, %{status: "uguu"}) + + object = Object.normalize(post_activity, false) + {:ok, valid_announce, []} = Builder.announce(announcer, object) + + %{ + valid_announce: valid_announce, + user: user, + post_activity: post_activity, + announcer: announcer + } + end + + test "returns ok for a valid announce", %{valid_announce: valid_announce} do + assert {:ok, _object, _meta} = ObjectValidator.validate(valid_announce, []) + end + + test "returns an error if the object can't be found", %{valid_announce: valid_announce} do + without_object = + valid_announce + |> Map.delete("object") + + {:error, cng} = ObjectValidator.validate(without_object, []) + + assert {:object, {"can't be blank", [validation: :required]}} in cng.errors + + nonexisting_object = + valid_announce + |> Map.put("object", "https://gensokyo.2hu/objects/99999999") + + {:error, cng} = ObjectValidator.validate(nonexisting_object, []) + + assert {:object, {"can't find object", []}} in cng.errors + end + + test "returns an error if we don't have the actor", %{valid_announce: valid_announce} do + nonexisting_actor = + valid_announce + |> Map.put("actor", "https://gensokyo.2hu/users/raymoo") + + {:error, cng} = ObjectValidator.validate(nonexisting_actor, []) + + assert {:actor, {"can't find user", []}} in cng.errors + end + + test "returns an error if the actor already announced the object", %{ + valid_announce: valid_announce, + announcer: announcer, + post_activity: post_activity + } do + _announce = CommonAPI.repeat(post_activity.id, announcer) + + {:error, cng} = ObjectValidator.validate(valid_announce, []) + + assert {:actor, {"already announced this object", []}} in cng.errors + assert {:object, {"already announced by this actor", []}} in cng.errors + end + + test "returns an error if the actor can't announce the object", %{ + announcer: announcer, + user: user + } do + {:ok, post_activity} = + CommonAPI.post(user, %{status: "a secret post", visibility: "private"}) + + object = Object.normalize(post_activity, false) + + # Another user can't announce it + {:ok, announce, []} = Builder.announce(announcer, object, public: false) + + {:error, cng} = ObjectValidator.validate(announce, []) + + assert {:actor, {"can not announce this object", []}} in cng.errors + + # The actor of the object can announce it + {:ok, announce, []} = Builder.announce(user, object, public: false) + + assert {:ok, _, _} = ObjectValidator.validate(announce, []) + + # The actor of the object can not announce it publicly + {:ok, announce, []} = Builder.announce(user, object, public: true) + + {:error, cng} = ObjectValidator.validate(announce, []) + + assert {:actor, {"can not announce this object publicly", []}} in cng.errors + end + end +end diff --git a/test/web/activity_pub/object_validators/attachment_validator_test.exs b/test/web/activity_pub/object_validators/attachment_validator_test.exs new file mode 100644 index 000000000..558bb3131 --- /dev/null +++ b/test/web/activity_pub/object_validators/attachment_validator_test.exs @@ -0,0 +1,74 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ActivityPub.ObjectValidators.AttachmentValidatorTest do + use Pleroma.DataCase + + alias Pleroma.Web.ActivityPub.ActivityPub + alias Pleroma.Web.ActivityPub.ObjectValidators.AttachmentValidator + + import Pleroma.Factory + + describe "attachments" do + test "works with honkerific attachments" do + attachment = %{ + "mediaType" => "", + "name" => "", + "summary" => "298p3RG7j27tfsZ9RQ.jpg", + "type" => "Document", + "url" => "https://honk.tedunangst.com/d/298p3RG7j27tfsZ9RQ.jpg" + } + + assert {:ok, attachment} = + AttachmentValidator.cast_and_validate(attachment) + |> Ecto.Changeset.apply_action(:insert) + + assert attachment.mediaType == "application/octet-stream" + end + + test "it turns mastodon attachments into our attachments" do + attachment = %{ + "url" => + "http://mastodon.example.org/system/media_attachments/files/000/000/002/original/334ce029e7bfb920.jpg", + "type" => "Document", + "name" => nil, + "mediaType" => "image/jpeg" + } + + {:ok, attachment} = + AttachmentValidator.cast_and_validate(attachment) + |> Ecto.Changeset.apply_action(:insert) + + assert [ + %{ + href: + "http://mastodon.example.org/system/media_attachments/files/000/000/002/original/334ce029e7bfb920.jpg", + type: "Link", + mediaType: "image/jpeg" + } + ] = attachment.url + + assert attachment.mediaType == "image/jpeg" + end + + test "it handles our own uploads" do + user = insert(:user) + + file = %Plug.Upload{ + content_type: "image/jpg", + path: Path.absname("test/fixtures/image.jpg"), + filename: "an_image.jpg" + } + + {:ok, attachment} = ActivityPub.upload(file, actor: user.ap_id) + + {:ok, attachment} = + attachment.data + |> AttachmentValidator.cast_and_validate() + |> Ecto.Changeset.apply_action(:insert) + + assert attachment.mediaType == "image/jpeg" + end + end +end diff --git a/test/web/activity_pub/object_validators/block_validation_test.exs b/test/web/activity_pub/object_validators/block_validation_test.exs new file mode 100644 index 000000000..c08d4b2e8 --- /dev/null +++ b/test/web/activity_pub/object_validators/block_validation_test.exs @@ -0,0 +1,39 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ActivityPub.ObjectValidators.BlockValidationTest do + use Pleroma.DataCase + + alias Pleroma.Web.ActivityPub.Builder + alias Pleroma.Web.ActivityPub.ObjectValidator + + import Pleroma.Factory + + describe "blocks" do + setup do + user = insert(:user, local: false) + blocked = insert(:user) + + {:ok, valid_block, []} = Builder.block(user, blocked) + + %{user: user, valid_block: valid_block} + end + + test "validates a basic object", %{ + valid_block: valid_block + } do + assert {:ok, _block, []} = ObjectValidator.validate(valid_block, []) + end + + test "returns an error if we don't know the blocked user", %{ + valid_block: valid_block + } do + block = + valid_block + |> Map.put("object", "https://gensokyo.2hu/users/raymoo") + + assert {:error, _cng} = ObjectValidator.validate(block, []) + end + end +end diff --git a/test/web/activity_pub/object_validators/chat_validation_test.exs b/test/web/activity_pub/object_validators/chat_validation_test.exs new file mode 100644 index 000000000..50bf03515 --- /dev/null +++ b/test/web/activity_pub/object_validators/chat_validation_test.exs @@ -0,0 +1,211 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ActivityPub.ObjectValidators.ChatValidationTest do + use Pleroma.DataCase + alias Pleroma.Object + alias Pleroma.Web.ActivityPub.ActivityPub + alias Pleroma.Web.ActivityPub.Builder + alias Pleroma.Web.ActivityPub.ObjectValidator + alias Pleroma.Web.CommonAPI + + import Pleroma.Factory + + describe "chat message create activities" do + test "it is invalid if the object already exists" do + user = insert(:user) + recipient = insert(:user) + {:ok, activity} = CommonAPI.post_chat_message(user, recipient, "hey") + object = Object.normalize(activity, false) + + {:ok, create_data, _} = Builder.create(user, object.data, [recipient.ap_id]) + + {:error, cng} = ObjectValidator.validate(create_data, []) + + assert {:object, {"The object to create already exists", []}} in cng.errors + end + + test "it is invalid if the object data has a different `to` or `actor` field" do + user = insert(:user) + recipient = insert(:user) + {:ok, object_data, _} = Builder.chat_message(recipient, user.ap_id, "Hey") + + {:ok, create_data, _} = Builder.create(user, object_data, [recipient.ap_id]) + + {:error, cng} = ObjectValidator.validate(create_data, []) + + assert {:to, {"Recipients don't match with object recipients", []}} in cng.errors + assert {:actor, {"Actor doesn't match with object actor", []}} in cng.errors + end + end + + describe "chat messages" do + setup do + clear_config([:instance, :remote_limit]) + user = insert(:user) + recipient = insert(:user, local: false) + + {:ok, valid_chat_message, _} = Builder.chat_message(user, recipient.ap_id, "hey :firefox:") + + %{user: user, recipient: recipient, valid_chat_message: valid_chat_message} + end + + test "let's through some basic html", %{user: user, recipient: recipient} do + {:ok, valid_chat_message, _} = + Builder.chat_message( + user, + recipient.ap_id, + "hey <a href='https://example.org'>example</a> <script>alert('uguu')</script>" + ) + + assert {:ok, object, _meta} = ObjectValidator.validate(valid_chat_message, []) + + assert object["content"] == + "hey <a href=\"https://example.org\">example</a> alert('uguu')" + end + + test "validates for a basic object we build", %{valid_chat_message: valid_chat_message} do + assert {:ok, object, _meta} = ObjectValidator.validate(valid_chat_message, []) + + assert Map.put(valid_chat_message, "attachment", nil) == object + end + + test "validates for a basic object with an attachment", %{ + valid_chat_message: valid_chat_message, + user: user + } do + file = %Plug.Upload{ + content_type: "image/jpg", + path: Path.absname("test/fixtures/image.jpg"), + filename: "an_image.jpg" + } + + {:ok, attachment} = ActivityPub.upload(file, actor: user.ap_id) + + valid_chat_message = + valid_chat_message + |> Map.put("attachment", attachment.data) + + assert {:ok, object, _meta} = ObjectValidator.validate(valid_chat_message, []) + + assert object["attachment"] + end + + test "validates for a basic object with an attachment in an array", %{ + valid_chat_message: valid_chat_message, + user: user + } do + file = %Plug.Upload{ + content_type: "image/jpg", + path: Path.absname("test/fixtures/image.jpg"), + filename: "an_image.jpg" + } + + {:ok, attachment} = ActivityPub.upload(file, actor: user.ap_id) + + valid_chat_message = + valid_chat_message + |> Map.put("attachment", [attachment.data]) + + assert {:ok, object, _meta} = ObjectValidator.validate(valid_chat_message, []) + + assert object["attachment"] + end + + test "validates for a basic object with an attachment but without content", %{ + valid_chat_message: valid_chat_message, + user: user + } do + file = %Plug.Upload{ + content_type: "image/jpg", + path: Path.absname("test/fixtures/image.jpg"), + filename: "an_image.jpg" + } + + {:ok, attachment} = ActivityPub.upload(file, actor: user.ap_id) + + valid_chat_message = + valid_chat_message + |> Map.put("attachment", attachment.data) + |> Map.delete("content") + + assert {:ok, object, _meta} = ObjectValidator.validate(valid_chat_message, []) + + assert object["attachment"] + end + + test "does not validate if the message has no content", %{ + valid_chat_message: valid_chat_message + } do + contentless = + valid_chat_message + |> Map.delete("content") + + refute match?({:ok, _object, _meta}, ObjectValidator.validate(contentless, [])) + end + + test "does not validate if the message is longer than the remote_limit", %{ + valid_chat_message: valid_chat_message + } do + Pleroma.Config.put([:instance, :remote_limit], 2) + refute match?({:ok, _object, _meta}, ObjectValidator.validate(valid_chat_message, [])) + end + + test "does not validate if the recipient is blocking the actor", %{ + valid_chat_message: valid_chat_message, + user: user, + recipient: recipient + } do + Pleroma.User.block(recipient, user) + refute match?({:ok, _object, _meta}, ObjectValidator.validate(valid_chat_message, [])) + end + + test "does not validate if the recipient is not accepting chat messages", %{ + valid_chat_message: valid_chat_message, + recipient: recipient + } do + recipient + |> Ecto.Changeset.change(%{accepts_chat_messages: false}) + |> Pleroma.Repo.update!() + + refute match?({:ok, _object, _meta}, ObjectValidator.validate(valid_chat_message, [])) + end + + test "does not validate if the actor or the recipient is not in our system", %{ + valid_chat_message: valid_chat_message + } do + chat_message = + valid_chat_message + |> Map.put("actor", "https://raymoo.com/raymoo") + + {:error, _} = ObjectValidator.validate(chat_message, []) + + chat_message = + valid_chat_message + |> Map.put("to", ["https://raymoo.com/raymoo"]) + + {:error, _} = ObjectValidator.validate(chat_message, []) + end + + test "does not validate for a message with multiple recipients", %{ + valid_chat_message: valid_chat_message, + user: user, + recipient: recipient + } do + chat_message = + valid_chat_message + |> Map.put("to", [user.ap_id, recipient.ap_id]) + + assert {:error, _} = ObjectValidator.validate(chat_message, []) + end + + test "does not validate if it doesn't concern local users" do + user = insert(:user, local: false) + recipient = insert(:user, local: false) + + {:ok, valid_chat_message, _} = Builder.chat_message(user, recipient.ap_id, "hey") + assert {:error, _} = ObjectValidator.validate(valid_chat_message, []) + end + end +end diff --git a/test/web/activity_pub/object_validators/delete_validation_test.exs b/test/web/activity_pub/object_validators/delete_validation_test.exs new file mode 100644 index 000000000..42cd18298 --- /dev/null +++ b/test/web/activity_pub/object_validators/delete_validation_test.exs @@ -0,0 +1,106 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ActivityPub.ObjectValidators.DeleteValidationTest do + use Pleroma.DataCase + + alias Pleroma.Object + alias Pleroma.Web.ActivityPub.Builder + alias Pleroma.Web.ActivityPub.ObjectValidator + alias Pleroma.Web.CommonAPI + + import Pleroma.Factory + + describe "deletes" do + setup do + user = insert(:user) + {:ok, post_activity} = CommonAPI.post(user, %{status: "cancel me daddy"}) + + {:ok, valid_post_delete, _} = Builder.delete(user, post_activity.data["object"]) + {:ok, valid_user_delete, _} = Builder.delete(user, user.ap_id) + + %{user: user, valid_post_delete: valid_post_delete, valid_user_delete: valid_user_delete} + end + + test "it is valid for a post deletion", %{valid_post_delete: valid_post_delete} do + {:ok, valid_post_delete, _} = ObjectValidator.validate(valid_post_delete, []) + + assert valid_post_delete["deleted_activity_id"] + end + + test "it is invalid if the object isn't in a list of certain types", %{ + valid_post_delete: valid_post_delete + } do + object = Object.get_by_ap_id(valid_post_delete["object"]) + + data = + object.data + |> Map.put("type", "Like") + + {:ok, _object} = + object + |> Ecto.Changeset.change(%{data: data}) + |> Object.update_and_set_cache() + + {:error, cng} = ObjectValidator.validate(valid_post_delete, []) + assert {:object, {"object not in allowed types", []}} in cng.errors + end + + test "it is valid for a user deletion", %{valid_user_delete: valid_user_delete} do + assert match?({:ok, _, _}, ObjectValidator.validate(valid_user_delete, [])) + end + + test "it's invalid if the id is missing", %{valid_post_delete: valid_post_delete} do + no_id = + valid_post_delete + |> Map.delete("id") + + {:error, cng} = ObjectValidator.validate(no_id, []) + + assert {:id, {"can't be blank", [validation: :required]}} in cng.errors + end + + test "it's invalid if the object doesn't exist", %{valid_post_delete: valid_post_delete} do + missing_object = + valid_post_delete + |> Map.put("object", "http://does.not/exist") + + {:error, cng} = ObjectValidator.validate(missing_object, []) + + assert {:object, {"can't find object", []}} in cng.errors + end + + test "it's invalid if the actor of the object and the actor of delete are from different domains", + %{valid_post_delete: valid_post_delete} do + valid_user = insert(:user) + + valid_other_actor = + valid_post_delete + |> Map.put("actor", valid_user.ap_id) + + assert match?({:ok, _, _}, ObjectValidator.validate(valid_other_actor, [])) + + invalid_other_actor = + valid_post_delete + |> Map.put("actor", "https://gensokyo.2hu/users/raymoo") + + {:error, cng} = ObjectValidator.validate(invalid_other_actor, []) + + assert {:actor, {"is not allowed to delete object", []}} in cng.errors + end + + test "it's valid if the actor of the object is a local superuser", + %{valid_post_delete: valid_post_delete} do + user = + insert(:user, local: true, is_moderator: true, ap_id: "https://gensokyo.2hu/users/raymoo") + + valid_other_actor = + valid_post_delete + |> Map.put("actor", user.ap_id) + + {:ok, _, meta} = ObjectValidator.validate(valid_other_actor, []) + assert meta[:do_not_federate] + end + end +end diff --git a/test/web/activity_pub/object_validators/emoji_react_validation_test.exs b/test/web/activity_pub/object_validators/emoji_react_validation_test.exs new file mode 100644 index 000000000..582e6d785 --- /dev/null +++ b/test/web/activity_pub/object_validators/emoji_react_validation_test.exs @@ -0,0 +1,53 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ActivityPub.ObjectValidators.EmojiReactHandlingTest do + use Pleroma.DataCase + + alias Pleroma.Web.ActivityPub.Builder + alias Pleroma.Web.ActivityPub.ObjectValidator + alias Pleroma.Web.CommonAPI + + import Pleroma.Factory + + describe "EmojiReacts" do + setup do + user = insert(:user) + {:ok, post_activity} = CommonAPI.post(user, %{status: "uguu"}) + + object = Pleroma.Object.get_by_ap_id(post_activity.data["object"]) + + {:ok, valid_emoji_react, []} = Builder.emoji_react(user, object, "👌") + + %{user: user, post_activity: post_activity, valid_emoji_react: valid_emoji_react} + end + + test "it validates a valid EmojiReact", %{valid_emoji_react: valid_emoji_react} do + assert {:ok, _, _} = ObjectValidator.validate(valid_emoji_react, []) + end + + test "it is not valid without a 'content' field", %{valid_emoji_react: valid_emoji_react} do + without_content = + valid_emoji_react + |> Map.delete("content") + + {:error, cng} = ObjectValidator.validate(without_content, []) + + refute cng.valid? + assert {:content, {"can't be blank", [validation: :required]}} in cng.errors + end + + test "it is not valid with a non-emoji content field", %{valid_emoji_react: valid_emoji_react} do + without_emoji_content = + valid_emoji_react + |> Map.put("content", "x") + + {:error, cng} = ObjectValidator.validate(without_emoji_content, []) + + refute cng.valid? + + assert {:content, {"must be a single character emoji", []}} in cng.errors + end + end +end diff --git a/test/web/activity_pub/object_validators/follow_validation_test.exs b/test/web/activity_pub/object_validators/follow_validation_test.exs new file mode 100644 index 000000000..6e1378be2 --- /dev/null +++ b/test/web/activity_pub/object_validators/follow_validation_test.exs @@ -0,0 +1,26 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ActivityPub.ObjectValidators.FollowValidationTest do + use Pleroma.DataCase + + alias Pleroma.Web.ActivityPub.Builder + alias Pleroma.Web.ActivityPub.ObjectValidator + + import Pleroma.Factory + + describe "Follows" do + setup do + follower = insert(:user) + followed = insert(:user) + + {:ok, valid_follow, []} = Builder.follow(follower, followed) + %{follower: follower, followed: followed, valid_follow: valid_follow} + end + + test "validates a basic follow object", %{valid_follow: valid_follow} do + assert {:ok, _follow, []} = ObjectValidator.validate(valid_follow, []) + end + end +end diff --git a/test/web/activity_pub/object_validators/like_validation_test.exs b/test/web/activity_pub/object_validators/like_validation_test.exs new file mode 100644 index 000000000..2c033b7e2 --- /dev/null +++ b/test/web/activity_pub/object_validators/like_validation_test.exs @@ -0,0 +1,113 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ActivityPub.ObjectValidators.LikeValidationTest do + use Pleroma.DataCase + + alias Pleroma.Web.ActivityPub.ObjectValidator + alias Pleroma.Web.ActivityPub.ObjectValidators.LikeValidator + alias Pleroma.Web.ActivityPub.Utils + alias Pleroma.Web.CommonAPI + + import Pleroma.Factory + + describe "likes" do + setup do + user = insert(:user) + {:ok, post_activity} = CommonAPI.post(user, %{status: "uguu"}) + + valid_like = %{ + "to" => [user.ap_id], + "cc" => [], + "type" => "Like", + "id" => Utils.generate_activity_id(), + "object" => post_activity.data["object"], + "actor" => user.ap_id, + "context" => "a context" + } + + %{valid_like: valid_like, user: user, post_activity: post_activity} + end + + test "returns ok when called in the ObjectValidator", %{valid_like: valid_like} do + {:ok, object, _meta} = ObjectValidator.validate(valid_like, []) + + assert "id" in Map.keys(object) + end + + test "is valid for a valid object", %{valid_like: valid_like} do + assert LikeValidator.cast_and_validate(valid_like).valid? + end + + test "sets the 'to' field to the object actor if no recipients are given", %{ + valid_like: valid_like, + user: user + } do + without_recipients = + valid_like + |> Map.delete("to") + + {:ok, object, _meta} = ObjectValidator.validate(without_recipients, []) + + assert object["to"] == [user.ap_id] + end + + test "sets the context field to the context of the object if no context is given", %{ + valid_like: valid_like, + post_activity: post_activity + } do + without_context = + valid_like + |> Map.delete("context") + + {:ok, object, _meta} = ObjectValidator.validate(without_context, []) + + assert object["context"] == post_activity.data["context"] + end + + test "it errors when the actor is missing or not known", %{valid_like: valid_like} do + without_actor = Map.delete(valid_like, "actor") + + refute LikeValidator.cast_and_validate(without_actor).valid? + + with_invalid_actor = Map.put(valid_like, "actor", "invalidactor") + + refute LikeValidator.cast_and_validate(with_invalid_actor).valid? + end + + test "it errors when the object is missing or not known", %{valid_like: valid_like} do + without_object = Map.delete(valid_like, "object") + + refute LikeValidator.cast_and_validate(without_object).valid? + + with_invalid_object = Map.put(valid_like, "object", "invalidobject") + + refute LikeValidator.cast_and_validate(with_invalid_object).valid? + end + + test "it errors when the actor has already like the object", %{ + valid_like: valid_like, + user: user, + post_activity: post_activity + } do + _like = CommonAPI.favorite(user, post_activity.id) + + refute LikeValidator.cast_and_validate(valid_like).valid? + end + + test "it works when actor or object are wrapped in maps", %{valid_like: valid_like} do + wrapped_like = + valid_like + |> Map.put("actor", %{"id" => valid_like["actor"]}) + |> Map.put("object", %{"id" => valid_like["object"]}) + + validated = LikeValidator.cast_and_validate(wrapped_like) + + assert validated.valid? + + assert {:actor, valid_like["actor"]} in validated.changes + assert {:object, valid_like["object"]} in validated.changes + end + end +end diff --git a/test/web/activity_pub/object_validators/undo_validation_test.exs b/test/web/activity_pub/object_validators/undo_validation_test.exs new file mode 100644 index 000000000..75bbcc4b6 --- /dev/null +++ b/test/web/activity_pub/object_validators/undo_validation_test.exs @@ -0,0 +1,53 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ActivityPub.ObjectValidators.UndoHandlingTest do + use Pleroma.DataCase + + alias Pleroma.Web.ActivityPub.Builder + alias Pleroma.Web.ActivityPub.ObjectValidator + alias Pleroma.Web.CommonAPI + + import Pleroma.Factory + + describe "Undos" do + setup do + user = insert(:user) + {:ok, post_activity} = CommonAPI.post(user, %{status: "uguu"}) + {:ok, like} = CommonAPI.favorite(user, post_activity.id) + {:ok, valid_like_undo, []} = Builder.undo(user, like) + + %{user: user, like: like, valid_like_undo: valid_like_undo} + end + + test "it validates a basic like undo", %{valid_like_undo: valid_like_undo} do + assert {:ok, _, _} = ObjectValidator.validate(valid_like_undo, []) + end + + test "it does not validate if the actor of the undo is not the actor of the object", %{ + valid_like_undo: valid_like_undo + } do + other_user = insert(:user, ap_id: "https://gensokyo.2hu/users/raymoo") + + bad_actor = + valid_like_undo + |> Map.put("actor", other_user.ap_id) + + {:error, cng} = ObjectValidator.validate(bad_actor, []) + + assert {:actor, {"not the same as object actor", []}} in cng.errors + end + + test "it does not validate if the object is missing", %{valid_like_undo: valid_like_undo} do + missing_object = + valid_like_undo + |> Map.put("object", "https://gensokyo.2hu/objects/1") + + {:error, cng} = ObjectValidator.validate(missing_object, []) + + assert {:object, {"can't find object", []}} in cng.errors + assert length(cng.errors) == 1 + end + end +end diff --git a/test/web/activity_pub/object_validators/update_validation_test.exs b/test/web/activity_pub/object_validators/update_validation_test.exs new file mode 100644 index 000000000..5e80cf731 --- /dev/null +++ b/test/web/activity_pub/object_validators/update_validation_test.exs @@ -0,0 +1,44 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ActivityPub.ObjectValidators.UpdateHandlingTest do + use Pleroma.DataCase + + alias Pleroma.Web.ActivityPub.Builder + alias Pleroma.Web.ActivityPub.ObjectValidator + + import Pleroma.Factory + + describe "updates" do + setup do + user = insert(:user) + + object = %{ + "id" => user.ap_id, + "name" => "A new name", + "summary" => "A new bio" + } + + {:ok, valid_update, []} = Builder.update(user, object) + + %{user: user, valid_update: valid_update} + end + + test "validates a basic object", %{valid_update: valid_update} do + assert {:ok, _update, []} = ObjectValidator.validate(valid_update, []) + end + + test "returns an error if the object can't be updated by the actor", %{ + valid_update: valid_update + } do + other_user = insert(:user) + + update = + valid_update + |> Map.put("actor", other_user.ap_id) + + assert {:error, _cng} = ObjectValidator.validate(update, []) + end + end +end diff --git a/test/web/activity_pub/publisher_test.exs b/test/web/activity_pub/publisher_test.exs index c2bc38d52..b9388b966 100644 --- a/test/web/activity_pub/publisher_test.exs +++ b/test/web/activity_pub/publisher_test.exs @@ -123,6 +123,39 @@ defmodule Pleroma.Web.ActivityPub.PublisherTest do end describe "publish_one/1" do + test "publish to url with with different ports" do + inbox80 = "http://42.site/users/nick1/inbox" + inbox42 = "http://42.site:42/users/nick1/inbox" + + mock(fn + %{method: :post, url: "http://42.site:42/users/nick1/inbox"} -> + {:ok, %Tesla.Env{status: 200, body: "port 42"}} + + %{method: :post, url: "http://42.site/users/nick1/inbox"} -> + {:ok, %Tesla.Env{status: 200, body: "port 80"}} + end) + + actor = insert(:user) + + assert {:ok, %{body: "port 42"}} = + Publisher.publish_one(%{ + inbox: inbox42, + json: "{}", + actor: actor, + id: 1, + unreachable_since: true + }) + + assert {:ok, %{body: "port 80"}} = + Publisher.publish_one(%{ + inbox: inbox80, + json: "{}", + actor: actor, + id: 1, + unreachable_since: true + }) + end + test_with_mock "calls `Instances.set_reachable` on successful federation if `unreachable_since` is not specified", Instances, [:passthrough], @@ -131,7 +164,6 @@ defmodule Pleroma.Web.ActivityPub.PublisherTest do inbox = "http://200.site/users/nick1/inbox" assert {:ok, _} = Publisher.publish_one(%{inbox: inbox, json: "{}", actor: actor, id: 1}) - assert called(Instances.set_reachable(inbox)) end diff --git a/test/web/activity_pub/relay_test.exs b/test/web/activity_pub/relay_test.exs index b3b573c9b..9d657ac4f 100644 --- a/test/web/activity_pub/relay_test.exs +++ b/test/web/activity_pub/relay_test.exs @@ -7,8 +7,8 @@ defmodule Pleroma.Web.ActivityPub.RelayTest do alias Pleroma.Activity alias Pleroma.User - alias Pleroma.Web.ActivityPub.ActivityPub alias Pleroma.Web.ActivityPub.Relay + alias Pleroma.Web.CommonAPI import ExUnit.CaptureLog import Pleroma.Factory @@ -53,8 +53,7 @@ defmodule Pleroma.Web.ActivityPub.RelayTest do test "returns activity" do user = insert(:user) service_actor = Relay.get_actor() - ActivityPub.follow(service_actor, user) - Pleroma.User.follow(service_actor, user) + CommonAPI.follow(service_actor, user) assert "#{user.ap_id}/followers" in User.following(service_actor) assert {:ok, %Activity{} = activity} = Relay.unfollow(user.ap_id) assert activity.actor == "#{Pleroma.Web.Endpoint.url()}/relay" @@ -74,6 +73,7 @@ defmodule Pleroma.Web.ActivityPub.RelayTest do assert Relay.publish(activity) == {:error, "Not implemented"} end + @tag capture_log: true test "returns error when activity not public" do activity = insert(:direct_note_activity) assert Relay.publish(activity) == {:error, false} diff --git a/test/web/activity_pub/side_effects_test.exs b/test/web/activity_pub/side_effects_test.exs index 12c9ef1da..2649b060a 100644 --- a/test/web/activity_pub/side_effects_test.exs +++ b/test/web/activity_pub/side_effects_test.exs @@ -64,6 +64,47 @@ defmodule Pleroma.Web.ActivityPub.SideEffectsTest do end end + describe "blocking users" do + setup do + user = insert(:user) + blocked = insert(:user) + User.follow(blocked, user) + User.follow(user, blocked) + + {:ok, block_data, []} = Builder.block(user, blocked) + {:ok, block, _meta} = ActivityPub.persist(block_data, local: true) + + %{user: user, blocked: blocked, block: block} + end + + test "it unfollows and blocks", %{user: user, blocked: blocked, block: block} do + assert User.following?(user, blocked) + assert User.following?(blocked, user) + + {:ok, _, _} = SideEffects.handle(block) + + refute User.following?(user, blocked) + refute User.following?(blocked, user) + assert User.blocks?(user, blocked) + end + + test "it blocks but does not unfollow if the relevant setting is set", %{ + user: user, + blocked: blocked, + block: block + } do + clear_config([:activitypub, :unfollow_blocked], false) + assert User.following?(user, blocked) + assert User.following?(blocked, user) + + {:ok, _, _} = SideEffects.handle(block) + + refute User.following?(user, blocked) + assert User.following?(blocked, user) + assert User.blocks?(user, blocked) + end + end + describe "update users" do setup do user = insert(:user) @@ -242,8 +283,7 @@ defmodule Pleroma.Web.ActivityPub.SideEffectsTest do {:ok, like} = CommonAPI.favorite(user, post.id) {:ok, reaction} = CommonAPI.react_with_emoji(post.id, user, "👍") {:ok, announce} = CommonAPI.repeat(post.id, user) - {:ok, block} = ActivityPub.block(user, poster) - User.block(user, poster) + {:ok, block} = CommonAPI.block(user, poster) {:ok, undo_data, _meta} = Builder.undo(user, like) {:ok, like_undo, _meta} = ActivityPub.persist(undo_data, local: true) @@ -549,10 +589,29 @@ defmodule Pleroma.Web.ActivityPub.SideEffectsTest do end test "it streams out the announce", %{announce: announce} do - with_mock Pleroma.Web.ActivityPub.ActivityPub, [:passthrough], stream_out: fn _ -> nil end do + with_mocks([ + { + Pleroma.Web.Streamer, + [], + [ + stream: fn _, _ -> nil end + ] + }, + { + Pleroma.Web.Push, + [], + [ + send: fn _ -> nil end + ] + } + ]) do {:ok, announce, _} = SideEffects.handle(announce) - assert called(Pleroma.Web.ActivityPub.ActivityPub.stream_out(announce)) + assert called( + Pleroma.Web.Streamer.stream(["user", "list", "public", "public:local"], announce) + ) + + assert called(Pleroma.Web.Push.send(:_)) end end end diff --git a/test/web/activity_pub/transmogrifier/block_handling_test.exs b/test/web/activity_pub/transmogrifier/block_handling_test.exs new file mode 100644 index 000000000..71f1a0ed5 --- /dev/null +++ b/test/web/activity_pub/transmogrifier/block_handling_test.exs @@ -0,0 +1,63 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ActivityPub.Transmogrifier.BlockHandlingTest do + use Pleroma.DataCase + + alias Pleroma.Activity + alias Pleroma.User + alias Pleroma.Web.ActivityPub.Transmogrifier + + import Pleroma.Factory + + test "it works for incoming blocks" do + user = insert(:user) + + data = + File.read!("test/fixtures/mastodon-block-activity.json") + |> Poison.decode!() + |> Map.put("object", user.ap_id) + + blocker = insert(:user, ap_id: data["actor"]) + + {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) + + assert data["type"] == "Block" + assert data["object"] == user.ap_id + assert data["actor"] == "http://mastodon.example.org/users/admin" + + assert User.blocks?(blocker, user) + end + + test "incoming blocks successfully tear down any follow relationship" do + blocker = insert(:user) + blocked = insert(:user) + + data = + File.read!("test/fixtures/mastodon-block-activity.json") + |> Poison.decode!() + |> Map.put("object", blocked.ap_id) + |> Map.put("actor", blocker.ap_id) + + {:ok, blocker} = User.follow(blocker, blocked) + {:ok, blocked} = User.follow(blocked, blocker) + + assert User.following?(blocker, blocked) + assert User.following?(blocked, blocker) + + {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) + + assert data["type"] == "Block" + assert data["object"] == blocked.ap_id + assert data["actor"] == blocker.ap_id + + blocker = User.get_cached_by_ap_id(data["actor"]) + blocked = User.get_cached_by_ap_id(data["object"]) + + assert User.blocks?(blocker, blocked) + + refute User.following?(blocker, blocked) + refute User.following?(blocked, blocker) + end +end diff --git a/test/web/activity_pub/transmogrifier/follow_handling_test.exs b/test/web/activity_pub/transmogrifier/follow_handling_test.exs index 06c39eed6..17e764ca1 100644 --- a/test/web/activity_pub/transmogrifier/follow_handling_test.exs +++ b/test/web/activity_pub/transmogrifier/follow_handling_test.exs @@ -160,7 +160,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier.FollowHandlingTest do |> Poison.decode!() |> Map.put("object", user.ap_id) - with_mock Pleroma.User, [:passthrough], follow: fn _, _ -> {:error, :testing} end do + with_mock Pleroma.User, [:passthrough], follow: fn _, _, _ -> {:error, :testing} end do {:ok, %Activity{data: %{"id" => id}}} = Transmogrifier.handle_incoming(data) %Activity{} = activity = Activity.get_by_ap_id(id) diff --git a/test/web/activity_pub/transmogrifier_test.exs b/test/web/activity_pub/transmogrifier_test.exs index 100821056..248b410c6 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.Object.Fetcher alias Pleroma.Tests.ObanHelpers alias Pleroma.User - alias Pleroma.Web.ActivityPub.ActivityPub alias Pleroma.Web.ActivityPub.Transmogrifier alias Pleroma.Web.AdminAPI.AccountView alias Pleroma.Web.CommonAPI @@ -445,56 +444,6 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do assert [^pending_follower] = User.get_follow_requests(user) end - test "it works for incoming blocks" do - user = insert(:user) - - data = - File.read!("test/fixtures/mastodon-block-activity.json") - |> Poison.decode!() - |> Map.put("object", user.ap_id) - - {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) - - assert data["type"] == "Block" - assert data["object"] == user.ap_id - assert data["actor"] == "http://mastodon.example.org/users/admin" - - blocker = User.get_cached_by_ap_id(data["actor"]) - - assert User.blocks?(blocker, user) - end - - test "incoming blocks successfully tear down any follow relationship" do - blocker = insert(:user) - blocked = insert(:user) - - data = - File.read!("test/fixtures/mastodon-block-activity.json") - |> Poison.decode!() - |> Map.put("object", blocked.ap_id) - |> Map.put("actor", blocker.ap_id) - - {:ok, blocker} = User.follow(blocker, blocked) - {:ok, blocked} = User.follow(blocked, blocker) - - assert User.following?(blocker, blocked) - assert User.following?(blocked, blocker) - - {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) - - assert data["type"] == "Block" - assert data["object"] == blocked.ap_id - assert data["actor"] == blocker.ap_id - - blocker = User.get_cached_by_ap_id(data["actor"]) - blocked = User.get_cached_by_ap_id(data["object"]) - - assert User.blocks?(blocker, blocked) - - refute User.following?(blocker, blocked) - refute User.following?(blocked, blocker) - end - test "it works for incoming accepts which were pre-accepted" do follower = insert(:user) followed = insert(:user) @@ -502,7 +451,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do {:ok, follower} = User.follow(follower, followed) assert User.following?(follower, followed) == true - {:ok, follow_activity} = ActivityPub.follow(follower, followed) + {:ok, _, _, follow_activity} = CommonAPI.follow(follower, followed) accept_data = File.read!("test/fixtures/mastodon-accept-activity.json") @@ -532,7 +481,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do follower = insert(:user) followed = insert(:user, locked: true) - {:ok, follow_activity} = ActivityPub.follow(follower, followed) + {:ok, _, _, follow_activity} = CommonAPI.follow(follower, followed) accept_data = File.read!("test/fixtures/mastodon-accept-activity.json") @@ -554,7 +503,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do follower = insert(:user) followed = insert(:user, locked: true) - {:ok, follow_activity} = ActivityPub.follow(follower, followed) + {:ok, _, _, follow_activity} = CommonAPI.follow(follower, followed) accept_data = File.read!("test/fixtures/mastodon-accept-activity.json") @@ -619,7 +568,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do followed = insert(:user, locked: true) {:ok, follower} = User.follow(follower, followed) - {:ok, _follow_activity} = ActivityPub.follow(follower, followed) + {:ok, _, _, _follow_activity} = CommonAPI.follow(follower, followed) assert User.following?(follower, followed) == true @@ -645,7 +594,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do followed = insert(:user, locked: true) {:ok, follower} = User.follow(follower, followed) - {:ok, follow_activity} = ActivityPub.follow(follower, followed) + {:ok, _, _, follow_activity} = CommonAPI.follow(follower, followed) assert User.following?(follower, followed) == true @@ -709,22 +658,44 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do "https://peertube.moe/videos/watch/df5f464b-be8d-46fb-ad81-2d4c2d1630e3" ) - attachment = %{ - "type" => "Link", - "mediaType" => "video/mp4", - "url" => [ - %{ - "href" => - "https://peertube.moe/static/webseed/df5f464b-be8d-46fb-ad81-2d4c2d1630e3-480.mp4", - "mediaType" => "video/mp4" - } - ] - } - assert object.data["url"] == "https://peertube.moe/videos/watch/df5f464b-be8d-46fb-ad81-2d4c2d1630e3" - assert object.data["attachment"] == [attachment] + assert object.data["attachment"] == [ + %{ + "type" => "Link", + "mediaType" => "video/mp4", + "url" => [ + %{ + "href" => + "https://peertube.moe/static/webseed/df5f464b-be8d-46fb-ad81-2d4c2d1630e3-480.mp4", + "mediaType" => "video/mp4" + } + ] + } + ] + + {:ok, object} = + Fetcher.fetch_object_from_id( + "https://framatube.org/videos/watch/6050732a-8a7a-43d4-a6cd-809525a1d206" + ) + + assert object.data["attachment"] == [ + %{ + "type" => "Link", + "mediaType" => "video/mp4", + "url" => [ + %{ + "href" => + "https://framatube.org/static/webseed/6050732a-8a7a-43d4-a6cd-809525a1d206-1080.mp4", + "mediaType" => "video/mp4" + } + ] + } + ] + + assert object.data["url"] == + "https://framatube.org/videos/watch/6050732a-8a7a-43d4-a6cd-809525a1d206" end test "it accepts Flag activities" do @@ -803,6 +774,29 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do assert [user.follower_address] == activity.data["to"] end + test "it correctly processes messages with weirdness in address fields" do + user = insert(:user) + + message = %{ + "@context" => "https://www.w3.org/ns/activitystreams", + "to" => [nil, user.follower_address], + "cc" => ["https://www.w3.org/ns/activitystreams#Public", ["¿"]], + "type" => "Create", + "object" => %{ + "content" => "…", + "type" => "Note", + "attributedTo" => user.ap_id, + "inReplyTo" => nil + }, + "actor" => user.ap_id + } + + assert {:ok, activity} = Transmogrifier.handle_incoming(message) + + assert ["https://www.w3.org/ns/activitystreams#Public"] == activity.data["cc"] + assert [user.follower_address] == activity.data["to"] + end + test "it accepts Move activities" do old_user = insert(:user) new_user = insert(:user) diff --git a/test/web/activity_pub/utils_test.exs b/test/web/activity_pub/utils_test.exs index 15f03f193..361dc5a41 100644 --- a/test/web/activity_pub/utils_test.exs +++ b/test/web/activity_pub/utils_test.exs @@ -8,7 +8,6 @@ defmodule Pleroma.Web.ActivityPub.UtilsTest do alias Pleroma.Object alias Pleroma.Repo alias Pleroma.User - alias Pleroma.Web.ActivityPub.ActivityPub alias Pleroma.Web.ActivityPub.Utils alias Pleroma.Web.AdminAPI.AccountView alias Pleroma.Web.CommonAPI @@ -27,16 +26,6 @@ defmodule Pleroma.Web.ActivityPub.UtilsTest do end end - describe "fetch the latest Block" do - test "fetches the latest Block activity" do - blocker = insert(:user) - blocked = insert(:user) - {:ok, activity} = ActivityPub.block(blocker, blocked) - - assert activity == Utils.fetch_latest_block(blocker, blocked) - end - end - describe "determine_explicit_mentions()" do test "works with an object that has mentions" do object = %{ @@ -207,8 +196,8 @@ defmodule Pleroma.Web.ActivityPub.UtilsTest do user = insert(:user, locked: true) follower = insert(:user) - {:ok, follow_activity} = ActivityPub.follow(follower, user) - {:ok, follow_activity_two} = ActivityPub.follow(follower, user) + {:ok, _, _, follow_activity} = CommonAPI.follow(follower, user) + {:ok, _, _, follow_activity_two} = CommonAPI.follow(follower, user) data = follow_activity_two.data @@ -231,8 +220,8 @@ defmodule Pleroma.Web.ActivityPub.UtilsTest do user = insert(:user, locked: true) follower = insert(:user) - {:ok, follow_activity} = ActivityPub.follow(follower, user) - {:ok, follow_activity_two} = ActivityPub.follow(follower, user) + {:ok, _, _, follow_activity} = CommonAPI.follow(follower, user) + {:ok, _, _, follow_activity_two} = CommonAPI.follow(follower, user) data = follow_activity_two.data @@ -344,9 +333,9 @@ defmodule Pleroma.Web.ActivityPub.UtilsTest do user1 = insert(:user) user2 = insert(:user) - assert {:ok, %Activity{} = _} = ActivityPub.block(user1, user2) - assert {:ok, %Activity{} = _} = ActivityPub.block(user1, user2) - assert {:ok, %Activity{} = activity} = ActivityPub.block(user1, user2) + assert {:ok, %Activity{} = _} = CommonAPI.block(user1, user2) + assert {:ok, %Activity{} = _} = CommonAPI.block(user1, user2) + assert {:ok, %Activity{} = activity} = CommonAPI.block(user1, user2) assert Utils.fetch_latest_block(user1, user2) == activity end diff --git a/test/web/activity_pub/views/user_view_test.exs b/test/web/activity_pub/views/user_view_test.exs index bec15a996..98c7c9d09 100644 --- a/test/web/activity_pub/views/user_view_test.exs +++ b/test/web/activity_pub/views/user_view_test.exs @@ -158,4 +158,23 @@ defmodule Pleroma.Web.ActivityPub.UserViewTest do assert %{"totalItems" => 1} = UserView.render("following.json", %{user: user}) end end + + describe "acceptsChatMessages" do + test "it returns this value if it is set" do + true_user = insert(:user, accepts_chat_messages: true) + false_user = insert(:user, accepts_chat_messages: false) + nil_user = insert(:user, accepts_chat_messages: nil) + + assert %{"capabilities" => %{"acceptsChatMessages" => true}} = + UserView.render("user.json", user: true_user) + + assert %{"capabilities" => %{"acceptsChatMessages" => false}} = + UserView.render("user.json", user: false_user) + + refute Map.has_key?( + UserView.render("user.json", user: nil_user)["capabilities"], + "acceptsChatMessages" + ) + end + end end diff --git a/test/web/admin_api/controllers/admin_api_controller_test.exs b/test/web/admin_api/controllers/admin_api_controller_test.exs index 48fb108ec..da91cd552 100644 --- a/test/web/admin_api/controllers/admin_api_controller_test.exs +++ b/test/web/admin_api/controllers/admin_api_controller_test.exs @@ -41,6 +41,16 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do {:ok, %{admin: admin, token: token, conn: conn}} end + test "with valid `admin_token` query parameter, skips OAuth scopes check" do + clear_config([:admin_token], "password123") + + user = insert(:user) + + conn = get(build_conn(), "/api/pleroma/admin/users/#{user.nickname}?admin_token=password123") + + assert json_response(conn, 200) + end + describe "with [:auth, :enforce_oauth_admin_scope_usage]," do setup do: clear_config([:auth, :enforce_oauth_admin_scope_usage], true) @@ -1514,6 +1524,15 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do end end + test "gets a remote users when [:instance, :limit_to_local_content] is set to :unauthenticated", + %{conn: conn} do + clear_config(Pleroma.Config.get([:instance, :limit_to_local_content]), :unauthenticated) + user = insert(:user, %{local: false, nickname: "u@peer1.com"}) + conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/credentials") + + assert json_response(conn, 200) + end + describe "GET /users/:nickname/credentials" do test "gets the user credentials", %{conn: conn} do user = insert(:user) diff --git a/test/web/admin_api/controllers/config_controller_test.exs b/test/web/admin_api/controllers/config_controller_test.exs index 064ef9bc7..61bc9fd39 100644 --- a/test/web/admin_api/controllers/config_controller_test.exs +++ b/test/web/admin_api/controllers/config_controller_test.exs @@ -152,6 +152,14 @@ defmodule Pleroma.Web.AdminAPI.ConfigControllerTest do assert emoji_val[:groups] == [a: 1, b: 2] assert assets_val[:mascots] == [a: 1, b: 2] end + + test "with valid `admin_token` query parameter, skips OAuth scopes check" do + clear_config([:admin_token], "password123") + + build_conn() + |> get("/api/pleroma/admin/config?admin_token=password123") + |> json_response_and_validate_schema(200) + end end test "POST /api/pleroma/admin/config error", %{conn: conn} do diff --git a/test/web/admin_api/controllers/report_controller_test.exs b/test/web/admin_api/controllers/report_controller_test.exs index 940bce340..f30dc8956 100644 --- a/test/web/admin_api/controllers/report_controller_test.exs +++ b/test/web/admin_api/controllers/report_controller_test.exs @@ -297,7 +297,7 @@ defmodule Pleroma.Web.AdminAPI.ReportControllerTest do |> get("/api/pleroma/admin/reports") assert json_response(conn, :forbidden) == - %{"error" => "User is not an admin or OAuth admin scope is not granted."} + %{"error" => "User is not an admin."} end test "returns 403 when requested by anonymous" do diff --git a/test/web/common_api/common_api_test.exs b/test/web/common_api/common_api_test.exs index 6bd26050e..7e11fede3 100644 --- a/test/web/common_api/common_api_test.exs +++ b/test/web/common_api/common_api_test.exs @@ -25,6 +25,52 @@ defmodule Pleroma.Web.CommonAPITest do setup do: clear_config([:instance, :limit]) setup do: clear_config([:instance, :max_pinned_statuses]) + describe "blocking" do + setup do + blocker = insert(:user) + blocked = insert(:user) + User.follow(blocker, blocked) + User.follow(blocked, blocker) + %{blocker: blocker, blocked: blocked} + end + + test "it blocks and federates", %{blocker: blocker, blocked: blocked} do + clear_config([:instance, :federating], true) + + with_mock Pleroma.Web.Federator, + publish: fn _ -> nil end do + assert {:ok, block} = CommonAPI.block(blocker, blocked) + + assert block.local + assert User.blocks?(blocker, blocked) + refute User.following?(blocker, blocked) + refute User.following?(blocked, blocker) + + assert called(Pleroma.Web.Federator.publish(block)) + end + end + + test "it blocks and does not federate if outgoing blocks are disabled", %{ + blocker: blocker, + blocked: blocked + } do + clear_config([:instance, :federating], true) + clear_config([:activitypub, :outgoing_blocks], false) + + with_mock Pleroma.Web.Federator, + publish: fn _ -> nil end do + assert {:ok, block} = CommonAPI.block(blocker, blocked) + + assert block.local + assert User.blocks?(blocker, blocked) + refute User.following?(blocker, blocked) + refute User.following?(blocked, blocker) + + refute called(Pleroma.Web.Federator.publish(block)) + end + end + end + describe "posting chat messages" do setup do: clear_config([:instance, :chat_limit]) @@ -445,6 +491,7 @@ defmodule Pleroma.Web.CommonAPITest do object = Object.normalize(activity) assert object.data["content"] == "<p><b>2hu</b></p>alert('xss')" + assert object.data["source"] == post end test "it filters out obviously bad tags when accepting a post as Markdown" do @@ -461,6 +508,7 @@ defmodule Pleroma.Web.CommonAPITest do object = Object.normalize(activity) assert object.data["content"] == "<p><b>2hu</b></p>alert('xss')" + assert object.data["source"] == post end test "it does not allow replies to direct messages that are not direct messages themselves" do @@ -886,6 +934,15 @@ defmodule Pleroma.Web.CommonAPITest do end end + describe "follow/2" do + test "directly follows a non-locked local user" do + [follower, followed] = insert_pair(:user) + {:ok, follower, followed, _} = CommonAPI.follow(follower, followed) + + assert User.following?(follower, followed) + end + end + describe "unfollow/2" do test "also unsubscribes a user" do [follower, followed] = insert_pair(:user) @@ -950,9 +1007,9 @@ defmodule Pleroma.Web.CommonAPITest do follower = insert(:user) follower_two = insert(:user) - {:ok, follow_activity} = ActivityPub.follow(follower, user) - {:ok, follow_activity_two} = ActivityPub.follow(follower, user) - {:ok, follow_activity_three} = ActivityPub.follow(follower_two, user) + {:ok, _, _, follow_activity} = CommonAPI.follow(follower, user) + {:ok, _, _, follow_activity_two} = CommonAPI.follow(follower, user) + {:ok, _, _, follow_activity_three} = CommonAPI.follow(follower_two, user) assert follow_activity.data["state"] == "pending" assert follow_activity_two.data["state"] == "pending" @@ -970,9 +1027,9 @@ defmodule Pleroma.Web.CommonAPITest do follower = insert(:user) follower_two = insert(:user) - {:ok, follow_activity} = ActivityPub.follow(follower, user) - {:ok, follow_activity_two} = ActivityPub.follow(follower, user) - {:ok, follow_activity_three} = ActivityPub.follow(follower_two, user) + {:ok, _, _, follow_activity} = CommonAPI.follow(follower, user) + {:ok, _, _, follow_activity_two} = CommonAPI.follow(follower, user) + {:ok, _, _, follow_activity_three} = CommonAPI.follow(follower_two, user) assert follow_activity.data["state"] == "pending" assert follow_activity_two.data["state"] == "pending" diff --git a/test/web/fallback_test.exs b/test/web/fallback_test.exs index 3919ef93a..a65865860 100644 --- a/test/web/fallback_test.exs +++ b/test/web/fallback_test.exs @@ -6,22 +6,56 @@ defmodule Pleroma.Web.FallbackTest do use Pleroma.Web.ConnCase import Pleroma.Factory - test "GET /registration/:token", %{conn: conn} do - assert conn - |> get("/registration/foo") - |> html_response(200) =~ "<!--server-generated-meta-->" + describe "neither preloaded data nor metadata attached to" do + test "GET /registration/:token", %{conn: conn} do + response = get(conn, "/registration/foo") + + assert html_response(response, 200) =~ "<!--server-generated-meta-->" + end + + test "GET /*path", %{conn: conn} do + assert conn + |> get("/foo") + |> html_response(200) =~ "<!--server-generated-meta-->" + end end - test "GET /:maybe_nickname_or_id", %{conn: conn} do - user = insert(:user) + describe "preloaded data and metadata attached to" do + test "GET /:maybe_nickname_or_id", %{conn: conn} do + user = insert(:user) + user_missing = get(conn, "/foo") + user_present = get(conn, "/#{user.nickname}") - assert conn - |> get("/foo") - |> html_response(200) =~ "<!--server-generated-meta-->" + assert(html_response(user_missing, 200) =~ "<!--server-generated-meta-->") + refute html_response(user_present, 200) =~ "<!--server-generated-meta-->" + assert html_response(user_present, 200) =~ "initial-results" + end - refute conn - |> get("/" <> user.nickname) - |> html_response(200) =~ "<!--server-generated-meta-->" + test "GET /*path", %{conn: conn} do + assert conn + |> get("/foo") + |> html_response(200) =~ "<!--server-generated-meta-->" + + refute conn + |> get("/foo/bar") + |> html_response(200) =~ "<!--server-generated-meta-->" + end + end + + describe "preloaded data is attached to" do + test "GET /main/public", %{conn: conn} do + public_page = get(conn, "/main/public") + + refute html_response(public_page, 200) =~ "<!--server-generated-meta-->" + assert html_response(public_page, 200) =~ "initial-results" + end + + test "GET /main/all", %{conn: conn} do + public_page = get(conn, "/main/all") + + refute html_response(public_page, 200) =~ "<!--server-generated-meta-->" + assert html_response(public_page, 200) =~ "initial-results" + end end test "GET /api*path", %{conn: conn} do @@ -34,16 +68,6 @@ defmodule Pleroma.Web.FallbackTest do assert redirected_to(get(conn, "/pleroma/admin")) =~ "/pleroma/admin/" end - test "GET /*path", %{conn: conn} do - assert conn - |> get("/foo") - |> html_response(200) =~ "<!--server-generated-meta-->" - - assert conn - |> get("/foo/bar") - |> html_response(200) =~ "<!--server-generated-meta-->" - end - test "OPTIONS /*path", %{conn: conn} do assert conn |> options("/foo") diff --git a/test/web/masto_fe_controller_test.exs b/test/web/masto_fe_controller_test.exs index 1d107d56c..f3b54b5f2 100644 --- a/test/web/masto_fe_controller_test.exs +++ b/test/web/masto_fe_controller_test.exs @@ -24,7 +24,7 @@ defmodule Pleroma.Web.MastodonAPI.MastoFEController do assert _result = json_response(conn, 200) user = User.get_cached_by_ap_id(user.ap_id) - assert user.settings == %{"programming" => "socks"} + assert user.mastofe_settings == %{"programming" => "socks"} end describe "index/2 redirections" do diff --git a/test/web/mastodon_api/controllers/account_controller/update_credentials_test.exs b/test/web/mastodon_api/controllers/account_controller/update_credentials_test.exs index f67d294ba..b888e4c71 100644 --- a/test/web/mastodon_api/controllers/account_controller/update_credentials_test.exs +++ b/test/web/mastodon_api/controllers/account_controller/update_credentials_test.exs @@ -108,6 +108,13 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController.UpdateCredentialsTest do assert user_data["locked"] == true end + test "updates the user's chat acceptance status", %{conn: conn} do + conn = patch(conn, "/api/v1/accounts/update_credentials", %{accepts_chat_messages: "false"}) + + assert user_data = json_response_and_validate_schema(conn, 200) + assert user_data["pleroma"]["accepts_chat_messages"] == false + end + test "updates the user's allow_following_move", %{user: user, conn: conn} do assert user.allow_following_move == true @@ -216,10 +223,21 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController.UpdateCredentialsTest do filename: "an_image.jpg" } - conn = patch(conn, "/api/v1/accounts/update_credentials", %{"avatar" => new_avatar}) + assert user.avatar == %{} - assert user_response = json_response_and_validate_schema(conn, 200) + res = patch(conn, "/api/v1/accounts/update_credentials", %{"avatar" => new_avatar}) + + assert user_response = json_response_and_validate_schema(res, 200) assert user_response["avatar"] != User.avatar_url(user) + + user = User.get_by_id(user.id) + refute user.avatar == %{} + + # Also resets it + _res = patch(conn, "/api/v1/accounts/update_credentials", %{"avatar" => ""}) + + user = User.get_by_id(user.id) + assert user.avatar == nil end test "updates the user's banner", %{user: user, conn: conn} do @@ -229,26 +247,39 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController.UpdateCredentialsTest do filename: "an_image.jpg" } - conn = patch(conn, "/api/v1/accounts/update_credentials", %{"header" => new_header}) + res = patch(conn, "/api/v1/accounts/update_credentials", %{"header" => new_header}) - assert user_response = json_response_and_validate_schema(conn, 200) + assert user_response = json_response_and_validate_schema(res, 200) assert user_response["header"] != User.banner_url(user) + + # Also resets it + _res = patch(conn, "/api/v1/accounts/update_credentials", %{"header" => ""}) + + user = User.get_by_id(user.id) + assert user.banner == nil end - test "updates the user's background", %{conn: conn} do + test "updates the user's background", %{conn: conn, user: user} do new_header = %Plug.Upload{ content_type: "image/jpg", path: Path.absname("test/fixtures/image.jpg"), filename: "an_image.jpg" } - conn = + res = patch(conn, "/api/v1/accounts/update_credentials", %{ "pleroma_background_image" => new_header }) - assert user_response = json_response_and_validate_schema(conn, 200) + assert user_response = json_response_and_validate_schema(res, 200) assert user_response["pleroma"]["background_image"] + # + # Also resets it + _res = + patch(conn, "/api/v1/accounts/update_credentials", %{"pleroma_background_image" => ""}) + + user = User.get_by_id(user.id) + assert user.background == nil end test "requires 'write:accounts' permission" do @@ -320,6 +351,30 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController.UpdateCredentialsTest do ] end + test "emojis in fields labels", %{conn: conn} do + fields = [ + %{"name" => ":firefox:", "value" => "is best 2hu"}, + %{"name" => "they wins", "value" => ":blank:"} + ] + + account_data = + conn + |> patch("/api/v1/accounts/update_credentials", %{"fields_attributes" => fields}) + |> json_response_and_validate_schema(200) + + assert account_data["fields"] == [ + %{"name" => ":firefox:", "value" => "is best 2hu"}, + %{"name" => "they wins", "value" => ":blank:"} + ] + + assert account_data["source"]["fields"] == [ + %{"name" => ":firefox:", "value" => "is best 2hu"}, + %{"name" => "they wins", "value" => ":blank:"} + ] + + assert [%{"shortcode" => "blank"}, %{"shortcode" => "firefox"}] = account_data["emojis"] + end + test "update fields via x-www-form-urlencoded", %{conn: conn} do fields = [ diff --git a/test/web/mastodon_api/controllers/account_controller_test.exs b/test/web/mastodon_api/controllers/account_controller_test.exs index ebfcedd01..9c7b5e9b2 100644 --- a/test/web/mastodon_api/controllers/account_controller_test.exs +++ b/test/web/mastodon_api/controllers/account_controller_test.exs @@ -708,7 +708,10 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do followed = insert(:user) other_user = insert(:user) - ret_conn = post(conn, "/api/v1/accounts/#{followed.id}/follow?reblogs=false") + ret_conn = + conn + |> put_req_header("content-type", "application/json") + |> post("/api/v1/accounts/#{followed.id}/follow", %{reblogs: false}) assert %{"showing_reblogs" => false} = json_response_and_validate_schema(ret_conn, 200) @@ -722,7 +725,8 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do assert %{"showing_reblogs" => true} = conn - |> post("/api/v1/accounts/#{followed.id}/follow?reblogs=true") + |> put_req_header("content-type", "application/json") + |> post("/api/v1/accounts/#{followed.id}/follow", %{reblogs: true}) |> json_response_and_validate_schema(200) assert [%{"id" => ^reblog_id}] = @@ -731,6 +735,35 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do |> json_response(200) end + test "following with reblogs" do + %{conn: conn} = oauth_access(["follow", "read:statuses"]) + followed = insert(:user) + other_user = insert(:user) + + ret_conn = post(conn, "/api/v1/accounts/#{followed.id}/follow") + + assert %{"showing_reblogs" => true} = json_response_and_validate_schema(ret_conn, 200) + + {:ok, activity} = CommonAPI.post(other_user, %{status: "hey"}) + {:ok, %{id: reblog_id}} = CommonAPI.repeat(activity.id, followed) + + assert [%{"id" => ^reblog_id}] = + conn + |> get("/api/v1/timelines/home") + |> json_response(200) + + assert %{"showing_reblogs" => false} = + conn + |> put_req_header("content-type", "application/json") + |> post("/api/v1/accounts/#{followed.id}/follow", %{reblogs: false}) + |> json_response_and_validate_schema(200) + + assert [] == + conn + |> get("/api/v1/timelines/home") + |> json_response(200) + end + test "following / unfollowing errors", %{user: user, conn: conn} do # self follow conn_res = post(conn, "/api/v1/accounts/#{user.id}/follow") @@ -780,7 +813,6 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do assert %{"id" => _id, "muting" => true, "muting_notifications" => true} = conn - |> put_req_header("content-type", "application/json") |> post("/api/v1/accounts/#{other_user.id}/mute") |> json_response_and_validate_schema(200) @@ -905,7 +937,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do %{ "access_token" => token, "created_at" => _created_at, - "scope" => _scope, + "scope" => ^scope, "token_type" => "Bearer" } = json_response_and_validate_schema(conn, 200) @@ -1067,7 +1099,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do assert %{ "access_token" => access_token, "created_at" => _, - "scope" => ["read", "write", "follow", "push"], + "scope" => "read write follow push", "token_type" => "Bearer" } = response @@ -1185,7 +1217,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do assert %{ "access_token" => access_token, "created_at" => _, - "scope" => ["read"], + "scope" => "read", "token_type" => "Bearer" } = conn diff --git a/test/web/mastodon_api/controllers/follow_request_controller_test.exs b/test/web/mastodon_api/controllers/follow_request_controller_test.exs index 44e12d15a..6749e0e83 100644 --- a/test/web/mastodon_api/controllers/follow_request_controller_test.exs +++ b/test/web/mastodon_api/controllers/follow_request_controller_test.exs @@ -6,7 +6,7 @@ defmodule Pleroma.Web.MastodonAPI.FollowRequestControllerTest do use Pleroma.Web.ConnCase alias Pleroma.User - alias Pleroma.Web.ActivityPub.ActivityPub + alias Pleroma.Web.CommonAPI import Pleroma.Factory @@ -20,7 +20,7 @@ defmodule Pleroma.Web.MastodonAPI.FollowRequestControllerTest do test "/api/v1/follow_requests works", %{user: user, conn: conn} do other_user = insert(:user) - {:ok, _activity} = ActivityPub.follow(other_user, user) + {:ok, _, _, _activity} = CommonAPI.follow(other_user, user) {:ok, other_user} = User.follow(other_user, user, :follow_pending) assert User.following?(other_user, user) == false @@ -34,7 +34,7 @@ defmodule Pleroma.Web.MastodonAPI.FollowRequestControllerTest do test "/api/v1/follow_requests/:id/authorize works", %{user: user, conn: conn} do other_user = insert(:user) - {:ok, _activity} = ActivityPub.follow(other_user, user) + {:ok, _, _, _activity} = CommonAPI.follow(other_user, user) {:ok, other_user} = User.follow(other_user, user, :follow_pending) user = User.get_cached_by_id(user.id) @@ -56,7 +56,7 @@ defmodule Pleroma.Web.MastodonAPI.FollowRequestControllerTest do test "/api/v1/follow_requests/:id/reject works", %{user: user, conn: conn} do other_user = insert(:user) - {:ok, _activity} = ActivityPub.follow(other_user, user) + {:ok, _, _, _activity} = CommonAPI.follow(other_user, user) user = User.get_cached_by_id(user.id) diff --git a/test/web/mastodon_api/controllers/instance_controller_test.exs b/test/web/mastodon_api/controllers/instance_controller_test.exs index 8bdfdddd1..cc880d82c 100644 --- a/test/web/mastodon_api/controllers/instance_controller_test.exs +++ b/test/web/mastodon_api/controllers/instance_controller_test.exs @@ -32,11 +32,15 @@ defmodule Pleroma.Web.MastodonAPI.InstanceControllerTest do "avatar_upload_limit" => _, "background_upload_limit" => _, "banner_upload_limit" => _, - "background_image" => _ + "background_image" => _, + "chat_limit" => _, + "description_limit" => _ } = result + assert result["pleroma"]["metadata"]["account_activation_required"] != nil assert result["pleroma"]["metadata"]["features"] assert result["pleroma"]["metadata"]["federation"] + assert result["pleroma"]["metadata"]["fields_limits"] assert result["pleroma"]["vapid_public_key"] assert email == from_config_email diff --git a/test/web/mastodon_api/controllers/search_controller_test.exs b/test/web/mastodon_api/controllers/search_controller_test.exs index 826f37fbc..24d1959f8 100644 --- a/test/web/mastodon_api/controllers/search_controller_test.exs +++ b/test/web/mastodon_api/controllers/search_controller_test.exs @@ -79,6 +79,7 @@ defmodule Pleroma.Web.MastodonAPI.SearchControllerTest do assert status["id"] == to_string(activity.id) end + @tag capture_log: true test "constructs hashtags from search query", %{conn: conn} do results = conn @@ -318,11 +319,13 @@ defmodule Pleroma.Web.MastodonAPI.SearchControllerTest do test "search fetches remote accounts", %{conn: conn} do user = insert(:user) + query = URI.encode_query(%{q: " mike@osada.macgirvin.com ", resolve: true}) + results = conn |> assign(:user, user) |> assign(:token, insert(:oauth_token, user: user, scopes: ["read"])) - |> get("/api/v1/search?q=mike@osada.macgirvin.com&resolve=true") + |> get("/api/v1/search?#{query}") |> json_response_and_validate_schema(200) [account] = results["accounts"] diff --git a/test/web/mastodon_api/controllers/status_controller_test.exs b/test/web/mastodon_api/controllers/status_controller_test.exs index a98e939e8..d34f300da 100644 --- a/test/web/mastodon_api/controllers/status_controller_test.exs +++ b/test/web/mastodon_api/controllers/status_controller_test.exs @@ -22,6 +22,8 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do setup do: clear_config([:instance, :federating]) setup do: clear_config([:instance, :allow_relay]) setup do: clear_config([:rich_media, :enabled]) + setup do: clear_config([:mrf, :policies]) + setup do: clear_config([:mrf_keyword, :reject]) describe "posting statuses" do setup do: oauth_access(["write:statuses"]) @@ -157,6 +159,17 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do |> json_response_and_validate_schema(422) end + test "Get MRF reason when posting a status is rejected by one", %{conn: conn} do + Pleroma.Config.put([:mrf_keyword, :reject], ["GNO"]) + Pleroma.Config.put([:mrf, :policies], [Pleroma.Web.ActivityPub.MRF.KeywordPolicy]) + + assert %{"error" => "[KeywordPolicy] Matches with rejected keyword"} = + conn + |> put_req_header("content-type", "application/json") + |> post("api/v1/statuses", %{"status" => "GNO/Linux"}) + |> json_response_and_validate_schema(422) + end + test "posting an undefined status with an attachment", %{user: user, conn: conn} do file = %Plug.Upload{ content_type: "image/jpg", @@ -760,13 +773,18 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do test "when you created it" do %{user: author, conn: conn} = oauth_access(["write:statuses"]) activity = insert(:note_activity, user: author) + object = Object.normalize(activity) - conn = + content = object.data["content"] + source = object.data["source"] + + result = conn |> assign(:user, author) |> delete("/api/v1/statuses/#{activity.id}") + |> json_response_and_validate_schema(200) - assert %{} = json_response_and_validate_schema(conn, 200) + assert match?(%{"content" => ^content, "text" => ^source}, result) refute Activity.get_by_id(activity.id) end @@ -789,7 +807,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do conn = delete(conn, "/api/v1/statuses/#{activity.id}") - assert %{"error" => _} = json_response_and_validate_schema(conn, 403) + assert %{"error" => "Record not found"} == json_response_and_validate_schema(conn, 404) assert Activity.get_by_id(activity.id) == activity end diff --git a/test/web/mastodon_api/controllers/timeline_controller_test.exs b/test/web/mastodon_api/controllers/timeline_controller_test.exs index f069390c1..50e0d783d 100644 --- a/test/web/mastodon_api/controllers/timeline_controller_test.exs +++ b/test/web/mastodon_api/controllers/timeline_controller_test.exs @@ -418,4 +418,78 @@ defmodule Pleroma.Web.MastodonAPI.TimelineControllerTest do assert [status_none] == json_response_and_validate_schema(all_test, :ok) end end + + describe "hashtag timeline handling of :restrict_unauthenticated setting" do + setup do + user = insert(:user) + {:ok, activity1} = CommonAPI.post(user, %{status: "test #tag1"}) + {:ok, _activity2} = CommonAPI.post(user, %{status: "test #tag1"}) + + activity1 + |> Ecto.Changeset.change(%{local: false}) + |> Pleroma.Repo.update() + + base_uri = "/api/v1/timelines/tag/tag1" + error_response = %{"error" => "authorization required for timeline view"} + + %{base_uri: base_uri, error_response: error_response} + end + + defp ensure_authenticated_access(base_uri) do + %{conn: auth_conn} = oauth_access(["read:statuses"]) + + res_conn = get(auth_conn, "#{base_uri}?local=true") + assert length(json_response(res_conn, 200)) == 1 + + res_conn = get(auth_conn, "#{base_uri}?local=false") + assert length(json_response(res_conn, 200)) == 2 + end + + test "with `%{local: true, federated: true}`, returns 403 for unauthenticated users", %{ + conn: conn, + base_uri: base_uri, + error_response: error_response + } do + clear_config([:restrict_unauthenticated, :timelines, :local], true) + clear_config([:restrict_unauthenticated, :timelines, :federated], true) + + for local <- [true, false] do + res_conn = get(conn, "#{base_uri}?local=#{local}") + + assert json_response(res_conn, :unauthorized) == error_response + end + + ensure_authenticated_access(base_uri) + end + + test "with `%{local: false, federated: true}`, forbids unauthenticated access to federated timeline", + %{conn: conn, base_uri: base_uri, error_response: error_response} do + clear_config([:restrict_unauthenticated, :timelines, :local], false) + clear_config([:restrict_unauthenticated, :timelines, :federated], true) + + res_conn = get(conn, "#{base_uri}?local=true") + assert length(json_response(res_conn, 200)) == 1 + + res_conn = get(conn, "#{base_uri}?local=false") + assert json_response(res_conn, :unauthorized) == error_response + + ensure_authenticated_access(base_uri) + end + + test "with `%{local: true, federated: false}`, forbids unauthenticated access to public timeline" <> + "(but not to local public activities which are delivered as part of federated timeline)", + %{conn: conn, base_uri: base_uri, error_response: error_response} do + clear_config([:restrict_unauthenticated, :timelines, :local], true) + clear_config([:restrict_unauthenticated, :timelines, :federated], false) + + res_conn = get(conn, "#{base_uri}?local=true") + assert json_response(res_conn, :unauthorized) == error_response + + # Note: local activities get delivered as part of federated timeline + res_conn = get(conn, "#{base_uri}?local=false") + assert length(json_response(res_conn, 200)) == 2 + + ensure_authenticated_access(base_uri) + end + end end diff --git a/test/web/mastodon_api/mastodon_api_test.exs b/test/web/mastodon_api/mastodon_api_test.exs index a7f9c5205..c08be37d4 100644 --- a/test/web/mastodon_api/mastodon_api_test.exs +++ b/test/web/mastodon_api/mastodon_api_test.exs @@ -18,7 +18,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPITest do follower = insert(:user) user = insert(:user, local: true, deactivated: true) {:error, error} = MastodonAPI.follow(follower, user) - assert error == "Could not follow user: #{user.nickname} is deactivated." + assert error == :rejected end test "following for user" do diff --git a/test/web/mastodon_api/views/account_view_test.exs b/test/web/mastodon_api/views/account_view_test.exs index 80b1f734c..a83bf90a3 100644 --- a/test/web/mastodon_api/views/account_view_test.exs +++ b/test/web/mastodon_api/views/account_view_test.exs @@ -5,6 +5,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do use Pleroma.DataCase + alias Pleroma.Config alias Pleroma.User alias Pleroma.UserRelationship alias Pleroma.Web.CommonAPI @@ -18,6 +19,8 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do :ok end + setup do: clear_config([:instances_favicons, :enabled]) + test "Represent a user account" do background_image = %{ "url" => [%{"href" => "https://example.com/images/asuka_hospital.png"}] @@ -75,6 +78,8 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do pleroma: %{ ap_id: user.ap_id, background_image: "https://example.com/images/asuka_hospital.png", + favicon: + "https://shitposter.club/plugins/Qvitter/img/gnusocial-favicons/favicon-16x16.png", confirmation_pending: false, tags: [], is_admin: false, @@ -85,22 +90,37 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do hide_followers_count: false, hide_follows_count: false, relationship: %{}, - skip_thread_containment: false + skip_thread_containment: false, + accepts_chat_messages: nil } } assert expected == AccountView.render("show.json", %{user: user}) end + test "Favicon is nil when :instances_favicons is disabled" do + user = insert(:user) + + Config.put([:instances_favicons, :enabled], true) + + assert %{ + pleroma: %{ + favicon: + "https://shitposter.club/plugins/Qvitter/img/gnusocial-favicons/favicon-16x16.png" + } + } = AccountView.render("show.json", %{user: user}) + + Config.put([:instances_favicons, :enabled], false) + + assert %{pleroma: %{favicon: nil}} = AccountView.render("show.json", %{user: user}) + end + test "Represent the user account for the account owner" do user = insert(:user) notification_settings = %{ - followers: true, - follows: true, - non_followers: true, - non_follows: true, - privacy_option: false + block_from_strangers: false, + hide_notification_contents: false } privacy = user.default_scope @@ -152,6 +172,8 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do pleroma: %{ ap_id: user.ap_id, background_image: nil, + favicon: + "https://shitposter.club/plugins/Qvitter/img/gnusocial-favicons/favicon-16x16.png", confirmation_pending: false, tags: [], is_admin: false, @@ -162,7 +184,8 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do hide_followers_count: false, hide_follows_count: false, relationship: %{}, - skip_thread_containment: false + skip_thread_containment: false, + accepts_chat_messages: nil } } @@ -372,6 +395,9 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do user = insert(:user, hide_followers: true, hide_follows: true) other_user = insert(:user) {:ok, user, other_user, _activity} = CommonAPI.follow(user, other_user) + + assert User.following?(user, other_user) + assert Pleroma.FollowingRelationship.follower_count(other_user) == 1 {:ok, _other_user, user, _activity} = CommonAPI.follow(other_user, user) assert %{ diff --git a/test/web/mastodon_api/views/status_view_test.exs b/test/web/mastodon_api/views/status_view_test.exs index 5cbadf0fc..fa26b3129 100644 --- a/test/web/mastodon_api/views/status_view_test.exs +++ b/test/web/mastodon_api/views/status_view_test.exs @@ -183,6 +183,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusViewTest do card: nil, reblog: nil, content: HTML.filter_tags(object_data["content"]), + text: nil, created_at: created_at, reblogs_count: 0, replies_count: 0, @@ -226,7 +227,8 @@ defmodule Pleroma.Web.MastodonAPI.StatusViewTest do expires_at: nil, direct_conversation_id: nil, thread_muted: false, - emoji_reactions: [] + emoji_reactions: [], + parent_visible: false } } @@ -620,4 +622,20 @@ defmodule Pleroma.Web.MastodonAPI.StatusViewTest do assert status.visibility == "list" end + + test "has a field for parent visibility" do + user = insert(:user) + poster = insert(:user) + + {:ok, invisible} = CommonAPI.post(poster, %{status: "hey", visibility: "private"}) + + {:ok, visible} = + CommonAPI.post(poster, %{status: "hey", visibility: "private", in_reply_to_id: invisible.id}) + + status = StatusView.render("show.json", activity: visible, for: user) + refute status.pleroma.parent_visible + + status = StatusView.render("show.json", activity: visible, for: poster) + assert status.pleroma.parent_visible + end end diff --git a/test/web/media_proxy/media_proxy_controller_test.exs b/test/web/media_proxy/media_proxy_controller_test.exs index d61cef83b..d4db44c63 100644 --- a/test/web/media_proxy/media_proxy_controller_test.exs +++ b/test/web/media_proxy/media_proxy_controller_test.exs @@ -4,82 +4,118 @@ defmodule Pleroma.Web.MediaProxy.MediaProxyControllerTest do use Pleroma.Web.ConnCase + import Mock - alias Pleroma.Config - setup do: clear_config(:media_proxy) - setup do: clear_config([Pleroma.Web.Endpoint, :secret_key_base]) + alias Pleroma.Web.MediaProxy + alias Pleroma.Web.MediaProxy.MediaProxyController + alias Plug.Conn setup do on_exit(fn -> Cachex.clear(:banned_urls_cache) end) end test "it returns 404 when MediaProxy disabled", %{conn: conn} do - Config.put([:media_proxy, :enabled], false) + clear_config([:media_proxy, :enabled], false) - assert %Plug.Conn{ + assert %Conn{ status: 404, resp_body: "Not Found" } = get(conn, "/proxy/hhgfh/eeeee") - assert %Plug.Conn{ + assert %Conn{ status: 404, resp_body: "Not Found" } = get(conn, "/proxy/hhgfh/eeee/fff") end - test "it returns 403 when signature invalidated", %{conn: conn} do - Config.put([:media_proxy, :enabled], true) - Config.put([Pleroma.Web.Endpoint, :secret_key_base], "00000000000") - path = URI.parse(Pleroma.Web.MediaProxy.encode_url("https://google.fn")).path - Config.put([Pleroma.Web.Endpoint, :secret_key_base], "000") - - assert %Plug.Conn{ - status: 403, - resp_body: "Forbidden" - } = get(conn, path) - - assert %Plug.Conn{ - status: 403, - resp_body: "Forbidden" - } = get(conn, "/proxy/hhgfh/eeee") - - assert %Plug.Conn{ - status: 403, - resp_body: "Forbidden" - } = get(conn, "/proxy/hhgfh/eeee/fff") - end + describe "" do + setup do + clear_config([:media_proxy, :enabled], true) + clear_config([Pleroma.Web.Endpoint, :secret_key_base], "00000000000") + [url: MediaProxy.encode_url("https://google.fn/test.png")] + end - test "redirects on valid url when filename invalidated", %{conn: conn} do - Config.put([:media_proxy, :enabled], true) - Config.put([Pleroma.Web.Endpoint, :secret_key_base], "00000000000") - url = Pleroma.Web.MediaProxy.encode_url("https://google.fn/test.png") - invalid_url = String.replace(url, "test.png", "test-file.png") - response = get(conn, invalid_url) - assert response.status == 302 - assert redirected_to(response) == url - end + test "it returns 403 for invalid signature", %{conn: conn, url: url} do + Pleroma.Config.put([Pleroma.Web.Endpoint, :secret_key_base], "000") + %{path: path} = URI.parse(url) + + assert %Conn{ + status: 403, + resp_body: "Forbidden" + } = get(conn, path) + + assert %Conn{ + status: 403, + resp_body: "Forbidden" + } = get(conn, "/proxy/hhgfh/eeee") + + assert %Conn{ + status: 403, + resp_body: "Forbidden" + } = get(conn, "/proxy/hhgfh/eeee/fff") + end - test "it performs ReverseProxy.call when signature valid", %{conn: conn} do - Config.put([:media_proxy, :enabled], true) - Config.put([Pleroma.Web.Endpoint, :secret_key_base], "00000000000") - url = Pleroma.Web.MediaProxy.encode_url("https://google.fn/test.png") + test "redirects on valid url when filename is invalidated", %{conn: conn, url: url} do + invalid_url = String.replace(url, "test.png", "test-file.png") + response = get(conn, invalid_url) + assert response.status == 302 + assert redirected_to(response) == url + end - with_mock Pleroma.ReverseProxy, - call: fn _conn, _url, _opts -> %Plug.Conn{status: :success} end do - assert %Plug.Conn{status: :success} = get(conn, url) + test "it performs ReverseProxy.call with valid signature", %{conn: conn, url: url} do + with_mock Pleroma.ReverseProxy, + call: fn _conn, _url, _opts -> %Conn{status: :success} end do + assert %Conn{status: :success} = get(conn, url) + end + end + + test "it returns 404 when url is in banned_urls cache", %{conn: conn, url: url} do + MediaProxy.put_in_banned_urls("https://google.fn/test.png") + + with_mock Pleroma.ReverseProxy, + call: fn _conn, _url, _opts -> %Conn{status: :success} end do + assert %Conn{status: 404, resp_body: "Not Found"} = get(conn, url) + end end end - test "it returns 404 when url contains in banned_urls cache", %{conn: conn} do - Config.put([:media_proxy, :enabled], true) - Config.put([Pleroma.Web.Endpoint, :secret_key_base], "00000000000") - url = Pleroma.Web.MediaProxy.encode_url("https://google.fn/test.png") - Pleroma.Web.MediaProxy.put_in_banned_urls("https://google.fn/test.png") + describe "filename_matches/3" do + test "preserves the encoded or decoded path" do + assert MediaProxyController.filename_matches( + %{"filename" => "/Hello world.jpg"}, + "/Hello world.jpg", + "http://pleroma.social/Hello world.jpg" + ) == :ok + + assert MediaProxyController.filename_matches( + %{"filename" => "/Hello%20world.jpg"}, + "/Hello%20world.jpg", + "http://pleroma.social/Hello%20world.jpg" + ) == :ok + + assert MediaProxyController.filename_matches( + %{"filename" => "/my%2Flong%2Furl%2F2019%2F07%2FS.jpg"}, + "/my%2Flong%2Furl%2F2019%2F07%2FS.jpg", + "http://pleroma.social/my%2Flong%2Furl%2F2019%2F07%2FS.jpg" + ) == :ok + + assert MediaProxyController.filename_matches( + %{"filename" => "/my%2Flong%2Furl%2F2019%2F07%2FS.jp"}, + "/my%2Flong%2Furl%2F2019%2F07%2FS.jp", + "http://pleroma.social/my%2Flong%2Furl%2F2019%2F07%2FS.jpg" + ) == {:wrong_filename, "my%2Flong%2Furl%2F2019%2F07%2FS.jpg"} + end + + test "encoded url are tried to match for proxy as `conn.request_path` encodes the url" do + # conn.request_path will return encoded url + request_path = "/ANALYSE-DAI-_-LE-STABLECOIN-100-D%C3%89CENTRALIS%C3%89-BQ.jpg" - with_mock Pleroma.ReverseProxy, - call: fn _conn, _url, _opts -> %Plug.Conn{status: :success} end do - assert %Plug.Conn{status: 404, resp_body: "Not Found"} = get(conn, url) + assert MediaProxyController.filename_matches( + true, + request_path, + "https://mydomain.com/uploads/2019/07/ANALYSE-DAI-_-LE-STABLECOIN-100-DÉCENTRALISÉ-BQ.jpg" + ) == :ok end end end diff --git a/test/web/media_proxy/media_proxy_test.exs b/test/web/media_proxy/media_proxy_test.exs index 69d2a71a6..72885cfdd 100644 --- a/test/web/media_proxy/media_proxy_test.exs +++ b/test/web/media_proxy/media_proxy_test.exs @@ -5,38 +5,33 @@ defmodule Pleroma.Web.MediaProxyTest do use ExUnit.Case use Pleroma.Tests.Helpers - import Pleroma.Web.MediaProxy - alias Pleroma.Web.MediaProxy.MediaProxyController - setup do: clear_config([:media_proxy, :enabled]) - setup do: clear_config(Pleroma.Upload) + alias Pleroma.Web.Endpoint + alias Pleroma.Web.MediaProxy describe "when enabled" do - setup do - Pleroma.Config.put([:media_proxy, :enabled], true) - :ok - end + setup do: clear_config([:media_proxy, :enabled], true) test "ignores invalid url" do - assert url(nil) == nil - assert url("") == nil + assert MediaProxy.url(nil) == nil + assert MediaProxy.url("") == nil end test "ignores relative url" do - assert url("/local") == "/local" - assert url("/") == "/" + assert MediaProxy.url("/local") == "/local" + assert MediaProxy.url("/") == "/" end test "ignores local url" do - local_url = Pleroma.Web.Endpoint.url() <> "/hello" - local_root = Pleroma.Web.Endpoint.url() - assert url(local_url) == local_url - assert url(local_root) == local_root + local_url = Endpoint.url() <> "/hello" + local_root = Endpoint.url() + assert MediaProxy.url(local_url) == local_url + assert MediaProxy.url(local_root) == local_root end test "encodes and decodes URL" do url = "https://pleroma.soykaf.com/static/logo.png" - encoded = url(url) + encoded = MediaProxy.url(url) assert String.starts_with?( encoded, @@ -50,86 +45,44 @@ defmodule Pleroma.Web.MediaProxyTest do test "encodes and decodes URL without a path" do url = "https://pleroma.soykaf.com" - encoded = url(url) + encoded = MediaProxy.url(url) assert decode_result(encoded) == url end test "encodes and decodes URL without an extension" do url = "https://pleroma.soykaf.com/path/" - encoded = url(url) + encoded = MediaProxy.url(url) assert String.ends_with?(encoded, "/path") assert decode_result(encoded) == url end test "encodes and decodes URL and ignores query params for the path" do url = "https://pleroma.soykaf.com/static/logo.png?93939393939&bunny=true" - encoded = url(url) + encoded = MediaProxy.url(url) assert String.ends_with?(encoded, "/logo.png") assert decode_result(encoded) == url end test "validates signature" do - secret_key_base = Pleroma.Config.get([Pleroma.Web.Endpoint, :secret_key_base]) - - on_exit(fn -> - Pleroma.Config.put([Pleroma.Web.Endpoint, :secret_key_base], secret_key_base) - end) - - encoded = url("https://pleroma.social") + encoded = MediaProxy.url("https://pleroma.social") - Pleroma.Config.put( - [Pleroma.Web.Endpoint, :secret_key_base], + clear_config( + [Endpoint, :secret_key_base], "00000000000000000000000000000000000000000000000" ) [_, "proxy", sig, base64 | _] = URI.parse(encoded).path |> String.split("/") - assert decode_url(sig, base64) == {:error, :invalid_signature} - end - - test "filename_matches preserves the encoded or decoded path" do - assert MediaProxyController.filename_matches( - %{"filename" => "/Hello world.jpg"}, - "/Hello world.jpg", - "http://pleroma.social/Hello world.jpg" - ) == :ok - - assert MediaProxyController.filename_matches( - %{"filename" => "/Hello%20world.jpg"}, - "/Hello%20world.jpg", - "http://pleroma.social/Hello%20world.jpg" - ) == :ok - - assert MediaProxyController.filename_matches( - %{"filename" => "/my%2Flong%2Furl%2F2019%2F07%2FS.jpg"}, - "/my%2Flong%2Furl%2F2019%2F07%2FS.jpg", - "http://pleroma.social/my%2Flong%2Furl%2F2019%2F07%2FS.jpg" - ) == :ok - - assert MediaProxyController.filename_matches( - %{"filename" => "/my%2Flong%2Furl%2F2019%2F07%2FS.jp"}, - "/my%2Flong%2Furl%2F2019%2F07%2FS.jp", - "http://pleroma.social/my%2Flong%2Furl%2F2019%2F07%2FS.jpg" - ) == {:wrong_filename, "my%2Flong%2Furl%2F2019%2F07%2FS.jpg"} - end - - test "encoded url are tried to match for proxy as `conn.request_path` encodes the url" do - # conn.request_path will return encoded url - request_path = "/ANALYSE-DAI-_-LE-STABLECOIN-100-D%C3%89CENTRALIS%C3%89-BQ.jpg" - - assert MediaProxyController.filename_matches( - true, - request_path, - "https://mydomain.com/uploads/2019/07/ANALYSE-DAI-_-LE-STABLECOIN-100-DÉCENTRALISÉ-BQ.jpg" - ) == :ok + assert MediaProxy.decode_url(sig, base64) == {:error, :invalid_signature} end test "uses the configured base_url" do - clear_config([:media_proxy, :base_url], "https://cache.pleroma.social") + base_url = "https://cache.pleroma.social" + clear_config([:media_proxy, :base_url], base_url) url = "https://pleroma.soykaf.com/static/logo.png" - encoded = url(url) + encoded = MediaProxy.url(url) - assert String.starts_with?(encoded, Pleroma.Config.get([:media_proxy, :base_url])) + assert String.starts_with?(encoded, base_url) end # Some sites expect ASCII encoded characters in the URL to be preserved even if @@ -140,7 +93,7 @@ defmodule Pleroma.Web.MediaProxyTest do url = "https://pleroma.com/%20/%21/%22/%23/%24/%25/%26/%27/%28/%29/%2A/%2B/%2C/%2D/%2E/%2F/%30/%31/%32/%33/%34/%35/%36/%37/%38/%39/%3A/%3B/%3C/%3D/%3E/%3F/%40/%41/%42/%43/%44/%45/%46/%47/%48/%49/%4A/%4B/%4C/%4D/%4E/%4F/%50/%51/%52/%53/%54/%55/%56/%57/%58/%59/%5A/%5B/%5C/%5D/%5E/%5F/%60/%61/%62/%63/%64/%65/%66/%67/%68/%69/%6A/%6B/%6C/%6D/%6E/%6F/%70/%71/%72/%73/%74/%75/%76/%77/%78/%79/%7A/%7B/%7C/%7D/%7E/%7F/%80/%81/%82/%83/%84/%85/%86/%87/%88/%89/%8A/%8B/%8C/%8D/%8E/%8F/%90/%91/%92/%93/%94/%95/%96/%97/%98/%99/%9A/%9B/%9C/%9D/%9E/%9F/%C2%A0/%A1/%A2/%A3/%A4/%A5/%A6/%A7/%A8/%A9/%AA/%AB/%AC/%C2%AD/%AE/%AF/%B0/%B1/%B2/%B3/%B4/%B5/%B6/%B7/%B8/%B9/%BA/%BB/%BC/%BD/%BE/%BF/%C0/%C1/%C2/%C3/%C4/%C5/%C6/%C7/%C8/%C9/%CA/%CB/%CC/%CD/%CE/%CF/%D0/%D1/%D2/%D3/%D4/%D5/%D6/%D7/%D8/%D9/%DA/%DB/%DC/%DD/%DE/%DF/%E0/%E1/%E2/%E3/%E4/%E5/%E6/%E7/%E8/%E9/%EA/%EB/%EC/%ED/%EE/%EF/%F0/%F1/%F2/%F3/%F4/%F5/%F6/%F7/%F8/%F9/%FA/%FB/%FC/%FD/%FE/%FF" - encoded = url(url) + encoded = MediaProxy.url(url) assert decode_result(encoded) == url end @@ -151,56 +104,49 @@ defmodule Pleroma.Web.MediaProxyTest do url = "https://pleroma.com/ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890-._~:/?#[]@!$&'()*+,;=|^`{}" - encoded = url(url) + encoded = MediaProxy.url(url) assert decode_result(encoded) == url end test "preserve unicode characters" do url = "https://ko.wikipedia.org/wiki/위키백과:대문" - encoded = url(url) + encoded = MediaProxy.url(url) assert decode_result(encoded) == url end end describe "when disabled" do - setup do - enabled = Pleroma.Config.get([:media_proxy, :enabled]) - - if enabled do - Pleroma.Config.put([:media_proxy, :enabled], false) - - on_exit(fn -> - Pleroma.Config.put([:media_proxy, :enabled], enabled) - :ok - end) - end - - :ok - end + setup do: clear_config([:media_proxy, :enabled], false) test "does not encode remote urls" do - assert url("https://google.fr") == "https://google.fr" + assert MediaProxy.url("https://google.fr") == "https://google.fr" end end defp decode_result(encoded) do [_, "proxy", sig, base64 | _] = URI.parse(encoded).path |> String.split("/") - {:ok, decoded} = decode_url(sig, base64) + {:ok, decoded} = MediaProxy.decode_url(sig, base64) decoded end describe "whitelist" do - setup do - Pleroma.Config.put([:media_proxy, :enabled], true) - :ok - end + setup do: clear_config([:media_proxy, :enabled], true) test "mediaproxy whitelist" do - Pleroma.Config.put([:media_proxy, :whitelist], ["google.com", "feld.me"]) + clear_config([:media_proxy, :whitelist], ["https://google.com", "https://feld.me"]) + url = "https://feld.me/foo.png" + + unencoded = MediaProxy.url(url) + assert unencoded == url + end + + # TODO: delete after removing support bare domains for media proxy whitelist + test "mediaproxy whitelist bare domains whitelist (deprecated)" do + clear_config([:media_proxy, :whitelist], ["google.com", "feld.me"]) url = "https://feld.me/foo.png" - unencoded = url(url) + unencoded = MediaProxy.url(url) assert unencoded == url end @@ -211,17 +157,17 @@ defmodule Pleroma.Web.MediaProxyTest do media_url = "https://mycdn.akamai.com" url = "#{media_url}/static/logo.png" - encoded = url(url) + encoded = MediaProxy.url(url) assert String.starts_with?(encoded, media_url) end test "ensure Pleroma.Upload base_url is always whitelisted" do media_url = "https://media.pleroma.social" - Pleroma.Config.put([Pleroma.Upload, :base_url], media_url) + clear_config([Pleroma.Upload, :base_url], media_url) url = "#{media_url}/static/logo.png" - encoded = url(url) + encoded = MediaProxy.url(url) assert String.starts_with?(encoded, media_url) end diff --git a/test/web/pleroma_api/controllers/account_controller_test.exs b/test/web/pleroma_api/controllers/account_controller_test.exs index 103997c31..07909d48b 100644 --- a/test/web/pleroma_api/controllers/account_controller_test.exs +++ b/test/web/pleroma_api/controllers/account_controller_test.exs @@ -13,8 +13,6 @@ defmodule Pleroma.Web.PleromaAPI.AccountControllerTest do import Pleroma.Factory import Swoosh.TestAssertions - @image "data:image/gif;base64,R0lGODlhEAAQAMQAAORHHOVSKudfOulrSOp3WOyDZu6QdvCchPGolfO0o/XBs/fNwfjZ0frl3/zy7////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAkAABAALAAAAAAQABAAAAVVICSOZGlCQAosJ6mu7fiyZeKqNKToQGDsM8hBADgUXoGAiqhSvp5QAnQKGIgUhwFUYLCVDFCrKUE1lBavAViFIDlTImbKC5Gm2hB0SlBCBMQiB0UjIQA7" - describe "POST /api/v1/pleroma/accounts/confirmation_resend" do setup do {:ok, user} = @@ -68,103 +66,6 @@ defmodule Pleroma.Web.PleromaAPI.AccountControllerTest do end end - describe "PATCH /api/v1/pleroma/accounts/update_avatar" do - setup do: oauth_access(["write:accounts"]) - - test "user avatar can be set", %{user: user, conn: conn} do - avatar_image = File.read!("test/fixtures/avatar_data_uri") - - conn = - conn - |> put_req_header("content-type", "multipart/form-data") - |> patch("/api/v1/pleroma/accounts/update_avatar", %{img: avatar_image}) - - user = refresh_record(user) - - assert %{ - "name" => _, - "type" => _, - "url" => [ - %{ - "href" => _, - "mediaType" => _, - "type" => _ - } - ] - } = user.avatar - - assert %{"url" => _} = json_response_and_validate_schema(conn, 200) - end - - test "user avatar can be reset", %{user: user, conn: conn} do - conn = - conn - |> put_req_header("content-type", "multipart/form-data") - |> patch("/api/v1/pleroma/accounts/update_avatar", %{img: ""}) - - user = User.get_cached_by_id(user.id) - - assert user.avatar == nil - - assert %{"url" => nil} = json_response_and_validate_schema(conn, 200) - end - end - - describe "PATCH /api/v1/pleroma/accounts/update_banner" do - setup do: oauth_access(["write:accounts"]) - - test "can set profile banner", %{user: user, conn: conn} do - conn = - conn - |> put_req_header("content-type", "multipart/form-data") - |> patch("/api/v1/pleroma/accounts/update_banner", %{"banner" => @image}) - - user = refresh_record(user) - assert user.banner["type"] == "Image" - - assert %{"url" => _} = json_response_and_validate_schema(conn, 200) - end - - test "can reset profile banner", %{user: user, conn: conn} do - conn = - conn - |> put_req_header("content-type", "multipart/form-data") - |> patch("/api/v1/pleroma/accounts/update_banner", %{"banner" => ""}) - - user = refresh_record(user) - assert user.banner == %{} - - assert %{"url" => nil} = json_response_and_validate_schema(conn, 200) - end - end - - describe "PATCH /api/v1/pleroma/accounts/update_background" do - setup do: oauth_access(["write:accounts"]) - - test "background image can be set", %{user: user, conn: conn} do - conn = - conn - |> put_req_header("content-type", "multipart/form-data") - |> patch("/api/v1/pleroma/accounts/update_background", %{"img" => @image}) - - user = refresh_record(user) - assert user.background["type"] == "Image" - # assert %{"url" => _} = json_response(conn, 200) - assert %{"url" => _} = json_response_and_validate_schema(conn, 200) - end - - test "background image can be reset", %{user: user, conn: conn} do - conn = - conn - |> put_req_header("content-type", "multipart/form-data") - |> patch("/api/v1/pleroma/accounts/update_background", %{"img" => ""}) - - user = refresh_record(user) - assert user.background == %{} - assert %{"url" => nil} = json_response_and_validate_schema(conn, 200) - end - end - describe "getting favorites timeline of specified user" do setup do [current_user, user] = insert_pair(:user, hide_favorites: false) diff --git a/test/web/preload/instance_test.exs b/test/web/preload/instance_test.exs new file mode 100644 index 000000000..a46f28312 --- /dev/null +++ b/test/web/preload/instance_test.exs @@ -0,0 +1,48 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.Preload.Providers.InstanceTest do + use Pleroma.DataCase + alias Pleroma.Web.Preload.Providers.Instance + + setup do: {:ok, Instance.generate_terms(nil)} + + test "it renders the info", %{"/api/v1/instance" => info} do + assert %{ + description: description, + email: "admin@example.com", + registrations: true + } = info + + assert String.equivalent?(description, "Pleroma: An efficient and flexible fediverse server") + end + + test "it renders the panel", %{"/instance/panel.html" => panel} do + assert String.contains?( + panel, + "<p>Welcome to <a href=\"https://pleroma.social\" target=\"_blank\">Pleroma!</a></p>" + ) + end + + test "it works with overrides" do + clear_config([:instance, :static_dir], "test/fixtures/preload_static") + + %{"/instance/panel.html" => panel} = Instance.generate_terms(nil) + + assert String.contains?( + panel, + "HEY!" + ) + end + + test "it renders the node_info", %{"/nodeinfo/2.0.json" => nodeinfo} do + %{ + metadata: metadata, + version: "2.0" + } = nodeinfo + + assert metadata.private == false + assert metadata.suggestions == %{enabled: false} + end +end diff --git a/test/web/preload/timeline_test.exs b/test/web/preload/timeline_test.exs new file mode 100644 index 000000000..fea95a6a4 --- /dev/null +++ b/test/web/preload/timeline_test.exs @@ -0,0 +1,74 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.Preload.Providers.TimelineTest do + use Pleroma.DataCase + import Pleroma.Factory + + alias Pleroma.Web.CommonAPI + alias Pleroma.Web.Preload.Providers.Timelines + + @public_url "/api/v1/timelines/public" + + describe "unauthenticated timeliness when restricted" do + setup do + svd_config = Pleroma.Config.get([:restrict_unauthenticated, :timelines]) + Pleroma.Config.put([:restrict_unauthenticated, :timelines], %{local: true, federated: true}) + + on_exit(fn -> + Pleroma.Config.put([:restrict_unauthenticated, :timelines], svd_config) + end) + + :ok + end + + test "return nothing" do + tl_data = Timelines.generate_terms(%{}) + + refute Map.has_key?(tl_data, "/api/v1/timelines/public") + end + end + + describe "unauthenticated timeliness when unrestricted" do + setup do + svd_config = Pleroma.Config.get([:restrict_unauthenticated, :timelines]) + + Pleroma.Config.put([:restrict_unauthenticated, :timelines], %{ + local: false, + federated: false + }) + + on_exit(fn -> + Pleroma.Config.put([:restrict_unauthenticated, :timelines], svd_config) + end) + + {:ok, user: insert(:user)} + end + + test "returns the timeline when not restricted" do + assert Timelines.generate_terms(%{}) + |> Map.has_key?(@public_url) + end + + test "returns public items", %{user: user} do + {:ok, _} = CommonAPI.post(user, %{status: "it's post 1!"}) + {:ok, _} = CommonAPI.post(user, %{status: "it's post 2!"}) + {:ok, _} = CommonAPI.post(user, %{status: "it's post 3!"}) + + assert Timelines.generate_terms(%{}) + |> Map.fetch!(@public_url) + |> Enum.count() == 3 + end + + test "does not return non-public items", %{user: user} do + {:ok, _} = CommonAPI.post(user, %{status: "it's post 1!", visibility: "unlisted"}) + {:ok, _} = CommonAPI.post(user, %{status: "it's post 2!", visibility: "direct"}) + {:ok, _} = CommonAPI.post(user, %{status: "it's post 3!"}) + + assert Timelines.generate_terms(%{}) + |> Map.fetch!(@public_url) + |> Enum.count() == 1 + end + end +end diff --git a/test/web/preload/user_test.exs b/test/web/preload/user_test.exs new file mode 100644 index 000000000..83f065e27 --- /dev/null +++ b/test/web/preload/user_test.exs @@ -0,0 +1,33 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.Preload.Providers.UserTest do + use Pleroma.DataCase + import Pleroma.Factory + alias Pleroma.Web.Preload.Providers.User + + describe "returns empty when user doesn't exist" do + test "nil user specified" do + assert User.generate_terms(%{user: nil}) == %{} + end + + test "missing user specified" do + assert User.generate_terms(%{user: :not_a_user}) == %{} + end + end + + describe "specified user exists" do + setup do + user = insert(:user) + + terms = User.generate_terms(%{user: user}) + %{terms: terms, user: user} + end + + test "account is rendered", %{terms: terms, user: user} do + account = terms["/api/v1/accounts/#{user.id}"] + assert %{acct: user, username: user} = account + end + end +end diff --git a/test/web/push/impl_test.exs b/test/web/push/impl_test.exs index b48952b29..aeb5c1fbd 100644 --- a/test/web/push/impl_test.exs +++ b/test/web/push/impl_test.exs @@ -238,9 +238,11 @@ defmodule Pleroma.Web.Push.ImplTest do } end - test "hides details for notifications when privacy option enabled" do + test "hides contents of notifications when option enabled" do user = insert(:user, nickname: "Bob") - user2 = insert(:user, nickname: "Rob", notification_settings: %{privacy_option: true}) + + user2 = + insert(:user, nickname: "Rob", notification_settings: %{hide_notification_contents: true}) {:ok, activity} = CommonAPI.post(user, %{ @@ -284,9 +286,11 @@ defmodule Pleroma.Web.Push.ImplTest do } end - test "returns regular content for notifications with privacy option disabled" do + test "returns regular content when hiding contents option disabled" do user = insert(:user, nickname: "Bob") - user2 = insert(:user, nickname: "Rob", notification_settings: %{privacy_option: false}) + + user2 = + insert(:user, nickname: "Rob", notification_settings: %{hide_notification_contents: false}) {:ok, activity} = CommonAPI.post(user, %{ diff --git a/test/web/static_fe/static_fe_controller_test.exs b/test/web/static_fe/static_fe_controller_test.exs index a49ab002f..1598bf675 100644 --- a/test/web/static_fe/static_fe_controller_test.exs +++ b/test/web/static_fe/static_fe_controller_test.exs @@ -87,6 +87,20 @@ defmodule Pleroma.Web.StaticFE.StaticFEControllerTest do assert html =~ "testing a thing!" end + test "redirects to json if requested", %{conn: conn, user: user} do + {:ok, activity} = CommonAPI.post(user, %{status: "testing a thing!"}) + + conn = + conn + |> put_req_header( + "accept", + "Accept: application/activity+json, application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\", text/html" + ) + |> get("/notice/#{activity.id}") + + assert redirected_to(conn, 302) =~ activity.data["object"] + end + test "filters HTML tags", %{conn: conn} do user = insert(:user) {:ok, activity} = CommonAPI.post(user, %{status: "<script>alert('xss')</script>"}) diff --git a/test/web/streamer/streamer_test.exs b/test/web/streamer/streamer_test.exs index 245f6e63f..d56d74464 100644 --- a/test/web/streamer/streamer_test.exs +++ b/test/web/streamer/streamer_test.exs @@ -116,6 +116,35 @@ defmodule Pleroma.Web.StreamerTest do refute Streamer.filtered_by_user?(user, announce) end + test "it does not stream announces of the user's own posts in the 'user' stream", %{ + user: user + } do + Streamer.get_topic_and_add_socket("user", user) + + other_user = insert(:user) + {:ok, activity} = CommonAPI.post(user, %{status: "hey"}) + {:ok, announce} = CommonAPI.repeat(activity.id, other_user) + + assert Streamer.filtered_by_user?(user, announce) + end + + test "it does stream notifications announces of the user's own posts in the 'user' stream", %{ + user: user + } do + Streamer.get_topic_and_add_socket("user", user) + + other_user = insert(:user) + {:ok, activity} = CommonAPI.post(user, %{status: "hey"}) + {:ok, announce} = CommonAPI.repeat(activity.id, other_user) + + notification = + Pleroma.Notification + |> Repo.get_by(%{user_id: user.id, activity_id: announce.id}) + |> Repo.preload(:activity) + + refute Streamer.filtered_by_user?(user, notification) + end + test "it streams boosts of mastodon user in the 'user' stream", %{user: user} do Streamer.get_topic_and_add_socket("user", user) diff --git a/test/web/twitter_api/util_controller_test.exs b/test/web/twitter_api/util_controller_test.exs index ad919d341..109c1e637 100644 --- a/test/web/twitter_api/util_controller_test.exs +++ b/test/web/twitter_api/util_controller_test.exs @@ -191,7 +191,7 @@ defmodule Pleroma.Web.TwitterAPI.UtilControllerTest do test "it updates notification settings", %{user: user, conn: conn} do conn |> put("/api/pleroma/notification_settings", %{ - "followers" => false, + "block_from_strangers" => true, "bar" => 1 }) |> json_response(:ok) @@ -199,130 +199,25 @@ defmodule Pleroma.Web.TwitterAPI.UtilControllerTest do user = refresh_record(user) assert %Pleroma.User.NotificationSetting{ - followers: false, - follows: true, - non_follows: true, - non_followers: true, - privacy_option: false + block_from_strangers: true, + hide_notification_contents: false } == user.notification_settings end - test "it updates notification privacy option", %{user: user, conn: conn} do + test "it updates notification settings to enable hiding contents", %{user: user, conn: conn} do conn - |> put("/api/pleroma/notification_settings", %{"privacy_option" => "1"}) + |> put("/api/pleroma/notification_settings", %{"hide_notification_contents" => "1"}) |> json_response(:ok) user = refresh_record(user) assert %Pleroma.User.NotificationSetting{ - followers: true, - follows: true, - non_follows: true, - non_followers: true, - privacy_option: true + block_from_strangers: false, + hide_notification_contents: true } == user.notification_settings end end - describe "GET /api/statusnet/config" do - test "it returns config in xml format", %{conn: conn} do - instance = Config.get(:instance) - - response = - conn - |> put_req_header("accept", "application/xml") - |> get("/api/statusnet/config") - |> response(:ok) - - assert response == - "<config>\n<site>\n<name>#{Keyword.get(instance, :name)}</name>\n<site>#{ - Pleroma.Web.base_url() - }</site>\n<textlimit>#{Keyword.get(instance, :limit)}</textlimit>\n<closed>#{ - !Keyword.get(instance, :registrations_open) - }</closed>\n</site>\n</config>\n" - end - - test "it returns config in json format", %{conn: conn} do - instance = Config.get(:instance) - Config.put([:instance, :managed_config], true) - Config.put([:instance, :registrations_open], false) - Config.put([:instance, :invites_enabled], true) - Config.put([:instance, :public], false) - Config.put([:frontend_configurations, :pleroma_fe], %{theme: "asuka-hospital"}) - - response = - conn - |> put_req_header("accept", "application/json") - |> get("/api/statusnet/config") - |> json_response(:ok) - - expected_data = %{ - "site" => %{ - "accountActivationRequired" => "0", - "closed" => "1", - "description" => Keyword.get(instance, :description), - "invitesEnabled" => "1", - "name" => Keyword.get(instance, :name), - "pleromafe" => %{"theme" => "asuka-hospital"}, - "private" => "1", - "safeDMMentionsEnabled" => "0", - "server" => Pleroma.Web.base_url(), - "textlimit" => to_string(Keyword.get(instance, :limit)), - "uploadlimit" => %{ - "avatarlimit" => to_string(Keyword.get(instance, :avatar_upload_limit)), - "backgroundlimit" => to_string(Keyword.get(instance, :background_upload_limit)), - "bannerlimit" => to_string(Keyword.get(instance, :banner_upload_limit)), - "uploadlimit" => to_string(Keyword.get(instance, :upload_limit)) - }, - "vapidPublicKey" => Keyword.get(Pleroma.Web.Push.vapid_config(), :public_key) - } - } - - assert response == expected_data - end - - test "returns the state of safe_dm_mentions flag", %{conn: conn} do - Config.put([:instance, :safe_dm_mentions], true) - - response = - conn - |> get("/api/statusnet/config.json") - |> json_response(:ok) - - assert response["site"]["safeDMMentionsEnabled"] == "1" - - Config.put([:instance, :safe_dm_mentions], false) - - response = - conn - |> get("/api/statusnet/config.json") - |> json_response(:ok) - - assert response["site"]["safeDMMentionsEnabled"] == "0" - end - - test "it returns the managed config", %{conn: conn} do - Config.put([:instance, :managed_config], false) - Config.put([:frontend_configurations, :pleroma_fe], %{theme: "asuka-hospital"}) - - response = - conn - |> get("/api/statusnet/config.json") - |> json_response(:ok) - - refute response["site"]["pleromafe"] - - Config.put([:instance, :managed_config], true) - - response = - conn - |> get("/api/statusnet/config.json") - |> json_response(:ok) - - assert response["site"]["pleromafe"] == %{"theme" => "asuka-hospital"} - end - end - describe "GET /api/pleroma/frontend_configurations" do test "returns everything in :pleroma, :frontend_configurations", %{conn: conn} do config = [ @@ -451,28 +346,6 @@ defmodule Pleroma.Web.TwitterAPI.UtilControllerTest do end end - describe "GET /api/statusnet/version" do - test "it returns version in xml format", %{conn: conn} do - response = - conn - |> put_req_header("accept", "application/xml") - |> get("/api/statusnet/version") - |> response(:ok) - - assert response == "<version>#{Pleroma.Application.named_version()}</version>" - end - - test "it returns version in json format", %{conn: conn} do - response = - conn - |> put_req_header("accept", "application/json") - |> get("/api/statusnet/version") - |> json_response(:ok) - - assert response == "#{Pleroma.Application.named_version()}" - end - end - describe "POST /main/ostatus - remote_subscribe/2" do setup do: clear_config([:instance, :federating], true) |