commit: 7dffaef4799b0e867e91e19b76567c0e1a19bb43
parent 6bf85440b373c9b2fa1e8e7184dcf87518600306
Author: Alexander Strizhakov <alex.strizhakov@gmail.com>
Date: Tue, 23 Jun 2020 18:16:47 +0300
tests consistency
Diffstat:
346 files changed, 12589 insertions(+), 12588 deletions(-)
diff --git a/test/fixtures/modules/runtime_module.ex b/test/fixtures/modules/runtime_module.ex
@@ -2,7 +2,7 @@
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
-defmodule RuntimeModule do
+defmodule Fixtures.Modules.RuntimeModule do
@moduledoc """
This is a dummy module to test custom runtime modules.
"""
diff --git a/test/activity/ir/topics_test.exs b/test/pleroma/activity/ir/topics_test.exs
diff --git a/test/activity_test.exs b/test/pleroma/activity_test.exs
diff --git a/test/bbs/handler_test.exs b/test/pleroma/bbs/handler_test.exs
diff --git a/test/bookmark_test.exs b/test/pleroma/bookmark_test.exs
diff --git a/test/captcha_test.exs b/test/pleroma/captcha_test.exs
diff --git a/test/chat/message_reference_test.exs b/test/pleroma/chat/message_reference_test.exs
diff --git a/test/chat_test.exs b/test/pleroma/chat_test.exs
diff --git a/test/config/deprecation_warnings_test.exs b/test/pleroma/config/deprecation_warnings_test.exs
diff --git a/test/config/holder_test.exs b/test/pleroma/config/holder_test.exs
diff --git a/test/config/loader_test.exs b/test/pleroma/config/loader_test.exs
diff --git a/test/config/transfer_task_test.exs b/test/pleroma/config/transfer_task_test.exs
diff --git a/test/config/config_db_test.exs b/test/pleroma/config_db_test.exs
diff --git a/test/config_test.exs b/test/pleroma/config_test.exs
diff --git a/test/conversation/participation_test.exs b/test/pleroma/conversation/participation_test.exs
diff --git a/test/conversation_test.exs b/test/pleroma/conversation_test.exs
diff --git a/test/docs/generator_test.exs b/test/pleroma/docs/generator_test.exs
diff --git a/test/earmark_renderer_test.exs b/test/pleroma/earmark_renderer_test.exs
diff --git a/test/pleroma/ecto_type/activity_pub/object_validators/date_time_test.exs b/test/pleroma/ecto_type/activity_pub/object_validators/date_time_test.exs
@@ -0,0 +1,36 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.EctoType.ActivityPub.ObjectValidators.DateTimeTest do
+ alias Pleroma.EctoType.ActivityPub.ObjectValidators.DateTime
+ use Pleroma.DataCase
+
+ test "it validates an xsd:Datetime" do
+ valid_strings = [
+ "2004-04-12T13:20:00",
+ "2004-04-12T13:20:15.5",
+ "2004-04-12T13:20:00-05:00",
+ "2004-04-12T13:20:00Z"
+ ]
+
+ invalid_strings = [
+ "2004-04-12T13:00",
+ "2004-04-1213:20:00",
+ "99-04-12T13:00",
+ "2004-04-12"
+ ]
+
+ assert {:ok, "2004-04-01T12:00:00Z"} == DateTime.cast("2004-04-01T12:00:00Z")
+
+ Enum.each(valid_strings, fn date_time ->
+ result = DateTime.cast(date_time)
+ assert {:ok, _} = result
+ end)
+
+ Enum.each(invalid_strings, fn date_time ->
+ result = DateTime.cast(date_time)
+ assert :error == result
+ end)
+ end
+end
diff --git a/test/pleroma/ecto_type/activity_pub/object_validators/object_id_test.exs b/test/pleroma/ecto_type/activity_pub/object_validators/object_id_test.exs
@@ -0,0 +1,41 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.EctoType.ActivityPub.ObjectValidators.ObjectIDTest do
+ alias Pleroma.EctoType.ActivityPub.ObjectValidators.ObjectID
+ use Pleroma.DataCase
+
+ @uris [
+ "http://lain.com/users/lain",
+ "http://lain.com",
+ "https://lain.com/object/1"
+ ]
+
+ @non_uris [
+ "https://",
+ "rin",
+ 1,
+ :x,
+ %{"1" => 2}
+ ]
+
+ test "it accepts http uris" do
+ Enum.each(@uris, fn uri ->
+ assert {:ok, uri} == ObjectID.cast(uri)
+ end)
+ end
+
+ test "it accepts an object with a nested uri id" do
+ Enum.each(@uris, fn uri ->
+ assert {:ok, uri} == ObjectID.cast(%{"id" => uri})
+ end)
+ end
+
+ test "it rejects non-uri strings" do
+ Enum.each(@non_uris, fn non_uri ->
+ assert :error == ObjectID.cast(non_uri)
+ assert :error == ObjectID.cast(%{"id" => non_uri})
+ end)
+ end
+end
diff --git a/test/pleroma/ecto_type/activity_pub/object_validators/recipients_test.exs b/test/pleroma/ecto_type/activity_pub/object_validators/recipients_test.exs
@@ -0,0 +1,31 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.EctoType.ActivityPub.ObjectValidators.RecipientsTest do
+ alias Pleroma.EctoType.ActivityPub.ObjectValidators.Recipients
+ use Pleroma.DataCase
+
+ test "it asserts that all elements of the list are object ids" do
+ list = ["https://lain.com/users/lain", "invalid"]
+
+ assert :error == Recipients.cast(list)
+ end
+
+ test "it works with a list" do
+ list = ["https://lain.com/users/lain"]
+ assert {:ok, list} == Recipients.cast(list)
+ end
+
+ test "it works with a list with whole objects" do
+ list = ["https://lain.com/users/lain", %{"id" => "https://gensokyo.2hu/users/raymoo"}]
+ resulting_list = ["https://gensokyo.2hu/users/raymoo", "https://lain.com/users/lain"]
+ assert {:ok, resulting_list} == Recipients.cast(list)
+ end
+
+ test "it turns a single string into a list" do
+ recipient = "https://lain.com/users/lain"
+
+ assert {:ok, [recipient]} == Recipients.cast(recipient)
+ end
+end
diff --git a/test/pleroma/ecto_type/activity_pub/object_validators/safe_text_test.exs b/test/pleroma/ecto_type/activity_pub/object_validators/safe_text_test.exs
@@ -0,0 +1,30 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.EctoType.ActivityPub.ObjectValidators.SafeTextTest do
+ use Pleroma.DataCase
+
+ alias Pleroma.EctoType.ActivityPub.ObjectValidators.SafeText
+
+ test "it lets normal text go through" do
+ text = "hey how are you"
+ assert {:ok, text} == SafeText.cast(text)
+ end
+
+ test "it removes html tags from text" do
+ text = "hey look xss <script>alert('foo')</script>"
+ assert {:ok, "hey look xss alert('foo')"} == SafeText.cast(text)
+ end
+
+ test "it keeps basic html tags" do
+ text = "hey <a href='http://gensokyo.2hu'>look</a> xss <script>alert('foo')</script>"
+
+ assert {:ok, "hey <a href=\"http://gensokyo.2hu\">look</a> xss alert('foo')"} ==
+ SafeText.cast(text)
+ end
+
+ test "errors for non-text" do
+ assert :error == SafeText.cast(1)
+ end
+end
diff --git a/test/emails/admin_email_test.exs b/test/pleroma/emails/admin_email_test.exs
diff --git a/test/emails/mailer_test.exs b/test/pleroma/emails/mailer_test.exs
diff --git a/test/emails/user_email_test.exs b/test/pleroma/emails/user_email_test.exs
diff --git a/test/emoji/formatter_test.exs b/test/pleroma/emoji/formatter_test.exs
diff --git a/test/emoji/loader_test.exs b/test/pleroma/emoji/loader_test.exs
diff --git a/test/emoji_test.exs b/test/pleroma/emoji_test.exs
diff --git a/test/filter_test.exs b/test/pleroma/filter_test.exs
diff --git a/test/following_relationship_test.exs b/test/pleroma/following_relationship_test.exs
diff --git a/test/formatter_test.exs b/test/pleroma/formatter_test.exs
diff --git a/test/healthcheck_test.exs b/test/pleroma/healthcheck_test.exs
diff --git a/test/html_test.exs b/test/pleroma/html_test.exs
diff --git a/test/http/adapter_helper/gun_test.exs b/test/pleroma/http/adapter_helper/gun_test.exs
diff --git a/test/http/adapter_helper/hackney_test.exs b/test/pleroma/http/adapter_helper/hackney_test.exs
diff --git a/test/http/adapter_helper_test.exs b/test/pleroma/http/adapter_helper_test.exs
diff --git a/test/http/request_builder_test.exs b/test/pleroma/http/request_builder_test.exs
diff --git a/test/http_test.exs b/test/pleroma/http_test.exs
diff --git a/test/web/instances/instance_test.exs b/test/pleroma/instances/instance_test.exs
diff --git a/test/web/instances/instances_test.exs b/test/pleroma/instances_test.exs
diff --git a/test/federation/federation_test.exs b/test/pleroma/integration/federation_test.exs
diff --git a/test/integration/mastodon_websocket_test.exs b/test/pleroma/integration/mastodon_websocket_test.exs
diff --git a/test/job_queue_monitor_test.exs b/test/pleroma/job_queue_monitor_test.exs
diff --git a/test/keys_test.exs b/test/pleroma/keys_test.exs
diff --git a/test/list_test.exs b/test/pleroma/list_test.exs
diff --git a/test/marker_test.exs b/test/pleroma/marker_test.exs
diff --git a/test/mfa/backup_codes_test.exs b/test/pleroma/mfa/backup_codes_test.exs
diff --git a/test/mfa/totp_test.exs b/test/pleroma/mfa/totp_test.exs
diff --git a/test/mfa_test.exs b/test/pleroma/mfa_test.exs
diff --git a/test/migration_helper/notification_backfill_test.exs b/test/pleroma/migration_helper/notification_backfill_test.exs
diff --git a/test/moderation_log_test.exs b/test/pleroma/moderation_log_test.exs
diff --git a/test/notification_test.exs b/test/pleroma/notification_test.exs
diff --git a/test/object/containment_test.exs b/test/pleroma/object/containment_test.exs
diff --git a/test/object/fetcher_test.exs b/test/pleroma/object/fetcher_test.exs
diff --git a/test/object_test.exs b/test/pleroma/object_test.exs
diff --git a/test/otp_version_test.exs b/test/pleroma/otp_version_test.exs
diff --git a/test/pagination_test.exs b/test/pleroma/pagination_test.exs
diff --git a/test/registration_test.exs b/test/pleroma/registration_test.exs
diff --git a/test/repo_test.exs b/test/pleroma/repo_test.exs
diff --git a/test/reverse_proxy/reverse_proxy_test.exs b/test/pleroma/reverse_proxy_test.exs
diff --git a/test/pleroma/runtime_test.exs b/test/pleroma/runtime_test.exs
@@ -0,0 +1,12 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.RuntimeTest do
+ use ExUnit.Case, async: true
+
+ test "it loads custom runtime modules" do
+ assert {:module, Fixtures.Modules.RuntimeModule} ==
+ Code.ensure_compiled(Fixtures.Modules.RuntimeModule)
+ end
+end
diff --git a/test/safe_jsonb_set_test.exs b/test/pleroma/safe_jsonb_set_test.exs
diff --git a/test/scheduled_activity_test.exs b/test/pleroma/scheduled_activity_test.exs
diff --git a/test/signature_test.exs b/test/pleroma/signature_test.exs
diff --git a/test/stats_test.exs b/test/pleroma/stats_test.exs
diff --git a/test/upload/filter/anonymize_filename_test.exs b/test/pleroma/upload/filter/anonymize_filename_test.exs
diff --git a/test/upload/filter/dedupe_test.exs b/test/pleroma/upload/filter/dedupe_test.exs
diff --git a/test/upload/filter/mogrifun_test.exs b/test/pleroma/upload/filter/mogrifun_test.exs
diff --git a/test/upload/filter/mogrify_test.exs b/test/pleroma/upload/filter/mogrify_test.exs
diff --git a/test/upload/filter_test.exs b/test/pleroma/upload/filter_test.exs
diff --git a/test/upload_test.exs b/test/pleroma/upload_test.exs
diff --git a/test/uploaders/local_test.exs b/test/pleroma/uploaders/local_test.exs
diff --git a/test/uploaders/s3_test.exs b/test/pleroma/uploaders/s3_test.exs
diff --git a/test/user/notification_setting_test.exs b/test/pleroma/user/notification_setting_test.exs
diff --git a/test/user_invite_token_test.exs b/test/pleroma/user_invite_token_test.exs
diff --git a/test/user_relationship_test.exs b/test/pleroma/user_relationship_test.exs
diff --git a/test/user_search_test.exs b/test/pleroma/user_search_test.exs
diff --git a/test/user_test.exs b/test/pleroma/user_test.exs
diff --git a/test/web/activity_pub/activity_pub_controller_test.exs b/test/pleroma/web/activity_pub/activity_pub_controller_test.exs
diff --git a/test/web/activity_pub/activity_pub_test.exs b/test/pleroma/web/activity_pub/activity_pub_test.exs
diff --git a/test/web/activity_pub/mrf/force_bot_unlisted_policy_test.exs b/test/pleroma/web/activity_pub/force_bot_unlisted_policy_test.exs
diff --git a/test/web/activity_pub/mrf/activity_expiration_policy_test.exs b/test/pleroma/web/activity_pub/mrf/activity_expiration_policy_test.exs
diff --git a/test/web/activity_pub/mrf/anti_followbot_policy_test.exs b/test/pleroma/web/activity_pub/mrf/anti_followbot_policy_test.exs
diff --git a/test/web/activity_pub/mrf/anti_link_spam_policy_test.exs b/test/pleroma/web/activity_pub/mrf/anti_link_spam_policy_test.exs
diff --git a/test/web/activity_pub/mrf/ensure_re_prepended_test.exs b/test/pleroma/web/activity_pub/mrf/ensure_re_prepended_test.exs
diff --git a/test/web/activity_pub/mrf/hellthread_policy_test.exs b/test/pleroma/web/activity_pub/mrf/hellthread_policy_test.exs
diff --git a/test/web/activity_pub/mrf/keyword_policy_test.exs b/test/pleroma/web/activity_pub/mrf/keyword_policy_test.exs
diff --git a/test/web/activity_pub/mrf/mediaproxy_warming_policy_test.exs b/test/pleroma/web/activity_pub/mrf/media_proxy_warming_policy_test.exs
diff --git a/test/web/activity_pub/mrf/mention_policy_test.exs b/test/pleroma/web/activity_pub/mrf/mention_policy_test.exs
diff --git a/test/web/activity_pub/mrf/no_placeholder_text_policy_test.exs b/test/pleroma/web/activity_pub/mrf/no_placeholder_text_policy_test.exs
diff --git a/test/web/activity_pub/mrf/normalize_markup_test.exs b/test/pleroma/web/activity_pub/mrf/normalize_markup_test.exs
diff --git a/test/web/activity_pub/mrf/object_age_policy_test.exs b/test/pleroma/web/activity_pub/mrf/object_age_policy_test.exs
diff --git a/test/web/activity_pub/mrf/reject_non_public_test.exs b/test/pleroma/web/activity_pub/mrf/reject_non_public_test.exs
diff --git a/test/web/activity_pub/mrf/simple_policy_test.exs b/test/pleroma/web/activity_pub/mrf/simple_policy_test.exs
diff --git a/test/web/activity_pub/mrf/steal_emoji_policy_test.exs b/test/pleroma/web/activity_pub/mrf/steal_emoji_policy_test.exs
diff --git a/test/web/activity_pub/mrf/subchain_policy_test.exs b/test/pleroma/web/activity_pub/mrf/subchain_policy_test.exs
diff --git a/test/web/activity_pub/mrf/tag_policy_test.exs b/test/pleroma/web/activity_pub/mrf/tag_policy_test.exs
diff --git a/test/web/activity_pub/mrf/user_allowlist_policy_test.exs b/test/pleroma/web/activity_pub/mrf/user_allow_list_policy_test.exs
diff --git a/test/web/activity_pub/mrf/vocabulary_policy_test.exs b/test/pleroma/web/activity_pub/mrf/vocabulary_policy_test.exs
diff --git a/test/web/activity_pub/mrf/mrf_test.exs b/test/pleroma/web/activity_pub/mrf_test.exs
diff --git a/test/web/activity_pub/pipeline_test.exs b/test/pleroma/web/activity_pub/pipeline_test.exs
diff --git a/test/web/activity_pub/publisher_test.exs b/test/pleroma/web/activity_pub/publisher_test.exs
diff --git a/test/web/activity_pub/relay_test.exs b/test/pleroma/web/activity_pub/relay_test.exs
diff --git a/test/web/activity_pub/side_effects_test.exs b/test/pleroma/web/activity_pub/side_effects_test.exs
diff --git a/test/web/activity_pub/transmogrifier/announce_handling_test.exs b/test/pleroma/web/activity_pub/transmogrifier/announce_handling_test.exs
diff --git a/test/web/activity_pub/transmogrifier/chat_message_test.exs b/test/pleroma/web/activity_pub/transmogrifier/chat_message_test.exs
diff --git a/test/web/activity_pub/transmogrifier/delete_handling_test.exs b/test/pleroma/web/activity_pub/transmogrifier/delete_handling_test.exs
diff --git a/test/web/activity_pub/transmogrifier/emoji_react_handling_test.exs b/test/pleroma/web/activity_pub/transmogrifier/emoji_react_handling_test.exs
diff --git a/test/web/activity_pub/transmogrifier/follow_handling_test.exs b/test/pleroma/web/activity_pub/transmogrifier/follow_handling_test.exs
diff --git a/test/web/activity_pub/transmogrifier/like_handling_test.exs b/test/pleroma/web/activity_pub/transmogrifier/like_handling_test.exs
diff --git a/test/web/activity_pub/transmogrifier/undo_handling_test.exs b/test/pleroma/web/activity_pub/transmogrifier/undo_handling_test.exs
diff --git a/test/web/activity_pub/transmogrifier_test.exs b/test/pleroma/web/activity_pub/transmogrifier_test.exs
diff --git a/test/web/activity_pub/utils_test.exs b/test/pleroma/web/activity_pub/utils_test.exs
diff --git a/test/web/activity_pub/views/object_view_test.exs b/test/pleroma/web/activity_pub/views/object_view_test.exs
diff --git a/test/web/activity_pub/views/user_view_test.exs b/test/pleroma/web/activity_pub/views/user_view_test.exs
diff --git a/test/web/activity_pub/visibilty_test.exs b/test/pleroma/web/activity_pub/visibility_test.exs
diff --git a/test/web/admin_api/controllers/admin_api_controller_test.exs b/test/pleroma/web/admin_api/controllers/admin_api_controller_test.exs
diff --git a/test/web/admin_api/controllers/config_controller_test.exs b/test/pleroma/web/admin_api/controllers/config_controller_test.exs
diff --git a/test/web/admin_api/controllers/invite_controller_test.exs b/test/pleroma/web/admin_api/controllers/invite_controller_test.exs
diff --git a/test/web/admin_api/controllers/media_proxy_cache_controller_test.exs b/test/pleroma/web/admin_api/controllers/media_proxy_cache_controller_test.exs
diff --git a/test/web/admin_api/controllers/oauth_app_controller_test.exs b/test/pleroma/web/admin_api/controllers/oauth_app_controller_test.exs
diff --git a/test/web/admin_api/controllers/relay_controller_test.exs b/test/pleroma/web/admin_api/controllers/relay_controller_test.exs
diff --git a/test/web/admin_api/controllers/report_controller_test.exs b/test/pleroma/web/admin_api/controllers/report_controller_test.exs
diff --git a/test/web/admin_api/controllers/status_controller_test.exs b/test/pleroma/web/admin_api/controllers/status_controller_test.exs
diff --git a/test/web/admin_api/search_test.exs b/test/pleroma/web/admin_api/search_test.exs
diff --git a/test/web/admin_api/views/report_view_test.exs b/test/pleroma/web/admin_api/views/report_view_test.exs
diff --git a/test/web/api_spec/schema_examples_test.exs b/test/pleroma/web/api_spec/schema_examples_test.exs
diff --git a/test/pleroma/web/auth/auth_controller_test.exs b/test/pleroma/web/auth/auth_controller_test.exs
@@ -0,0 +1,242 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.Auth.AuthControllerTest do
+ use Pleroma.Web.ConnCase
+
+ import Pleroma.Factory
+
+ describe "do_oauth_check" do
+ test "serves with proper OAuth token (fulfilling requested scopes)" do
+ %{conn: good_token_conn, user: user} = oauth_access(["read"])
+
+ assert %{"user_id" => user.id} ==
+ good_token_conn
+ |> get("/test/authenticated_api/do_oauth_check")
+ |> json_response(200)
+
+ # Unintended usage (:api) — use with :authenticated_api instead
+ assert %{"user_id" => user.id} ==
+ good_token_conn
+ |> get("/test/api/do_oauth_check")
+ |> json_response(200)
+ end
+
+ test "fails on no token / missing scope(s)" do
+ %{conn: bad_token_conn} = oauth_access(["irrelevant_scope"])
+
+ bad_token_conn
+ |> get("/test/authenticated_api/do_oauth_check")
+ |> json_response(403)
+
+ bad_token_conn
+ |> assign(:token, nil)
+ |> get("/test/api/do_oauth_check")
+ |> json_response(403)
+ end
+ end
+
+ describe "fallback_oauth_check" do
+ test "serves with proper OAuth token (fulfilling requested scopes)" do
+ %{conn: good_token_conn, user: user} = oauth_access(["read"])
+
+ assert %{"user_id" => user.id} ==
+ good_token_conn
+ |> get("/test/api/fallback_oauth_check")
+ |> json_response(200)
+
+ # Unintended usage (:authenticated_api) — use with :api instead
+ assert %{"user_id" => user.id} ==
+ good_token_conn
+ |> get("/test/authenticated_api/fallback_oauth_check")
+ |> json_response(200)
+ end
+
+ test "for :api on public instance, drops :user and renders on no token / missing scope(s)" do
+ clear_config([:instance, :public], true)
+
+ %{conn: bad_token_conn} = oauth_access(["irrelevant_scope"])
+
+ assert %{"user_id" => nil} ==
+ bad_token_conn
+ |> get("/test/api/fallback_oauth_check")
+ |> json_response(200)
+
+ assert %{"user_id" => nil} ==
+ bad_token_conn
+ |> assign(:token, nil)
+ |> get("/test/api/fallback_oauth_check")
+ |> json_response(200)
+ end
+
+ test "for :api on private instance, fails on no token / missing scope(s)" do
+ clear_config([:instance, :public], false)
+
+ %{conn: bad_token_conn} = oauth_access(["irrelevant_scope"])
+
+ bad_token_conn
+ |> get("/test/api/fallback_oauth_check")
+ |> json_response(403)
+
+ bad_token_conn
+ |> assign(:token, nil)
+ |> get("/test/api/fallback_oauth_check")
+ |> json_response(403)
+ end
+ end
+
+ describe "skip_oauth_check" do
+ test "for :authenticated_api, serves if :user is set (regardless of token / token scopes)" do
+ user = insert(:user)
+
+ assert %{"user_id" => user.id} ==
+ build_conn()
+ |> assign(:user, user)
+ |> get("/test/authenticated_api/skip_oauth_check")
+ |> json_response(200)
+
+ %{conn: bad_token_conn, user: user} = oauth_access(["irrelevant_scope"])
+
+ assert %{"user_id" => user.id} ==
+ bad_token_conn
+ |> get("/test/authenticated_api/skip_oauth_check")
+ |> json_response(200)
+ end
+
+ test "serves via :api on public instance if :user is not set" do
+ clear_config([:instance, :public], true)
+
+ assert %{"user_id" => nil} ==
+ build_conn()
+ |> get("/test/api/skip_oauth_check")
+ |> json_response(200)
+
+ build_conn()
+ |> get("/test/authenticated_api/skip_oauth_check")
+ |> json_response(403)
+ end
+
+ test "fails on private instance if :user is not set" do
+ clear_config([:instance, :public], false)
+
+ build_conn()
+ |> get("/test/api/skip_oauth_check")
+ |> json_response(403)
+
+ build_conn()
+ |> get("/test/authenticated_api/skip_oauth_check")
+ |> json_response(403)
+ end
+ end
+
+ describe "fallback_oauth_skip_publicity_check" do
+ test "serves with proper OAuth token (fulfilling requested scopes)" do
+ %{conn: good_token_conn, user: user} = oauth_access(["read"])
+
+ assert %{"user_id" => user.id} ==
+ good_token_conn
+ |> get("/test/api/fallback_oauth_skip_publicity_check")
+ |> json_response(200)
+
+ # Unintended usage (:authenticated_api)
+ assert %{"user_id" => user.id} ==
+ good_token_conn
+ |> get("/test/authenticated_api/fallback_oauth_skip_publicity_check")
+ |> json_response(200)
+ end
+
+ test "for :api on private / public instance, drops :user and renders on token issue" do
+ %{conn: bad_token_conn} = oauth_access(["irrelevant_scope"])
+
+ for is_public <- [true, false] do
+ clear_config([:instance, :public], is_public)
+
+ assert %{"user_id" => nil} ==
+ bad_token_conn
+ |> get("/test/api/fallback_oauth_skip_publicity_check")
+ |> json_response(200)
+
+ assert %{"user_id" => nil} ==
+ bad_token_conn
+ |> assign(:token, nil)
+ |> get("/test/api/fallback_oauth_skip_publicity_check")
+ |> json_response(200)
+ end
+ end
+ end
+
+ describe "skip_oauth_skip_publicity_check" do
+ test "for :authenticated_api, serves if :user is set (regardless of token / token scopes)" do
+ user = insert(:user)
+
+ assert %{"user_id" => user.id} ==
+ build_conn()
+ |> assign(:user, user)
+ |> get("/test/authenticated_api/skip_oauth_skip_publicity_check")
+ |> json_response(200)
+
+ %{conn: bad_token_conn, user: user} = oauth_access(["irrelevant_scope"])
+
+ assert %{"user_id" => user.id} ==
+ bad_token_conn
+ |> get("/test/authenticated_api/skip_oauth_skip_publicity_check")
+ |> json_response(200)
+ end
+
+ test "for :api, serves on private and public instances regardless of whether :user is set" do
+ user = insert(:user)
+
+ for is_public <- [true, false] do
+ clear_config([:instance, :public], is_public)
+
+ assert %{"user_id" => nil} ==
+ build_conn()
+ |> get("/test/api/skip_oauth_skip_publicity_check")
+ |> json_response(200)
+
+ assert %{"user_id" => user.id} ==
+ build_conn()
+ |> assign(:user, user)
+ |> get("/test/api/skip_oauth_skip_publicity_check")
+ |> json_response(200)
+ end
+ end
+ end
+
+ describe "missing_oauth_check_definition" do
+ def test_missing_oauth_check_definition_failure(endpoint, expected_error) do
+ %{conn: conn} = oauth_access(["read", "write", "follow", "push", "admin"])
+
+ assert %{"error" => expected_error} ==
+ conn
+ |> get(endpoint)
+ |> json_response(403)
+ end
+
+ test "fails if served via :authenticated_api" do
+ test_missing_oauth_check_definition_failure(
+ "/test/authenticated_api/missing_oauth_check_definition",
+ "Security violation: OAuth scopes check was neither handled nor explicitly skipped."
+ )
+ end
+
+ test "fails if served via :api and the instance is private" do
+ clear_config([:instance, :public], false)
+
+ test_missing_oauth_check_definition_failure(
+ "/test/api/missing_oauth_check_definition",
+ "This resource requires authentication."
+ )
+ end
+
+ test "succeeds with dropped :user if served via :api on public instance" do
+ %{conn: conn} = oauth_access(["read", "write", "follow", "push", "admin"])
+
+ assert %{"user_id" => nil} ==
+ conn
+ |> get("/test/api/missing_oauth_check_definition")
+ |> json_response(200)
+ end
+ end
+end
diff --git a/test/web/auth/authenticator_test.exs b/test/pleroma/web/auth/authenticator_test.exs
diff --git a/test/web/auth/basic_auth_test.exs b/test/pleroma/web/auth/basic_auth_test.exs
diff --git a/test/web/auth/pleroma_authenticator_test.exs b/test/pleroma/web/auth/pleroma_authenticator_test.exs
diff --git a/test/web/auth/totp_authenticator_test.exs b/test/pleroma/web/auth/totp_authenticator_test.exs
diff --git a/test/web/chat_channel_test.exs b/test/pleroma/web/chat_channel_test.exs
diff --git a/test/web/common_api/common_api_utils_test.exs b/test/pleroma/web/common_api/utils_test.exs
diff --git a/test/web/common_api/common_api_test.exs b/test/pleroma/web/common_api_test.exs
diff --git a/test/web/fallback_test.exs b/test/pleroma/web/fallback_test.exs
diff --git a/test/web/federator_test.exs b/test/pleroma/web/federator_test.exs
diff --git a/test/web/feed/tag_controller_test.exs b/test/pleroma/web/feed/tag_controller_test.exs
diff --git a/test/web/feed/user_controller_test.exs b/test/pleroma/web/feed/user_controller_test.exs
diff --git a/test/web/mastodon_api/controllers/account_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/account_controller_test.exs
diff --git a/test/web/mastodon_api/controllers/app_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/app_controller_test.exs
diff --git a/test/web/mastodon_api/controllers/auth_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/auth_controller_test.exs
diff --git a/test/web/mastodon_api/controllers/conversation_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/conversation_controller_test.exs
diff --git a/test/web/mastodon_api/controllers/custom_emoji_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/custom_emoji_controller_test.exs
diff --git a/test/web/mastodon_api/controllers/domain_block_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/domain_block_controller_test.exs
diff --git a/test/web/mastodon_api/controllers/filter_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/filter_controller_test.exs
diff --git a/test/web/mastodon_api/controllers/follow_request_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/follow_request_controller_test.exs
diff --git a/test/web/mastodon_api/controllers/instance_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/instance_controller_test.exs
diff --git a/test/web/mastodon_api/controllers/list_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/list_controller_test.exs
diff --git a/test/web/mastodon_api/controllers/marker_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/marker_controller_test.exs
diff --git a/test/web/mastodon_api/controllers/media_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/media_controller_test.exs
diff --git a/test/web/mastodon_api/controllers/notification_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/notification_controller_test.exs
diff --git a/test/web/mastodon_api/controllers/poll_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/poll_controller_test.exs
diff --git a/test/web/mastodon_api/controllers/report_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/report_controller_test.exs
diff --git a/test/web/mastodon_api/controllers/scheduled_activity_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/scheduled_activity_controller_test.exs
diff --git a/test/web/mastodon_api/controllers/search_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/search_controller_test.exs
diff --git a/test/web/mastodon_api/controllers/status_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/status_controller_test.exs
diff --git a/test/web/mastodon_api/controllers/subscription_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/subscription_controller_test.exs
diff --git a/test/web/mastodon_api/controllers/suggestion_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/suggestion_controller_test.exs
diff --git a/test/web/mastodon_api/controllers/timeline_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/timeline_controller_test.exs
diff --git a/test/pleroma/web/mastodon_api/masto_fe_controller_test.exs b/test/pleroma/web/mastodon_api/masto_fe_controller_test.exs
@@ -0,0 +1,85 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.MastodonAPI.MastoFEControllerTest do
+ use Pleroma.Web.ConnCase
+
+ alias Pleroma.Config
+ alias Pleroma.User
+
+ import Pleroma.Factory
+
+ setup do: clear_config([:instance, :public])
+
+ test "put settings", %{conn: conn} do
+ user = insert(:user)
+
+ conn =
+ conn
+ |> assign(:user, user)
+ |> assign(:token, insert(:oauth_token, user: user, scopes: ["write:accounts"]))
+ |> put("/api/web/settings", %{"data" => %{"programming" => "socks"}})
+
+ assert _result = json_response(conn, 200)
+
+ user = User.get_cached_by_ap_id(user.ap_id)
+ assert user.mastofe_settings == %{"programming" => "socks"}
+ end
+
+ describe "index/2 redirections" do
+ setup %{conn: conn} do
+ session_opts = [
+ store: :cookie,
+ key: "_test",
+ signing_salt: "cooldude"
+ ]
+
+ conn =
+ conn
+ |> Plug.Session.call(Plug.Session.init(session_opts))
+ |> fetch_session()
+
+ test_path = "/web/statuses/test"
+ %{conn: conn, path: test_path}
+ end
+
+ test "redirects not logged-in users to the login page", %{conn: conn, path: path} do
+ conn = get(conn, path)
+
+ assert conn.status == 302
+ assert redirected_to(conn) == "/web/login"
+ end
+
+ test "redirects not logged-in users to the login page on private instances", %{
+ conn: conn,
+ path: path
+ } do
+ Config.put([:instance, :public], false)
+
+ conn = get(conn, path)
+
+ assert conn.status == 302
+ assert redirected_to(conn) == "/web/login"
+ end
+
+ test "does not redirect logged in users to the login page", %{conn: conn, path: path} do
+ token = insert(:oauth_token, scopes: ["read"])
+
+ conn =
+ conn
+ |> assign(:user, token.user)
+ |> assign(:token, token)
+ |> get(path)
+
+ assert conn.status == 200
+ end
+
+ test "saves referer path to session", %{conn: conn, path: path} do
+ conn = get(conn, path)
+ return_to = Plug.Conn.get_session(conn, :return_to)
+
+ assert return_to == path
+ end
+ end
+end
diff --git a/test/web/mastodon_api/mastodon_api_controller_test.exs b/test/pleroma/web/mastodon_api/mastodon_api_controller_test.exs
diff --git a/test/web/mastodon_api/mastodon_api_test.exs b/test/pleroma/web/mastodon_api/mastodon_api_test.exs
diff --git a/test/pleroma/web/mastodon_api/update_credentials_test.exs b/test/pleroma/web/mastodon_api/update_credentials_test.exs
@@ -0,0 +1,529 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.MastodonAPI.UpdateCredentialsTest do
+ alias Pleroma.Repo
+ alias Pleroma.User
+
+ use Pleroma.Web.ConnCase
+
+ import Mock
+ import Pleroma.Factory
+
+ setup do: clear_config([:instance, :max_account_fields])
+
+ describe "updating credentials" do
+ setup do: oauth_access(["write:accounts"])
+ setup :request_content_type
+
+ test "sets user settings in a generic way", %{conn: conn} do
+ res_conn =
+ patch(conn, "/api/v1/accounts/update_credentials", %{
+ "pleroma_settings_store" => %{
+ pleroma_fe: %{
+ theme: "bla"
+ }
+ }
+ })
+
+ assert user_data = json_response_and_validate_schema(res_conn, 200)
+ assert user_data["pleroma"]["settings_store"] == %{"pleroma_fe" => %{"theme" => "bla"}}
+
+ user = Repo.get(User, user_data["id"])
+
+ res_conn =
+ conn
+ |> assign(:user, user)
+ |> patch("/api/v1/accounts/update_credentials", %{
+ "pleroma_settings_store" => %{
+ masto_fe: %{
+ theme: "bla"
+ }
+ }
+ })
+
+ assert user_data = json_response_and_validate_schema(res_conn, 200)
+
+ assert user_data["pleroma"]["settings_store"] ==
+ %{
+ "pleroma_fe" => %{"theme" => "bla"},
+ "masto_fe" => %{"theme" => "bla"}
+ }
+
+ user = Repo.get(User, user_data["id"])
+
+ clear_config([:instance, :federating], true)
+
+ with_mock Pleroma.Web.Federator,
+ publish: fn _activity -> :ok end do
+ res_conn =
+ conn
+ |> assign(:user, user)
+ |> patch("/api/v1/accounts/update_credentials", %{
+ "pleroma_settings_store" => %{
+ masto_fe: %{
+ theme: "blub"
+ }
+ }
+ })
+
+ assert user_data = json_response_and_validate_schema(res_conn, 200)
+
+ assert user_data["pleroma"]["settings_store"] ==
+ %{
+ "pleroma_fe" => %{"theme" => "bla"},
+ "masto_fe" => %{"theme" => "blub"}
+ }
+
+ assert_called(Pleroma.Web.Federator.publish(:_))
+ end
+ end
+
+ test "updates the user's bio", %{conn: conn} do
+ user2 = insert(:user)
+
+ raw_bio = "I drink #cofe with @#{user2.nickname}\n\nsuya.."
+
+ conn = patch(conn, "/api/v1/accounts/update_credentials", %{"note" => raw_bio})
+
+ assert user_data = json_response_and_validate_schema(conn, 200)
+
+ assert user_data["note"] ==
+ ~s(I drink <a class="hashtag" data-tag="cofe" href="http://localhost:4001/tag/cofe">#cofe</a> with <span class="h-card"><a class="u-url mention" data-user="#{
+ user2.id
+ }" href="#{user2.ap_id}" rel="ugc">@<span>#{user2.nickname}</span></a></span><br/><br/>suya..)
+
+ assert user_data["source"]["note"] == raw_bio
+
+ user = Repo.get(User, user_data["id"])
+
+ assert user.raw_bio == raw_bio
+ end
+
+ test "updates the user's locking status", %{conn: conn} do
+ conn = patch(conn, "/api/v1/accounts/update_credentials", %{locked: "true"})
+
+ assert user_data = json_response_and_validate_schema(conn, 200)
+ 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
+
+ conn = patch(conn, "/api/v1/accounts/update_credentials", %{allow_following_move: "false"})
+
+ assert refresh_record(user).allow_following_move == false
+ assert user_data = json_response_and_validate_schema(conn, 200)
+ assert user_data["pleroma"]["allow_following_move"] == false
+ end
+
+ test "updates the user's default scope", %{conn: conn} do
+ conn = patch(conn, "/api/v1/accounts/update_credentials", %{default_scope: "unlisted"})
+
+ assert user_data = json_response_and_validate_schema(conn, 200)
+ assert user_data["source"]["privacy"] == "unlisted"
+ end
+
+ test "updates the user's privacy", %{conn: conn} do
+ conn = patch(conn, "/api/v1/accounts/update_credentials", %{source: %{privacy: "unlisted"}})
+
+ assert user_data = json_response_and_validate_schema(conn, 200)
+ assert user_data["source"]["privacy"] == "unlisted"
+ end
+
+ test "updates the user's hide_followers status", %{conn: conn} do
+ conn = patch(conn, "/api/v1/accounts/update_credentials", %{hide_followers: "true"})
+
+ assert user_data = json_response_and_validate_schema(conn, 200)
+ assert user_data["pleroma"]["hide_followers"] == true
+ end
+
+ test "updates the user's discoverable status", %{conn: conn} do
+ assert %{"source" => %{"pleroma" => %{"discoverable" => true}}} =
+ conn
+ |> patch("/api/v1/accounts/update_credentials", %{discoverable: "true"})
+ |> json_response_and_validate_schema(:ok)
+
+ assert %{"source" => %{"pleroma" => %{"discoverable" => false}}} =
+ conn
+ |> patch("/api/v1/accounts/update_credentials", %{discoverable: "false"})
+ |> json_response_and_validate_schema(:ok)
+ end
+
+ test "updates the user's hide_followers_count and hide_follows_count", %{conn: conn} do
+ conn =
+ patch(conn, "/api/v1/accounts/update_credentials", %{
+ hide_followers_count: "true",
+ hide_follows_count: "true"
+ })
+
+ assert user_data = json_response_and_validate_schema(conn, 200)
+ assert user_data["pleroma"]["hide_followers_count"] == true
+ assert user_data["pleroma"]["hide_follows_count"] == true
+ end
+
+ test "updates the user's skip_thread_containment option", %{user: user, conn: conn} do
+ response =
+ conn
+ |> patch("/api/v1/accounts/update_credentials", %{skip_thread_containment: "true"})
+ |> json_response_and_validate_schema(200)
+
+ assert response["pleroma"]["skip_thread_containment"] == true
+ assert refresh_record(user).skip_thread_containment
+ end
+
+ test "updates the user's hide_follows status", %{conn: conn} do
+ conn = patch(conn, "/api/v1/accounts/update_credentials", %{hide_follows: "true"})
+
+ assert user_data = json_response_and_validate_schema(conn, 200)
+ assert user_data["pleroma"]["hide_follows"] == true
+ end
+
+ test "updates the user's hide_favorites status", %{conn: conn} do
+ conn = patch(conn, "/api/v1/accounts/update_credentials", %{hide_favorites: "true"})
+
+ assert user_data = json_response_and_validate_schema(conn, 200)
+ assert user_data["pleroma"]["hide_favorites"] == true
+ end
+
+ test "updates the user's show_role status", %{conn: conn} do
+ conn = patch(conn, "/api/v1/accounts/update_credentials", %{show_role: "false"})
+
+ assert user_data = json_response_and_validate_schema(conn, 200)
+ assert user_data["source"]["pleroma"]["show_role"] == false
+ end
+
+ test "updates the user's no_rich_text status", %{conn: conn} do
+ conn = patch(conn, "/api/v1/accounts/update_credentials", %{no_rich_text: "true"})
+
+ assert user_data = json_response_and_validate_schema(conn, 200)
+ assert user_data["source"]["pleroma"]["no_rich_text"] == true
+ end
+
+ test "updates the user's name", %{conn: conn} do
+ conn =
+ patch(conn, "/api/v1/accounts/update_credentials", %{"display_name" => "markorepairs"})
+
+ assert user_data = json_response_and_validate_schema(conn, 200)
+ assert user_data["display_name"] == "markorepairs"
+
+ update_activity = Repo.one(Pleroma.Activity)
+ assert update_activity.data["type"] == "Update"
+ assert update_activity.data["object"]["name"] == "markorepairs"
+ end
+
+ test "updates the user's avatar", %{user: user, conn: conn} do
+ new_avatar = %Plug.Upload{
+ content_type: "image/jpg",
+ path: Path.absname("test/fixtures/image.jpg"),
+ filename: "an_image.jpg"
+ }
+
+ assert user.avatar == %{}
+
+ 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
+ new_header = %Plug.Upload{
+ content_type: "image/jpg",
+ path: Path.absname("test/fixtures/image.jpg"),
+ filename: "an_image.jpg"
+ }
+
+ res = patch(conn, "/api/v1/accounts/update_credentials", %{"header" => new_header})
+
+ 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, user: user} do
+ new_header = %Plug.Upload{
+ content_type: "image/jpg",
+ path: Path.absname("test/fixtures/image.jpg"),
+ filename: "an_image.jpg"
+ }
+
+ res =
+ patch(conn, "/api/v1/accounts/update_credentials", %{
+ "pleroma_background_image" => new_header
+ })
+
+ 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
+ token1 = insert(:oauth_token, scopes: ["read"])
+ token2 = insert(:oauth_token, scopes: ["write", "follow"])
+
+ for token <- [token1, token2] do
+ conn =
+ build_conn()
+ |> put_req_header("content-type", "multipart/form-data")
+ |> put_req_header("authorization", "Bearer #{token.token}")
+ |> patch("/api/v1/accounts/update_credentials", %{})
+
+ if token == token1 do
+ assert %{"error" => "Insufficient permissions: write:accounts."} ==
+ json_response_and_validate_schema(conn, 403)
+ else
+ assert json_response_and_validate_schema(conn, 200)
+ end
+ end
+ end
+
+ test "updates profile emojos", %{user: user, conn: conn} do
+ note = "*sips :blank:*"
+ name = "I am :firefox:"
+
+ ret_conn =
+ patch(conn, "/api/v1/accounts/update_credentials", %{
+ "note" => note,
+ "display_name" => name
+ })
+
+ assert json_response_and_validate_schema(ret_conn, 200)
+
+ conn = get(conn, "/api/v1/accounts/#{user.id}")
+
+ assert user_data = json_response_and_validate_schema(conn, 200)
+
+ assert user_data["note"] == note
+ assert user_data["display_name"] == name
+ assert [%{"shortcode" => "blank"}, %{"shortcode" => "firefox"}] = user_data["emojis"]
+ end
+
+ test "update fields", %{conn: conn} do
+ fields = [
+ %{"name" => "<a href=\"http://google.com\">foo</a>", "value" => "<script>bar</script>"},
+ %{"name" => "link.io", "value" => "cofe.io"}
+ ]
+
+ account_data =
+ conn
+ |> patch("/api/v1/accounts/update_credentials", %{"fields_attributes" => fields})
+ |> json_response_and_validate_schema(200)
+
+ assert account_data["fields"] == [
+ %{"name" => "<a href=\"http://google.com\">foo</a>", "value" => "bar"},
+ %{
+ "name" => "link.io",
+ "value" => ~S(<a href="http://cofe.io" rel="ugc">cofe.io</a>)
+ }
+ ]
+
+ assert account_data["source"]["fields"] == [
+ %{
+ "name" => "<a href=\"http://google.com\">foo</a>",
+ "value" => "<script>bar</script>"
+ },
+ %{"name" => "link.io", "value" => "cofe.io"}
+ ]
+ 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 =
+ [
+ "fields_attributes[1][name]=link",
+ "fields_attributes[1][value]=http://cofe.io",
+ "fields_attributes[0][name]=foo",
+ "fields_attributes[0][value]=bar"
+ ]
+ |> Enum.join("&")
+
+ account =
+ conn
+ |> put_req_header("content-type", "application/x-www-form-urlencoded")
+ |> patch("/api/v1/accounts/update_credentials", fields)
+ |> json_response_and_validate_schema(200)
+
+ assert account["fields"] == [
+ %{"name" => "foo", "value" => "bar"},
+ %{
+ "name" => "link",
+ "value" => ~S(<a href="http://cofe.io" rel="ugc">http://cofe.io</a>)
+ }
+ ]
+
+ assert account["source"]["fields"] == [
+ %{"name" => "foo", "value" => "bar"},
+ %{"name" => "link", "value" => "http://cofe.io"}
+ ]
+ end
+
+ test "update fields with empty name", %{conn: conn} do
+ fields = [
+ %{"name" => "foo", "value" => ""},
+ %{"name" => "", "value" => "bar"}
+ ]
+
+ account =
+ conn
+ |> patch("/api/v1/accounts/update_credentials", %{"fields_attributes" => fields})
+ |> json_response_and_validate_schema(200)
+
+ assert account["fields"] == [
+ %{"name" => "foo", "value" => ""}
+ ]
+ end
+
+ test "update fields when invalid request", %{conn: conn} do
+ name_limit = Pleroma.Config.get([:instance, :account_field_name_length])
+ value_limit = Pleroma.Config.get([:instance, :account_field_value_length])
+
+ long_name = Enum.map(0..name_limit, fn _ -> "x" end) |> Enum.join()
+ long_value = Enum.map(0..value_limit, fn _ -> "x" end) |> Enum.join()
+
+ fields = [%{"name" => "foo", "value" => long_value}]
+
+ assert %{"error" => "Invalid request"} ==
+ conn
+ |> patch("/api/v1/accounts/update_credentials", %{"fields_attributes" => fields})
+ |> json_response_and_validate_schema(403)
+
+ fields = [%{"name" => long_name, "value" => "bar"}]
+
+ assert %{"error" => "Invalid request"} ==
+ conn
+ |> patch("/api/v1/accounts/update_credentials", %{"fields_attributes" => fields})
+ |> json_response_and_validate_schema(403)
+
+ Pleroma.Config.put([:instance, :max_account_fields], 1)
+
+ fields = [
+ %{"name" => "foo", "value" => "bar"},
+ %{"name" => "link", "value" => "cofe.io"}
+ ]
+
+ assert %{"error" => "Invalid request"} ==
+ conn
+ |> patch("/api/v1/accounts/update_credentials", %{"fields_attributes" => fields})
+ |> json_response_and_validate_schema(403)
+ end
+ end
+
+ describe "Mark account as bot" do
+ setup do: oauth_access(["write:accounts"])
+ setup :request_content_type
+
+ test "changing actor_type to Service makes account a bot", %{conn: conn} do
+ account =
+ conn
+ |> patch("/api/v1/accounts/update_credentials", %{actor_type: "Service"})
+ |> json_response_and_validate_schema(200)
+
+ assert account["bot"]
+ assert account["source"]["pleroma"]["actor_type"] == "Service"
+ end
+
+ test "changing actor_type to Person makes account a human", %{conn: conn} do
+ account =
+ conn
+ |> patch("/api/v1/accounts/update_credentials", %{actor_type: "Person"})
+ |> json_response_and_validate_schema(200)
+
+ refute account["bot"]
+ assert account["source"]["pleroma"]["actor_type"] == "Person"
+ end
+
+ test "changing actor_type to Application causes error", %{conn: conn} do
+ response =
+ conn
+ |> patch("/api/v1/accounts/update_credentials", %{actor_type: "Application"})
+ |> json_response_and_validate_schema(403)
+
+ assert %{"error" => "Invalid request"} == response
+ end
+
+ test "changing bot field to true changes actor_type to Service", %{conn: conn} do
+ account =
+ conn
+ |> patch("/api/v1/accounts/update_credentials", %{bot: "true"})
+ |> json_response_and_validate_schema(200)
+
+ assert account["bot"]
+ assert account["source"]["pleroma"]["actor_type"] == "Service"
+ end
+
+ test "changing bot field to false changes actor_type to Person", %{conn: conn} do
+ account =
+ conn
+ |> patch("/api/v1/accounts/update_credentials", %{bot: "false"})
+ |> json_response_and_validate_schema(200)
+
+ refute account["bot"]
+ assert account["source"]["pleroma"]["actor_type"] == "Person"
+ end
+
+ test "actor_type field has a higher priority than bot", %{conn: conn} do
+ account =
+ conn
+ |> patch("/api/v1/accounts/update_credentials", %{
+ actor_type: "Person",
+ bot: "true"
+ })
+ |> json_response_and_validate_schema(200)
+
+ refute account["bot"]
+ assert account["source"]["pleroma"]["actor_type"] == "Person"
+ end
+ end
+end
diff --git a/test/web/mastodon_api/views/account_view_test.exs b/test/pleroma/web/mastodon_api/views/account_view_test.exs
diff --git a/test/web/mastodon_api/views/conversation_view_test.exs b/test/pleroma/web/mastodon_api/views/conversation_view_test.exs
diff --git a/test/web/mastodon_api/views/list_view_test.exs b/test/pleroma/web/mastodon_api/views/list_view_test.exs
diff --git a/test/web/mastodon_api/views/marker_view_test.exs b/test/pleroma/web/mastodon_api/views/marker_view_test.exs
diff --git a/test/web/mastodon_api/views/notification_view_test.exs b/test/pleroma/web/mastodon_api/views/notification_view_test.exs
diff --git a/test/web/mastodon_api/views/poll_view_test.exs b/test/pleroma/web/mastodon_api/views/poll_view_test.exs
diff --git a/test/web/mastodon_api/views/scheduled_activity_view_test.exs b/test/pleroma/web/mastodon_api/views/scheduled_activity_view_test.exs
diff --git a/test/web/mastodon_api/views/status_view_test.exs b/test/pleroma/web/mastodon_api/views/status_view_test.exs
diff --git a/test/web/mastodon_api/views/subscription_view_test.exs b/test/pleroma/web/mastodon_api/views/subscription_view_test.exs
diff --git a/test/pleroma/web/media_proxy/invalidation/http_test.exs b/test/pleroma/web/media_proxy/invalidation/http_test.exs
@@ -0,0 +1,43 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.MediaProxy.Invalidation.HttpTest do
+ use ExUnit.Case
+ alias Pleroma.Web.MediaProxy.Invalidation
+
+ import ExUnit.CaptureLog
+ import Tesla.Mock
+
+ setup do
+ on_exit(fn -> Cachex.clear(:banned_urls_cache) end)
+ end
+
+ test "logs hasn't error message when request is valid" do
+ mock(fn
+ %{method: :purge, url: "http://example.com/media/example.jpg"} ->
+ %Tesla.Env{status: 200}
+ end)
+
+ refute capture_log(fn ->
+ assert Invalidation.Http.purge(
+ ["http://example.com/media/example.jpg"],
+ []
+ ) == {:ok, ["http://example.com/media/example.jpg"]}
+ end) =~ "Error while cache purge"
+ end
+
+ test "it write error message in logs when request invalid" do
+ mock(fn
+ %{method: :purge, url: "http://example.com/media/example1.jpg"} ->
+ %Tesla.Env{status: 404}
+ end)
+
+ assert capture_log(fn ->
+ assert Invalidation.Http.purge(
+ ["http://example.com/media/example1.jpg"],
+ []
+ ) == {:ok, ["http://example.com/media/example1.jpg"]}
+ end) =~ "Error while cache purge: url - http://example.com/media/example1.jpg"
+ end
+end
diff --git a/test/pleroma/web/media_proxy/invalidation/script_test.exs b/test/pleroma/web/media_proxy/invalidation/script_test.exs
@@ -0,0 +1,30 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.MediaProxy.Invalidation.ScriptTest do
+ use ExUnit.Case
+ alias Pleroma.Web.MediaProxy.Invalidation
+
+ import ExUnit.CaptureLog
+
+ setup do
+ on_exit(fn -> Cachex.clear(:banned_urls_cache) end)
+ end
+
+ test "it logger error when script not found" do
+ assert capture_log(fn ->
+ assert Invalidation.Script.purge(
+ ["http://example.com/media/example.jpg"],
+ script_path: "./example"
+ ) == {:error, "%ErlangError{original: :enoent}"}
+ end) =~ "Error while cache purge: %ErlangError{original: :enoent}"
+
+ capture_log(fn ->
+ assert Invalidation.Script.purge(
+ ["http://example.com/media/example.jpg"],
+ []
+ ) == {:error, "\"not found script path\""}
+ end)
+ end
+end
diff --git a/test/web/media_proxy/invalidation_test.exs b/test/pleroma/web/media_proxy/invalidation_test.exs
diff --git a/test/pleroma/web/media_proxy/media_proxy_controller_test.exs b/test/pleroma/web/media_proxy/media_proxy_controller_test.exs
@@ -0,0 +1,342 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.MediaProxy.MediaProxyControllerTest do
+ use Pleroma.Web.ConnCase
+
+ import Mock
+
+ alias Pleroma.Web.MediaProxy
+ alias Plug.Conn
+
+ setup do
+ on_exit(fn -> Cachex.clear(:banned_urls_cache) end)
+ end
+
+ describe "Media Proxy" 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 "it returns 404 when disabled", %{conn: conn} do
+ clear_config([:media_proxy, :enabled], false)
+
+ assert %Conn{
+ status: 404,
+ resp_body: "Not Found"
+ } = get(conn, "/proxy/hhgfh/eeeee")
+
+ assert %Conn{
+ status: 404,
+ resp_body: "Not Found"
+ } = get(conn, "/proxy/hhgfh/eeee/fff")
+ 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 "redirects to 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
+
+ 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
+
+ describe "Media Preview Proxy" do
+ def assert_dependencies_installed do
+ missing_dependencies = Pleroma.Helpers.MediaHelper.missing_dependencies()
+
+ assert missing_dependencies == [],
+ "Error: missing dependencies (please refer to `docs/installation`): #{
+ inspect(missing_dependencies)
+ }"
+ end
+
+ setup do
+ clear_config([:media_proxy, :enabled], true)
+ clear_config([:media_preview_proxy, :enabled], true)
+ clear_config([Pleroma.Web.Endpoint, :secret_key_base], "00000000000")
+
+ original_url = "https://google.fn/test.png"
+
+ [
+ url: MediaProxy.encode_preview_url(original_url),
+ media_proxy_url: MediaProxy.encode_url(original_url)
+ ]
+ end
+
+ test "returns 404 when media proxy is disabled", %{conn: conn} do
+ clear_config([:media_proxy, :enabled], false)
+
+ assert %Conn{
+ status: 404,
+ resp_body: "Not Found"
+ } = get(conn, "/proxy/preview/hhgfh/eeeee")
+
+ assert %Conn{
+ status: 404,
+ resp_body: "Not Found"
+ } = get(conn, "/proxy/preview/hhgfh/fff")
+ end
+
+ test "returns 404 when disabled", %{conn: conn} do
+ clear_config([:media_preview_proxy, :enabled], false)
+
+ assert %Conn{
+ status: 404,
+ resp_body: "Not Found"
+ } = get(conn, "/proxy/preview/hhgfh/eeeee")
+
+ assert %Conn{
+ status: 404,
+ resp_body: "Not Found"
+ } = get(conn, "/proxy/preview/hhgfh/fff")
+ 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/preview/hhgfh/eeee")
+
+ assert %Conn{
+ status: 403,
+ resp_body: "Forbidden"
+ } = get(conn, "/proxy/preview/hhgfh/eeee/fff")
+ end
+
+ test "redirects to 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
+
+ test "responds with 424 Failed Dependency if HEAD request to media proxy fails", %{
+ conn: conn,
+ url: url,
+ media_proxy_url: media_proxy_url
+ } do
+ Tesla.Mock.mock(fn
+ %{method: "head", url: ^media_proxy_url} ->
+ %Tesla.Env{status: 500, body: ""}
+ end)
+
+ response = get(conn, url)
+ assert response.status == 424
+ assert response.resp_body == "Can't fetch HTTP headers (HTTP 500)."
+ end
+
+ test "redirects to media proxy URI on unsupported content type", %{
+ conn: conn,
+ url: url,
+ media_proxy_url: media_proxy_url
+ } do
+ Tesla.Mock.mock(fn
+ %{method: "head", url: ^media_proxy_url} ->
+ %Tesla.Env{status: 200, body: "", headers: [{"content-type", "application/pdf"}]}
+ end)
+
+ response = get(conn, url)
+ assert response.status == 302
+ assert redirected_to(response) == media_proxy_url
+ end
+
+ test "with `static=true` and GIF image preview requested, responds with JPEG image", %{
+ conn: conn,
+ url: url,
+ media_proxy_url: media_proxy_url
+ } do
+ assert_dependencies_installed()
+
+ # Setting a high :min_content_length to ensure this scenario is not affected by its logic
+ clear_config([:media_preview_proxy, :min_content_length], 1_000_000_000)
+
+ Tesla.Mock.mock(fn
+ %{method: "head", url: ^media_proxy_url} ->
+ %Tesla.Env{
+ status: 200,
+ body: "",
+ headers: [{"content-type", "image/gif"}, {"content-length", "1001718"}]
+ }
+
+ %{method: :get, url: ^media_proxy_url} ->
+ %Tesla.Env{status: 200, body: File.read!("test/fixtures/image.gif")}
+ end)
+
+ response = get(conn, url <> "?static=true")
+
+ assert response.status == 200
+ assert Conn.get_resp_header(response, "content-type") == ["image/jpeg"]
+ assert response.resp_body != ""
+ end
+
+ test "with GIF image preview requested and no `static` param, redirects to media proxy URI",
+ %{
+ conn: conn,
+ url: url,
+ media_proxy_url: media_proxy_url
+ } do
+ Tesla.Mock.mock(fn
+ %{method: "head", url: ^media_proxy_url} ->
+ %Tesla.Env{status: 200, body: "", headers: [{"content-type", "image/gif"}]}
+ end)
+
+ response = get(conn, url)
+
+ assert response.status == 302
+ assert redirected_to(response) == media_proxy_url
+ end
+
+ test "with `static` param and non-GIF image preview requested, " <>
+ "redirects to media preview proxy URI without `static` param",
+ %{
+ conn: conn,
+ url: url,
+ media_proxy_url: media_proxy_url
+ } do
+ Tesla.Mock.mock(fn
+ %{method: "head", url: ^media_proxy_url} ->
+ %Tesla.Env{status: 200, body: "", headers: [{"content-type", "image/jpeg"}]}
+ end)
+
+ response = get(conn, url <> "?static=true")
+
+ assert response.status == 302
+ assert redirected_to(response) == url
+ end
+
+ test "with :min_content_length setting not matched by Content-Length header, " <>
+ "redirects to media proxy URI",
+ %{
+ conn: conn,
+ url: url,
+ media_proxy_url: media_proxy_url
+ } do
+ clear_config([:media_preview_proxy, :min_content_length], 100_000)
+
+ Tesla.Mock.mock(fn
+ %{method: "head", url: ^media_proxy_url} ->
+ %Tesla.Env{
+ status: 200,
+ body: "",
+ headers: [{"content-type", "image/gif"}, {"content-length", "5000"}]
+ }
+ end)
+
+ response = get(conn, url)
+
+ assert response.status == 302
+ assert redirected_to(response) == media_proxy_url
+ end
+
+ test "thumbnails PNG images into PNG", %{
+ conn: conn,
+ url: url,
+ media_proxy_url: media_proxy_url
+ } do
+ assert_dependencies_installed()
+
+ Tesla.Mock.mock(fn
+ %{method: "head", url: ^media_proxy_url} ->
+ %Tesla.Env{status: 200, body: "", headers: [{"content-type", "image/png"}]}
+
+ %{method: :get, url: ^media_proxy_url} ->
+ %Tesla.Env{status: 200, body: File.read!("test/fixtures/image.png")}
+ end)
+
+ response = get(conn, url)
+
+ assert response.status == 200
+ assert Conn.get_resp_header(response, "content-type") == ["image/png"]
+ assert response.resp_body != ""
+ end
+
+ test "thumbnails JPEG images into JPEG", %{
+ conn: conn,
+ url: url,
+ media_proxy_url: media_proxy_url
+ } do
+ assert_dependencies_installed()
+
+ Tesla.Mock.mock(fn
+ %{method: "head", url: ^media_proxy_url} ->
+ %Tesla.Env{status: 200, body: "", headers: [{"content-type", "image/jpeg"}]}
+
+ %{method: :get, url: ^media_proxy_url} ->
+ %Tesla.Env{status: 200, body: File.read!("test/fixtures/image.jpg")}
+ end)
+
+ response = get(conn, url)
+
+ assert response.status == 200
+ assert Conn.get_resp_header(response, "content-type") == ["image/jpeg"]
+ assert response.resp_body != ""
+ end
+
+ test "redirects to media proxy URI in case of thumbnailing error", %{
+ conn: conn,
+ url: url,
+ media_proxy_url: media_proxy_url
+ } do
+ Tesla.Mock.mock(fn
+ %{method: "head", url: ^media_proxy_url} ->
+ %Tesla.Env{status: 200, body: "", headers: [{"content-type", "image/jpeg"}]}
+
+ %{method: :get, url: ^media_proxy_url} ->
+ %Tesla.Env{status: 200, body: "<html><body>error</body></html>"}
+ end)
+
+ response = get(conn, url)
+
+ assert response.status == 302
+ assert redirected_to(response) == media_proxy_url
+ end
+ end
+end
diff --git a/test/pleroma/web/media_proxy_test.exs b/test/pleroma/web/media_proxy_test.exs
@@ -0,0 +1,234 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.MediaProxyTest do
+ use ExUnit.Case
+ use Pleroma.Tests.Helpers
+
+ alias Pleroma.Config
+ alias Pleroma.Web.Endpoint
+ alias Pleroma.Web.MediaProxy
+
+ defp decode_result(encoded) do
+ [_, "proxy", sig, base64 | _] = URI.parse(encoded).path |> String.split("/")
+ {:ok, decoded} = MediaProxy.decode_url(sig, base64)
+ decoded
+ end
+
+ describe "when enabled" do
+ setup do: clear_config([:media_proxy, :enabled], true)
+
+ test "ignores invalid url" do
+ assert MediaProxy.url(nil) == nil
+ assert MediaProxy.url("") == nil
+ end
+
+ test "ignores relative url" do
+ assert MediaProxy.url("/local") == "/local"
+ assert MediaProxy.url("/") == "/"
+ end
+
+ test "ignores local url" do
+ 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 = MediaProxy.url(url)
+
+ assert String.starts_with?(
+ encoded,
+ Config.get([:media_proxy, :base_url], Pleroma.Web.base_url())
+ )
+
+ assert String.ends_with?(encoded, "/logo.png")
+
+ assert decode_result(encoded) == url
+ end
+
+ test "encodes and decodes URL without a path" do
+ url = "https://pleroma.soykaf.com"
+ 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 = 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 = MediaProxy.url(url)
+ assert String.ends_with?(encoded, "/logo.png")
+ assert decode_result(encoded) == url
+ end
+
+ test "validates signature" do
+ encoded = MediaProxy.url("https://pleroma.social")
+
+ clear_config(
+ [Endpoint, :secret_key_base],
+ "00000000000000000000000000000000000000000000000"
+ )
+
+ [_, "proxy", sig, base64 | _] = URI.parse(encoded).path |> String.split("/")
+ assert MediaProxy.decode_url(sig, base64) == {:error, :invalid_signature}
+ end
+
+ def test_verify_request_path_and_url(request_path, url, expected_result) do
+ assert MediaProxy.verify_request_path_and_url(request_path, url) == expected_result
+
+ assert MediaProxy.verify_request_path_and_url(
+ %Plug.Conn{
+ params: %{"filename" => Path.basename(request_path)},
+ request_path: request_path
+ },
+ url
+ ) == expected_result
+ end
+
+ test "if first arg of `verify_request_path_and_url/2` is a Plug.Conn without \"filename\" " <>
+ "parameter, `verify_request_path_and_url/2` returns :ok " do
+ assert MediaProxy.verify_request_path_and_url(
+ %Plug.Conn{params: %{}, request_path: "/some/path"},
+ "https://instance.com/file.jpg"
+ ) == :ok
+
+ assert MediaProxy.verify_request_path_and_url(
+ %Plug.Conn{params: %{}, request_path: "/path/to/file.jpg"},
+ "https://instance.com/file.jpg"
+ ) == :ok
+ end
+
+ test "`verify_request_path_and_url/2` preserves the encoded or decoded path" do
+ test_verify_request_path_and_url(
+ "/Hello world.jpg",
+ "http://pleroma.social/Hello world.jpg",
+ :ok
+ )
+
+ test_verify_request_path_and_url(
+ "/Hello%20world.jpg",
+ "http://pleroma.social/Hello%20world.jpg",
+ :ok
+ )
+
+ test_verify_request_path_and_url(
+ "/my%2Flong%2Furl%2F2019%2F07%2FS.jpg",
+ "http://pleroma.social/my%2Flong%2Furl%2F2019%2F07%2FS.jpg",
+ :ok
+ )
+
+ test_verify_request_path_and_url(
+ # Note: `conn.request_path` returns encoded url
+ "/ANALYSE-DAI-_-LE-STABLECOIN-100-D%C3%89CENTRALIS%C3%89-BQ.jpg",
+ "https://mydomain.com/uploads/2019/07/ANALYSE-DAI-_-LE-STABLECOIN-100-DÉCENTRALISÉ-BQ.jpg",
+ :ok
+ )
+
+ test_verify_request_path_and_url(
+ "/my%2Flong%2Furl%2F2019%2F07%2FS",
+ "http://pleroma.social/my%2Flong%2Furl%2F2019%2F07%2FS.jpg",
+ {:wrong_filename, "my%2Flong%2Furl%2F2019%2F07%2FS.jpg"}
+ )
+ end
+
+ test "uses the configured base_url" do
+ base_url = "https://cache.pleroma.social"
+ clear_config([:media_proxy, :base_url], base_url)
+
+ url = "https://pleroma.soykaf.com/static/logo.png"
+ encoded = MediaProxy.url(url)
+
+ assert String.starts_with?(encoded, base_url)
+ end
+
+ # Some sites expect ASCII encoded characters in the URL to be preserved even if
+ # unnecessary.
+ # Issues: https://git.pleroma.social/pleroma/pleroma/issues/580
+ # https://git.pleroma.social/pleroma/pleroma/issues/1055
+ test "preserve ASCII encoding" 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 = MediaProxy.url(url)
+ assert decode_result(encoded) == url
+ end
+
+ # This includes unsafe/reserved characters which are not interpreted as part of the URL
+ # and would otherwise have to be ASCII encoded. It is our role to ensure the proxied URL
+ # is unmodified, so we are testing these characters anyway.
+ test "preserve non-unicode characters per RFC3986" do
+ url =
+ "https://pleroma.com/ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890-._~:/?#[]@!$&'()*+,;=|^`{}"
+
+ encoded = MediaProxy.url(url)
+ assert decode_result(encoded) == url
+ end
+
+ test "preserve unicode characters" do
+ url = "https://ko.wikipedia.org/wiki/위키백과:대문"
+
+ encoded = MediaProxy.url(url)
+ assert decode_result(encoded) == url
+ end
+ end
+
+ describe "when disabled" do
+ setup do: clear_config([:media_proxy, :enabled], false)
+
+ test "does not encode remote urls" do
+ assert MediaProxy.url("https://google.fr") == "https://google.fr"
+ end
+ end
+
+ describe "whitelist" do
+ setup do: clear_config([:media_proxy, :enabled], true)
+
+ test "mediaproxy whitelist" do
+ 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 = MediaProxy.url(url)
+ assert unencoded == url
+ end
+
+ test "does not change whitelisted urls" do
+ clear_config([:media_proxy, :whitelist], ["mycdn.akamai.com"])
+ clear_config([:media_proxy, :base_url], "https://cache.pleroma.social")
+
+ media_url = "https://mycdn.akamai.com"
+
+ url = "#{media_url}/static/logo.png"
+ 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"
+ clear_config([Pleroma.Upload, :base_url], media_url)
+
+ url = "#{media_url}/static/logo.png"
+ encoded = MediaProxy.url(url)
+
+ assert String.starts_with?(encoded, media_url)
+ end
+ end
+end
diff --git a/test/pleroma/web/metadata/player_view_test.exs b/test/pleroma/web/metadata/player_view_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.Metadata.PlayerViewTest do
+ use Pleroma.DataCase
+
+ alias Pleroma.Web.Metadata.PlayerView
+
+ test "it renders audio tag" do
+ res =
+ PlayerView.render(
+ "player.html",
+ %{"mediaType" => "audio", "href" => "test-href"}
+ )
+ |> Phoenix.HTML.safe_to_string()
+
+ assert res ==
+ "<audio controls><source src=\"test-href\" type=\"audio\">Your browser does not support audio playback.</audio>"
+ end
+
+ test "it renders videos tag" do
+ res =
+ PlayerView.render(
+ "player.html",
+ %{"mediaType" => "video", "href" => "test-href"}
+ )
+ |> Phoenix.HTML.safe_to_string()
+
+ assert res ==
+ "<video controls loop><source src=\"test-href\" type=\"video\">Your browser does not support video playback.</video>"
+ end
+end
diff --git a/test/pleroma/web/metadata/providers/feed_test.exs b/test/pleroma/web/metadata/providers/feed_test.exs
@@ -0,0 +1,18 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.Metadata.Providers.FeedTest do
+ use Pleroma.DataCase
+ import Pleroma.Factory
+ alias Pleroma.Web.Metadata.Providers.Feed
+
+ test "it renders a link to user's atom feed" do
+ user = insert(:user, nickname: "lain")
+
+ assert Feed.build_tags(%{user: user}) == [
+ {:link,
+ [rel: "alternate", type: "application/atom+xml", href: "/users/lain/feed.atom"], []}
+ ]
+ end
+end
diff --git a/test/pleroma/web/metadata/providers/open_graph_test.exs b/test/pleroma/web/metadata/providers/open_graph_test.exs
@@ -0,0 +1,96 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.Metadata.Providers.OpenGraphTest do
+ use Pleroma.DataCase
+ import Pleroma.Factory
+ alias Pleroma.Web.Metadata.Providers.OpenGraph
+
+ setup do: clear_config([Pleroma.Web.Metadata, :unfurl_nsfw])
+
+ test "it renders all supported types of attachments and skips unknown types" do
+ user = insert(:user)
+
+ note =
+ insert(:note, %{
+ data: %{
+ "actor" => user.ap_id,
+ "tag" => [],
+ "id" => "https://pleroma.gov/objects/whatever",
+ "content" => "pleroma in a nutshell",
+ "attachment" => [
+ %{
+ "url" => [
+ %{"mediaType" => "image/png", "href" => "https://pleroma.gov/tenshi.png"}
+ ]
+ },
+ %{
+ "url" => [
+ %{
+ "mediaType" => "application/octet-stream",
+ "href" => "https://pleroma.gov/fqa/badapple.sfc"
+ }
+ ]
+ },
+ %{
+ "url" => [
+ %{"mediaType" => "video/webm", "href" => "https://pleroma.gov/about/juche.webm"}
+ ]
+ },
+ %{
+ "url" => [
+ %{
+ "mediaType" => "audio/basic",
+ "href" => "http://www.gnu.org/music/free-software-song.au"
+ }
+ ]
+ }
+ ]
+ }
+ })
+
+ result = OpenGraph.build_tags(%{object: note, url: note.data["id"], user: user})
+
+ assert Enum.all?(
+ [
+ {:meta, [property: "og:image", content: "https://pleroma.gov/tenshi.png"], []},
+ {:meta,
+ [property: "og:audio", content: "http://www.gnu.org/music/free-software-song.au"],
+ []},
+ {:meta, [property: "og:video", content: "https://pleroma.gov/about/juche.webm"],
+ []}
+ ],
+ fn element -> element in result end
+ )
+ end
+
+ test "it does not render attachments if post is nsfw" do
+ Pleroma.Config.put([Pleroma.Web.Metadata, :unfurl_nsfw], false)
+ user = insert(:user, avatar: %{"url" => [%{"href" => "https://pleroma.gov/tenshi.png"}]})
+
+ note =
+ insert(:note, %{
+ data: %{
+ "actor" => user.ap_id,
+ "id" => "https://pleroma.gov/objects/whatever",
+ "content" => "#cuteposting #nsfw #hambaga",
+ "tag" => ["cuteposting", "nsfw", "hambaga"],
+ "sensitive" => true,
+ "attachment" => [
+ %{
+ "url" => [
+ %{"mediaType" => "image/png", "href" => "https://misskey.microsoft/corndog.png"}
+ ]
+ }
+ ]
+ }
+ })
+
+ result = OpenGraph.build_tags(%{object: note, url: note.data["id"], user: user})
+
+ assert {:meta, [property: "og:image", content: "https://pleroma.gov/tenshi.png"], []} in result
+
+ refute {:meta, [property: "og:image", content: "https://misskey.microsoft/corndog.png"], []} in result
+ end
+end
diff --git a/test/pleroma/web/metadata/providers/rel_me_test.exs b/test/pleroma/web/metadata/providers/rel_me_test.exs
@@ -0,0 +1,21 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.Metadata.Providers.RelMeTest do
+ use Pleroma.DataCase
+ import Pleroma.Factory
+ alias Pleroma.Web.Metadata.Providers.RelMe
+
+ test "it renders all links with rel='me' from user bio" do
+ bio =
+ ~s(<a href="https://some-link.com">https://some-link.com</a> <a rel="me" href="https://another-link.com">https://another-link.com</a> <link href="http://some.com"> <link rel="me" href="http://some3.com">)
+
+ user = insert(:user, %{bio: bio})
+
+ assert RelMe.build_tags(%{user: user}) == [
+ {:link, [rel: "me", href: "http://some3.com"], []},
+ {:link, [rel: "me", href: "https://another-link.com"], []}
+ ]
+ end
+end
diff --git a/test/pleroma/web/metadata/providers/restrict_indexing_test.exs b/test/pleroma/web/metadata/providers/restrict_indexing_test.exs
@@ -0,0 +1,27 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.Metadata.Providers.RestrictIndexingTest do
+ use ExUnit.Case, async: true
+
+ describe "build_tags/1" do
+ test "for remote user" do
+ assert Pleroma.Web.Metadata.Providers.RestrictIndexing.build_tags(%{
+ user: %Pleroma.User{local: false}
+ }) == [{:meta, [name: "robots", content: "noindex, noarchive"], []}]
+ end
+
+ test "for local user" do
+ assert Pleroma.Web.Metadata.Providers.RestrictIndexing.build_tags(%{
+ user: %Pleroma.User{local: true, discoverable: true}
+ }) == []
+ end
+
+ test "for local user when discoverable is false" do
+ assert Pleroma.Web.Metadata.Providers.RestrictIndexing.build_tags(%{
+ user: %Pleroma.User{local: true, discoverable: false}
+ }) == [{:meta, [name: "robots", content: "noindex, noarchive"], []}]
+ end
+ end
+end
diff --git a/test/pleroma/web/metadata/providers/twitter_card_test.exs b/test/pleroma/web/metadata/providers/twitter_card_test.exs
@@ -0,0 +1,150 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.Metadata.Providers.TwitterCardTest do
+ use Pleroma.DataCase
+ import Pleroma.Factory
+
+ alias Pleroma.User
+ alias Pleroma.Web.CommonAPI
+ alias Pleroma.Web.Endpoint
+ alias Pleroma.Web.Metadata.Providers.TwitterCard
+ alias Pleroma.Web.Metadata.Utils
+ alias Pleroma.Web.Router
+
+ setup do: clear_config([Pleroma.Web.Metadata, :unfurl_nsfw])
+
+ test "it renders twitter card for user info" do
+ user = insert(:user, name: "Jimmy Hendriks", bio: "born 19 March 1994")
+ avatar_url = Utils.attachment_url(User.avatar_url(user))
+ res = TwitterCard.build_tags(%{user: user})
+
+ assert res == [
+ {:meta, [property: "twitter:title", content: Utils.user_name_string(user)], []},
+ {:meta, [property: "twitter:description", content: "born 19 March 1994"], []},
+ {:meta, [property: "twitter:image", content: avatar_url], []},
+ {:meta, [property: "twitter:card", content: "summary"], []}
+ ]
+ end
+
+ test "it uses summary twittercard if post has no attachment" do
+ user = insert(:user, name: "Jimmy Hendriks", bio: "born 19 March 1994")
+ {:ok, activity} = CommonAPI.post(user, %{status: "HI"})
+
+ note =
+ insert(:note, %{
+ data: %{
+ "actor" => user.ap_id,
+ "tag" => [],
+ "id" => "https://pleroma.gov/objects/whatever",
+ "content" => "pleroma in a nutshell"
+ }
+ })
+
+ result = TwitterCard.build_tags(%{object: note, user: user, activity_id: activity.id})
+
+ assert [
+ {:meta, [property: "twitter:title", content: Utils.user_name_string(user)], []},
+ {:meta, [property: "twitter:description", content: "“pleroma in a nutshell”"], []},
+ {:meta, [property: "twitter:image", content: "http://localhost:4001/images/avi.png"],
+ []},
+ {:meta, [property: "twitter:card", content: "summary"], []}
+ ] == result
+ end
+
+ test "it renders avatar not attachment if post is nsfw and unfurl_nsfw is disabled" do
+ Pleroma.Config.put([Pleroma.Web.Metadata, :unfurl_nsfw], false)
+ user = insert(:user, name: "Jimmy Hendriks", bio: "born 19 March 1994")
+ {:ok, activity} = CommonAPI.post(user, %{status: "HI"})
+
+ note =
+ insert(:note, %{
+ data: %{
+ "actor" => user.ap_id,
+ "tag" => [],
+ "id" => "https://pleroma.gov/objects/whatever",
+ "content" => "pleroma in a nutshell",
+ "sensitive" => true,
+ "attachment" => [
+ %{
+ "url" => [%{"mediaType" => "image/png", "href" => "https://pleroma.gov/tenshi.png"}]
+ },
+ %{
+ "url" => [
+ %{
+ "mediaType" => "application/octet-stream",
+ "href" => "https://pleroma.gov/fqa/badapple.sfc"
+ }
+ ]
+ },
+ %{
+ "url" => [
+ %{"mediaType" => "video/webm", "href" => "https://pleroma.gov/about/juche.webm"}
+ ]
+ }
+ ]
+ }
+ })
+
+ result = TwitterCard.build_tags(%{object: note, user: user, activity_id: activity.id})
+
+ assert [
+ {:meta, [property: "twitter:title", content: Utils.user_name_string(user)], []},
+ {:meta, [property: "twitter:description", content: "“pleroma in a nutshell”"], []},
+ {:meta, [property: "twitter:image", content: "http://localhost:4001/images/avi.png"],
+ []},
+ {:meta, [property: "twitter:card", content: "summary"], []}
+ ] == result
+ end
+
+ test "it renders supported types of attachments and skips unknown types" do
+ user = insert(:user, name: "Jimmy Hendriks", bio: "born 19 March 1994")
+ {:ok, activity} = CommonAPI.post(user, %{status: "HI"})
+
+ note =
+ insert(:note, %{
+ data: %{
+ "actor" => user.ap_id,
+ "tag" => [],
+ "id" => "https://pleroma.gov/objects/whatever",
+ "content" => "pleroma in a nutshell",
+ "attachment" => [
+ %{
+ "url" => [%{"mediaType" => "image/png", "href" => "https://pleroma.gov/tenshi.png"}]
+ },
+ %{
+ "url" => [
+ %{
+ "mediaType" => "application/octet-stream",
+ "href" => "https://pleroma.gov/fqa/badapple.sfc"
+ }
+ ]
+ },
+ %{
+ "url" => [
+ %{"mediaType" => "video/webm", "href" => "https://pleroma.gov/about/juche.webm"}
+ ]
+ }
+ ]
+ }
+ })
+
+ result = TwitterCard.build_tags(%{object: note, user: user, activity_id: activity.id})
+
+ assert [
+ {:meta, [property: "twitter:title", content: Utils.user_name_string(user)], []},
+ {:meta, [property: "twitter:description", content: "“pleroma in a nutshell”"], []},
+ {:meta, [property: "twitter:card", content: "summary_large_image"], []},
+ {:meta, [property: "twitter:player", content: "https://pleroma.gov/tenshi.png"], []},
+ {:meta, [property: "twitter:card", content: "player"], []},
+ {:meta,
+ [
+ property: "twitter:player",
+ content: Router.Helpers.o_status_url(Endpoint, :notice_player, activity.id)
+ ], []},
+ {:meta, [property: "twitter:player:width", content: "480"], []},
+ {:meta, [property: "twitter:player:height", content: "480"], []}
+ ] == result
+ end
+end
diff --git a/test/pleroma/web/metadata/utils_test.exs b/test/pleroma/web/metadata/utils_test.exs
@@ -0,0 +1,32 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.Metadata.UtilsTest do
+ use Pleroma.DataCase
+ import Pleroma.Factory
+ alias Pleroma.Web.Metadata.Utils
+
+ describe "scrub_html_and_truncate/1" do
+ test "it returns text without encode HTML" do
+ user = insert(:user)
+
+ note =
+ insert(:note, %{
+ data: %{
+ "actor" => user.ap_id,
+ "id" => "https://pleroma.gov/objects/whatever",
+ "content" => "Pleroma's really cool!"
+ }
+ })
+
+ assert Utils.scrub_html_and_truncate(note) == "Pleroma's really cool!"
+ end
+ end
+
+ describe "scrub_html_and_truncate/2" do
+ test "it returns text without encode HTML" do
+ assert Utils.scrub_html_and_truncate("Pleroma's really cool!") == "Pleroma's really cool!"
+ end
+ end
+end
diff --git a/test/pleroma/web/metadata_test.exs b/test/pleroma/web/metadata_test.exs
@@ -0,0 +1,49 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.MetadataTest do
+ use Pleroma.DataCase, async: true
+
+ import Pleroma.Factory
+
+ describe "restrict indexing remote users" do
+ test "for remote user" do
+ user = insert(:user, local: false)
+
+ assert Pleroma.Web.Metadata.build_tags(%{user: user}) =~
+ "<meta content=\"noindex, noarchive\" name=\"robots\">"
+ end
+
+ test "for local user" do
+ user = insert(:user, discoverable: false)
+
+ assert Pleroma.Web.Metadata.build_tags(%{user: user}) =~
+ "<meta content=\"noindex, noarchive\" name=\"robots\">"
+ end
+
+ test "for local user set to discoverable" do
+ user = insert(:user, discoverable: true)
+
+ refute Pleroma.Web.Metadata.build_tags(%{user: user}) =~
+ "<meta content=\"noindex, noarchive\" name=\"robots\">"
+ end
+ end
+
+ describe "no metadata for private instances" do
+ test "for local user set to discoverable" do
+ clear_config([:instance, :public], false)
+ user = insert(:user, bio: "This is my secret fedi account bio", discoverable: true)
+
+ assert "" = Pleroma.Web.Metadata.build_tags(%{user: user})
+ end
+
+ test "search exclusion metadata is included" do
+ clear_config([:instance, :public], false)
+ user = insert(:user, bio: "This is my secret fedi account bio", discoverable: false)
+
+ assert ~s(<meta content="noindex, noarchive" name="robots">) ==
+ Pleroma.Web.Metadata.build_tags(%{user: user})
+ end
+ end
+end
diff --git a/test/pleroma/web/mongoose_im_controller_test.exs b/test/pleroma/web/mongoose_im_controller_test.exs
@@ -0,0 +1,81 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.MongooseIMControllerTest do
+ use Pleroma.Web.ConnCase
+ import Pleroma.Factory
+
+ test "/user_exists", %{conn: conn} do
+ _user = insert(:user, nickname: "lain")
+ _remote_user = insert(:user, nickname: "alice", local: false)
+ _deactivated_user = insert(:user, nickname: "konata", deactivated: true)
+
+ res =
+ conn
+ |> get(mongoose_im_path(conn, :user_exists), user: "lain")
+ |> json_response(200)
+
+ assert res == true
+
+ res =
+ conn
+ |> get(mongoose_im_path(conn, :user_exists), user: "alice")
+ |> json_response(404)
+
+ assert res == false
+
+ res =
+ conn
+ |> get(mongoose_im_path(conn, :user_exists), user: "bob")
+ |> json_response(404)
+
+ assert res == false
+
+ res =
+ conn
+ |> get(mongoose_im_path(conn, :user_exists), user: "konata")
+ |> json_response(404)
+
+ assert res == false
+ end
+
+ test "/check_password", %{conn: conn} do
+ user = insert(:user, password_hash: Pbkdf2.hash_pwd_salt("cool"))
+
+ _deactivated_user =
+ insert(:user,
+ nickname: "konata",
+ deactivated: true,
+ password_hash: Pbkdf2.hash_pwd_salt("cool")
+ )
+
+ res =
+ conn
+ |> get(mongoose_im_path(conn, :check_password), user: user.nickname, pass: "cool")
+ |> json_response(200)
+
+ assert res == true
+
+ res =
+ conn
+ |> get(mongoose_im_path(conn, :check_password), user: user.nickname, pass: "uncool")
+ |> json_response(403)
+
+ assert res == false
+
+ res =
+ conn
+ |> get(mongoose_im_path(conn, :check_password), user: "konata", pass: "cool")
+ |> json_response(404)
+
+ assert res == false
+
+ res =
+ conn
+ |> get(mongoose_im_path(conn, :check_password), user: "nobody", pass: "cool")
+ |> json_response(404)
+
+ assert res == false
+ end
+end
diff --git a/test/pleroma/web/node_info_test.exs b/test/pleroma/web/node_info_test.exs
@@ -0,0 +1,188 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.NodeInfoTest do
+ use Pleroma.Web.ConnCase
+
+ import Pleroma.Factory
+
+ alias Pleroma.Config
+
+ setup do: clear_config([:mrf_simple])
+ setup do: clear_config(:instance)
+
+ test "GET /.well-known/nodeinfo", %{conn: conn} do
+ links =
+ conn
+ |> get("/.well-known/nodeinfo")
+ |> json_response(200)
+ |> Map.fetch!("links")
+
+ Enum.each(links, fn link ->
+ href = Map.fetch!(link, "href")
+
+ conn
+ |> get(href)
+ |> json_response(200)
+ end)
+ end
+
+ test "nodeinfo shows staff accounts", %{conn: conn} do
+ moderator = insert(:user, local: true, is_moderator: true)
+ admin = insert(:user, local: true, is_admin: true)
+
+ conn =
+ conn
+ |> get("/nodeinfo/2.1.json")
+
+ assert result = json_response(conn, 200)
+
+ assert moderator.ap_id in result["metadata"]["staffAccounts"]
+ assert admin.ap_id in result["metadata"]["staffAccounts"]
+ end
+
+ test "nodeinfo shows restricted nicknames", %{conn: conn} do
+ conn =
+ conn
+ |> get("/nodeinfo/2.1.json")
+
+ assert result = json_response(conn, 200)
+
+ assert Config.get([Pleroma.User, :restricted_nicknames]) ==
+ result["metadata"]["restrictedNicknames"]
+ end
+
+ test "returns software.repository field in nodeinfo 2.1", %{conn: conn} do
+ conn
+ |> get("/.well-known/nodeinfo")
+ |> json_response(200)
+
+ conn =
+ conn
+ |> get("/nodeinfo/2.1.json")
+
+ assert result = json_response(conn, 200)
+ assert Pleroma.Application.repository() == result["software"]["repository"]
+ end
+
+ test "returns fieldsLimits field", %{conn: conn} do
+ clear_config([:instance, :max_account_fields], 10)
+ clear_config([:instance, :max_remote_account_fields], 15)
+ clear_config([:instance, :account_field_name_length], 255)
+ clear_config([:instance, :account_field_value_length], 2048)
+
+ response =
+ conn
+ |> get("/nodeinfo/2.1.json")
+ |> json_response(:ok)
+
+ assert response["metadata"]["fieldsLimits"]["maxFields"] == 10
+ assert response["metadata"]["fieldsLimits"]["maxRemoteFields"] == 15
+ assert response["metadata"]["fieldsLimits"]["nameLength"] == 255
+ assert response["metadata"]["fieldsLimits"]["valueLength"] == 2048
+ end
+
+ test "it returns the safe_dm_mentions feature if enabled", %{conn: conn} do
+ clear_config([:instance, :safe_dm_mentions], true)
+
+ response =
+ conn
+ |> get("/nodeinfo/2.1.json")
+ |> json_response(:ok)
+
+ assert "safe_dm_mentions" in response["metadata"]["features"]
+
+ Config.put([:instance, :safe_dm_mentions], false)
+
+ response =
+ conn
+ |> get("/nodeinfo/2.1.json")
+ |> json_response(:ok)
+
+ refute "safe_dm_mentions" in response["metadata"]["features"]
+ end
+
+ describe "`metadata/federation/enabled`" do
+ setup do: clear_config([:instance, :federating])
+
+ test "it shows if federation is enabled/disabled", %{conn: conn} do
+ Config.put([:instance, :federating], true)
+
+ response =
+ conn
+ |> get("/nodeinfo/2.1.json")
+ |> json_response(:ok)
+
+ assert response["metadata"]["federation"]["enabled"] == true
+
+ Config.put([:instance, :federating], false)
+
+ response =
+ conn
+ |> get("/nodeinfo/2.1.json")
+ |> json_response(:ok)
+
+ assert response["metadata"]["federation"]["enabled"] == false
+ end
+ end
+
+ test "it shows default features flags", %{conn: conn} do
+ response =
+ conn
+ |> get("/nodeinfo/2.1.json")
+ |> json_response(:ok)
+
+ default_features = [
+ "pleroma_api",
+ "mastodon_api",
+ "mastodon_api_streaming",
+ "polls",
+ "pleroma_explicit_addressing",
+ "shareable_emoji_packs",
+ "multifetch",
+ "pleroma_emoji_reactions",
+ "pleroma:api/v1/notifications:include_types_filter",
+ "pleroma_chat_messages"
+ ]
+
+ assert MapSet.subset?(
+ MapSet.new(default_features),
+ MapSet.new(response["metadata"]["features"])
+ )
+ end
+
+ test "it shows MRF transparency data if enabled", %{conn: conn} do
+ clear_config([:mrf, :policies], [Pleroma.Web.ActivityPub.MRF.SimplePolicy])
+ clear_config([:mrf, :transparency], true)
+
+ simple_config = %{"reject" => ["example.com"]}
+ clear_config(:mrf_simple, simple_config)
+
+ response =
+ conn
+ |> get("/nodeinfo/2.1.json")
+ |> json_response(:ok)
+
+ assert response["metadata"]["federation"]["mrf_simple"] == simple_config
+ end
+
+ test "it performs exclusions from MRF transparency data if configured", %{conn: conn} do
+ clear_config([:mrf, :policies], [Pleroma.Web.ActivityPub.MRF.SimplePolicy])
+ clear_config([:mrf, :transparency], true)
+ clear_config([:mrf, :transparency_exclusions], ["other.site"])
+
+ simple_config = %{"reject" => ["example.com", "other.site"]}
+ clear_config(:mrf_simple, simple_config)
+
+ expected_config = %{"reject" => ["example.com"]}
+
+ response =
+ conn
+ |> get("/nodeinfo/2.1.json")
+ |> json_response(:ok)
+
+ assert response["metadata"]["federation"]["mrf_simple"] == expected_config
+ assert response["metadata"]["federation"]["exclusions"] == true
+ end
+end
diff --git a/test/pleroma/web/o_auth/app_test.exs b/test/pleroma/web/o_auth/app_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.OAuth.AppTest do
+ use Pleroma.DataCase
+
+ alias Pleroma.Web.OAuth.App
+ import Pleroma.Factory
+
+ describe "get_or_make/2" do
+ test "gets exist app" do
+ attrs = %{client_name: "Mastodon-Local", redirect_uris: "."}
+ app = insert(:oauth_app, Map.merge(attrs, %{scopes: ["read", "write"]}))
+ {:ok, %App{} = exist_app} = App.get_or_make(attrs, [])
+ assert exist_app == app
+ end
+
+ test "make app" do
+ attrs = %{client_name: "Mastodon-Local", redirect_uris: "."}
+ {:ok, %App{} = app} = App.get_or_make(attrs, ["write"])
+ assert app.scopes == ["write"]
+ end
+
+ test "gets exist app and updates scopes" do
+ attrs = %{client_name: "Mastodon-Local", redirect_uris: "."}
+ app = insert(:oauth_app, Map.merge(attrs, %{scopes: ["read", "write"]}))
+ {:ok, %App{} = exist_app} = App.get_or_make(attrs, ["read", "write", "follow", "push"])
+ assert exist_app.id == app.id
+ assert exist_app.scopes == ["read", "write", "follow", "push"]
+ end
+
+ test "has unique client_id" do
+ insert(:oauth_app, client_name: "", redirect_uris: "", client_id: "boop")
+
+ error =
+ catch_error(insert(:oauth_app, client_name: "", redirect_uris: "", client_id: "boop"))
+
+ assert %Ecto.ConstraintError{} = error
+ assert error.constraint == "apps_client_id_index"
+ assert error.type == :unique
+ end
+ end
+end
diff --git a/test/pleroma/web/o_auth/authorization_test.exs b/test/pleroma/web/o_auth/authorization_test.exs
@@ -0,0 +1,77 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.OAuth.AuthorizationTest do
+ use Pleroma.DataCase
+ alias Pleroma.Web.OAuth.App
+ alias Pleroma.Web.OAuth.Authorization
+ import Pleroma.Factory
+
+ setup do
+ {:ok, app} =
+ Repo.insert(
+ App.register_changeset(%App{}, %{
+ client_name: "client",
+ scopes: ["read", "write"],
+ redirect_uris: "url"
+ })
+ )
+
+ %{app: app}
+ end
+
+ test "create an authorization token for a valid app", %{app: app} do
+ user = insert(:user)
+
+ {:ok, auth1} = Authorization.create_authorization(app, user)
+ assert auth1.scopes == app.scopes
+
+ {:ok, auth2} = Authorization.create_authorization(app, user, ["read"])
+ assert auth2.scopes == ["read"]
+
+ for auth <- [auth1, auth2] do
+ assert auth.user_id == user.id
+ assert auth.app_id == app.id
+ assert String.length(auth.token) > 10
+ assert auth.used == false
+ end
+ end
+
+ test "use up a token", %{app: app} do
+ user = insert(:user)
+
+ {:ok, auth} = Authorization.create_authorization(app, user)
+
+ {:ok, auth} = Authorization.use_token(auth)
+
+ assert auth.used == true
+
+ assert {:error, "already used"} == Authorization.use_token(auth)
+
+ expired_auth = %Authorization{
+ user_id: user.id,
+ app_id: app.id,
+ valid_until: NaiveDateTime.add(NaiveDateTime.utc_now(), -10),
+ token: "mytoken",
+ used: false
+ }
+
+ {:ok, expired_auth} = Repo.insert(expired_auth)
+
+ assert {:error, "token expired"} == Authorization.use_token(expired_auth)
+ end
+
+ test "delete authorizations", %{app: app} do
+ user = insert(:user)
+
+ {:ok, auth} = Authorization.create_authorization(app, user)
+ {:ok, auth} = Authorization.use_token(auth)
+
+ Authorization.delete_user_authorizations(user)
+
+ {_, invalid} = Authorization.use_token(auth)
+
+ assert auth != invalid
+ end
+end
diff --git a/test/pleroma/web/o_auth/ldap_authorization_test.exs b/test/pleroma/web/o_auth/ldap_authorization_test.exs
@@ -0,0 +1,135 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.OAuth.LDAPAuthorizationTest do
+ use Pleroma.Web.ConnCase
+ alias Pleroma.Repo
+ alias Pleroma.Web.OAuth.Token
+ import Pleroma.Factory
+ import Mock
+
+ @skip if !Code.ensure_loaded?(:eldap), do: :skip
+
+ setup_all do: clear_config([:ldap, :enabled], true)
+
+ setup_all do: clear_config(Pleroma.Web.Auth.Authenticator, Pleroma.Web.Auth.LDAPAuthenticator)
+
+ @tag @skip
+ test "authorizes the existing user using LDAP credentials" do
+ password = "testpassword"
+ user = insert(:user, password_hash: Pbkdf2.hash_pwd_salt(password))
+ app = insert(:oauth_app, scopes: ["read", "write"])
+
+ host = Pleroma.Config.get([:ldap, :host]) |> to_charlist
+ port = Pleroma.Config.get([:ldap, :port])
+
+ with_mocks [
+ {:eldap, [],
+ [
+ open: fn [^host], [{:port, ^port}, {:ssl, false} | _] -> {:ok, self()} end,
+ simple_bind: fn _connection, _dn, ^password -> :ok end,
+ close: fn _connection ->
+ send(self(), :close_connection)
+ :ok
+ end
+ ]}
+ ] do
+ conn =
+ build_conn()
+ |> post("/oauth/token", %{
+ "grant_type" => "password",
+ "username" => user.nickname,
+ "password" => password,
+ "client_id" => app.client_id,
+ "client_secret" => app.client_secret
+ })
+
+ assert %{"access_token" => token} = json_response(conn, 200)
+
+ token = Repo.get_by(Token, token: token)
+
+ assert token.user_id == user.id
+ assert_received :close_connection
+ end
+ end
+
+ @tag @skip
+ test "creates a new user after successful LDAP authorization" do
+ password = "testpassword"
+ user = build(:user)
+ app = insert(:oauth_app, scopes: ["read", "write"])
+
+ host = Pleroma.Config.get([:ldap, :host]) |> to_charlist
+ port = Pleroma.Config.get([:ldap, :port])
+
+ with_mocks [
+ {:eldap, [],
+ [
+ open: fn [^host], [{:port, ^port}, {:ssl, false} | _] -> {:ok, self()} end,
+ simple_bind: fn _connection, _dn, ^password -> :ok end,
+ equalityMatch: fn _type, _value -> :ok end,
+ wholeSubtree: fn -> :ok end,
+ search: fn _connection, _options ->
+ {:ok, {:eldap_search_result, [{:eldap_entry, '', []}], []}}
+ end,
+ close: fn _connection ->
+ send(self(), :close_connection)
+ :ok
+ end
+ ]}
+ ] do
+ conn =
+ build_conn()
+ |> post("/oauth/token", %{
+ "grant_type" => "password",
+ "username" => user.nickname,
+ "password" => password,
+ "client_id" => app.client_id,
+ "client_secret" => app.client_secret
+ })
+
+ assert %{"access_token" => token} = json_response(conn, 200)
+
+ token = Repo.get_by(Token, token: token) |> Repo.preload(:user)
+
+ assert token.user.nickname == user.nickname
+ assert_received :close_connection
+ end
+ end
+
+ @tag @skip
+ test "disallow authorization for wrong LDAP credentials" do
+ password = "testpassword"
+ user = insert(:user, password_hash: Pbkdf2.hash_pwd_salt(password))
+ app = insert(:oauth_app, scopes: ["read", "write"])
+
+ host = Pleroma.Config.get([:ldap, :host]) |> to_charlist
+ port = Pleroma.Config.get([:ldap, :port])
+
+ with_mocks [
+ {:eldap, [],
+ [
+ open: fn [^host], [{:port, ^port}, {:ssl, false} | _] -> {:ok, self()} end,
+ simple_bind: fn _connection, _dn, ^password -> {:error, :invalidCredentials} end,
+ close: fn _connection ->
+ send(self(), :close_connection)
+ :ok
+ end
+ ]}
+ ] do
+ conn =
+ build_conn()
+ |> post("/oauth/token", %{
+ "grant_type" => "password",
+ "username" => user.nickname,
+ "password" => password,
+ "client_id" => app.client_id,
+ "client_secret" => app.client_secret
+ })
+
+ assert %{"error" => "Invalid credentials"} = json_response(conn, 400)
+ assert_received :close_connection
+ end
+ end
+end
diff --git a/test/pleroma/web/o_auth/mfa_controller_test.exs b/test/pleroma/web/o_auth/mfa_controller_test.exs
@@ -0,0 +1,306 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2018 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.OAuth.MFAControllerTest do
+ use Pleroma.Web.ConnCase
+ import Pleroma.Factory
+
+ alias Pleroma.MFA
+ alias Pleroma.MFA.BackupCodes
+ alias Pleroma.MFA.TOTP
+ alias Pleroma.Repo
+ alias Pleroma.Web.OAuth.Authorization
+ alias Pleroma.Web.OAuth.OAuthController
+
+ setup %{conn: conn} do
+ otp_secret = TOTP.generate_secret()
+
+ user =
+ insert(:user,
+ multi_factor_authentication_settings: %MFA.Settings{
+ enabled: true,
+ backup_codes: [Pbkdf2.hash_pwd_salt("test-code")],
+ totp: %MFA.Settings.TOTP{secret: otp_secret, confirmed: true}
+ }
+ )
+
+ app = insert(:oauth_app)
+ {:ok, conn: conn, user: user, app: app}
+ end
+
+ describe "show" do
+ setup %{conn: conn, user: user, app: app} do
+ mfa_token =
+ insert(:mfa_token,
+ user: user,
+ authorization: build(:oauth_authorization, app: app, scopes: ["write"])
+ )
+
+ {:ok, conn: conn, mfa_token: mfa_token}
+ end
+
+ test "GET /oauth/mfa renders mfa forms", %{conn: conn, mfa_token: mfa_token} do
+ conn =
+ get(
+ conn,
+ "/oauth/mfa",
+ %{
+ "mfa_token" => mfa_token.token,
+ "state" => "a_state",
+ "redirect_uri" => "http://localhost:8080/callback"
+ }
+ )
+
+ assert response = html_response(conn, 200)
+ assert response =~ "Two-factor authentication"
+ assert response =~ mfa_token.token
+ assert response =~ "http://localhost:8080/callback"
+ end
+
+ test "GET /oauth/mfa renders mfa recovery forms", %{conn: conn, mfa_token: mfa_token} do
+ conn =
+ get(
+ conn,
+ "/oauth/mfa",
+ %{
+ "mfa_token" => mfa_token.token,
+ "state" => "a_state",
+ "redirect_uri" => "http://localhost:8080/callback",
+ "challenge_type" => "recovery"
+ }
+ )
+
+ assert response = html_response(conn, 200)
+ assert response =~ "Two-factor recovery"
+ assert response =~ mfa_token.token
+ assert response =~ "http://localhost:8080/callback"
+ end
+ end
+
+ describe "verify" do
+ setup %{conn: conn, user: user, app: app} do
+ mfa_token =
+ insert(:mfa_token,
+ user: user,
+ authorization: build(:oauth_authorization, app: app, scopes: ["write"])
+ )
+
+ {:ok, conn: conn, user: user, mfa_token: mfa_token, app: app}
+ end
+
+ test "POST /oauth/mfa/verify, verify totp code", %{
+ conn: conn,
+ user: user,
+ mfa_token: mfa_token,
+ app: app
+ } do
+ otp_token = TOTP.generate_token(user.multi_factor_authentication_settings.totp.secret)
+
+ conn =
+ conn
+ |> post("/oauth/mfa/verify", %{
+ "mfa" => %{
+ "mfa_token" => mfa_token.token,
+ "challenge_type" => "totp",
+ "code" => otp_token,
+ "state" => "a_state",
+ "redirect_uri" => OAuthController.default_redirect_uri(app)
+ }
+ })
+
+ target = redirected_to(conn)
+ target_url = %URI{URI.parse(target) | query: nil} |> URI.to_string()
+ query = URI.parse(target).query |> URI.query_decoder() |> Map.new()
+ assert %{"state" => "a_state", "code" => code} = query
+ assert target_url == OAuthController.default_redirect_uri(app)
+ auth = Repo.get_by(Authorization, token: code)
+ assert auth.scopes == ["write"]
+ end
+
+ test "POST /oauth/mfa/verify, verify recovery code", %{
+ conn: conn,
+ mfa_token: mfa_token,
+ app: app
+ } do
+ conn =
+ conn
+ |> post("/oauth/mfa/verify", %{
+ "mfa" => %{
+ "mfa_token" => mfa_token.token,
+ "challenge_type" => "recovery",
+ "code" => "test-code",
+ "state" => "a_state",
+ "redirect_uri" => OAuthController.default_redirect_uri(app)
+ }
+ })
+
+ target = redirected_to(conn)
+ target_url = %URI{URI.parse(target) | query: nil} |> URI.to_string()
+ query = URI.parse(target).query |> URI.query_decoder() |> Map.new()
+ assert %{"state" => "a_state", "code" => code} = query
+ assert target_url == OAuthController.default_redirect_uri(app)
+ auth = Repo.get_by(Authorization, token: code)
+ assert auth.scopes == ["write"]
+ end
+ end
+
+ describe "challenge/totp" do
+ test "returns access token with valid code", %{conn: conn, user: user, app: app} do
+ otp_token = TOTP.generate_token(user.multi_factor_authentication_settings.totp.secret)
+
+ mfa_token =
+ insert(:mfa_token,
+ user: user,
+ authorization: build(:oauth_authorization, app: app, scopes: ["write"])
+ )
+
+ response =
+ conn
+ |> post("/oauth/mfa/challenge", %{
+ "mfa_token" => mfa_token.token,
+ "challenge_type" => "totp",
+ "code" => otp_token,
+ "client_id" => app.client_id,
+ "client_secret" => app.client_secret
+ })
+ |> json_response(:ok)
+
+ ap_id = user.ap_id
+
+ assert match?(
+ %{
+ "access_token" => _,
+ "expires_in" => 600,
+ "me" => ^ap_id,
+ "refresh_token" => _,
+ "scope" => "write",
+ "token_type" => "Bearer"
+ },
+ response
+ )
+ end
+
+ test "returns errors when mfa token invalid", %{conn: conn, user: user, app: app} do
+ otp_token = TOTP.generate_token(user.multi_factor_authentication_settings.totp.secret)
+
+ response =
+ conn
+ |> post("/oauth/mfa/challenge", %{
+ "mfa_token" => "XXX",
+ "challenge_type" => "totp",
+ "code" => otp_token,
+ "client_id" => app.client_id,
+ "client_secret" => app.client_secret
+ })
+ |> json_response(400)
+
+ assert response == %{"error" => "Invalid code"}
+ end
+
+ test "returns error when otp code is invalid", %{conn: conn, user: user, app: app} do
+ mfa_token = insert(:mfa_token, user: user)
+
+ response =
+ conn
+ |> post("/oauth/mfa/challenge", %{
+ "mfa_token" => mfa_token.token,
+ "challenge_type" => "totp",
+ "code" => "XXX",
+ "client_id" => app.client_id,
+ "client_secret" => app.client_secret
+ })
+ |> json_response(400)
+
+ assert response == %{"error" => "Invalid code"}
+ end
+
+ test "returns error when client credentails is wrong ", %{conn: conn, user: user} do
+ otp_token = TOTP.generate_token(user.multi_factor_authentication_settings.totp.secret)
+ mfa_token = insert(:mfa_token, user: user)
+
+ response =
+ conn
+ |> post("/oauth/mfa/challenge", %{
+ "mfa_token" => mfa_token.token,
+ "challenge_type" => "totp",
+ "code" => otp_token,
+ "client_id" => "xxx",
+ "client_secret" => "xxx"
+ })
+ |> json_response(400)
+
+ assert response == %{"error" => "Invalid code"}
+ end
+ end
+
+ describe "challenge/recovery" do
+ setup %{conn: conn} do
+ app = insert(:oauth_app)
+ {:ok, conn: conn, app: app}
+ end
+
+ test "returns access token with valid code", %{conn: conn, app: app} do
+ otp_secret = TOTP.generate_secret()
+
+ [code | _] = backup_codes = BackupCodes.generate()
+
+ hashed_codes =
+ backup_codes
+ |> Enum.map(&Pbkdf2.hash_pwd_salt(&1))
+
+ user =
+ insert(:user,
+ multi_factor_authentication_settings: %MFA.Settings{
+ enabled: true,
+ backup_codes: hashed_codes,
+ totp: %MFA.Settings.TOTP{secret: otp_secret, confirmed: true}
+ }
+ )
+
+ mfa_token =
+ insert(:mfa_token,
+ user: user,
+ authorization: build(:oauth_authorization, app: app, scopes: ["write"])
+ )
+
+ response =
+ conn
+ |> post("/oauth/mfa/challenge", %{
+ "mfa_token" => mfa_token.token,
+ "challenge_type" => "recovery",
+ "code" => code,
+ "client_id" => app.client_id,
+ "client_secret" => app.client_secret
+ })
+ |> json_response(:ok)
+
+ ap_id = user.ap_id
+
+ assert match?(
+ %{
+ "access_token" => _,
+ "expires_in" => 600,
+ "me" => ^ap_id,
+ "refresh_token" => _,
+ "scope" => "write",
+ "token_type" => "Bearer"
+ },
+ response
+ )
+
+ error_response =
+ conn
+ |> post("/oauth/mfa/challenge", %{
+ "mfa_token" => mfa_token.token,
+ "challenge_type" => "recovery",
+ "code" => code,
+ "client_id" => app.client_id,
+ "client_secret" => app.client_secret
+ })
+ |> json_response(400)
+
+ assert error_response == %{"error" => "Invalid code"}
+ end
+ end
+end
diff --git a/test/pleroma/web/o_auth/o_auth_controller_test.exs b/test/pleroma/web/o_auth/o_auth_controller_test.exs
@@ -0,0 +1,1232 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.OAuth.OAuthControllerTest do
+ use Pleroma.Web.ConnCase
+ import Pleroma.Factory
+
+ alias Pleroma.MFA
+ alias Pleroma.MFA.TOTP
+ alias Pleroma.Repo
+ alias Pleroma.User
+ alias Pleroma.Web.OAuth.Authorization
+ alias Pleroma.Web.OAuth.OAuthController
+ alias Pleroma.Web.OAuth.Token
+
+ @session_opts [
+ store: :cookie,
+ key: "_test",
+ signing_salt: "cooldude"
+ ]
+ setup do
+ clear_config([:instance, :account_activation_required])
+ clear_config([:instance, :account_approval_required])
+ end
+
+ describe "in OAuth consumer mode, " do
+ setup do
+ [
+ app: insert(:oauth_app),
+ conn:
+ build_conn()
+ |> Plug.Session.call(Plug.Session.init(@session_opts))
+ |> fetch_session()
+ ]
+ end
+
+ setup do: clear_config([:auth, :oauth_consumer_strategies], ~w(twitter facebook))
+
+ test "GET /oauth/authorize renders auth forms, including OAuth consumer form", %{
+ app: app,
+ conn: conn
+ } do
+ conn =
+ get(
+ conn,
+ "/oauth/authorize",
+ %{
+ "response_type" => "code",
+ "client_id" => app.client_id,
+ "redirect_uri" => OAuthController.default_redirect_uri(app),
+ "scope" => "read"
+ }
+ )
+
+ assert response = html_response(conn, 200)
+ assert response =~ "Sign in with Twitter"
+ assert response =~ o_auth_path(conn, :prepare_request)
+ end
+
+ test "GET /oauth/prepare_request encodes parameters as `state` and redirects", %{
+ app: app,
+ conn: conn
+ } do
+ conn =
+ get(
+ conn,
+ "/oauth/prepare_request",
+ %{
+ "provider" => "twitter",
+ "authorization" => %{
+ "scope" => "read follow",
+ "client_id" => app.client_id,
+ "redirect_uri" => OAuthController.default_redirect_uri(app),
+ "state" => "a_state"
+ }
+ }
+ )
+
+ assert response = html_response(conn, 302)
+
+ redirect_query = URI.parse(redirected_to(conn)).query
+ assert %{"state" => state_param} = URI.decode_query(redirect_query)
+ assert {:ok, state_components} = Poison.decode(state_param)
+
+ expected_client_id = app.client_id
+ expected_redirect_uri = app.redirect_uris
+
+ assert %{
+ "scope" => "read follow",
+ "client_id" => ^expected_client_id,
+ "redirect_uri" => ^expected_redirect_uri,
+ "state" => "a_state"
+ } = state_components
+ end
+
+ test "with user-bound registration, GET /oauth/<provider>/callback redirects to `redirect_uri` with `code`",
+ %{app: app, conn: conn} do
+ registration = insert(:registration)
+ redirect_uri = OAuthController.default_redirect_uri(app)
+
+ state_params = %{
+ "scope" => Enum.join(app.scopes, " "),
+ "client_id" => app.client_id,
+ "redirect_uri" => redirect_uri,
+ "state" => ""
+ }
+
+ conn =
+ conn
+ |> assign(:ueberauth_auth, %{provider: registration.provider, uid: registration.uid})
+ |> get(
+ "/oauth/twitter/callback",
+ %{
+ "oauth_token" => "G-5a3AAAAAAAwMH9AAABaektfSM",
+ "oauth_verifier" => "QZl8vUqNvXMTKpdmUnGejJxuHG75WWWs",
+ "provider" => "twitter",
+ "state" => Poison.encode!(state_params)
+ }
+ )
+
+ assert response = html_response(conn, 302)
+ assert redirected_to(conn) =~ ~r/#{redirect_uri}\?code=.+/
+ end
+
+ test "with user-unbound registration, GET /oauth/<provider>/callback renders registration_details page",
+ %{app: app, conn: conn} do
+ user = insert(:user)
+
+ state_params = %{
+ "scope" => "read write",
+ "client_id" => app.client_id,
+ "redirect_uri" => OAuthController.default_redirect_uri(app),
+ "state" => "a_state"
+ }
+
+ conn =
+ conn
+ |> assign(:ueberauth_auth, %{
+ provider: "twitter",
+ uid: "171799000",
+ info: %{nickname: user.nickname, email: user.email, name: user.name, description: nil}
+ })
+ |> get(
+ "/oauth/twitter/callback",
+ %{
+ "oauth_token" => "G-5a3AAAAAAAwMH9AAABaektfSM",
+ "oauth_verifier" => "QZl8vUqNvXMTKpdmUnGejJxuHG75WWWs",
+ "provider" => "twitter",
+ "state" => Poison.encode!(state_params)
+ }
+ )
+
+ assert response = html_response(conn, 200)
+ assert response =~ ~r/name="op" type="submit" value="register"/
+ assert response =~ ~r/name="op" type="submit" value="connect"/
+ assert response =~ user.email
+ assert response =~ user.nickname
+ end
+
+ test "on authentication error, GET /oauth/<provider>/callback redirects to `redirect_uri`", %{
+ app: app,
+ conn: conn
+ } do
+ state_params = %{
+ "scope" => Enum.join(app.scopes, " "),
+ "client_id" => app.client_id,
+ "redirect_uri" => OAuthController.default_redirect_uri(app),
+ "state" => ""
+ }
+
+ conn =
+ conn
+ |> assign(:ueberauth_failure, %{errors: [%{message: "(error description)"}]})
+ |> get(
+ "/oauth/twitter/callback",
+ %{
+ "oauth_token" => "G-5a3AAAAAAAwMH9AAABaektfSM",
+ "oauth_verifier" => "QZl8vUqNvXMTKpdmUnGejJxuHG75WWWs",
+ "provider" => "twitter",
+ "state" => Poison.encode!(state_params)
+ }
+ )
+
+ assert response = html_response(conn, 302)
+ assert redirected_to(conn) == app.redirect_uris
+ assert get_flash(conn, :error) == "Failed to authenticate: (error description)."
+ end
+
+ test "GET /oauth/registration_details renders registration details form", %{
+ app: app,
+ conn: conn
+ } do
+ conn =
+ get(
+ conn,
+ "/oauth/registration_details",
+ %{
+ "authorization" => %{
+ "scopes" => app.scopes,
+ "client_id" => app.client_id,
+ "redirect_uri" => OAuthController.default_redirect_uri(app),
+ "state" => "a_state",
+ "nickname" => nil,
+ "email" => "john@doe.com"
+ }
+ }
+ )
+
+ assert response = html_response(conn, 200)
+ assert response =~ ~r/name="op" type="submit" value="register"/
+ assert response =~ ~r/name="op" type="submit" value="connect"/
+ end
+
+ test "with valid params, POST /oauth/register?op=register redirects to `redirect_uri` with `code`",
+ %{
+ app: app,
+ conn: conn
+ } do
+ registration = insert(:registration, user: nil, info: %{"nickname" => nil, "email" => nil})
+ redirect_uri = OAuthController.default_redirect_uri(app)
+
+ conn =
+ conn
+ |> put_session(:registration_id, registration.id)
+ |> post(
+ "/oauth/register",
+ %{
+ "op" => "register",
+ "authorization" => %{
+ "scopes" => app.scopes,
+ "client_id" => app.client_id,
+ "redirect_uri" => redirect_uri,
+ "state" => "a_state",
+ "nickname" => "availablenick",
+ "email" => "available@email.com"
+ }
+ }
+ )
+
+ assert response = html_response(conn, 302)
+ assert redirected_to(conn) =~ ~r/#{redirect_uri}\?code=.+/
+ end
+
+ test "with unlisted `redirect_uri`, POST /oauth/register?op=register results in HTTP 401",
+ %{
+ app: app,
+ conn: conn
+ } do
+ registration = insert(:registration, user: nil, info: %{"nickname" => nil, "email" => nil})
+ unlisted_redirect_uri = "http://cross-site-request.com"
+
+ conn =
+ conn
+ |> put_session(:registration_id, registration.id)
+ |> post(
+ "/oauth/register",
+ %{
+ "op" => "register",
+ "authorization" => %{
+ "scopes" => app.scopes,
+ "client_id" => app.client_id,
+ "redirect_uri" => unlisted_redirect_uri,
+ "state" => "a_state",
+ "nickname" => "availablenick",
+ "email" => "available@email.com"
+ }
+ }
+ )
+
+ assert response = html_response(conn, 401)
+ end
+
+ test "with invalid params, POST /oauth/register?op=register renders registration_details page",
+ %{
+ app: app,
+ conn: conn
+ } do
+ another_user = insert(:user)
+ registration = insert(:registration, user: nil, info: %{"nickname" => nil, "email" => nil})
+
+ params = %{
+ "op" => "register",
+ "authorization" => %{
+ "scopes" => app.scopes,
+ "client_id" => app.client_id,
+ "redirect_uri" => OAuthController.default_redirect_uri(app),
+ "state" => "a_state",
+ "nickname" => "availablenickname",
+ "email" => "available@email.com"
+ }
+ }
+
+ for {bad_param, bad_param_value} <-
+ [{"nickname", another_user.nickname}, {"email", another_user.email}] do
+ bad_registration_attrs = %{
+ "authorization" => Map.put(params["authorization"], bad_param, bad_param_value)
+ }
+
+ bad_params = Map.merge(params, bad_registration_attrs)
+
+ conn =
+ conn
+ |> put_session(:registration_id, registration.id)
+ |> post("/oauth/register", bad_params)
+
+ assert html_response(conn, 403) =~ ~r/name="op" type="submit" value="register"/
+ assert get_flash(conn, :error) == "Error: #{bad_param} has already been taken."
+ end
+ end
+
+ test "with valid params, POST /oauth/register?op=connect redirects to `redirect_uri` with `code`",
+ %{
+ app: app,
+ conn: conn
+ } do
+ user = insert(:user, password_hash: Pbkdf2.hash_pwd_salt("testpassword"))
+ registration = insert(:registration, user: nil)
+ redirect_uri = OAuthController.default_redirect_uri(app)
+
+ conn =
+ conn
+ |> put_session(:registration_id, registration.id)
+ |> post(
+ "/oauth/register",
+ %{
+ "op" => "connect",
+ "authorization" => %{
+ "scopes" => app.scopes,
+ "client_id" => app.client_id,
+ "redirect_uri" => redirect_uri,
+ "state" => "a_state",
+ "name" => user.nickname,
+ "password" => "testpassword"
+ }
+ }
+ )
+
+ assert response = html_response(conn, 302)
+ assert redirected_to(conn) =~ ~r/#{redirect_uri}\?code=.+/
+ end
+
+ test "with unlisted `redirect_uri`, POST /oauth/register?op=connect results in HTTP 401`",
+ %{
+ app: app,
+ conn: conn
+ } do
+ user = insert(:user, password_hash: Pbkdf2.hash_pwd_salt("testpassword"))
+ registration = insert(:registration, user: nil)
+ unlisted_redirect_uri = "http://cross-site-request.com"
+
+ conn =
+ conn
+ |> put_session(:registration_id, registration.id)
+ |> post(
+ "/oauth/register",
+ %{
+ "op" => "connect",
+ "authorization" => %{
+ "scopes" => app.scopes,
+ "client_id" => app.client_id,
+ "redirect_uri" => unlisted_redirect_uri,
+ "state" => "a_state",
+ "name" => user.nickname,
+ "password" => "testpassword"
+ }
+ }
+ )
+
+ assert response = html_response(conn, 401)
+ end
+
+ test "with invalid params, POST /oauth/register?op=connect renders registration_details page",
+ %{
+ app: app,
+ conn: conn
+ } do
+ user = insert(:user)
+ registration = insert(:registration, user: nil)
+
+ params = %{
+ "op" => "connect",
+ "authorization" => %{
+ "scopes" => app.scopes,
+ "client_id" => app.client_id,
+ "redirect_uri" => OAuthController.default_redirect_uri(app),
+ "state" => "a_state",
+ "name" => user.nickname,
+ "password" => "wrong password"
+ }
+ }
+
+ conn =
+ conn
+ |> put_session(:registration_id, registration.id)
+ |> post("/oauth/register", params)
+
+ assert html_response(conn, 401) =~ ~r/name="op" type="submit" value="connect"/
+ assert get_flash(conn, :error) == "Invalid Username/Password"
+ end
+ end
+
+ describe "GET /oauth/authorize" do
+ setup do
+ [
+ app: insert(:oauth_app, redirect_uris: "https://redirect.url"),
+ conn:
+ build_conn()
+ |> Plug.Session.call(Plug.Session.init(@session_opts))
+ |> fetch_session()
+ ]
+ end
+
+ test "renders authentication page", %{app: app, conn: conn} do
+ conn =
+ get(
+ conn,
+ "/oauth/authorize",
+ %{
+ "response_type" => "code",
+ "client_id" => app.client_id,
+ "redirect_uri" => OAuthController.default_redirect_uri(app),
+ "scope" => "read"
+ }
+ )
+
+ assert html_response(conn, 200) =~ ~s(type="submit")
+ end
+
+ test "properly handles internal calls with `authorization`-wrapped params", %{
+ app: app,
+ conn: conn
+ } do
+ conn =
+ get(
+ conn,
+ "/oauth/authorize",
+ %{
+ "authorization" => %{
+ "response_type" => "code",
+ "client_id" => app.client_id,
+ "redirect_uri" => OAuthController.default_redirect_uri(app),
+ "scope" => "read"
+ }
+ }
+ )
+
+ assert html_response(conn, 200) =~ ~s(type="submit")
+ end
+
+ test "renders authentication page if user is already authenticated but `force_login` is tru-ish",
+ %{app: app, conn: conn} do
+ token = insert(:oauth_token, app: app)
+
+ conn =
+ conn
+ |> put_session(:oauth_token, token.token)
+ |> get(
+ "/oauth/authorize",
+ %{
+ "response_type" => "code",
+ "client_id" => app.client_id,
+ "redirect_uri" => OAuthController.default_redirect_uri(app),
+ "scope" => "read",
+ "force_login" => "true"
+ }
+ )
+
+ assert html_response(conn, 200) =~ ~s(type="submit")
+ end
+
+ test "renders authentication page if user is already authenticated but user request with another client",
+ %{
+ app: app,
+ conn: conn
+ } do
+ token = insert(:oauth_token, app: app)
+
+ conn =
+ conn
+ |> put_session(:oauth_token, token.token)
+ |> get(
+ "/oauth/authorize",
+ %{
+ "response_type" => "code",
+ "client_id" => "another_client_id",
+ "redirect_uri" => OAuthController.default_redirect_uri(app),
+ "scope" => "read"
+ }
+ )
+
+ assert html_response(conn, 200) =~ ~s(type="submit")
+ end
+
+ test "with existing authentication and non-OOB `redirect_uri`, redirects to app with `token` and `state` params",
+ %{
+ app: app,
+ conn: conn
+ } do
+ token = insert(:oauth_token, app: app)
+
+ conn =
+ conn
+ |> put_session(:oauth_token, token.token)
+ |> get(
+ "/oauth/authorize",
+ %{
+ "response_type" => "code",
+ "client_id" => app.client_id,
+ "redirect_uri" => OAuthController.default_redirect_uri(app),
+ "state" => "specific_client_state",
+ "scope" => "read"
+ }
+ )
+
+ assert URI.decode(redirected_to(conn)) ==
+ "https://redirect.url?access_token=#{token.token}&state=specific_client_state"
+ end
+
+ test "with existing authentication and unlisted non-OOB `redirect_uri`, redirects without credentials",
+ %{
+ app: app,
+ conn: conn
+ } do
+ unlisted_redirect_uri = "http://cross-site-request.com"
+ token = insert(:oauth_token, app: app)
+
+ conn =
+ conn
+ |> put_session(:oauth_token, token.token)
+ |> get(
+ "/oauth/authorize",
+ %{
+ "response_type" => "code",
+ "client_id" => app.client_id,
+ "redirect_uri" => unlisted_redirect_uri,
+ "state" => "specific_client_state",
+ "scope" => "read"
+ }
+ )
+
+ assert redirected_to(conn) == unlisted_redirect_uri
+ end
+
+ test "with existing authentication and OOB `redirect_uri`, redirects to app with `token` and `state` params",
+ %{
+ app: app,
+ conn: conn
+ } do
+ token = insert(:oauth_token, app: app)
+
+ conn =
+ conn
+ |> put_session(:oauth_token, token.token)
+ |> get(
+ "/oauth/authorize",
+ %{
+ "response_type" => "code",
+ "client_id" => app.client_id,
+ "redirect_uri" => "urn:ietf:wg:oauth:2.0:oob",
+ "scope" => "read"
+ }
+ )
+
+ assert html_response(conn, 200) =~ "Authorization exists"
+ end
+ end
+
+ describe "POST /oauth/authorize" do
+ test "redirects with oauth authorization, " <>
+ "granting requested app-supported scopes to both admin- and non-admin users" do
+ app_scopes = ["read", "write", "admin", "secret_scope"]
+ app = insert(:oauth_app, scopes: app_scopes)
+ redirect_uri = OAuthController.default_redirect_uri(app)
+
+ non_admin = insert(:user, is_admin: false)
+ admin = insert(:user, is_admin: true)
+ scopes_subset = ["read:subscope", "write", "admin"]
+
+ # In case scope param is missing, expecting _all_ app-supported scopes to be granted
+ for user <- [non_admin, admin],
+ {requested_scopes, expected_scopes} <-
+ %{scopes_subset => scopes_subset, nil: app_scopes} do
+ conn =
+ post(
+ build_conn(),
+ "/oauth/authorize",
+ %{
+ "authorization" => %{
+ "name" => user.nickname,
+ "password" => "test",
+ "client_id" => app.client_id,
+ "redirect_uri" => redirect_uri,
+ "scope" => requested_scopes,
+ "state" => "statepassed"
+ }
+ }
+ )
+
+ target = redirected_to(conn)
+ assert target =~ redirect_uri
+
+ query = URI.parse(target).query |> URI.query_decoder() |> Map.new()
+
+ assert %{"state" => "statepassed", "code" => code} = query
+ auth = Repo.get_by(Authorization, token: code)
+ assert auth
+ assert auth.scopes == expected_scopes
+ end
+ end
+
+ test "redirect to on two-factor auth page" do
+ otp_secret = TOTP.generate_secret()
+
+ user =
+ insert(:user,
+ multi_factor_authentication_settings: %MFA.Settings{
+ enabled: true,
+ totp: %MFA.Settings.TOTP{secret: otp_secret, confirmed: true}
+ }
+ )
+
+ app = insert(:oauth_app, scopes: ["read", "write", "follow"])
+
+ conn =
+ build_conn()
+ |> post("/oauth/authorize", %{
+ "authorization" => %{
+ "name" => user.nickname,
+ "password" => "test",
+ "client_id" => app.client_id,
+ "redirect_uri" => app.redirect_uris,
+ "scope" => "read write",
+ "state" => "statepassed"
+ }
+ })
+
+ result = html_response(conn, 200)
+
+ mfa_token = Repo.get_by(MFA.Token, user_id: user.id)
+ assert result =~ app.redirect_uris
+ assert result =~ "statepassed"
+ assert result =~ mfa_token.token
+ assert result =~ "Two-factor authentication"
+ end
+
+ test "returns 401 for wrong credentials", %{conn: conn} do
+ user = insert(:user)
+ app = insert(:oauth_app)
+ redirect_uri = OAuthController.default_redirect_uri(app)
+
+ result =
+ conn
+ |> post("/oauth/authorize", %{
+ "authorization" => %{
+ "name" => user.nickname,
+ "password" => "wrong",
+ "client_id" => app.client_id,
+ "redirect_uri" => redirect_uri,
+ "state" => "statepassed",
+ "scope" => Enum.join(app.scopes, " ")
+ }
+ })
+ |> html_response(:unauthorized)
+
+ # Keep the details
+ assert result =~ app.client_id
+ assert result =~ redirect_uri
+
+ # Error message
+ assert result =~ "Invalid Username/Password"
+ end
+
+ test "returns 401 for missing scopes" do
+ user = insert(:user, is_admin: false)
+ app = insert(:oauth_app, scopes: ["read", "write", "admin"])
+ redirect_uri = OAuthController.default_redirect_uri(app)
+
+ result =
+ build_conn()
+ |> post("/oauth/authorize", %{
+ "authorization" => %{
+ "name" => user.nickname,
+ "password" => "test",
+ "client_id" => app.client_id,
+ "redirect_uri" => redirect_uri,
+ "state" => "statepassed",
+ "scope" => ""
+ }
+ })
+ |> html_response(:unauthorized)
+
+ # Keep the details
+ assert result =~ app.client_id
+ assert result =~ redirect_uri
+
+ # Error message
+ assert result =~ "This action is outside the authorized scopes"
+ end
+
+ test "returns 401 for scopes beyond app scopes hierarchy", %{conn: conn} do
+ user = insert(:user)
+ app = insert(:oauth_app, scopes: ["read", "write"])
+ redirect_uri = OAuthController.default_redirect_uri(app)
+
+ result =
+ conn
+ |> post("/oauth/authorize", %{
+ "authorization" => %{
+ "name" => user.nickname,
+ "password" => "test",
+ "client_id" => app.client_id,
+ "redirect_uri" => redirect_uri,
+ "state" => "statepassed",
+ "scope" => "read write follow"
+ }
+ })
+ |> html_response(:unauthorized)
+
+ # Keep the details
+ assert result =~ app.client_id
+ assert result =~ redirect_uri
+
+ # Error message
+ assert result =~ "This action is outside the authorized scopes"
+ end
+ end
+
+ describe "POST /oauth/token" do
+ test "issues a token for an all-body request" do
+ user = insert(:user)
+ app = insert(:oauth_app, scopes: ["read", "write"])
+
+ {:ok, auth} = Authorization.create_authorization(app, user, ["write"])
+
+ conn =
+ build_conn()
+ |> post("/oauth/token", %{
+ "grant_type" => "authorization_code",
+ "code" => auth.token,
+ "redirect_uri" => OAuthController.default_redirect_uri(app),
+ "client_id" => app.client_id,
+ "client_secret" => app.client_secret
+ })
+
+ assert %{"access_token" => token, "me" => ap_id} = json_response(conn, 200)
+
+ token = Repo.get_by(Token, token: token)
+ assert token
+ assert token.scopes == auth.scopes
+ assert user.ap_id == ap_id
+ end
+
+ test "issues a token for `password` grant_type with valid credentials, with full permissions by default" do
+ password = "testpassword"
+ user = insert(:user, password_hash: Pbkdf2.hash_pwd_salt(password))
+
+ app = insert(:oauth_app, scopes: ["read", "write"])
+
+ # Note: "scope" param is intentionally omitted
+ conn =
+ build_conn()
+ |> post("/oauth/token", %{
+ "grant_type" => "password",
+ "username" => user.nickname,
+ "password" => password,
+ "client_id" => app.client_id,
+ "client_secret" => app.client_secret
+ })
+
+ assert %{"access_token" => token} = json_response(conn, 200)
+
+ token = Repo.get_by(Token, token: token)
+ assert token
+ assert token.scopes == app.scopes
+ end
+
+ test "issues a mfa token for `password` grant_type, when MFA enabled" do
+ password = "testpassword"
+ otp_secret = TOTP.generate_secret()
+
+ user =
+ insert(:user,
+ password_hash: Pbkdf2.hash_pwd_salt(password),
+ multi_factor_authentication_settings: %MFA.Settings{
+ enabled: true,
+ totp: %MFA.Settings.TOTP{secret: otp_secret, confirmed: true}
+ }
+ )
+
+ app = insert(:oauth_app, scopes: ["read", "write"])
+
+ response =
+ build_conn()
+ |> post("/oauth/token", %{
+ "grant_type" => "password",
+ "username" => user.nickname,
+ "password" => password,
+ "client_id" => app.client_id,
+ "client_secret" => app.client_secret
+ })
+ |> json_response(403)
+
+ assert match?(
+ %{
+ "supported_challenge_types" => "totp",
+ "mfa_token" => _,
+ "error" => "mfa_required"
+ },
+ response
+ )
+
+ token = Repo.get_by(MFA.Token, token: response["mfa_token"])
+ assert token.user_id == user.id
+ assert token.authorization_id
+ end
+
+ test "issues a token for request with HTTP basic auth client credentials" do
+ user = insert(:user)
+ app = insert(:oauth_app, scopes: ["scope1", "scope2", "scope3"])
+
+ {:ok, auth} = Authorization.create_authorization(app, user, ["scope1", "scope2"])
+ assert auth.scopes == ["scope1", "scope2"]
+
+ app_encoded =
+ (URI.encode_www_form(app.client_id) <> ":" <> URI.encode_www_form(app.client_secret))
+ |> Base.encode64()
+
+ conn =
+ build_conn()
+ |> put_req_header("authorization", "Basic " <> app_encoded)
+ |> post("/oauth/token", %{
+ "grant_type" => "authorization_code",
+ "code" => auth.token,
+ "redirect_uri" => OAuthController.default_redirect_uri(app)
+ })
+
+ assert %{"access_token" => token, "scope" => scope} = json_response(conn, 200)
+
+ assert scope == "scope1 scope2"
+
+ token = Repo.get_by(Token, token: token)
+ assert token
+ assert token.scopes == ["scope1", "scope2"]
+ end
+
+ test "issue a token for client_credentials grant type" do
+ app = insert(:oauth_app, scopes: ["read", "write"])
+
+ conn =
+ build_conn()
+ |> post("/oauth/token", %{
+ "grant_type" => "client_credentials",
+ "client_id" => app.client_id,
+ "client_secret" => app.client_secret
+ })
+
+ assert %{"access_token" => token, "refresh_token" => refresh, "scope" => scope} =
+ json_response(conn, 200)
+
+ assert token
+ token_from_db = Repo.get_by(Token, token: token)
+ assert token_from_db
+ assert refresh
+ assert scope == "read write"
+ end
+
+ test "rejects token exchange with invalid client credentials" do
+ user = insert(:user)
+ app = insert(:oauth_app)
+
+ {:ok, auth} = Authorization.create_authorization(app, user)
+
+ conn =
+ build_conn()
+ |> put_req_header("authorization", "Basic JTIxOiVGMCU5RiVBNCVCNwo=")
+ |> post("/oauth/token", %{
+ "grant_type" => "authorization_code",
+ "code" => auth.token,
+ "redirect_uri" => OAuthController.default_redirect_uri(app)
+ })
+
+ assert resp = json_response(conn, 400)
+ assert %{"error" => _} = resp
+ refute Map.has_key?(resp, "access_token")
+ end
+
+ test "rejects token exchange for valid credentials belonging to unconfirmed user and confirmation is required" do
+ Pleroma.Config.put([:instance, :account_activation_required], true)
+ password = "testpassword"
+
+ {:ok, user} =
+ insert(:user, password_hash: Pbkdf2.hash_pwd_salt(password))
+ |> User.confirmation_changeset(need_confirmation: true)
+ |> User.update_and_set_cache()
+
+ refute Pleroma.User.account_status(user) == :active
+
+ app = insert(:oauth_app)
+
+ conn =
+ build_conn()
+ |> post("/oauth/token", %{
+ "grant_type" => "password",
+ "username" => user.nickname,
+ "password" => password,
+ "client_id" => app.client_id,
+ "client_secret" => app.client_secret
+ })
+
+ assert resp = json_response(conn, 403)
+ assert %{"error" => _} = resp
+ refute Map.has_key?(resp, "access_token")
+ end
+
+ test "rejects token exchange for valid credentials belonging to deactivated user" do
+ password = "testpassword"
+
+ user =
+ insert(:user,
+ password_hash: Pbkdf2.hash_pwd_salt(password),
+ deactivated: true
+ )
+
+ app = insert(:oauth_app)
+
+ resp =
+ build_conn()
+ |> post("/oauth/token", %{
+ "grant_type" => "password",
+ "username" => user.nickname,
+ "password" => password,
+ "client_id" => app.client_id,
+ "client_secret" => app.client_secret
+ })
+ |> json_response(403)
+
+ assert resp == %{
+ "error" => "Your account is currently disabled",
+ "identifier" => "account_is_disabled"
+ }
+ end
+
+ test "rejects token exchange for user with password_reset_pending set to true" do
+ password = "testpassword"
+
+ user =
+ insert(:user,
+ password_hash: Pbkdf2.hash_pwd_salt(password),
+ password_reset_pending: true
+ )
+
+ app = insert(:oauth_app, scopes: ["read", "write"])
+
+ resp =
+ build_conn()
+ |> post("/oauth/token", %{
+ "grant_type" => "password",
+ "username" => user.nickname,
+ "password" => password,
+ "client_id" => app.client_id,
+ "client_secret" => app.client_secret
+ })
+ |> json_response(403)
+
+ assert resp == %{
+ "error" => "Password reset is required",
+ "identifier" => "password_reset_required"
+ }
+ end
+
+ test "rejects token exchange for user with confirmation_pending set to true" do
+ Pleroma.Config.put([:instance, :account_activation_required], true)
+ password = "testpassword"
+
+ user =
+ insert(:user,
+ password_hash: Pbkdf2.hash_pwd_salt(password),
+ confirmation_pending: true
+ )
+
+ app = insert(:oauth_app, scopes: ["read", "write"])
+
+ resp =
+ build_conn()
+ |> post("/oauth/token", %{
+ "grant_type" => "password",
+ "username" => user.nickname,
+ "password" => password,
+ "client_id" => app.client_id,
+ "client_secret" => app.client_secret
+ })
+ |> json_response(403)
+
+ assert resp == %{
+ "error" => "Your login is missing a confirmed e-mail address",
+ "identifier" => "missing_confirmed_email"
+ }
+ end
+
+ test "rejects token exchange for valid credentials belonging to an unapproved user" do
+ password = "testpassword"
+
+ user = insert(:user, password_hash: Pbkdf2.hash_pwd_salt(password), approval_pending: true)
+
+ refute Pleroma.User.account_status(user) == :active
+
+ app = insert(:oauth_app)
+
+ conn =
+ build_conn()
+ |> post("/oauth/token", %{
+ "grant_type" => "password",
+ "username" => user.nickname,
+ "password" => password,
+ "client_id" => app.client_id,
+ "client_secret" => app.client_secret
+ })
+
+ assert resp = json_response(conn, 403)
+ assert %{"error" => _} = resp
+ refute Map.has_key?(resp, "access_token")
+ end
+
+ test "rejects an invalid authorization code" do
+ app = insert(:oauth_app)
+
+ conn =
+ build_conn()
+ |> post("/oauth/token", %{
+ "grant_type" => "authorization_code",
+ "code" => "Imobviouslyinvalid",
+ "redirect_uri" => OAuthController.default_redirect_uri(app),
+ "client_id" => app.client_id,
+ "client_secret" => app.client_secret
+ })
+
+ assert resp = json_response(conn, 400)
+ assert %{"error" => _} = json_response(conn, 400)
+ refute Map.has_key?(resp, "access_token")
+ end
+ end
+
+ describe "POST /oauth/token - refresh token" do
+ setup do: clear_config([:oauth2, :issue_new_refresh_token])
+
+ test "issues a new access token with keep fresh token" do
+ Pleroma.Config.put([:oauth2, :issue_new_refresh_token], true)
+ user = insert(:user)
+ app = insert(:oauth_app, scopes: ["read", "write"])
+
+ {:ok, auth} = Authorization.create_authorization(app, user, ["write"])
+ {:ok, token} = Token.exchange_token(app, auth)
+
+ response =
+ build_conn()
+ |> post("/oauth/token", %{
+ "grant_type" => "refresh_token",
+ "refresh_token" => token.refresh_token,
+ "client_id" => app.client_id,
+ "client_secret" => app.client_secret
+ })
+ |> json_response(200)
+
+ ap_id = user.ap_id
+
+ assert match?(
+ %{
+ "scope" => "write",
+ "token_type" => "Bearer",
+ "expires_in" => 600,
+ "access_token" => _,
+ "refresh_token" => _,
+ "me" => ^ap_id
+ },
+ response
+ )
+
+ refute Repo.get_by(Token, token: token.token)
+ new_token = Repo.get_by(Token, token: response["access_token"])
+ assert new_token.refresh_token == token.refresh_token
+ assert new_token.scopes == auth.scopes
+ assert new_token.user_id == user.id
+ assert new_token.app_id == app.id
+ end
+
+ test "issues a new access token with new fresh token" do
+ Pleroma.Config.put([:oauth2, :issue_new_refresh_token], false)
+ user = insert(:user)
+ app = insert(:oauth_app, scopes: ["read", "write"])
+
+ {:ok, auth} = Authorization.create_authorization(app, user, ["write"])
+ {:ok, token} = Token.exchange_token(app, auth)
+
+ response =
+ build_conn()
+ |> post("/oauth/token", %{
+ "grant_type" => "refresh_token",
+ "refresh_token" => token.refresh_token,
+ "client_id" => app.client_id,
+ "client_secret" => app.client_secret
+ })
+ |> json_response(200)
+
+ ap_id = user.ap_id
+
+ assert match?(
+ %{
+ "scope" => "write",
+ "token_type" => "Bearer",
+ "expires_in" => 600,
+ "access_token" => _,
+ "refresh_token" => _,
+ "me" => ^ap_id
+ },
+ response
+ )
+
+ refute Repo.get_by(Token, token: token.token)
+ new_token = Repo.get_by(Token, token: response["access_token"])
+ refute new_token.refresh_token == token.refresh_token
+ assert new_token.scopes == auth.scopes
+ assert new_token.user_id == user.id
+ assert new_token.app_id == app.id
+ end
+
+ test "returns 400 if we try use access token" do
+ user = insert(:user)
+ app = insert(:oauth_app, scopes: ["read", "write"])
+
+ {:ok, auth} = Authorization.create_authorization(app, user, ["write"])
+ {:ok, token} = Token.exchange_token(app, auth)
+
+ response =
+ build_conn()
+ |> post("/oauth/token", %{
+ "grant_type" => "refresh_token",
+ "refresh_token" => token.token,
+ "client_id" => app.client_id,
+ "client_secret" => app.client_secret
+ })
+ |> json_response(400)
+
+ assert %{"error" => "Invalid credentials"} == response
+ end
+
+ test "returns 400 if refresh_token invalid" do
+ app = insert(:oauth_app, scopes: ["read", "write"])
+
+ response =
+ build_conn()
+ |> post("/oauth/token", %{
+ "grant_type" => "refresh_token",
+ "refresh_token" => "token.refresh_token",
+ "client_id" => app.client_id,
+ "client_secret" => app.client_secret
+ })
+ |> json_response(400)
+
+ assert %{"error" => "Invalid credentials"} == response
+ end
+
+ test "issues a new token if token expired" do
+ user = insert(:user)
+ app = insert(:oauth_app, scopes: ["read", "write"])
+
+ {:ok, auth} = Authorization.create_authorization(app, user, ["write"])
+ {:ok, token} = Token.exchange_token(app, auth)
+
+ change =
+ Ecto.Changeset.change(
+ token,
+ %{valid_until: NaiveDateTime.add(NaiveDateTime.utc_now(), -86_400 * 30)}
+ )
+
+ {:ok, access_token} = Repo.update(change)
+
+ response =
+ build_conn()
+ |> post("/oauth/token", %{
+ "grant_type" => "refresh_token",
+ "refresh_token" => access_token.refresh_token,
+ "client_id" => app.client_id,
+ "client_secret" => app.client_secret
+ })
+ |> json_response(200)
+
+ ap_id = user.ap_id
+
+ assert match?(
+ %{
+ "scope" => "write",
+ "token_type" => "Bearer",
+ "expires_in" => 600,
+ "access_token" => _,
+ "refresh_token" => _,
+ "me" => ^ap_id
+ },
+ response
+ )
+
+ refute Repo.get_by(Token, token: token.token)
+ token = Repo.get_by(Token, token: response["access_token"])
+ assert token
+ assert token.scopes == auth.scopes
+ assert token.user_id == user.id
+ assert token.app_id == app.id
+ end
+ end
+
+ describe "POST /oauth/token - bad request" do
+ test "returns 500" do
+ response =
+ build_conn()
+ |> post("/oauth/token", %{})
+ |> json_response(500)
+
+ assert %{"error" => "Bad request"} == response
+ end
+ end
+
+ describe "POST /oauth/revoke - bad request" do
+ test "returns 500" do
+ response =
+ build_conn()
+ |> post("/oauth/revoke", %{})
+ |> json_response(500)
+
+ assert %{"error" => "Bad request"} == response
+ end
+ end
+end
diff --git a/test/pleroma/web/o_auth/token/utils_test.exs b/test/pleroma/web/o_auth/token/utils_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.OAuth.Token.UtilsTest do
+ use Pleroma.DataCase
+ alias Pleroma.Web.OAuth.Token.Utils
+ import Pleroma.Factory
+
+ describe "fetch_app/1" do
+ test "returns error when credentials is invalid" do
+ assert {:error, :not_found} =
+ Utils.fetch_app(%Plug.Conn{params: %{"client_id" => 1, "client_secret" => "x"}})
+ end
+
+ test "returns App by params credentails" do
+ app = insert(:oauth_app)
+
+ assert {:ok, load_app} =
+ Utils.fetch_app(%Plug.Conn{
+ params: %{"client_id" => app.client_id, "client_secret" => app.client_secret}
+ })
+
+ assert load_app == app
+ end
+
+ test "returns App by header credentails" do
+ app = insert(:oauth_app)
+ header = "Basic " <> Base.encode64("#{app.client_id}:#{app.client_secret}")
+
+ conn =
+ %Plug.Conn{}
+ |> Plug.Conn.put_req_header("authorization", header)
+
+ assert {:ok, load_app} = Utils.fetch_app(conn)
+ assert load_app == app
+ end
+ end
+
+ describe "format_created_at/1" do
+ test "returns formatted created at" do
+ token = insert(:oauth_token)
+ date = Utils.format_created_at(token)
+
+ token_date =
+ token.inserted_at
+ |> DateTime.from_naive!("Etc/UTC")
+ |> DateTime.to_unix()
+
+ assert token_date == date
+ end
+ end
+end
diff --git a/test/pleroma/web/o_auth/token_test.exs b/test/pleroma/web/o_auth/token_test.exs
@@ -0,0 +1,72 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.OAuth.TokenTest do
+ use Pleroma.DataCase
+ alias Pleroma.Repo
+ alias Pleroma.Web.OAuth.App
+ alias Pleroma.Web.OAuth.Authorization
+ alias Pleroma.Web.OAuth.Token
+
+ import Pleroma.Factory
+
+ test "exchanges a auth token for an access token, preserving `scopes`" do
+ {:ok, app} =
+ Repo.insert(
+ App.register_changeset(%App{}, %{
+ client_name: "client",
+ scopes: ["read", "write"],
+ redirect_uris: "url"
+ })
+ )
+
+ user = insert(:user)
+
+ {:ok, auth} = Authorization.create_authorization(app, user, ["read"])
+ assert auth.scopes == ["read"]
+
+ {:ok, token} = Token.exchange_token(app, auth)
+
+ assert token.app_id == app.id
+ assert token.user_id == user.id
+ assert token.scopes == auth.scopes
+ assert String.length(token.token) > 10
+ assert String.length(token.refresh_token) > 10
+
+ auth = Repo.get(Authorization, auth.id)
+ {:error, "already used"} = Token.exchange_token(app, auth)
+ end
+
+ test "deletes all tokens of a user" do
+ {:ok, app1} =
+ Repo.insert(
+ App.register_changeset(%App{}, %{
+ client_name: "client1",
+ scopes: ["scope"],
+ redirect_uris: "url"
+ })
+ )
+
+ {:ok, app2} =
+ Repo.insert(
+ App.register_changeset(%App{}, %{
+ client_name: "client2",
+ scopes: ["scope"],
+ redirect_uris: "url"
+ })
+ )
+
+ user = insert(:user)
+
+ {:ok, auth1} = Authorization.create_authorization(app1, user)
+ {:ok, auth2} = Authorization.create_authorization(app2, user)
+
+ {:ok, _token1} = Token.exchange_token(app1, auth1)
+ {:ok, _token2} = Token.exchange_token(app2, auth2)
+
+ {tokens, _} = Token.delete_user_tokens(user)
+
+ assert tokens == 2
+ end
+end
diff --git a/test/pleroma/web/o_status/o_status_controller_test.exs b/test/pleroma/web/o_status/o_status_controller_test.exs
@@ -0,0 +1,338 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.OStatus.OStatusControllerTest do
+ use Pleroma.Web.ConnCase
+
+ import Pleroma.Factory
+
+ alias Pleroma.Config
+ alias Pleroma.Object
+ alias Pleroma.User
+ alias Pleroma.Web.ActivityPub.ActivityPub
+ alias Pleroma.Web.CommonAPI
+ alias Pleroma.Web.Endpoint
+
+ require Pleroma.Constants
+
+ setup_all do
+ Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end)
+ :ok
+ end
+
+ setup do: clear_config([:instance, :federating], true)
+
+ describe "Mastodon compatibility routes" do
+ setup %{conn: conn} do
+ conn = put_req_header(conn, "accept", "text/html")
+
+ {:ok, object} =
+ %{
+ "type" => "Note",
+ "content" => "hey",
+ "id" => Endpoint.url() <> "/users/raymoo/statuses/999999999",
+ "actor" => Endpoint.url() <> "/users/raymoo",
+ "to" => [Pleroma.Constants.as_public()]
+ }
+ |> Object.create()
+
+ {:ok, activity, _} =
+ %{
+ "id" => object.data["id"] <> "/activity",
+ "type" => "Create",
+ "object" => object.data["id"],
+ "actor" => object.data["actor"],
+ "to" => object.data["to"]
+ }
+ |> ActivityPub.persist(local: true)
+
+ %{conn: conn, activity: activity}
+ end
+
+ test "redirects to /notice/:id for html format", %{conn: conn, activity: activity} do
+ conn = get(conn, "/users/raymoo/statuses/999999999")
+ assert redirected_to(conn) == "/notice/#{activity.id}"
+ end
+
+ test "redirects to /notice/:id for html format for activity", %{
+ conn: conn,
+ activity: activity
+ } do
+ conn = get(conn, "/users/raymoo/statuses/999999999/activity")
+ assert redirected_to(conn) == "/notice/#{activity.id}"
+ end
+ end
+
+ # Note: see ActivityPubControllerTest for JSON format tests
+ describe "GET /objects/:uuid (text/html)" do
+ setup %{conn: conn} do
+ conn = put_req_header(conn, "accept", "text/html")
+ %{conn: conn}
+ end
+
+ test "redirects to /notice/id for html format", %{conn: conn} do
+ note_activity = insert(:note_activity)
+ object = Object.normalize(note_activity)
+ [_, uuid] = hd(Regex.scan(~r/.+\/([\w-]+)$/, object.data["id"]))
+ url = "/objects/#{uuid}"
+
+ conn = get(conn, url)
+ assert redirected_to(conn) == "/notice/#{note_activity.id}"
+ end
+
+ test "404s on private objects", %{conn: conn} do
+ note_activity = insert(:direct_note_activity)
+ object = Object.normalize(note_activity)
+ [_, uuid] = hd(Regex.scan(~r/.+\/([\w-]+)$/, object.data["id"]))
+
+ conn
+ |> get("/objects/#{uuid}")
+ |> response(404)
+ end
+
+ test "404s on non-existing objects", %{conn: conn} do
+ conn
+ |> get("/objects/123")
+ |> response(404)
+ end
+ end
+
+ # Note: see ActivityPubControllerTest for JSON format tests
+ describe "GET /activities/:uuid (text/html)" do
+ setup %{conn: conn} do
+ conn = put_req_header(conn, "accept", "text/html")
+ %{conn: conn}
+ end
+
+ test "redirects to /notice/id for html format", %{conn: conn} do
+ note_activity = insert(:note_activity)
+ [_, uuid] = hd(Regex.scan(~r/.+\/([\w-]+)$/, note_activity.data["id"]))
+
+ conn = get(conn, "/activities/#{uuid}")
+ assert redirected_to(conn) == "/notice/#{note_activity.id}"
+ end
+
+ test "404s on private activities", %{conn: conn} do
+ note_activity = insert(:direct_note_activity)
+ [_, uuid] = hd(Regex.scan(~r/.+\/([\w-]+)$/, note_activity.data["id"]))
+
+ conn
+ |> get("/activities/#{uuid}")
+ |> response(404)
+ end
+
+ test "404s on nonexistent activities", %{conn: conn} do
+ conn
+ |> get("/activities/123")
+ |> response(404)
+ end
+ end
+
+ describe "GET notice/2" do
+ test "redirects to a proper object URL when json requested and the object is local", %{
+ conn: conn
+ } do
+ note_activity = insert(:note_activity)
+ expected_redirect_url = Object.normalize(note_activity).data["id"]
+
+ redirect_url =
+ conn
+ |> put_req_header("accept", "application/activity+json")
+ |> get("/notice/#{note_activity.id}")
+ |> redirected_to()
+
+ assert redirect_url == expected_redirect_url
+ end
+
+ test "returns a 404 on remote notice when json requested", %{conn: conn} do
+ note_activity = insert(:note_activity, local: false)
+
+ conn
+ |> put_req_header("accept", "application/activity+json")
+ |> get("/notice/#{note_activity.id}")
+ |> response(404)
+ end
+
+ test "500s when actor not found", %{conn: conn} do
+ note_activity = insert(:note_activity)
+ user = User.get_cached_by_ap_id(note_activity.data["actor"])
+ User.invalidate_cache(user)
+ Pleroma.Repo.delete(user)
+
+ conn =
+ conn
+ |> get("/notice/#{note_activity.id}")
+
+ assert response(conn, 500) == ~S({"error":"Something went wrong"})
+ end
+
+ test "render html for redirect for html format", %{conn: conn} do
+ note_activity = insert(:note_activity)
+
+ resp =
+ conn
+ |> put_req_header("accept", "text/html")
+ |> get("/notice/#{note_activity.id}")
+ |> response(200)
+
+ assert resp =~
+ "<meta content=\"#{Pleroma.Web.base_url()}/notice/#{note_activity.id}\" property=\"og:url\">"
+
+ user = insert(:user)
+
+ {:ok, like_activity} = CommonAPI.favorite(user, note_activity.id)
+
+ assert like_activity.data["type"] == "Like"
+
+ resp =
+ conn
+ |> put_req_header("accept", "text/html")
+ |> get("/notice/#{like_activity.id}")
+ |> response(200)
+
+ assert resp =~ "<!--server-generated-meta-->"
+ end
+
+ test "404s a private notice", %{conn: conn} do
+ note_activity = insert(:direct_note_activity)
+ url = "/notice/#{note_activity.id}"
+
+ conn =
+ conn
+ |> get(url)
+
+ assert response(conn, 404)
+ end
+
+ test "404s a non-existing notice", %{conn: conn} do
+ url = "/notice/123"
+
+ conn =
+ conn
+ |> get(url)
+
+ assert response(conn, 404)
+ end
+
+ test "it requires authentication if instance is NOT federating", %{
+ conn: conn
+ } do
+ user = insert(:user)
+ note_activity = insert(:note_activity)
+
+ conn = put_req_header(conn, "accept", "text/html")
+
+ ensure_federating_or_authenticated(conn, "/notice/#{note_activity.id}", user)
+ end
+ end
+
+ describe "GET /notice/:id/embed_player" do
+ setup do
+ note_activity = insert(:note_activity)
+ object = Pleroma.Object.normalize(note_activity)
+
+ object_data =
+ Map.put(object.data, "attachment", [
+ %{
+ "url" => [
+ %{
+ "href" =>
+ "https://peertube.moe/static/webseed/df5f464b-be8d-46fb-ad81-2d4c2d1630e3-480.mp4",
+ "mediaType" => "video/mp4",
+ "type" => "Link"
+ }
+ ]
+ }
+ ])
+
+ object
+ |> Ecto.Changeset.change(data: object_data)
+ |> Pleroma.Repo.update()
+
+ %{note_activity: note_activity}
+ end
+
+ test "renders embed player", %{conn: conn, note_activity: note_activity} do
+ conn = get(conn, "/notice/#{note_activity.id}/embed_player")
+
+ assert Plug.Conn.get_resp_header(conn, "x-frame-options") == ["ALLOW"]
+
+ assert Plug.Conn.get_resp_header(
+ conn,
+ "content-security-policy"
+ ) == [
+ "default-src 'none';style-src 'self' 'unsafe-inline';img-src 'self' data: https:; media-src 'self' https:;"
+ ]
+
+ assert response(conn, 200) =~
+ "<video controls loop><source src=\"https://peertube.moe/static/webseed/df5f464b-be8d-46fb-ad81-2d4c2d1630e3-480.mp4\" type=\"video/mp4\">Your browser does not support video/mp4 playback.</video>"
+ end
+
+ test "404s when activity isn't create", %{conn: conn} do
+ note_activity = insert(:note_activity, data_attrs: %{"type" => "Like"})
+
+ assert conn
+ |> get("/notice/#{note_activity.id}/embed_player")
+ |> response(404)
+ end
+
+ test "404s when activity is direct message", %{conn: conn} do
+ note_activity = insert(:note_activity, data_attrs: %{"directMessage" => true})
+
+ assert conn
+ |> get("/notice/#{note_activity.id}/embed_player")
+ |> response(404)
+ end
+
+ test "404s when attachment is empty", %{conn: conn} do
+ note_activity = insert(:note_activity)
+ object = Pleroma.Object.normalize(note_activity)
+ object_data = Map.put(object.data, "attachment", [])
+
+ object
+ |> Ecto.Changeset.change(data: object_data)
+ |> Pleroma.Repo.update()
+
+ assert conn
+ |> get("/notice/#{note_activity.id}/embed_player")
+ |> response(404)
+ end
+
+ test "404s when attachment isn't audio or video", %{conn: conn} do
+ note_activity = insert(:note_activity)
+ object = Pleroma.Object.normalize(note_activity)
+
+ object_data =
+ Map.put(object.data, "attachment", [
+ %{
+ "url" => [
+ %{
+ "href" => "https://peertube.moe/static/webseed/480.jpg",
+ "mediaType" => "image/jpg",
+ "type" => "Link"
+ }
+ ]
+ }
+ ])
+
+ object
+ |> Ecto.Changeset.change(data: object_data)
+ |> Pleroma.Repo.update()
+
+ conn
+ |> get("/notice/#{note_activity.id}/embed_player")
+ |> response(404)
+ end
+
+ test "it requires authentication if instance is NOT federating", %{
+ conn: conn,
+ note_activity: note_activity
+ } do
+ user = insert(:user)
+ conn = put_req_header(conn, "accept", "text/html")
+
+ ensure_federating_or_authenticated(conn, "/notice/#{note_activity.id}/embed_player", user)
+ end
+ end
+end
diff --git a/test/pleroma/web/pleroma_api/controllers/account_controller_test.exs b/test/pleroma/web/pleroma_api/controllers/account_controller_test.exs
@@ -0,0 +1,284 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.PleromaAPI.AccountControllerTest do
+ use Pleroma.Web.ConnCase
+
+ alias Pleroma.Config
+ alias Pleroma.Tests.ObanHelpers
+ alias Pleroma.User
+ alias Pleroma.Web.CommonAPI
+
+ import Pleroma.Factory
+ import Swoosh.TestAssertions
+
+ describe "POST /api/v1/pleroma/accounts/confirmation_resend" do
+ setup do
+ {:ok, user} =
+ insert(:user)
+ |> User.confirmation_changeset(need_confirmation: true)
+ |> User.update_and_set_cache()
+
+ assert user.confirmation_pending
+
+ [user: user]
+ end
+
+ setup do: clear_config([:instance, :account_activation_required], true)
+
+ test "resend account confirmation email", %{conn: conn, user: user} do
+ conn
+ |> put_req_header("content-type", "application/json")
+ |> post("/api/v1/pleroma/accounts/confirmation_resend?email=#{user.email}")
+ |> json_response_and_validate_schema(:no_content)
+
+ ObanHelpers.perform_all()
+
+ email = Pleroma.Emails.UserEmail.account_confirmation_email(user)
+ notify_email = Config.get([:instance, :notify_email])
+ instance_name = Config.get([:instance, :name])
+
+ assert_email_sent(
+ from: {instance_name, notify_email},
+ to: {user.name, user.email},
+ html_body: email.html_body
+ )
+ end
+
+ test "resend account confirmation email (with nickname)", %{conn: conn, user: user} do
+ conn
+ |> put_req_header("content-type", "application/json")
+ |> post("/api/v1/pleroma/accounts/confirmation_resend?nickname=#{user.nickname}")
+ |> json_response_and_validate_schema(:no_content)
+
+ ObanHelpers.perform_all()
+
+ email = Pleroma.Emails.UserEmail.account_confirmation_email(user)
+ notify_email = Config.get([:instance, :notify_email])
+ instance_name = Config.get([:instance, :name])
+
+ assert_email_sent(
+ from: {instance_name, notify_email},
+ to: {user.name, user.email},
+ html_body: email.html_body
+ )
+ end
+ end
+
+ describe "getting favorites timeline of specified user" do
+ setup do
+ [current_user, user] = insert_pair(:user, hide_favorites: false)
+ %{user: current_user, conn: conn} = oauth_access(["read:favourites"], user: current_user)
+ [current_user: current_user, user: user, conn: conn]
+ end
+
+ test "returns list of statuses favorited by specified user", %{
+ conn: conn,
+ user: user
+ } do
+ [activity | _] = insert_pair(:note_activity)
+ CommonAPI.favorite(user, activity.id)
+
+ response =
+ conn
+ |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
+ |> json_response_and_validate_schema(:ok)
+
+ [like] = response
+
+ assert length(response) == 1
+ assert like["id"] == activity.id
+ end
+
+ test "returns favorites for specified user_id when requester is not logged in", %{
+ user: user
+ } do
+ activity = insert(:note_activity)
+ CommonAPI.favorite(user, activity.id)
+
+ response =
+ build_conn()
+ |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
+ |> json_response_and_validate_schema(200)
+
+ assert length(response) == 1
+ end
+
+ test "returns favorited DM only when user is logged in and he is one of recipients", %{
+ current_user: current_user,
+ user: user
+ } do
+ {:ok, direct} =
+ CommonAPI.post(current_user, %{
+ status: "Hi @#{user.nickname}!",
+ visibility: "direct"
+ })
+
+ CommonAPI.favorite(user, direct.id)
+
+ for u <- [user, current_user] do
+ response =
+ build_conn()
+ |> assign(:user, u)
+ |> assign(:token, insert(:oauth_token, user: u, scopes: ["read:favourites"]))
+ |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
+ |> json_response_and_validate_schema(:ok)
+
+ assert length(response) == 1
+ end
+
+ response =
+ build_conn()
+ |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
+ |> json_response_and_validate_schema(200)
+
+ assert length(response) == 0
+ end
+
+ test "does not return others' favorited DM when user is not one of recipients", %{
+ conn: conn,
+ user: user
+ } do
+ user_two = insert(:user)
+
+ {:ok, direct} =
+ CommonAPI.post(user_two, %{
+ status: "Hi @#{user.nickname}!",
+ visibility: "direct"
+ })
+
+ CommonAPI.favorite(user, direct.id)
+
+ response =
+ conn
+ |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
+ |> json_response_and_validate_schema(:ok)
+
+ assert Enum.empty?(response)
+ end
+
+ test "paginates favorites using since_id and max_id", %{
+ conn: conn,
+ user: user
+ } do
+ activities = insert_list(10, :note_activity)
+
+ Enum.each(activities, fn activity ->
+ CommonAPI.favorite(user, activity.id)
+ end)
+
+ third_activity = Enum.at(activities, 2)
+ seventh_activity = Enum.at(activities, 6)
+
+ response =
+ conn
+ |> get(
+ "/api/v1/pleroma/accounts/#{user.id}/favourites?since_id=#{third_activity.id}&max_id=#{
+ seventh_activity.id
+ }"
+ )
+ |> json_response_and_validate_schema(:ok)
+
+ assert length(response) == 3
+ refute third_activity in response
+ refute seventh_activity in response
+ end
+
+ test "limits favorites using limit parameter", %{
+ conn: conn,
+ user: user
+ } do
+ 7
+ |> insert_list(:note_activity)
+ |> Enum.each(fn activity ->
+ CommonAPI.favorite(user, activity.id)
+ end)
+
+ response =
+ conn
+ |> get("/api/v1/pleroma/accounts/#{user.id}/favourites?limit=3")
+ |> json_response_and_validate_schema(:ok)
+
+ assert length(response) == 3
+ end
+
+ test "returns empty response when user does not have any favorited statuses", %{
+ conn: conn,
+ user: user
+ } do
+ response =
+ conn
+ |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
+ |> json_response_and_validate_schema(:ok)
+
+ assert Enum.empty?(response)
+ end
+
+ test "returns 404 error when specified user is not exist", %{conn: conn} do
+ conn = get(conn, "/api/v1/pleroma/accounts/test/favourites")
+
+ assert json_response_and_validate_schema(conn, 404) == %{"error" => "Record not found"}
+ end
+
+ test "returns 403 error when user has hidden own favorites", %{conn: conn} do
+ user = insert(:user, hide_favorites: true)
+ activity = insert(:note_activity)
+ CommonAPI.favorite(user, activity.id)
+
+ conn = get(conn, "/api/v1/pleroma/accounts/#{user.id}/favourites")
+
+ assert json_response_and_validate_schema(conn, 403) == %{"error" => "Can't get favorites"}
+ end
+
+ test "hides favorites for new users by default", %{conn: conn} do
+ user = insert(:user)
+ activity = insert(:note_activity)
+ CommonAPI.favorite(user, activity.id)
+
+ assert user.hide_favorites
+ conn = get(conn, "/api/v1/pleroma/accounts/#{user.id}/favourites")
+
+ assert json_response_and_validate_schema(conn, 403) == %{"error" => "Can't get favorites"}
+ end
+ end
+
+ describe "subscribing / unsubscribing" do
+ test "subscribing / unsubscribing to a user" do
+ %{user: user, conn: conn} = oauth_access(["follow"])
+ subscription_target = insert(:user)
+
+ ret_conn =
+ conn
+ |> assign(:user, user)
+ |> post("/api/v1/pleroma/accounts/#{subscription_target.id}/subscribe")
+
+ assert %{"id" => _id, "subscribing" => true} =
+ json_response_and_validate_schema(ret_conn, 200)
+
+ conn = post(conn, "/api/v1/pleroma/accounts/#{subscription_target.id}/unsubscribe")
+
+ assert %{"id" => _id, "subscribing" => false} = json_response_and_validate_schema(conn, 200)
+ end
+ end
+
+ describe "subscribing" do
+ test "returns 404 when subscription_target not found" do
+ %{conn: conn} = oauth_access(["write:follows"])
+
+ conn = post(conn, "/api/v1/pleroma/accounts/target_id/subscribe")
+
+ assert %{"error" => "Record not found"} = json_response_and_validate_schema(conn, 404)
+ end
+ end
+
+ describe "unsubscribing" do
+ test "returns 404 when subscription_target not found" do
+ %{conn: conn} = oauth_access(["follow"])
+
+ conn = post(conn, "/api/v1/pleroma/accounts/target_id/unsubscribe")
+
+ assert %{"error" => "Record not found"} = json_response_and_validate_schema(conn, 404)
+ end
+ end
+end
diff --git a/test/pleroma/web/pleroma_api/controllers/chat_controller_test.exs b/test/pleroma/web/pleroma_api/controllers/chat_controller_test.exs
@@ -0,0 +1,410 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+defmodule Pleroma.Web.PleromaAPI.ChatControllerTest do
+ use Pleroma.Web.ConnCase
+
+ alias Pleroma.Chat
+ alias Pleroma.Chat.MessageReference
+ alias Pleroma.Object
+ alias Pleroma.User
+ alias Pleroma.Web.ActivityPub.ActivityPub
+ alias Pleroma.Web.CommonAPI
+
+ import Pleroma.Factory
+
+ describe "POST /api/v1/pleroma/chats/:id/messages/:message_id/read" do
+ setup do: oauth_access(["write:chats"])
+
+ test "it marks one message as read", %{conn: conn, user: user} do
+ other_user = insert(:user)
+
+ {:ok, create} = CommonAPI.post_chat_message(other_user, user, "sup")
+ {:ok, _create} = CommonAPI.post_chat_message(other_user, user, "sup part 2")
+ {:ok, chat} = Chat.get_or_create(user.id, other_user.ap_id)
+ object = Object.normalize(create, false)
+ cm_ref = MessageReference.for_chat_and_object(chat, object)
+
+ assert cm_ref.unread == true
+
+ result =
+ conn
+ |> post("/api/v1/pleroma/chats/#{chat.id}/messages/#{cm_ref.id}/read")
+ |> json_response_and_validate_schema(200)
+
+ assert result["unread"] == false
+
+ cm_ref = MessageReference.for_chat_and_object(chat, object)
+
+ assert cm_ref.unread == false
+ end
+ end
+
+ describe "POST /api/v1/pleroma/chats/:id/read" do
+ setup do: oauth_access(["write:chats"])
+
+ test "given a `last_read_id`, it marks everything until then as read", %{
+ conn: conn,
+ user: user
+ } do
+ other_user = insert(:user)
+
+ {:ok, create} = CommonAPI.post_chat_message(other_user, user, "sup")
+ {:ok, _create} = CommonAPI.post_chat_message(other_user, user, "sup part 2")
+ {:ok, chat} = Chat.get_or_create(user.id, other_user.ap_id)
+ object = Object.normalize(create, false)
+ cm_ref = MessageReference.for_chat_and_object(chat, object)
+
+ assert cm_ref.unread == true
+
+ result =
+ conn
+ |> put_req_header("content-type", "application/json")
+ |> post("/api/v1/pleroma/chats/#{chat.id}/read", %{"last_read_id" => cm_ref.id})
+ |> json_response_and_validate_schema(200)
+
+ assert result["unread"] == 1
+
+ cm_ref = MessageReference.for_chat_and_object(chat, object)
+
+ assert cm_ref.unread == false
+ end
+ end
+
+ describe "POST /api/v1/pleroma/chats/:id/messages" do
+ setup do: oauth_access(["write:chats"])
+
+ test "it posts a message to the chat", %{conn: conn, user: user} do
+ other_user = insert(:user)
+
+ {:ok, chat} = Chat.get_or_create(user.id, other_user.ap_id)
+
+ result =
+ conn
+ |> put_req_header("content-type", "application/json")
+ |> post("/api/v1/pleroma/chats/#{chat.id}/messages", %{"content" => "Hallo!!"})
+ |> json_response_and_validate_schema(200)
+
+ assert result["content"] == "Hallo!!"
+ assert result["chat_id"] == chat.id |> to_string()
+ end
+
+ test "it fails if there is no content", %{conn: conn, user: user} do
+ other_user = insert(:user)
+
+ {:ok, chat} = Chat.get_or_create(user.id, other_user.ap_id)
+
+ result =
+ conn
+ |> put_req_header("content-type", "application/json")
+ |> post("/api/v1/pleroma/chats/#{chat.id}/messages")
+ |> json_response_and_validate_schema(400)
+
+ assert %{"error" => "no_content"} == result
+ end
+
+ test "it works with an attachment", %{conn: conn, user: user} do
+ file = %Plug.Upload{
+ content_type: "image/jpg",
+ path: Path.absname("test/fixtures/image.jpg"),
+ filename: "an_image.jpg"
+ }
+
+ {:ok, upload} = ActivityPub.upload(file, actor: user.ap_id)
+
+ other_user = insert(:user)
+
+ {:ok, chat} = Chat.get_or_create(user.id, other_user.ap_id)
+
+ result =
+ conn
+ |> put_req_header("content-type", "application/json")
+ |> post("/api/v1/pleroma/chats/#{chat.id}/messages", %{
+ "media_id" => to_string(upload.id)
+ })
+ |> json_response_and_validate_schema(200)
+
+ assert result["attachment"]
+ end
+
+ test "gets MRF reason when rejected", %{conn: conn, user: user} do
+ clear_config([:mrf_keyword, :reject], ["GNO"])
+ clear_config([:mrf, :policies], [Pleroma.Web.ActivityPub.MRF.KeywordPolicy])
+
+ other_user = insert(:user)
+
+ {:ok, chat} = Chat.get_or_create(user.id, other_user.ap_id)
+
+ result =
+ conn
+ |> put_req_header("content-type", "application/json")
+ |> post("/api/v1/pleroma/chats/#{chat.id}/messages", %{"content" => "GNO/Linux"})
+ |> json_response_and_validate_schema(422)
+
+ assert %{"error" => "[KeywordPolicy] Matches with rejected keyword"} == result
+ end
+ end
+
+ describe "DELETE /api/v1/pleroma/chats/:id/messages/:message_id" do
+ setup do: oauth_access(["write:chats"])
+
+ test "it deletes a message from the chat", %{conn: conn, user: user} do
+ recipient = insert(:user)
+
+ {:ok, message} =
+ CommonAPI.post_chat_message(user, recipient, "Hello darkness my old friend")
+
+ {:ok, other_message} = CommonAPI.post_chat_message(recipient, user, "nico nico ni")
+
+ object = Object.normalize(message, false)
+
+ chat = Chat.get(user.id, recipient.ap_id)
+
+ cm_ref = MessageReference.for_chat_and_object(chat, object)
+
+ # Deleting your own message removes the message and the reference
+ result =
+ conn
+ |> put_req_header("content-type", "application/json")
+ |> delete("/api/v1/pleroma/chats/#{chat.id}/messages/#{cm_ref.id}")
+ |> json_response_and_validate_schema(200)
+
+ assert result["id"] == cm_ref.id
+ refute MessageReference.get_by_id(cm_ref.id)
+ assert %{data: %{"type" => "Tombstone"}} = Object.get_by_id(object.id)
+
+ # Deleting other people's messages just removes the reference
+ object = Object.normalize(other_message, false)
+ cm_ref = MessageReference.for_chat_and_object(chat, object)
+
+ result =
+ conn
+ |> put_req_header("content-type", "application/json")
+ |> delete("/api/v1/pleroma/chats/#{chat.id}/messages/#{cm_ref.id}")
+ |> json_response_and_validate_schema(200)
+
+ assert result["id"] == cm_ref.id
+ refute MessageReference.get_by_id(cm_ref.id)
+ assert Object.get_by_id(object.id)
+ end
+ end
+
+ describe "GET /api/v1/pleroma/chats/:id/messages" do
+ setup do: oauth_access(["read:chats"])
+
+ test "it paginates", %{conn: conn, user: user} do
+ recipient = insert(:user)
+
+ Enum.each(1..30, fn _ ->
+ {:ok, _} = CommonAPI.post_chat_message(user, recipient, "hey")
+ end)
+
+ chat = Chat.get(user.id, recipient.ap_id)
+
+ response = get(conn, "/api/v1/pleroma/chats/#{chat.id}/messages")
+ result = json_response_and_validate_schema(response, 200)
+
+ [next, prev] = get_resp_header(response, "link") |> hd() |> String.split(", ")
+ api_endpoint = "/api/v1/pleroma/chats/"
+
+ assert String.match?(
+ next,
+ ~r(#{api_endpoint}.*/messages\?id=.*&limit=\d+&max_id=.*; rel=\"next\"$)
+ )
+
+ assert String.match?(
+ prev,
+ ~r(#{api_endpoint}.*/messages\?id=.*&limit=\d+&min_id=.*; rel=\"prev\"$)
+ )
+
+ assert length(result) == 20
+
+ response =
+ get(conn, "/api/v1/pleroma/chats/#{chat.id}/messages?max_id=#{List.last(result)["id"]}")
+
+ result = json_response_and_validate_schema(response, 200)
+ [next, prev] = get_resp_header(response, "link") |> hd() |> String.split(", ")
+
+ assert String.match?(
+ next,
+ ~r(#{api_endpoint}.*/messages\?id=.*&limit=\d+&max_id=.*; rel=\"next\"$)
+ )
+
+ assert String.match?(
+ prev,
+ ~r(#{api_endpoint}.*/messages\?id=.*&limit=\d+&max_id=.*&min_id=.*; rel=\"prev\"$)
+ )
+
+ assert length(result) == 10
+ end
+
+ test "it returns the messages for a given chat", %{conn: conn, user: user} do
+ other_user = insert(:user)
+ third_user = insert(:user)
+
+ {:ok, _} = CommonAPI.post_chat_message(user, other_user, "hey")
+ {:ok, _} = CommonAPI.post_chat_message(user, third_user, "hey")
+ {:ok, _} = CommonAPI.post_chat_message(user, other_user, "how are you?")
+ {:ok, _} = CommonAPI.post_chat_message(other_user, user, "fine, how about you?")
+
+ chat = Chat.get(user.id, other_user.ap_id)
+
+ result =
+ conn
+ |> get("/api/v1/pleroma/chats/#{chat.id}/messages")
+ |> json_response_and_validate_schema(200)
+
+ result
+ |> Enum.each(fn message ->
+ assert message["chat_id"] == chat.id |> to_string()
+ end)
+
+ assert length(result) == 3
+
+ # Trying to get the chat of a different user
+ conn
+ |> assign(:user, other_user)
+ |> get("/api/v1/pleroma/chats/#{chat.id}/messages")
+ |> json_response_and_validate_schema(404)
+ end
+ end
+
+ describe "POST /api/v1/pleroma/chats/by-account-id/:id" do
+ setup do: oauth_access(["write:chats"])
+
+ test "it creates or returns a chat", %{conn: conn} do
+ other_user = insert(:user)
+
+ result =
+ conn
+ |> post("/api/v1/pleroma/chats/by-account-id/#{other_user.id}")
+ |> json_response_and_validate_schema(200)
+
+ assert result["id"]
+ end
+ end
+
+ describe "GET /api/v1/pleroma/chats/:id" do
+ setup do: oauth_access(["read:chats"])
+
+ test "it returns a chat", %{conn: conn, user: user} do
+ other_user = insert(:user)
+
+ {:ok, chat} = Chat.get_or_create(user.id, other_user.ap_id)
+
+ result =
+ conn
+ |> get("/api/v1/pleroma/chats/#{chat.id}")
+ |> json_response_and_validate_schema(200)
+
+ assert result["id"] == to_string(chat.id)
+ end
+ end
+
+ describe "GET /api/v1/pleroma/chats" do
+ setup do: oauth_access(["read:chats"])
+
+ test "it does not return chats with deleted users", %{conn: conn, user: user} do
+ recipient = insert(:user)
+ {:ok, _} = Chat.get_or_create(user.id, recipient.ap_id)
+
+ Pleroma.Repo.delete(recipient)
+ User.invalidate_cache(recipient)
+
+ result =
+ conn
+ |> get("/api/v1/pleroma/chats")
+ |> json_response_and_validate_schema(200)
+
+ assert length(result) == 0
+ end
+
+ test "it does not return chats with users you blocked", %{conn: conn, user: user} do
+ recipient = insert(:user)
+
+ {:ok, _} = Chat.get_or_create(user.id, recipient.ap_id)
+
+ result =
+ conn
+ |> get("/api/v1/pleroma/chats")
+ |> json_response_and_validate_schema(200)
+
+ assert length(result) == 1
+
+ User.block(user, recipient)
+
+ result =
+ conn
+ |> get("/api/v1/pleroma/chats")
+ |> json_response_and_validate_schema(200)
+
+ assert length(result) == 0
+ end
+
+ test "it returns all chats", %{conn: conn, user: user} do
+ Enum.each(1..30, fn _ ->
+ recipient = insert(:user)
+ {:ok, _} = Chat.get_or_create(user.id, recipient.ap_id)
+ end)
+
+ result =
+ conn
+ |> get("/api/v1/pleroma/chats")
+ |> json_response_and_validate_schema(200)
+
+ assert length(result) == 30
+ end
+
+ test "it return a list of chats the current user is participating in, in descending order of updates",
+ %{conn: conn, user: user} do
+ har = insert(:user)
+ jafnhar = insert(:user)
+ tridi = insert(:user)
+
+ {:ok, chat_1} = Chat.get_or_create(user.id, har.ap_id)
+ :timer.sleep(1000)
+ {:ok, _chat_2} = Chat.get_or_create(user.id, jafnhar.ap_id)
+ :timer.sleep(1000)
+ {:ok, chat_3} = Chat.get_or_create(user.id, tridi.ap_id)
+ :timer.sleep(1000)
+
+ # bump the second one
+ {:ok, chat_2} = Chat.bump_or_create(user.id, jafnhar.ap_id)
+
+ result =
+ conn
+ |> get("/api/v1/pleroma/chats")
+ |> json_response_and_validate_schema(200)
+
+ ids = Enum.map(result, & &1["id"])
+
+ assert ids == [
+ chat_2.id |> to_string(),
+ chat_3.id |> to_string(),
+ chat_1.id |> to_string()
+ ]
+ end
+
+ test "it is not affected by :restrict_unauthenticated setting (issue #1973)", %{
+ conn: conn,
+ user: user
+ } do
+ clear_config([:restrict_unauthenticated, :profiles, :local], true)
+ clear_config([:restrict_unauthenticated, :profiles, :remote], true)
+
+ user2 = insert(:user)
+ user3 = insert(:user, local: false)
+
+ {:ok, _chat_12} = Chat.get_or_create(user.id, user2.ap_id)
+ {:ok, _chat_13} = Chat.get_or_create(user.id, user3.ap_id)
+
+ result =
+ conn
+ |> get("/api/v1/pleroma/chats")
+ |> json_response_and_validate_schema(200)
+
+ account_ids = Enum.map(result, &get_in(&1, ["account", "id"]))
+ assert Enum.sort(account_ids) == Enum.sort([user2.id, user3.id])
+ end
+ end
+end
diff --git a/test/pleroma/web/pleroma_api/controllers/conversation_controller_test.exs b/test/pleroma/web/pleroma_api/controllers/conversation_controller_test.exs
@@ -0,0 +1,136 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.PleromaAPI.ConversationControllerTest do
+ use Pleroma.Web.ConnCase
+
+ alias Pleroma.Conversation.Participation
+ alias Pleroma.Repo
+ alias Pleroma.User
+ alias Pleroma.Web.CommonAPI
+
+ import Pleroma.Factory
+
+ test "/api/v1/pleroma/conversations/:id" do
+ user = insert(:user)
+ %{user: other_user, conn: conn} = oauth_access(["read:statuses"])
+
+ {:ok, _activity} =
+ CommonAPI.post(user, %{status: "Hi @#{other_user.nickname}!", visibility: "direct"})
+
+ [participation] = Participation.for_user(other_user)
+
+ result =
+ conn
+ |> get("/api/v1/pleroma/conversations/#{participation.id}")
+ |> json_response_and_validate_schema(200)
+
+ assert result["id"] == participation.id |> to_string()
+ end
+
+ test "/api/v1/pleroma/conversations/:id/statuses" do
+ user = insert(:user)
+ %{user: other_user, conn: conn} = oauth_access(["read:statuses"])
+ third_user = insert(:user)
+
+ {:ok, _activity} =
+ CommonAPI.post(user, %{status: "Hi @#{third_user.nickname}!", visibility: "direct"})
+
+ {:ok, activity} =
+ CommonAPI.post(user, %{status: "Hi @#{other_user.nickname}!", visibility: "direct"})
+
+ [participation] = Participation.for_user(other_user)
+
+ {:ok, activity_two} =
+ CommonAPI.post(other_user, %{
+ status: "Hi!",
+ in_reply_to_status_id: activity.id,
+ in_reply_to_conversation_id: participation.id
+ })
+
+ result =
+ conn
+ |> get("/api/v1/pleroma/conversations/#{participation.id}/statuses")
+ |> json_response_and_validate_schema(200)
+
+ assert length(result) == 2
+
+ id_one = activity.id
+ id_two = activity_two.id
+ assert [%{"id" => ^id_one}, %{"id" => ^id_two}] = result
+
+ {:ok, %{id: id_three}} =
+ CommonAPI.post(other_user, %{
+ status: "Bye!",
+ in_reply_to_status_id: activity.id,
+ in_reply_to_conversation_id: participation.id
+ })
+
+ assert [%{"id" => ^id_two}, %{"id" => ^id_three}] =
+ conn
+ |> get("/api/v1/pleroma/conversations/#{participation.id}/statuses?limit=2")
+ |> json_response_and_validate_schema(:ok)
+
+ assert [%{"id" => ^id_three}] =
+ conn
+ |> get("/api/v1/pleroma/conversations/#{participation.id}/statuses?min_id=#{id_two}")
+ |> json_response_and_validate_schema(:ok)
+ end
+
+ test "PATCH /api/v1/pleroma/conversations/:id" do
+ %{user: user, conn: conn} = oauth_access(["write:conversations"])
+ other_user = insert(:user)
+
+ {:ok, _activity} = CommonAPI.post(user, %{status: "Hi", visibility: "direct"})
+
+ [participation] = Participation.for_user(user)
+
+ participation = Repo.preload(participation, :recipients)
+
+ user = User.get_cached_by_id(user.id)
+ assert [user] == participation.recipients
+ assert other_user not in participation.recipients
+
+ query = "recipients[]=#{user.id}&recipients[]=#{other_user.id}"
+
+ result =
+ conn
+ |> patch("/api/v1/pleroma/conversations/#{participation.id}?#{query}")
+ |> json_response_and_validate_schema(200)
+
+ assert result["id"] == participation.id |> to_string
+
+ [participation] = Participation.for_user(user)
+ participation = Repo.preload(participation, :recipients)
+
+ assert user in participation.recipients
+ assert other_user in participation.recipients
+ end
+
+ test "POST /api/v1/pleroma/conversations/read" do
+ user = insert(:user)
+ %{user: other_user, conn: conn} = oauth_access(["write:conversations"])
+
+ {:ok, _activity} =
+ CommonAPI.post(user, %{status: "Hi @#{other_user.nickname}", visibility: "direct"})
+
+ {:ok, _activity} =
+ CommonAPI.post(user, %{status: "Hi @#{other_user.nickname}", visibility: "direct"})
+
+ [participation2, participation1] = Participation.for_user(other_user)
+ assert Participation.get(participation2.id).read == false
+ assert Participation.get(participation1.id).read == false
+ assert User.get_cached_by_id(other_user.id).unread_conversation_count == 2
+
+ [%{"unread" => false}, %{"unread" => false}] =
+ conn
+ |> post("/api/v1/pleroma/conversations/read", %{})
+ |> json_response_and_validate_schema(200)
+
+ [participation2, participation1] = Participation.for_user(other_user)
+ assert Participation.get(participation2.id).read == true
+ assert Participation.get(participation1.id).read == true
+ assert User.get_cached_by_id(other_user.id).unread_conversation_count == 0
+ end
+end
diff --git a/test/pleroma/web/pleroma_api/controllers/emoji_pack_controller_test.exs b/test/pleroma/web/pleroma_api/controllers/emoji_pack_controller_test.exs
@@ -0,0 +1,604 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.PleromaAPI.EmojiPackControllerTest do
+ use Pleroma.Web.ConnCase
+
+ import Tesla.Mock
+ import Pleroma.Factory
+
+ @emoji_path Path.join(
+ Pleroma.Config.get!([:instance, :static_dir]),
+ "emoji"
+ )
+ setup do: clear_config([:auth, :enforce_oauth_admin_scope_usage], false)
+
+ setup do: clear_config([:instance, :public], true)
+
+ setup do
+ admin = insert(:user, is_admin: true)
+ token = insert(:oauth_admin_token, user: admin)
+
+ admin_conn =
+ build_conn()
+ |> assign(:user, admin)
+ |> assign(:token, token)
+
+ Pleroma.Emoji.reload()
+ {:ok, %{admin_conn: admin_conn}}
+ end
+
+ test "GET /api/pleroma/emoji/packs when :public: false", %{conn: conn} do
+ Config.put([:instance, :public], false)
+ conn |> get("/api/pleroma/emoji/packs") |> json_response_and_validate_schema(200)
+ end
+
+ test "GET /api/pleroma/emoji/packs", %{conn: conn} do
+ resp = conn |> get("/api/pleroma/emoji/packs") |> json_response_and_validate_schema(200)
+
+ assert resp["count"] == 4
+
+ assert resp["packs"]
+ |> Map.keys()
+ |> length() == 4
+
+ shared = resp["packs"]["test_pack"]
+ assert shared["files"] == %{"blank" => "blank.png", "blank2" => "blank2.png"}
+ assert Map.has_key?(shared["pack"], "download-sha256")
+ assert shared["pack"]["can-download"]
+ assert shared["pack"]["share-files"]
+
+ non_shared = resp["packs"]["test_pack_nonshared"]
+ assert non_shared["pack"]["share-files"] == false
+ assert non_shared["pack"]["can-download"] == false
+
+ resp =
+ conn
+ |> get("/api/pleroma/emoji/packs?page_size=1")
+ |> json_response_and_validate_schema(200)
+
+ assert resp["count"] == 4
+
+ packs = Map.keys(resp["packs"])
+
+ assert length(packs) == 1
+
+ [pack1] = packs
+
+ resp =
+ conn
+ |> get("/api/pleroma/emoji/packs?page_size=1&page=2")
+ |> json_response_and_validate_schema(200)
+
+ assert resp["count"] == 4
+ packs = Map.keys(resp["packs"])
+ assert length(packs) == 1
+ [pack2] = packs
+
+ resp =
+ conn
+ |> get("/api/pleroma/emoji/packs?page_size=1&page=3")
+ |> json_response_and_validate_schema(200)
+
+ assert resp["count"] == 4
+ packs = Map.keys(resp["packs"])
+ assert length(packs) == 1
+ [pack3] = packs
+
+ resp =
+ conn
+ |> get("/api/pleroma/emoji/packs?page_size=1&page=4")
+ |> json_response_and_validate_schema(200)
+
+ assert resp["count"] == 4
+ packs = Map.keys(resp["packs"])
+ assert length(packs) == 1
+ [pack4] = packs
+ assert [pack1, pack2, pack3, pack4] |> Enum.uniq() |> length() == 4
+ end
+
+ describe "GET /api/pleroma/emoji/packs/remote" do
+ test "shareable instance", %{admin_conn: admin_conn, conn: conn} do
+ resp =
+ conn
+ |> get("/api/pleroma/emoji/packs?page=2&page_size=1")
+ |> json_response_and_validate_schema(200)
+
+ mock(fn
+ %{method: :get, url: "https://example.com/.well-known/nodeinfo"} ->
+ json(%{links: [%{href: "https://example.com/nodeinfo/2.1.json"}]})
+
+ %{method: :get, url: "https://example.com/nodeinfo/2.1.json"} ->
+ json(%{metadata: %{features: ["shareable_emoji_packs"]}})
+
+ %{method: :get, url: "https://example.com/api/pleroma/emoji/packs?page=2&page_size=1"} ->
+ json(resp)
+ end)
+
+ assert admin_conn
+ |> get("/api/pleroma/emoji/packs/remote?url=https://example.com&page=2&page_size=1")
+ |> json_response_and_validate_schema(200) == resp
+ end
+
+ test "non shareable instance", %{admin_conn: admin_conn} do
+ mock(fn
+ %{method: :get, url: "https://example.com/.well-known/nodeinfo"} ->
+ json(%{links: [%{href: "https://example.com/nodeinfo/2.1.json"}]})
+
+ %{method: :get, url: "https://example.com/nodeinfo/2.1.json"} ->
+ json(%{metadata: %{features: []}})
+ end)
+
+ assert admin_conn
+ |> get("/api/pleroma/emoji/packs/remote?url=https://example.com")
+ |> json_response_and_validate_schema(500) == %{
+ "error" => "The requested instance does not support sharing emoji packs"
+ }
+ end
+ end
+
+ describe "GET /api/pleroma/emoji/packs/archive?name=:name" do
+ test "download shared pack", %{conn: conn} do
+ resp =
+ conn
+ |> get("/api/pleroma/emoji/packs/archive?name=test_pack")
+ |> response(200)
+
+ {:ok, arch} = :zip.unzip(resp, [:memory])
+
+ assert Enum.find(arch, fn {n, _} -> n == 'pack.json' end)
+ assert Enum.find(arch, fn {n, _} -> n == 'blank.png' end)
+ end
+
+ test "non existing pack", %{conn: conn} do
+ assert conn
+ |> get("/api/pleroma/emoji/packs/archive?name=test_pack_for_import")
+ |> json_response_and_validate_schema(:not_found) == %{
+ "error" => "Pack test_pack_for_import does not exist"
+ }
+ end
+
+ test "non downloadable pack", %{conn: conn} do
+ assert conn
+ |> get("/api/pleroma/emoji/packs/archive?name=test_pack_nonshared")
+ |> json_response_and_validate_schema(:forbidden) == %{
+ "error" =>
+ "Pack test_pack_nonshared cannot be downloaded from this instance, either pack sharing was disabled for this pack or some files are missing"
+ }
+ end
+ end
+
+ describe "POST /api/pleroma/emoji/packs/download" do
+ test "shared pack from remote and non shared from fallback-src", %{
+ admin_conn: admin_conn,
+ conn: conn
+ } do
+ mock(fn
+ %{method: :get, url: "https://example.com/.well-known/nodeinfo"} ->
+ json(%{links: [%{href: "https://example.com/nodeinfo/2.1.json"}]})
+
+ %{method: :get, url: "https://example.com/nodeinfo/2.1.json"} ->
+ json(%{metadata: %{features: ["shareable_emoji_packs"]}})
+
+ %{
+ method: :get,
+ url: "https://example.com/api/pleroma/emoji/pack?name=test_pack"
+ } ->
+ conn
+ |> get("/api/pleroma/emoji/pack?name=test_pack")
+ |> json_response_and_validate_schema(200)
+ |> json()
+
+ %{
+ method: :get,
+ url: "https://example.com/api/pleroma/emoji/packs/archive?name=test_pack"
+ } ->
+ conn
+ |> get("/api/pleroma/emoji/packs/archive?name=test_pack")
+ |> response(200)
+ |> text()
+
+ %{
+ method: :get,
+ url: "https://example.com/api/pleroma/emoji/pack?name=test_pack_nonshared"
+ } ->
+ conn
+ |> get("/api/pleroma/emoji/pack?name=test_pack_nonshared")
+ |> json_response_and_validate_schema(200)
+ |> json()
+
+ %{
+ method: :get,
+ url: "https://nonshared-pack"
+ } ->
+ text(File.read!("#{@emoji_path}/test_pack_nonshared/nonshared.zip"))
+ end)
+
+ assert admin_conn
+ |> put_req_header("content-type", "multipart/form-data")
+ |> post("/api/pleroma/emoji/packs/download", %{
+ url: "https://example.com",
+ name: "test_pack",
+ as: "test_pack2"
+ })
+ |> json_response_and_validate_schema(200) == "ok"
+
+ assert File.exists?("#{@emoji_path}/test_pack2/pack.json")
+ assert File.exists?("#{@emoji_path}/test_pack2/blank.png")
+
+ assert admin_conn
+ |> delete("/api/pleroma/emoji/pack?name=test_pack2")
+ |> json_response_and_validate_schema(200) == "ok"
+
+ refute File.exists?("#{@emoji_path}/test_pack2")
+
+ assert admin_conn
+ |> put_req_header("content-type", "multipart/form-data")
+ |> post(
+ "/api/pleroma/emoji/packs/download",
+ %{
+ url: "https://example.com",
+ name: "test_pack_nonshared",
+ as: "test_pack_nonshared2"
+ }
+ )
+ |> json_response_and_validate_schema(200) == "ok"
+
+ assert File.exists?("#{@emoji_path}/test_pack_nonshared2/pack.json")
+ assert File.exists?("#{@emoji_path}/test_pack_nonshared2/blank.png")
+
+ assert admin_conn
+ |> delete("/api/pleroma/emoji/pack?name=test_pack_nonshared2")
+ |> json_response_and_validate_schema(200) == "ok"
+
+ refute File.exists?("#{@emoji_path}/test_pack_nonshared2")
+ end
+
+ test "nonshareable instance", %{admin_conn: admin_conn} do
+ mock(fn
+ %{method: :get, url: "https://old-instance/.well-known/nodeinfo"} ->
+ json(%{links: [%{href: "https://old-instance/nodeinfo/2.1.json"}]})
+
+ %{method: :get, url: "https://old-instance/nodeinfo/2.1.json"} ->
+ json(%{metadata: %{features: []}})
+ end)
+
+ assert admin_conn
+ |> put_req_header("content-type", "multipart/form-data")
+ |> post(
+ "/api/pleroma/emoji/packs/download",
+ %{
+ url: "https://old-instance",
+ name: "test_pack",
+ as: "test_pack2"
+ }
+ )
+ |> json_response_and_validate_schema(500) == %{
+ "error" => "The requested instance does not support sharing emoji packs"
+ }
+ end
+
+ test "checksum fail", %{admin_conn: admin_conn} do
+ mock(fn
+ %{method: :get, url: "https://example.com/.well-known/nodeinfo"} ->
+ json(%{links: [%{href: "https://example.com/nodeinfo/2.1.json"}]})
+
+ %{method: :get, url: "https://example.com/nodeinfo/2.1.json"} ->
+ json(%{metadata: %{features: ["shareable_emoji_packs"]}})
+
+ %{
+ method: :get,
+ url: "https://example.com/api/pleroma/emoji/pack?name=pack_bad_sha"
+ } ->
+ {:ok, pack} = Pleroma.Emoji.Pack.load_pack("pack_bad_sha")
+ %Tesla.Env{status: 200, body: Jason.encode!(pack)}
+
+ %{
+ method: :get,
+ url: "https://example.com/api/pleroma/emoji/packs/archive?name=pack_bad_sha"
+ } ->
+ %Tesla.Env{
+ status: 200,
+ body: File.read!("test/instance_static/emoji/pack_bad_sha/pack_bad_sha.zip")
+ }
+ end)
+
+ assert admin_conn
+ |> put_req_header("content-type", "multipart/form-data")
+ |> post("/api/pleroma/emoji/packs/download", %{
+ url: "https://example.com",
+ name: "pack_bad_sha",
+ as: "pack_bad_sha2"
+ })
+ |> json_response_and_validate_schema(:internal_server_error) == %{
+ "error" => "SHA256 for the pack doesn't match the one sent by the server"
+ }
+ end
+
+ test "other error", %{admin_conn: admin_conn} do
+ mock(fn
+ %{method: :get, url: "https://example.com/.well-known/nodeinfo"} ->
+ json(%{links: [%{href: "https://example.com/nodeinfo/2.1.json"}]})
+
+ %{method: :get, url: "https://example.com/nodeinfo/2.1.json"} ->
+ json(%{metadata: %{features: ["shareable_emoji_packs"]}})
+
+ %{
+ method: :get,
+ url: "https://example.com/api/pleroma/emoji/pack?name=test_pack"
+ } ->
+ {:ok, pack} = Pleroma.Emoji.Pack.load_pack("test_pack")
+ %Tesla.Env{status: 200, body: Jason.encode!(pack)}
+ end)
+
+ assert admin_conn
+ |> put_req_header("content-type", "multipart/form-data")
+ |> post("/api/pleroma/emoji/packs/download", %{
+ url: "https://example.com",
+ name: "test_pack",
+ as: "test_pack2"
+ })
+ |> json_response_and_validate_schema(:internal_server_error) == %{
+ "error" =>
+ "The pack was not set as shared and there is no fallback src to download from"
+ }
+ end
+ end
+
+ describe "PATCH /api/pleroma/emoji/pack?name=:name" do
+ setup do
+ pack_file = "#{@emoji_path}/test_pack/pack.json"
+ original_content = File.read!(pack_file)
+
+ on_exit(fn ->
+ File.write!(pack_file, original_content)
+ end)
+
+ {:ok,
+ pack_file: pack_file,
+ new_data: %{
+ "license" => "Test license changed",
+ "homepage" => "https://pleroma.social",
+ "description" => "Test description",
+ "share-files" => false
+ }}
+ end
+
+ test "for a pack without a fallback source", ctx do
+ assert ctx[:admin_conn]
+ |> put_req_header("content-type", "multipart/form-data")
+ |> patch("/api/pleroma/emoji/pack?name=test_pack", %{
+ "metadata" => ctx[:new_data]
+ })
+ |> json_response_and_validate_schema(200) == ctx[:new_data]
+
+ assert Jason.decode!(File.read!(ctx[:pack_file]))["pack"] == ctx[:new_data]
+ end
+
+ test "for a pack with a fallback source", ctx do
+ mock(fn
+ %{
+ method: :get,
+ url: "https://nonshared-pack"
+ } ->
+ text(File.read!("#{@emoji_path}/test_pack_nonshared/nonshared.zip"))
+ end)
+
+ new_data = Map.put(ctx[:new_data], "fallback-src", "https://nonshared-pack")
+
+ new_data_with_sha =
+ Map.put(
+ new_data,
+ "fallback-src-sha256",
+ "1967BB4E42BCC34BCC12D57BE7811D3B7BE52F965BCE45C87BD377B9499CE11D"
+ )
+
+ assert ctx[:admin_conn]
+ |> put_req_header("content-type", "multipart/form-data")
+ |> patch("/api/pleroma/emoji/pack?name=test_pack", %{metadata: new_data})
+ |> json_response_and_validate_schema(200) == new_data_with_sha
+
+ assert Jason.decode!(File.read!(ctx[:pack_file]))["pack"] == new_data_with_sha
+ end
+
+ test "when the fallback source doesn't have all the files", ctx do
+ mock(fn
+ %{
+ method: :get,
+ url: "https://nonshared-pack"
+ } ->
+ {:ok, {'empty.zip', empty_arch}} = :zip.zip('empty.zip', [], [:memory])
+ text(empty_arch)
+ end)
+
+ new_data = Map.put(ctx[:new_data], "fallback-src", "https://nonshared-pack")
+
+ assert ctx[:admin_conn]
+ |> put_req_header("content-type", "multipart/form-data")
+ |> patch("/api/pleroma/emoji/pack?name=test_pack", %{metadata: new_data})
+ |> json_response_and_validate_schema(:bad_request) == %{
+ "error" => "The fallback archive does not have all files specified in pack.json"
+ }
+ end
+ end
+
+ describe "POST/DELETE /api/pleroma/emoji/pack?name=:name" do
+ test "creating and deleting a pack", %{admin_conn: admin_conn} do
+ assert admin_conn
+ |> post("/api/pleroma/emoji/pack?name=test_created")
+ |> json_response_and_validate_schema(200) == "ok"
+
+ assert File.exists?("#{@emoji_path}/test_created/pack.json")
+
+ assert Jason.decode!(File.read!("#{@emoji_path}/test_created/pack.json")) == %{
+ "pack" => %{},
+ "files" => %{},
+ "files_count" => 0
+ }
+
+ assert admin_conn
+ |> delete("/api/pleroma/emoji/pack?name=test_created")
+ |> json_response_and_validate_schema(200) == "ok"
+
+ refute File.exists?("#{@emoji_path}/test_created/pack.json")
+ end
+
+ test "if pack exists", %{admin_conn: admin_conn} do
+ path = Path.join(@emoji_path, "test_created")
+ File.mkdir(path)
+ pack_file = Jason.encode!(%{files: %{}, pack: %{}})
+ File.write!(Path.join(path, "pack.json"), pack_file)
+
+ assert admin_conn
+ |> post("/api/pleroma/emoji/pack?name=test_created")
+ |> json_response_and_validate_schema(:conflict) == %{
+ "error" => "A pack named \"test_created\" already exists"
+ }
+
+ on_exit(fn -> File.rm_rf(path) end)
+ end
+
+ test "with empty name", %{admin_conn: admin_conn} do
+ assert admin_conn
+ |> post("/api/pleroma/emoji/pack?name= ")
+ |> json_response_and_validate_schema(:bad_request) == %{
+ "error" => "pack name cannot be empty"
+ }
+ end
+ end
+
+ test "deleting nonexisting pack", %{admin_conn: admin_conn} do
+ assert admin_conn
+ |> delete("/api/pleroma/emoji/pack?name=non_existing")
+ |> json_response_and_validate_schema(:not_found) == %{
+ "error" => "Pack non_existing does not exist"
+ }
+ end
+
+ test "deleting with empty name", %{admin_conn: admin_conn} do
+ assert admin_conn
+ |> delete("/api/pleroma/emoji/pack?name= ")
+ |> json_response_and_validate_schema(:bad_request) == %{
+ "error" => "pack name cannot be empty"
+ }
+ end
+
+ test "filesystem import", %{admin_conn: admin_conn, conn: conn} do
+ on_exit(fn ->
+ File.rm!("#{@emoji_path}/test_pack_for_import/emoji.txt")
+ File.rm!("#{@emoji_path}/test_pack_for_import/pack.json")
+ end)
+
+ resp = conn |> get("/api/pleroma/emoji/packs") |> json_response_and_validate_schema(200)
+
+ refute Map.has_key?(resp["packs"], "test_pack_for_import")
+
+ assert admin_conn
+ |> get("/api/pleroma/emoji/packs/import")
+ |> json_response_and_validate_schema(200) == ["test_pack_for_import"]
+
+ resp = conn |> get("/api/pleroma/emoji/packs") |> json_response_and_validate_schema(200)
+ assert resp["packs"]["test_pack_for_import"]["files"] == %{"blank" => "blank.png"}
+
+ File.rm!("#{@emoji_path}/test_pack_for_import/pack.json")
+ refute File.exists?("#{@emoji_path}/test_pack_for_import/pack.json")
+
+ emoji_txt_content = """
+ blank, blank.png, Fun
+ blank2, blank.png
+ foo, /emoji/test_pack_for_import/blank.png
+ bar
+ """
+
+ File.write!("#{@emoji_path}/test_pack_for_import/emoji.txt", emoji_txt_content)
+
+ assert admin_conn
+ |> get("/api/pleroma/emoji/packs/import")
+ |> json_response_and_validate_schema(200) == ["test_pack_for_import"]
+
+ resp = conn |> get("/api/pleroma/emoji/packs") |> json_response_and_validate_schema(200)
+
+ assert resp["packs"]["test_pack_for_import"]["files"] == %{
+ "blank" => "blank.png",
+ "blank2" => "blank.png",
+ "foo" => "blank.png"
+ }
+ end
+
+ describe "GET /api/pleroma/emoji/pack?name=:name" do
+ test "shows pack.json", %{conn: conn} do
+ assert %{
+ "files" => files,
+ "files_count" => 2,
+ "pack" => %{
+ "can-download" => true,
+ "description" => "Test description",
+ "download-sha256" => _,
+ "homepage" => "https://pleroma.social",
+ "license" => "Test license",
+ "share-files" => true
+ }
+ } =
+ conn
+ |> get("/api/pleroma/emoji/pack?name=test_pack")
+ |> json_response_and_validate_schema(200)
+
+ assert files == %{"blank" => "blank.png", "blank2" => "blank2.png"}
+
+ assert %{
+ "files" => files,
+ "files_count" => 2
+ } =
+ conn
+ |> get("/api/pleroma/emoji/pack?name=test_pack&page_size=1")
+ |> json_response_and_validate_schema(200)
+
+ assert files |> Map.keys() |> length() == 1
+
+ assert %{
+ "files" => files,
+ "files_count" => 2
+ } =
+ conn
+ |> get("/api/pleroma/emoji/pack?name=test_pack&page_size=1&page=2")
+ |> json_response_and_validate_schema(200)
+
+ assert files |> Map.keys() |> length() == 1
+ end
+
+ test "for pack name with special chars", %{conn: conn} do
+ assert %{
+ "files" => files,
+ "files_count" => 1,
+ "pack" => %{
+ "can-download" => true,
+ "description" => "Test description",
+ "download-sha256" => _,
+ "homepage" => "https://pleroma.social",
+ "license" => "Test license",
+ "share-files" => true
+ }
+ } =
+ conn
+ |> get("/api/pleroma/emoji/pack?name=blobs.gg")
+ |> json_response_and_validate_schema(200)
+ end
+
+ test "non existing pack", %{conn: conn} do
+ assert conn
+ |> get("/api/pleroma/emoji/pack?name=non_existing")
+ |> json_response_and_validate_schema(:not_found) == %{
+ "error" => "Pack non_existing does not exist"
+ }
+ end
+
+ test "error name", %{conn: conn} do
+ assert conn
+ |> get("/api/pleroma/emoji/pack?name= ")
+ |> json_response_and_validate_schema(:bad_request) == %{
+ "error" => "pack name cannot be empty"
+ }
+ end
+ end
+end
diff --git a/test/pleroma/web/pleroma_api/controllers/emoji_reaction_controller_test.exs b/test/pleroma/web/pleroma_api/controllers/emoji_reaction_controller_test.exs
@@ -0,0 +1,149 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.PleromaAPI.EmojiReactionControllerTest do
+ use Oban.Testing, repo: Pleroma.Repo
+ use Pleroma.Web.ConnCase
+
+ alias Pleroma.Object
+ alias Pleroma.Tests.ObanHelpers
+ alias Pleroma.User
+ alias Pleroma.Web.CommonAPI
+
+ import Pleroma.Factory
+
+ test "PUT /api/v1/pleroma/statuses/:id/reactions/:emoji", %{conn: conn} do
+ user = insert(:user)
+ other_user = insert(:user)
+
+ {:ok, activity} = CommonAPI.post(user, %{status: "#cofe"})
+
+ result =
+ conn
+ |> assign(:user, other_user)
+ |> assign(:token, insert(:oauth_token, user: other_user, scopes: ["write:statuses"]))
+ |> put("/api/v1/pleroma/statuses/#{activity.id}/reactions/☕")
+ |> json_response_and_validate_schema(200)
+
+ # We return the status, but this our implementation detail.
+ assert %{"id" => id} = result
+ assert to_string(activity.id) == id
+
+ assert result["pleroma"]["emoji_reactions"] == [
+ %{"name" => "☕", "count" => 1, "me" => true}
+ ]
+
+ # Reacting with a non-emoji
+ assert conn
+ |> assign(:user, other_user)
+ |> assign(:token, insert(:oauth_token, user: other_user, scopes: ["write:statuses"]))
+ |> put("/api/v1/pleroma/statuses/#{activity.id}/reactions/x")
+ |> json_response_and_validate_schema(400)
+ end
+
+ test "DELETE /api/v1/pleroma/statuses/:id/reactions/:emoji", %{conn: conn} do
+ user = insert(:user)
+ other_user = insert(:user)
+
+ {:ok, activity} = CommonAPI.post(user, %{status: "#cofe"})
+ {:ok, _reaction_activity} = CommonAPI.react_with_emoji(activity.id, other_user, "☕")
+
+ ObanHelpers.perform_all()
+
+ result =
+ conn
+ |> assign(:user, other_user)
+ |> assign(:token, insert(:oauth_token, user: other_user, scopes: ["write:statuses"]))
+ |> delete("/api/v1/pleroma/statuses/#{activity.id}/reactions/☕")
+
+ assert %{"id" => id} = json_response_and_validate_schema(result, 200)
+ assert to_string(activity.id) == id
+
+ ObanHelpers.perform_all()
+
+ object = Object.get_by_ap_id(activity.data["object"])
+
+ assert object.data["reaction_count"] == 0
+ end
+
+ test "GET /api/v1/pleroma/statuses/:id/reactions", %{conn: conn} do
+ user = insert(:user)
+ other_user = insert(:user)
+ doomed_user = insert(:user)
+
+ {:ok, activity} = CommonAPI.post(user, %{status: "#cofe"})
+
+ result =
+ conn
+ |> get("/api/v1/pleroma/statuses/#{activity.id}/reactions")
+ |> json_response_and_validate_schema(200)
+
+ assert result == []
+
+ {:ok, _} = CommonAPI.react_with_emoji(activity.id, other_user, "🎅")
+ {:ok, _} = CommonAPI.react_with_emoji(activity.id, doomed_user, "🎅")
+
+ User.perform(:delete, doomed_user)
+
+ result =
+ conn
+ |> get("/api/v1/pleroma/statuses/#{activity.id}/reactions")
+ |> json_response_and_validate_schema(200)
+
+ [%{"name" => "🎅", "count" => 1, "accounts" => [represented_user], "me" => false}] = result
+
+ assert represented_user["id"] == other_user.id
+
+ result =
+ conn
+ |> assign(:user, other_user)
+ |> assign(:token, insert(:oauth_token, user: other_user, scopes: ["read:statuses"]))
+ |> get("/api/v1/pleroma/statuses/#{activity.id}/reactions")
+ |> json_response_and_validate_schema(200)
+
+ assert [%{"name" => "🎅", "count" => 1, "accounts" => [_represented_user], "me" => true}] =
+ result
+ end
+
+ test "GET /api/v1/pleroma/statuses/:id/reactions with :show_reactions disabled", %{conn: conn} do
+ clear_config([:instance, :show_reactions], false)
+
+ user = insert(:user)
+ other_user = insert(:user)
+
+ {:ok, activity} = CommonAPI.post(user, %{status: "#cofe"})
+ {:ok, _} = CommonAPI.react_with_emoji(activity.id, other_user, "🎅")
+
+ result =
+ conn
+ |> get("/api/v1/pleroma/statuses/#{activity.id}/reactions")
+ |> json_response_and_validate_schema(200)
+
+ assert result == []
+ end
+
+ test "GET /api/v1/pleroma/statuses/:id/reactions/:emoji", %{conn: conn} do
+ user = insert(:user)
+ other_user = insert(:user)
+
+ {:ok, activity} = CommonAPI.post(user, %{status: "#cofe"})
+
+ result =
+ conn
+ |> get("/api/v1/pleroma/statuses/#{activity.id}/reactions/🎅")
+ |> json_response_and_validate_schema(200)
+
+ assert result == []
+
+ {:ok, _} = CommonAPI.react_with_emoji(activity.id, other_user, "🎅")
+ {:ok, _} = CommonAPI.react_with_emoji(activity.id, other_user, "☕")
+
+ assert [%{"name" => "🎅", "count" => 1, "accounts" => [represented_user], "me" => false}] =
+ conn
+ |> get("/api/v1/pleroma/statuses/#{activity.id}/reactions/🎅")
+ |> json_response_and_validate_schema(200)
+
+ assert represented_user["id"] == other_user.id
+ end
+end
diff --git a/test/pleroma/web/pleroma_api/controllers/mascot_controller_test.exs b/test/pleroma/web/pleroma_api/controllers/mascot_controller_test.exs
@@ -0,0 +1,73 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.PleromaAPI.MascotControllerTest do
+ use Pleroma.Web.ConnCase
+
+ alias Pleroma.User
+
+ test "mascot upload" do
+ %{conn: conn} = oauth_access(["write:accounts"])
+
+ non_image_file = %Plug.Upload{
+ content_type: "audio/mpeg",
+ path: Path.absname("test/fixtures/sound.mp3"),
+ filename: "sound.mp3"
+ }
+
+ ret_conn =
+ conn
+ |> put_req_header("content-type", "multipart/form-data")
+ |> put("/api/v1/pleroma/mascot", %{"file" => non_image_file})
+
+ assert json_response_and_validate_schema(ret_conn, 415)
+
+ file = %Plug.Upload{
+ content_type: "image/jpg",
+ path: Path.absname("test/fixtures/image.jpg"),
+ filename: "an_image.jpg"
+ }
+
+ conn =
+ conn
+ |> put_req_header("content-type", "multipart/form-data")
+ |> put("/api/v1/pleroma/mascot", %{"file" => file})
+
+ assert %{"id" => _, "type" => image} = json_response_and_validate_schema(conn, 200)
+ end
+
+ test "mascot retrieving" do
+ %{user: user, conn: conn} = oauth_access(["read:accounts", "write:accounts"])
+
+ # When user hasn't set a mascot, we should just get pleroma tan back
+ ret_conn = get(conn, "/api/v1/pleroma/mascot")
+
+ assert %{"url" => url} = json_response_and_validate_schema(ret_conn, 200)
+ assert url =~ "pleroma-fox-tan-smol"
+
+ # When a user sets their mascot, we should get that back
+ file = %Plug.Upload{
+ content_type: "image/jpg",
+ path: Path.absname("test/fixtures/image.jpg"),
+ filename: "an_image.jpg"
+ }
+
+ ret_conn =
+ conn
+ |> put_req_header("content-type", "multipart/form-data")
+ |> put("/api/v1/pleroma/mascot", %{"file" => file})
+
+ assert json_response_and_validate_schema(ret_conn, 200)
+
+ user = User.get_cached_by_id(user.id)
+
+ conn =
+ conn
+ |> assign(:user, user)
+ |> get("/api/v1/pleroma/mascot")
+
+ assert %{"url" => url, "type" => "image"} = json_response_and_validate_schema(conn, 200)
+ assert url =~ "an_image"
+ end
+end
diff --git a/test/pleroma/web/pleroma_api/controllers/notification_controller_test.exs b/test/pleroma/web/pleroma_api/controllers/notification_controller_test.exs
@@ -0,0 +1,68 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.PleromaAPI.NotificationControllerTest do
+ use Pleroma.Web.ConnCase
+
+ alias Pleroma.Notification
+ alias Pleroma.Repo
+ alias Pleroma.Web.CommonAPI
+
+ import Pleroma.Factory
+
+ describe "POST /api/v1/pleroma/notifications/read" do
+ setup do: oauth_access(["write:notifications"])
+
+ test "it marks a single notification as read", %{user: user1, conn: conn} do
+ user2 = insert(:user)
+ {:ok, activity1} = CommonAPI.post(user2, %{status: "hi @#{user1.nickname}"})
+ {:ok, activity2} = CommonAPI.post(user2, %{status: "hi @#{user1.nickname}"})
+ {:ok, [notification1]} = Notification.create_notifications(activity1)
+ {:ok, [notification2]} = Notification.create_notifications(activity2)
+
+ response =
+ conn
+ |> put_req_header("content-type", "application/json")
+ |> post("/api/v1/pleroma/notifications/read", %{id: notification1.id})
+ |> json_response_and_validate_schema(:ok)
+
+ assert %{"pleroma" => %{"is_seen" => true}} = response
+ assert Repo.get(Notification, notification1.id).seen
+ refute Repo.get(Notification, notification2.id).seen
+ end
+
+ test "it marks multiple notifications as read", %{user: user1, conn: conn} do
+ user2 = insert(:user)
+ {:ok, _activity1} = CommonAPI.post(user2, %{status: "hi @#{user1.nickname}"})
+ {:ok, _activity2} = CommonAPI.post(user2, %{status: "hi @#{user1.nickname}"})
+ {:ok, _activity3} = CommonAPI.post(user2, %{status: "HIE @#{user1.nickname}"})
+
+ [notification3, notification2, notification1] = Notification.for_user(user1, %{limit: 3})
+
+ [response1, response2] =
+ conn
+ |> put_req_header("content-type", "application/json")
+ |> post("/api/v1/pleroma/notifications/read", %{max_id: notification2.id})
+ |> json_response_and_validate_schema(:ok)
+
+ assert %{"pleroma" => %{"is_seen" => true}} = response1
+ assert %{"pleroma" => %{"is_seen" => true}} = response2
+ assert Repo.get(Notification, notification1.id).seen
+ assert Repo.get(Notification, notification2.id).seen
+ refute Repo.get(Notification, notification3.id).seen
+ end
+
+ test "it returns error when notification not found", %{conn: conn} do
+ response =
+ conn
+ |> put_req_header("content-type", "application/json")
+ |> post("/api/v1/pleroma/notifications/read", %{
+ id: 22_222_222_222_222
+ })
+ |> json_response_and_validate_schema(:bad_request)
+
+ assert response == %{"error" => "Cannot get notification"}
+ end
+ end
+end
diff --git a/test/pleroma/web/pleroma_api/controllers/scrobble_controller_test.exs b/test/pleroma/web/pleroma_api/controllers/scrobble_controller_test.exs
@@ -0,0 +1,60 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.PleromaAPI.ScrobbleControllerTest do
+ use Pleroma.Web.ConnCase
+
+ alias Pleroma.Web.CommonAPI
+
+ describe "POST /api/v1/pleroma/scrobble" do
+ test "works correctly" do
+ %{conn: conn} = oauth_access(["write"])
+
+ conn =
+ conn
+ |> put_req_header("content-type", "application/json")
+ |> post("/api/v1/pleroma/scrobble", %{
+ "title" => "lain radio episode 1",
+ "artist" => "lain",
+ "album" => "lain radio",
+ "length" => "180000"
+ })
+
+ assert %{"title" => "lain radio episode 1"} = json_response_and_validate_schema(conn, 200)
+ end
+ end
+
+ describe "GET /api/v1/pleroma/accounts/:id/scrobbles" do
+ test "works correctly" do
+ %{user: user, conn: conn} = oauth_access(["read"])
+
+ {:ok, _activity} =
+ CommonAPI.listen(user, %{
+ title: "lain radio episode 1",
+ artist: "lain",
+ album: "lain radio"
+ })
+
+ {:ok, _activity} =
+ CommonAPI.listen(user, %{
+ title: "lain radio episode 2",
+ artist: "lain",
+ album: "lain radio"
+ })
+
+ {:ok, _activity} =
+ CommonAPI.listen(user, %{
+ title: "lain radio episode 3",
+ artist: "lain",
+ album: "lain radio"
+ })
+
+ conn = get(conn, "/api/v1/pleroma/accounts/#{user.id}/scrobbles")
+
+ result = json_response_and_validate_schema(conn, 200)
+
+ assert length(result) == 3
+ end
+ end
+end
diff --git a/test/pleroma/web/pleroma_api/controllers/two_factor_authentication_controller_test.exs b/test/pleroma/web/pleroma_api/controllers/two_factor_authentication_controller_test.exs
@@ -0,0 +1,264 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.PleromaAPI.TwoFactorAuthenticationControllerTest do
+ use Pleroma.Web.ConnCase
+
+ import Pleroma.Factory
+ alias Pleroma.MFA.Settings
+ alias Pleroma.MFA.TOTP
+
+ describe "GET /api/pleroma/accounts/mfa/settings" do
+ test "returns user mfa settings for new user", %{conn: conn} do
+ token = insert(:oauth_token, scopes: ["read", "follow"])
+ token2 = insert(:oauth_token, scopes: ["write"])
+
+ assert conn
+ |> put_req_header("authorization", "Bearer #{token.token}")
+ |> get("/api/pleroma/accounts/mfa")
+ |> json_response(:ok) == %{
+ "settings" => %{"enabled" => false, "totp" => false}
+ }
+
+ assert conn
+ |> put_req_header("authorization", "Bearer #{token2.token}")
+ |> get("/api/pleroma/accounts/mfa")
+ |> json_response(403) == %{
+ "error" => "Insufficient permissions: read:security."
+ }
+ end
+
+ test "returns user mfa settings with enabled totp", %{conn: conn} do
+ user =
+ insert(:user,
+ multi_factor_authentication_settings: %Settings{
+ enabled: true,
+ totp: %Settings.TOTP{secret: "XXX", delivery_type: "app", confirmed: true}
+ }
+ )
+
+ token = insert(:oauth_token, scopes: ["read", "follow"], user: user)
+
+ assert conn
+ |> put_req_header("authorization", "Bearer #{token.token}")
+ |> get("/api/pleroma/accounts/mfa")
+ |> json_response(:ok) == %{
+ "settings" => %{"enabled" => true, "totp" => true}
+ }
+ end
+ end
+
+ describe "GET /api/pleroma/accounts/mfa/backup_codes" do
+ test "returns backup codes", %{conn: conn} do
+ user =
+ insert(:user,
+ multi_factor_authentication_settings: %Settings{
+ backup_codes: ["1", "2", "3"],
+ totp: %Settings.TOTP{secret: "secret"}
+ }
+ )
+
+ token = insert(:oauth_token, scopes: ["write", "follow"], user: user)
+ token2 = insert(:oauth_token, scopes: ["read"])
+
+ response =
+ conn
+ |> put_req_header("authorization", "Bearer #{token.token}")
+ |> get("/api/pleroma/accounts/mfa/backup_codes")
+ |> json_response(:ok)
+
+ assert [<<_::bytes-size(6)>>, <<_::bytes-size(6)>>] = response["codes"]
+ user = refresh_record(user)
+ mfa_settings = user.multi_factor_authentication_settings
+ assert mfa_settings.totp.secret == "secret"
+ refute mfa_settings.backup_codes == ["1", "2", "3"]
+ refute mfa_settings.backup_codes == []
+
+ assert conn
+ |> put_req_header("authorization", "Bearer #{token2.token}")
+ |> get("/api/pleroma/accounts/mfa/backup_codes")
+ |> json_response(403) == %{
+ "error" => "Insufficient permissions: write:security."
+ }
+ end
+ end
+
+ describe "GET /api/pleroma/accounts/mfa/setup/totp" do
+ test "return errors when method is invalid", %{conn: conn} do
+ user = insert(:user)
+ token = insert(:oauth_token, scopes: ["write", "follow"], user: user)
+
+ response =
+ conn
+ |> put_req_header("authorization", "Bearer #{token.token}")
+ |> get("/api/pleroma/accounts/mfa/setup/torf")
+ |> json_response(400)
+
+ assert response == %{"error" => "undefined method"}
+ end
+
+ test "returns key and provisioning_uri", %{conn: conn} do
+ user =
+ insert(:user,
+ multi_factor_authentication_settings: %Settings{backup_codes: ["1", "2", "3"]}
+ )
+
+ token = insert(:oauth_token, scopes: ["write", "follow"], user: user)
+ token2 = insert(:oauth_token, scopes: ["read"])
+
+ response =
+ conn
+ |> put_req_header("authorization", "Bearer #{token.token}")
+ |> get("/api/pleroma/accounts/mfa/setup/totp")
+ |> json_response(:ok)
+
+ user = refresh_record(user)
+ mfa_settings = user.multi_factor_authentication_settings
+ secret = mfa_settings.totp.secret
+ refute mfa_settings.enabled
+ assert mfa_settings.backup_codes == ["1", "2", "3"]
+
+ assert response == %{
+ "key" => secret,
+ "provisioning_uri" => TOTP.provisioning_uri(secret, "#{user.email}")
+ }
+
+ assert conn
+ |> put_req_header("authorization", "Bearer #{token2.token}")
+ |> get("/api/pleroma/accounts/mfa/setup/totp")
+ |> json_response(403) == %{
+ "error" => "Insufficient permissions: write:security."
+ }
+ end
+ end
+
+ describe "GET /api/pleroma/accounts/mfa/confirm/totp" do
+ test "returns success result", %{conn: conn} do
+ secret = TOTP.generate_secret()
+ code = TOTP.generate_token(secret)
+
+ user =
+ insert(:user,
+ multi_factor_authentication_settings: %Settings{
+ backup_codes: ["1", "2", "3"],
+ totp: %Settings.TOTP{secret: secret}
+ }
+ )
+
+ token = insert(:oauth_token, scopes: ["write", "follow"], user: user)
+ token2 = insert(:oauth_token, scopes: ["read"])
+
+ assert conn
+ |> put_req_header("authorization", "Bearer #{token.token}")
+ |> post("/api/pleroma/accounts/mfa/confirm/totp", %{password: "test", code: code})
+ |> json_response(:ok)
+
+ settings = refresh_record(user).multi_factor_authentication_settings
+ assert settings.enabled
+ assert settings.totp.secret == secret
+ assert settings.totp.confirmed
+ assert settings.backup_codes == ["1", "2", "3"]
+
+ assert conn
+ |> put_req_header("authorization", "Bearer #{token2.token}")
+ |> post("/api/pleroma/accounts/mfa/confirm/totp", %{password: "test", code: code})
+ |> json_response(403) == %{
+ "error" => "Insufficient permissions: write:security."
+ }
+ end
+
+ test "returns error if password incorrect", %{conn: conn} do
+ secret = TOTP.generate_secret()
+ code = TOTP.generate_token(secret)
+
+ user =
+ insert(:user,
+ multi_factor_authentication_settings: %Settings{
+ backup_codes: ["1", "2", "3"],
+ totp: %Settings.TOTP{secret: secret}
+ }
+ )
+
+ token = insert(:oauth_token, scopes: ["write", "follow"], user: user)
+
+ response =
+ conn
+ |> put_req_header("authorization", "Bearer #{token.token}")
+ |> post("/api/pleroma/accounts/mfa/confirm/totp", %{password: "xxx", code: code})
+ |> json_response(422)
+
+ settings = refresh_record(user).multi_factor_authentication_settings
+ refute settings.enabled
+ refute settings.totp.confirmed
+ assert settings.backup_codes == ["1", "2", "3"]
+ assert response == %{"error" => "Invalid password."}
+ end
+
+ test "returns error if code incorrect", %{conn: conn} do
+ secret = TOTP.generate_secret()
+
+ user =
+ insert(:user,
+ multi_factor_authentication_settings: %Settings{
+ backup_codes: ["1", "2", "3"],
+ totp: %Settings.TOTP{secret: secret}
+ }
+ )
+
+ token = insert(:oauth_token, scopes: ["write", "follow"], user: user)
+ token2 = insert(:oauth_token, scopes: ["read"])
+
+ response =
+ conn
+ |> put_req_header("authorization", "Bearer #{token.token}")
+ |> post("/api/pleroma/accounts/mfa/confirm/totp", %{password: "test", code: "code"})
+ |> json_response(422)
+
+ settings = refresh_record(user).multi_factor_authentication_settings
+ refute settings.enabled
+ refute settings.totp.confirmed
+ assert settings.backup_codes == ["1", "2", "3"]
+ assert response == %{"error" => "invalid_token"}
+
+ assert conn
+ |> put_req_header("authorization", "Bearer #{token2.token}")
+ |> post("/api/pleroma/accounts/mfa/confirm/totp", %{password: "test", code: "code"})
+ |> json_response(403) == %{
+ "error" => "Insufficient permissions: write:security."
+ }
+ end
+ end
+
+ describe "DELETE /api/pleroma/accounts/mfa/totp" do
+ test "returns success result", %{conn: conn} do
+ user =
+ insert(:user,
+ multi_factor_authentication_settings: %Settings{
+ backup_codes: ["1", "2", "3"],
+ totp: %Settings.TOTP{secret: "secret"}
+ }
+ )
+
+ token = insert(:oauth_token, scopes: ["write", "follow"], user: user)
+ token2 = insert(:oauth_token, scopes: ["read"])
+
+ assert conn
+ |> put_req_header("authorization", "Bearer #{token.token}")
+ |> delete("/api/pleroma/accounts/mfa/totp", %{password: "test"})
+ |> json_response(:ok)
+
+ settings = refresh_record(user).multi_factor_authentication_settings
+ refute settings.enabled
+ assert settings.totp.secret == nil
+ refute settings.totp.confirmed
+
+ assert conn
+ |> put_req_header("authorization", "Bearer #{token2.token}")
+ |> delete("/api/pleroma/accounts/mfa/totp", %{password: "test"})
+ |> json_response(403) == %{
+ "error" => "Insufficient permissions: write:security."
+ }
+ end
+ end
+end
diff --git a/test/pleroma/web/pleroma_api/views/chat_message_reference_view_test.exs b/test/pleroma/web/pleroma_api/views/chat_message_reference_view_test.exs
@@ -0,0 +1,72 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.PleromaAPI.ChatMessageReferenceViewTest do
+ use Pleroma.DataCase
+
+ alias Pleroma.Chat
+ alias Pleroma.Chat.MessageReference
+ alias Pleroma.Object
+ alias Pleroma.Web.ActivityPub.ActivityPub
+ alias Pleroma.Web.CommonAPI
+ alias Pleroma.Web.PleromaAPI.Chat.MessageReferenceView
+
+ import Pleroma.Factory
+
+ test "it displays a chat message" do
+ user = insert(:user)
+ recipient = insert(:user)
+
+ file = %Plug.Upload{
+ content_type: "image/jpg",
+ path: Path.absname("test/fixtures/image.jpg"),
+ filename: "an_image.jpg"
+ }
+
+ {:ok, upload} = ActivityPub.upload(file, actor: user.ap_id)
+ {:ok, activity} = CommonAPI.post_chat_message(user, recipient, "kippis :firefox:")
+
+ chat = Chat.get(user.id, recipient.ap_id)
+
+ object = Object.normalize(activity)
+
+ cm_ref = MessageReference.for_chat_and_object(chat, object)
+
+ chat_message = MessageReferenceView.render("show.json", chat_message_reference: cm_ref)
+
+ assert chat_message[:id] == cm_ref.id
+ assert chat_message[:content] == "kippis :firefox:"
+ assert chat_message[:account_id] == user.id
+ assert chat_message[:chat_id]
+ assert chat_message[:created_at]
+ assert chat_message[:unread] == false
+ assert match?([%{shortcode: "firefox"}], chat_message[:emojis])
+
+ clear_config([:rich_media, :enabled], true)
+
+ Tesla.Mock.mock(fn
+ %{url: "https://example.com/ogp"} ->
+ %Tesla.Env{status: 200, body: File.read!("test/fixtures/rich_media/ogp.html")}
+ end)
+
+ {:ok, activity} =
+ CommonAPI.post_chat_message(recipient, user, "gkgkgk https://example.com/ogp",
+ media_id: upload.id
+ )
+
+ object = Object.normalize(activity)
+
+ cm_ref = MessageReference.for_chat_and_object(chat, object)
+
+ chat_message_two = MessageReferenceView.render("show.json", chat_message_reference: cm_ref)
+
+ assert chat_message_two[:id] == cm_ref.id
+ assert chat_message_two[:content] == object.data["content"]
+ assert chat_message_two[:account_id] == recipient.id
+ assert chat_message_two[:chat_id] == chat_message[:chat_id]
+ assert chat_message_two[:attachment]
+ assert chat_message_two[:unread] == true
+ assert chat_message_two[:card]
+ end
+end
diff --git a/test/pleroma/web/pleroma_api/views/chat_view_test.exs b/test/pleroma/web/pleroma_api/views/chat_view_test.exs
@@ -0,0 +1,49 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.PleromaAPI.ChatViewTest do
+ use Pleroma.DataCase
+
+ alias Pleroma.Chat
+ alias Pleroma.Chat.MessageReference
+ alias Pleroma.Object
+ alias Pleroma.Web.CommonAPI
+ alias Pleroma.Web.CommonAPI.Utils
+ alias Pleroma.Web.MastodonAPI.AccountView
+ alias Pleroma.Web.PleromaAPI.Chat.MessageReferenceView
+ alias Pleroma.Web.PleromaAPI.ChatView
+
+ import Pleroma.Factory
+
+ test "it represents a chat" do
+ user = insert(:user)
+ recipient = insert(:user)
+
+ {:ok, chat} = Chat.get_or_create(user.id, recipient.ap_id)
+
+ represented_chat = ChatView.render("show.json", chat: chat)
+
+ assert represented_chat == %{
+ id: "#{chat.id}",
+ account:
+ AccountView.render("show.json", user: recipient, skip_visibility_check: true),
+ unread: 0,
+ last_message: nil,
+ updated_at: Utils.to_masto_date(chat.updated_at)
+ }
+
+ {:ok, chat_message_creation} = CommonAPI.post_chat_message(user, recipient, "hello")
+
+ chat_message = Object.normalize(chat_message_creation, false)
+
+ {:ok, chat} = Chat.get_or_create(user.id, recipient.ap_id)
+
+ represented_chat = ChatView.render("show.json", chat: chat)
+
+ cm_ref = MessageReference.for_chat_and_object(chat, chat_message)
+
+ assert represented_chat[:last_message] ==
+ MessageReferenceView.render("show.json", chat_message_reference: cm_ref)
+ end
+end
diff --git a/test/pleroma/web/pleroma_api/views/scrobble_view_test.exs b/test/pleroma/web/pleroma_api/views/scrobble_view_test.exs
@@ -0,0 +1,20 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.PleromaAPI.StatusViewTest do
+ use Pleroma.DataCase
+
+ alias Pleroma.Web.PleromaAPI.ScrobbleView
+
+ import Pleroma.Factory
+
+ test "successfully renders a Listen activity (pleroma extension)" do
+ listen_activity = insert(:listen)
+
+ status = ScrobbleView.render("show.json", activity: listen_activity)
+
+ assert status.length == listen_activity.data["object"]["length"]
+ assert status.title == listen_activity.data["object"]["title"]
+ end
+end
diff --git a/test/pleroma/web/plugs/admin_secret_authentication_plug_test.exs b/test/pleroma/web/plugs/admin_secret_authentication_plug_test.exs
@@ -0,0 +1,75 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.Plugs.AdminSecretAuthenticationPlugTest do
+ use Pleroma.Web.ConnCase
+
+ import Mock
+ import Pleroma.Factory
+
+ alias Pleroma.Plugs.AdminSecretAuthenticationPlug
+ alias Pleroma.Plugs.OAuthScopesPlug
+ alias Pleroma.Plugs.PlugHelper
+ alias Pleroma.Plugs.RateLimiter
+
+ test "does nothing if a user is assigned", %{conn: conn} do
+ user = insert(:user)
+
+ conn =
+ conn
+ |> assign(:user, user)
+
+ ret_conn =
+ conn
+ |> AdminSecretAuthenticationPlug.call(%{})
+
+ assert conn == ret_conn
+ end
+
+ describe "when secret set it assigns an admin user" do
+ setup do: clear_config([:admin_token])
+
+ setup_with_mocks([{RateLimiter, [:passthrough], []}]) do
+ :ok
+ end
+
+ test "with `admin_token` query parameter", %{conn: conn} do
+ Pleroma.Config.put(:admin_token, "password123")
+
+ conn =
+ %{conn | params: %{"admin_token" => "wrong_password"}}
+ |> AdminSecretAuthenticationPlug.call(%{})
+
+ refute conn.assigns[:user]
+ assert called(RateLimiter.call(conn, name: :authentication))
+
+ conn =
+ %{conn | params: %{"admin_token" => "password123"}}
+ |> AdminSecretAuthenticationPlug.call(%{})
+
+ assert conn.assigns[:user].is_admin
+ assert PlugHelper.plug_skipped?(conn, OAuthScopesPlug)
+ end
+
+ test "with `x-admin-token` HTTP header", %{conn: conn} do
+ Pleroma.Config.put(:admin_token, "☕️")
+
+ conn =
+ conn
+ |> put_req_header("x-admin-token", "🥛")
+ |> AdminSecretAuthenticationPlug.call(%{})
+
+ refute conn.assigns[:user]
+ assert called(RateLimiter.call(conn, name: :authentication))
+
+ conn =
+ conn
+ |> put_req_header("x-admin-token", "☕️")
+ |> AdminSecretAuthenticationPlug.call(%{})
+
+ assert conn.assigns[:user].is_admin
+ assert PlugHelper.plug_skipped?(conn, OAuthScopesPlug)
+ end
+ end
+end
diff --git a/test/pleroma/web/plugs/authentication_plug_test.exs b/test/pleroma/web/plugs/authentication_plug_test.exs
@@ -0,0 +1,125 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.Plugs.AuthenticationPlugTest do
+ use Pleroma.Web.ConnCase, async: true
+
+ alias Pleroma.Plugs.AuthenticationPlug
+ alias Pleroma.Plugs.OAuthScopesPlug
+ alias Pleroma.Plugs.PlugHelper
+ alias Pleroma.User
+
+ import ExUnit.CaptureLog
+ import Pleroma.Factory
+
+ setup %{conn: conn} do
+ user = %User{
+ id: 1,
+ name: "dude",
+ password_hash: Pbkdf2.hash_pwd_salt("guy")
+ }
+
+ conn =
+ conn
+ |> assign(:auth_user, user)
+
+ %{user: user, conn: conn}
+ end
+
+ test "it does nothing if a user is assigned", %{conn: conn} do
+ conn =
+ conn
+ |> assign(:user, %User{})
+
+ ret_conn =
+ conn
+ |> AuthenticationPlug.call(%{})
+
+ assert ret_conn == conn
+ end
+
+ test "with a correct password in the credentials, " <>
+ "it assigns the auth_user and marks OAuthScopesPlug as skipped",
+ %{conn: conn} do
+ conn =
+ conn
+ |> assign(:auth_credentials, %{password: "guy"})
+ |> AuthenticationPlug.call(%{})
+
+ assert conn.assigns.user == conn.assigns.auth_user
+ assert PlugHelper.plug_skipped?(conn, OAuthScopesPlug)
+ end
+
+ test "with a bcrypt hash, it updates to a pkbdf2 hash", %{conn: conn} do
+ user = insert(:user, password_hash: Bcrypt.hash_pwd_salt("123"))
+ assert "$2" <> _ = user.password_hash
+
+ conn =
+ conn
+ |> assign(:auth_user, user)
+ |> assign(:auth_credentials, %{password: "123"})
+ |> AuthenticationPlug.call(%{})
+
+ assert conn.assigns.user.id == conn.assigns.auth_user.id
+ assert PlugHelper.plug_skipped?(conn, OAuthScopesPlug)
+
+ user = User.get_by_id(user.id)
+ assert "$pbkdf2" <> _ = user.password_hash
+ end
+
+ @tag :skip_on_mac
+ test "with a crypt hash, it updates to a pkbdf2 hash", %{conn: conn} do
+ user =
+ insert(:user,
+ password_hash:
+ "$6$9psBWV8gxkGOZWBz$PmfCycChoxeJ3GgGzwvhlgacb9mUoZ.KUXNCssekER4SJ7bOK53uXrHNb2e4i8yPFgSKyzaW9CcmrDXWIEMtD1"
+ )
+
+ conn =
+ conn
+ |> assign(:auth_user, user)
+ |> assign(:auth_credentials, %{password: "password"})
+ |> AuthenticationPlug.call(%{})
+
+ assert conn.assigns.user.id == conn.assigns.auth_user.id
+ assert PlugHelper.plug_skipped?(conn, OAuthScopesPlug)
+
+ user = User.get_by_id(user.id)
+ assert "$pbkdf2" <> _ = user.password_hash
+ end
+
+ describe "checkpw/2" do
+ test "check pbkdf2 hash" do
+ hash =
+ "$pbkdf2-sha512$160000$loXqbp8GYls43F0i6lEfIw$AY.Ep.2pGe57j2hAPY635sI/6w7l9Q9u9Bp02PkPmF3OrClDtJAI8bCiivPr53OKMF7ph6iHhN68Rom5nEfC2A"
+
+ assert AuthenticationPlug.checkpw("test-password", hash)
+ refute AuthenticationPlug.checkpw("test-password1", hash)
+ end
+
+ @tag :skip_on_mac
+ test "check sha512-crypt hash" do
+ hash =
+ "$6$9psBWV8gxkGOZWBz$PmfCycChoxeJ3GgGzwvhlgacb9mUoZ.KUXNCssekER4SJ7bOK53uXrHNb2e4i8yPFgSKyzaW9CcmrDXWIEMtD1"
+
+ assert AuthenticationPlug.checkpw("password", hash)
+ end
+
+ test "check bcrypt hash" do
+ hash = "$2a$10$uyhC/R/zoE1ndwwCtMusK.TLVzkQ/Ugsbqp3uXI.CTTz0gBw.24jS"
+
+ assert AuthenticationPlug.checkpw("password", hash)
+ refute AuthenticationPlug.checkpw("password1", hash)
+ end
+
+ test "it returns false when hash invalid" do
+ hash =
+ "psBWV8gxkGOZWBz$PmfCycChoxeJ3GgGzwvhlgacb9mUoZ.KUXNCssekER4SJ7bOK53uXrHNb2e4i8yPFgSKyzaW9CcmrDXWIEMtD1"
+
+ assert capture_log(fn ->
+ refute Pleroma.Plugs.AuthenticationPlug.checkpw("password", hash)
+ end) =~ "[error] Password hash not recognized"
+ end
+ end
+end
diff --git a/test/pleroma/web/plugs/basic_auth_decoder_plug_test.exs b/test/pleroma/web/plugs/basic_auth_decoder_plug_test.exs
@@ -0,0 +1,35 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.Plugs.BasicAuthDecoderPlugTest do
+ use Pleroma.Web.ConnCase, async: true
+
+ alias Pleroma.Plugs.BasicAuthDecoderPlug
+
+ defp basic_auth_enc(username, password) do
+ "Basic " <> Base.encode64("#{username}:#{password}")
+ end
+
+ test "it puts the decoded credentials into the assigns", %{conn: conn} do
+ header = basic_auth_enc("moonman", "iloverobek")
+
+ conn =
+ conn
+ |> put_req_header("authorization", header)
+ |> BasicAuthDecoderPlug.call(%{})
+
+ assert conn.assigns[:auth_credentials] == %{
+ username: "moonman",
+ password: "iloverobek"
+ }
+ end
+
+ test "without a authorization header it doesn't do anything", %{conn: conn} do
+ ret_conn =
+ conn
+ |> BasicAuthDecoderPlug.call(%{})
+
+ assert conn == ret_conn
+ end
+end
diff --git a/test/pleroma/web/plugs/cache_control_test.exs b/test/pleroma/web/plugs/cache_control_test.exs
@@ -0,0 +1,20 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.Plugs.CacheControlTest do
+ use Pleroma.Web.ConnCase
+ alias Plug.Conn
+
+ test "Verify Cache-Control header on static assets", %{conn: conn} do
+ conn = get(conn, "/index.html")
+
+ assert Conn.get_resp_header(conn, "cache-control") == ["public, no-cache"]
+ end
+
+ test "Verify Cache-Control header on the API", %{conn: conn} do
+ conn = get(conn, "/api/v1/instance")
+
+ assert Conn.get_resp_header(conn, "cache-control") == ["max-age=0, private, must-revalidate"]
+ end
+end
diff --git a/test/pleroma/web/plugs/cache_test.exs b/test/pleroma/web/plugs/cache_test.exs
@@ -0,0 +1,186 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.Plugs.CacheTest do
+ use ExUnit.Case, async: true
+ use Plug.Test
+
+ alias Pleroma.Plugs.Cache
+
+ @miss_resp {200,
+ [
+ {"cache-control", "max-age=0, private, must-revalidate"},
+ {"content-type", "cofe/hot; charset=utf-8"},
+ {"x-cache", "MISS from Pleroma"}
+ ], "cofe"}
+
+ @hit_resp {200,
+ [
+ {"cache-control", "max-age=0, private, must-revalidate"},
+ {"content-type", "cofe/hot; charset=utf-8"},
+ {"x-cache", "HIT from Pleroma"}
+ ], "cofe"}
+
+ @ttl 5
+
+ setup do
+ Cachex.clear(:web_resp_cache)
+ :ok
+ end
+
+ test "caches a response" do
+ assert @miss_resp ==
+ conn(:get, "/")
+ |> Cache.call(%{query_params: false, ttl: nil})
+ |> put_resp_content_type("cofe/hot")
+ |> send_resp(:ok, "cofe")
+ |> sent_resp()
+
+ assert_raise(Plug.Conn.AlreadySentError, fn ->
+ conn(:get, "/")
+ |> Cache.call(%{query_params: false, ttl: nil})
+ |> put_resp_content_type("cofe/hot")
+ |> send_resp(:ok, "cofe")
+ |> sent_resp()
+ end)
+
+ assert @hit_resp ==
+ conn(:get, "/")
+ |> Cache.call(%{query_params: false, ttl: nil})
+ |> sent_resp()
+ end
+
+ test "ttl is set" do
+ assert @miss_resp ==
+ conn(:get, "/")
+ |> Cache.call(%{query_params: false, ttl: @ttl})
+ |> put_resp_content_type("cofe/hot")
+ |> send_resp(:ok, "cofe")
+ |> sent_resp()
+
+ assert @hit_resp ==
+ conn(:get, "/")
+ |> Cache.call(%{query_params: false, ttl: @ttl})
+ |> sent_resp()
+
+ :timer.sleep(@ttl + 1)
+
+ assert @miss_resp ==
+ conn(:get, "/")
+ |> Cache.call(%{query_params: false, ttl: @ttl})
+ |> put_resp_content_type("cofe/hot")
+ |> send_resp(:ok, "cofe")
+ |> sent_resp()
+ end
+
+ test "set ttl via conn.assigns" do
+ assert @miss_resp ==
+ conn(:get, "/")
+ |> Cache.call(%{query_params: false, ttl: nil})
+ |> put_resp_content_type("cofe/hot")
+ |> assign(:cache_ttl, @ttl)
+ |> send_resp(:ok, "cofe")
+ |> sent_resp()
+
+ assert @hit_resp ==
+ conn(:get, "/")
+ |> Cache.call(%{query_params: false, ttl: nil})
+ |> sent_resp()
+
+ :timer.sleep(@ttl + 1)
+
+ assert @miss_resp ==
+ conn(:get, "/")
+ |> Cache.call(%{query_params: false, ttl: nil})
+ |> put_resp_content_type("cofe/hot")
+ |> send_resp(:ok, "cofe")
+ |> sent_resp()
+ end
+
+ test "ignore query string when `query_params` is false" do
+ assert @miss_resp ==
+ conn(:get, "/?cofe")
+ |> Cache.call(%{query_params: false, ttl: nil})
+ |> put_resp_content_type("cofe/hot")
+ |> send_resp(:ok, "cofe")
+ |> sent_resp()
+
+ assert @hit_resp ==
+ conn(:get, "/?cofefe")
+ |> Cache.call(%{query_params: false, ttl: nil})
+ |> sent_resp()
+ end
+
+ test "take query string into account when `query_params` is true" do
+ assert @miss_resp ==
+ conn(:get, "/?cofe")
+ |> Cache.call(%{query_params: true, ttl: nil})
+ |> put_resp_content_type("cofe/hot")
+ |> send_resp(:ok, "cofe")
+ |> sent_resp()
+
+ assert @miss_resp ==
+ conn(:get, "/?cofefe")
+ |> Cache.call(%{query_params: true, ttl: nil})
+ |> put_resp_content_type("cofe/hot")
+ |> send_resp(:ok, "cofe")
+ |> sent_resp()
+ end
+
+ test "take specific query params into account when `query_params` is list" do
+ assert @miss_resp ==
+ conn(:get, "/?a=1&b=2&c=3&foo=bar")
+ |> fetch_query_params()
+ |> Cache.call(%{query_params: ["a", "b", "c"], ttl: nil})
+ |> put_resp_content_type("cofe/hot")
+ |> send_resp(:ok, "cofe")
+ |> sent_resp()
+
+ assert @hit_resp ==
+ conn(:get, "/?bar=foo&c=3&b=2&a=1")
+ |> fetch_query_params()
+ |> Cache.call(%{query_params: ["a", "b", "c"], ttl: nil})
+ |> sent_resp()
+
+ assert @miss_resp ==
+ conn(:get, "/?bar=foo&c=3&b=2&a=2")
+ |> fetch_query_params()
+ |> Cache.call(%{query_params: ["a", "b", "c"], ttl: nil})
+ |> put_resp_content_type("cofe/hot")
+ |> send_resp(:ok, "cofe")
+ |> sent_resp()
+ end
+
+ test "ignore not GET requests" do
+ expected =
+ {200,
+ [
+ {"cache-control", "max-age=0, private, must-revalidate"},
+ {"content-type", "cofe/hot; charset=utf-8"}
+ ], "cofe"}
+
+ assert expected ==
+ conn(:post, "/")
+ |> Cache.call(%{query_params: true, ttl: nil})
+ |> put_resp_content_type("cofe/hot")
+ |> send_resp(:ok, "cofe")
+ |> sent_resp()
+ end
+
+ test "ignore non-successful responses" do
+ expected =
+ {418,
+ [
+ {"cache-control", "max-age=0, private, must-revalidate"},
+ {"content-type", "tea/iced; charset=utf-8"}
+ ], "🥤"}
+
+ assert expected ==
+ conn(:get, "/cofe")
+ |> Cache.call(%{query_params: true, ttl: nil})
+ |> put_resp_content_type("tea/iced")
+ |> send_resp(:im_a_teapot, "🥤")
+ |> sent_resp()
+ end
+end
diff --git a/test/pleroma/web/plugs/ensure_authenticated_plug_test.exs b/test/pleroma/web/plugs/ensure_authenticated_plug_test.exs
@@ -0,0 +1,96 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.Plugs.EnsureAuthenticatedPlugTest do
+ use Pleroma.Web.ConnCase, async: true
+
+ alias Pleroma.Plugs.EnsureAuthenticatedPlug
+ alias Pleroma.User
+
+ describe "without :if_func / :unless_func options" do
+ test "it halts if user is NOT assigned", %{conn: conn} do
+ conn = EnsureAuthenticatedPlug.call(conn, %{})
+
+ assert conn.status == 403
+ assert conn.halted == true
+ end
+
+ test "it continues if a user is assigned", %{conn: conn} do
+ conn = assign(conn, :user, %User{})
+ ret_conn = EnsureAuthenticatedPlug.call(conn, %{})
+
+ refute ret_conn.halted
+ end
+ end
+
+ test "it halts if user is assigned and MFA enabled", %{conn: conn} do
+ conn =
+ conn
+ |> assign(:user, %User{multi_factor_authentication_settings: %{enabled: true}})
+ |> assign(:auth_credentials, %{password: "xd-42"})
+ |> EnsureAuthenticatedPlug.call(%{})
+
+ assert conn.status == 403
+ assert conn.halted == true
+
+ assert conn.resp_body ==
+ "{\"error\":\"Two-factor authentication enabled, you must use a access token.\"}"
+ end
+
+ test "it continues if user is assigned and MFA disabled", %{conn: conn} do
+ conn =
+ conn
+ |> assign(:user, %User{multi_factor_authentication_settings: %{enabled: false}})
+ |> assign(:auth_credentials, %{password: "xd-42"})
+ |> EnsureAuthenticatedPlug.call(%{})
+
+ refute conn.status == 403
+ refute conn.halted
+ end
+
+ describe "with :if_func / :unless_func options" do
+ setup do
+ %{
+ true_fn: fn _conn -> true end,
+ false_fn: fn _conn -> false end
+ }
+ end
+
+ test "it continues if a user is assigned", %{conn: conn, true_fn: true_fn, false_fn: false_fn} do
+ conn = assign(conn, :user, %User{})
+ refute EnsureAuthenticatedPlug.call(conn, if_func: true_fn).halted
+ refute EnsureAuthenticatedPlug.call(conn, if_func: false_fn).halted
+ refute EnsureAuthenticatedPlug.call(conn, unless_func: true_fn).halted
+ refute EnsureAuthenticatedPlug.call(conn, unless_func: false_fn).halted
+ end
+
+ test "it continues if a user is NOT assigned but :if_func evaluates to `false`",
+ %{conn: conn, false_fn: false_fn} do
+ ret_conn = EnsureAuthenticatedPlug.call(conn, if_func: false_fn)
+ refute ret_conn.halted
+ end
+
+ test "it continues if a user is NOT assigned but :unless_func evaluates to `true`",
+ %{conn: conn, true_fn: true_fn} do
+ ret_conn = EnsureAuthenticatedPlug.call(conn, unless_func: true_fn)
+ refute ret_conn.halted
+ end
+
+ test "it halts if a user is NOT assigned and :if_func evaluates to `true`",
+ %{conn: conn, true_fn: true_fn} do
+ conn = EnsureAuthenticatedPlug.call(conn, if_func: true_fn)
+
+ assert conn.status == 403
+ assert conn.halted == true
+ end
+
+ test "it halts if a user is NOT assigned and :unless_func evaluates to `false`",
+ %{conn: conn, false_fn: false_fn} do
+ conn = EnsureAuthenticatedPlug.call(conn, unless_func: false_fn)
+
+ assert conn.status == 403
+ assert conn.halted == true
+ end
+ end
+end
diff --git a/test/pleroma/web/plugs/ensure_public_or_authenticated_plug_test.exs b/test/pleroma/web/plugs/ensure_public_or_authenticated_plug_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.Plugs.EnsurePublicOrAuthenticatedPlugTest do
+ use Pleroma.Web.ConnCase, async: true
+
+ alias Pleroma.Config
+ alias Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug
+ alias Pleroma.User
+
+ setup do: clear_config([:instance, :public])
+
+ test "it halts if not public and no user is assigned", %{conn: conn} do
+ Config.put([:instance, :public], false)
+
+ conn =
+ conn
+ |> EnsurePublicOrAuthenticatedPlug.call(%{})
+
+ assert conn.status == 403
+ assert conn.halted == true
+ end
+
+ test "it continues if public", %{conn: conn} do
+ Config.put([:instance, :public], true)
+
+ ret_conn =
+ conn
+ |> EnsurePublicOrAuthenticatedPlug.call(%{})
+
+ refute ret_conn.halted
+ end
+
+ test "it continues if a user is assigned, even if not public", %{conn: conn} do
+ Config.put([:instance, :public], false)
+
+ conn =
+ conn
+ |> assign(:user, %User{})
+
+ ret_conn =
+ conn
+ |> EnsurePublicOrAuthenticatedPlug.call(%{})
+
+ refute ret_conn.halted
+ end
+end
diff --git a/test/pleroma/web/plugs/ensure_user_key_plug_test.exs b/test/pleroma/web/plugs/ensure_user_key_plug_test.exs
@@ -0,0 +1,29 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.Plugs.EnsureUserKeyPlugTest do
+ use Pleroma.Web.ConnCase, async: true
+
+ alias Pleroma.Plugs.EnsureUserKeyPlug
+
+ test "if the conn has a user key set, it does nothing", %{conn: conn} do
+ conn =
+ conn
+ |> assign(:user, 1)
+
+ ret_conn =
+ conn
+ |> EnsureUserKeyPlug.call(%{})
+
+ assert conn == ret_conn
+ end
+
+ test "if the conn has no key set, it sets it to nil", %{conn: conn} do
+ conn =
+ conn
+ |> EnsureUserKeyPlug.call(%{})
+
+ assert Map.has_key?(conn.assigns, :user)
+ end
+end
diff --git a/test/pleroma/web/plugs/federating_plug_test.exs b/test/pleroma/web/plugs/federating_plug_test.exs
@@ -0,0 +1,31 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.Plugs.FederatingPlugTest do
+ use Pleroma.Web.ConnCase
+
+ setup do: clear_config([:instance, :federating])
+
+ test "returns and halt the conn when federating is disabled" do
+ Pleroma.Config.put([:instance, :federating], false)
+
+ conn =
+ build_conn()
+ |> Pleroma.Web.FederatingPlug.call(%{})
+
+ assert conn.status == 404
+ assert conn.halted
+ end
+
+ test "does nothing when federating is enabled" do
+ Pleroma.Config.put([:instance, :federating], true)
+
+ conn =
+ build_conn()
+ |> Pleroma.Web.FederatingPlug.call(%{})
+
+ refute conn.status
+ refute conn.halted
+ end
+end
diff --git a/test/plugs/http_security_plug_test.exs b/test/pleroma/web/plugs/http_security_plug_test.exs
diff --git a/test/plugs/http_signature_plug_test.exs b/test/pleroma/web/plugs/http_signature_plug_test.exs
diff --git a/test/pleroma/web/plugs/idempotency_plug_test.exs b/test/pleroma/web/plugs/idempotency_plug_test.exs
@@ -0,0 +1,110 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.Plugs.IdempotencyPlugTest do
+ use ExUnit.Case, async: true
+ use Plug.Test
+
+ alias Pleroma.Plugs.IdempotencyPlug
+ alias Plug.Conn
+
+ test "returns result from cache" do
+ key = "test1"
+ orig_request_id = "test1"
+ second_request_id = "test2"
+ body = "testing"
+ status = 200
+
+ :post
+ |> conn("/cofe")
+ |> put_req_header("idempotency-key", key)
+ |> Conn.put_resp_header("x-request-id", orig_request_id)
+ |> Conn.put_resp_content_type("application/json")
+ |> IdempotencyPlug.call([])
+ |> Conn.send_resp(status, body)
+
+ conn =
+ :post
+ |> conn("/cofe")
+ |> put_req_header("idempotency-key", key)
+ |> Conn.put_resp_header("x-request-id", second_request_id)
+ |> Conn.put_resp_content_type("application/json")
+ |> IdempotencyPlug.call([])
+
+ assert_raise Conn.AlreadySentError, fn ->
+ Conn.send_resp(conn, :im_a_teapot, "no cofe")
+ end
+
+ assert conn.resp_body == body
+ assert conn.status == status
+
+ assert [^second_request_id] = Conn.get_resp_header(conn, "x-request-id")
+ assert [^orig_request_id] = Conn.get_resp_header(conn, "x-original-request-id")
+ assert [^key] = Conn.get_resp_header(conn, "idempotency-key")
+ assert ["true"] = Conn.get_resp_header(conn, "idempotent-replayed")
+ assert ["application/json; charset=utf-8"] = Conn.get_resp_header(conn, "content-type")
+ end
+
+ test "pass conn downstream if the cache not found" do
+ key = "test2"
+ orig_request_id = "test3"
+ body = "testing"
+ status = 200
+
+ conn =
+ :post
+ |> conn("/cofe")
+ |> put_req_header("idempotency-key", key)
+ |> Conn.put_resp_header("x-request-id", orig_request_id)
+ |> Conn.put_resp_content_type("application/json")
+ |> IdempotencyPlug.call([])
+ |> Conn.send_resp(status, body)
+
+ assert conn.resp_body == body
+ assert conn.status == status
+
+ assert [] = Conn.get_resp_header(conn, "idempotent-replayed")
+ assert [^key] = Conn.get_resp_header(conn, "idempotency-key")
+ end
+
+ test "passes conn downstream if idempotency is not present in headers" do
+ orig_request_id = "test4"
+ body = "testing"
+ status = 200
+
+ conn =
+ :post
+ |> conn("/cofe")
+ |> Conn.put_resp_header("x-request-id", orig_request_id)
+ |> Conn.put_resp_content_type("application/json")
+ |> IdempotencyPlug.call([])
+ |> Conn.send_resp(status, body)
+
+ assert [] = Conn.get_resp_header(conn, "idempotency-key")
+ end
+
+ test "doesn't work with GET/DELETE" do
+ key = "test3"
+ body = "testing"
+ status = 200
+
+ conn =
+ :get
+ |> conn("/cofe")
+ |> put_req_header("idempotency-key", key)
+ |> IdempotencyPlug.call([])
+ |> Conn.send_resp(status, body)
+
+ assert [] = Conn.get_resp_header(conn, "idempotency-key")
+
+ conn =
+ :delete
+ |> conn("/cofe")
+ |> put_req_header("idempotency-key", key)
+ |> IdempotencyPlug.call([])
+ |> Conn.send_resp(status, body)
+
+ assert [] = Conn.get_resp_header(conn, "idempotency-key")
+ end
+end
diff --git a/test/pleroma/web/plugs/instance_static_test.exs b/test/pleroma/web/plugs/instance_static_test.exs
@@ -0,0 +1,65 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.Plugs.InstanceStaticTest do
+ use Pleroma.Web.ConnCase
+
+ @dir "test/tmp/instance_static"
+
+ setup do
+ File.mkdir_p!(@dir)
+ on_exit(fn -> File.rm_rf(@dir) end)
+ end
+
+ setup do: clear_config([:instance, :static_dir], @dir)
+
+ test "overrides index" do
+ bundled_index = get(build_conn(), "/")
+ refute html_response(bundled_index, 200) == "hello world"
+
+ File.write!(@dir <> "/index.html", "hello world")
+
+ index = get(build_conn(), "/")
+ assert html_response(index, 200) == "hello world"
+ end
+
+ test "also overrides frontend files", %{conn: conn} do
+ name = "pelmora"
+ ref = "uguu"
+
+ clear_config([:frontends, :primary], %{"name" => name, "ref" => ref})
+
+ bundled_index = get(conn, "/")
+ refute html_response(bundled_index, 200) == "from frontend plug"
+
+ path = "#{@dir}/frontends/#{name}/#{ref}"
+ File.mkdir_p!(path)
+ File.write!("#{path}/index.html", "from frontend plug")
+
+ index = get(conn, "/")
+ assert html_response(index, 200) == "from frontend plug"
+
+ File.write!(@dir <> "/index.html", "from instance static")
+
+ index = get(conn, "/")
+ assert html_response(index, 200) == "from instance static"
+ end
+
+ test "overrides any file in static/static" do
+ bundled_index = get(build_conn(), "/static/terms-of-service.html")
+
+ assert html_response(bundled_index, 200) ==
+ File.read!("priv/static/static/terms-of-service.html")
+
+ File.mkdir!(@dir <> "/static")
+ File.write!(@dir <> "/static/terms-of-service.html", "plz be kind")
+
+ index = get(build_conn(), "/static/terms-of-service.html")
+ assert html_response(index, 200) == "plz be kind"
+
+ File.write!(@dir <> "/static/kaniini.html", "<h1>rabbit hugs as a service</h1>")
+ index = get(build_conn(), "/static/kaniini.html")
+ assert html_response(index, 200) == "<h1>rabbit hugs as a service</h1>"
+ end
+end
diff --git a/test/pleroma/web/plugs/legacy_authentication_plug_test.exs b/test/pleroma/web/plugs/legacy_authentication_plug_test.exs
@@ -0,0 +1,82 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.Plugs.LegacyAuthenticationPlugTest do
+ use Pleroma.Web.ConnCase
+
+ import Pleroma.Factory
+
+ alias Pleroma.Plugs.LegacyAuthenticationPlug
+ alias Pleroma.Plugs.OAuthScopesPlug
+ alias Pleroma.Plugs.PlugHelper
+ alias Pleroma.User
+
+ setup do
+ user =
+ insert(:user,
+ password: "password",
+ password_hash:
+ "$6$9psBWV8gxkGOZWBz$PmfCycChoxeJ3GgGzwvhlgacb9mUoZ.KUXNCssekER4SJ7bOK53uXrHNb2e4i8yPFgSKyzaW9CcmrDXWIEMtD1"
+ )
+
+ %{user: user}
+ end
+
+ test "it does nothing if a user is assigned", %{conn: conn, user: user} do
+ conn =
+ conn
+ |> assign(:auth_credentials, %{username: "dude", password: "password"})
+ |> assign(:auth_user, user)
+ |> assign(:user, %User{})
+
+ ret_conn =
+ conn
+ |> LegacyAuthenticationPlug.call(%{})
+
+ assert ret_conn == conn
+ end
+
+ @tag :skip_on_mac
+ test "if `auth_user` is present and password is correct, " <>
+ "it authenticates the user, resets the password, marks OAuthScopesPlug as skipped",
+ %{
+ conn: conn,
+ user: user
+ } do
+ conn =
+ conn
+ |> assign(:auth_credentials, %{username: "dude", password: "password"})
+ |> assign(:auth_user, user)
+
+ conn = LegacyAuthenticationPlug.call(conn, %{})
+
+ assert conn.assigns.user.id == user.id
+ assert PlugHelper.plug_skipped?(conn, OAuthScopesPlug)
+ end
+
+ @tag :skip_on_mac
+ test "it does nothing if the password is wrong", %{
+ conn: conn,
+ user: user
+ } do
+ conn =
+ conn
+ |> assign(:auth_credentials, %{username: "dude", password: "wrong_password"})
+ |> assign(:auth_user, user)
+
+ ret_conn =
+ conn
+ |> LegacyAuthenticationPlug.call(%{})
+
+ assert conn == ret_conn
+ end
+
+ test "with no credentials or user it does nothing", %{conn: conn} do
+ ret_conn =
+ conn
+ |> LegacyAuthenticationPlug.call(%{})
+
+ assert ret_conn == conn
+ end
+end
diff --git a/test/plugs/mapped_identity_to_signature_plug_test.exs b/test/pleroma/web/plugs/mapped_signature_to_identity_plug_test.exs
diff --git a/test/pleroma/web/plugs/o_auth_plug_test.exs b/test/pleroma/web/plugs/o_auth_plug_test.exs
@@ -0,0 +1,80 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.Plugs.OAuthPlugTest do
+ use Pleroma.Web.ConnCase, async: true
+
+ alias Pleroma.Plugs.OAuthPlug
+ import Pleroma.Factory
+
+ @session_opts [
+ store: :cookie,
+ key: "_test",
+ signing_salt: "cooldude"
+ ]
+
+ setup %{conn: conn} do
+ user = insert(:user)
+ {:ok, %{token: token}} = Pleroma.Web.OAuth.Token.create(insert(:oauth_app), user)
+ %{user: user, token: token, conn: conn}
+ end
+
+ test "with valid token(uppercase), it assigns the user", %{conn: conn} = opts do
+ conn =
+ conn
+ |> put_req_header("authorization", "BEARER #{opts[:token]}")
+ |> OAuthPlug.call(%{})
+
+ assert conn.assigns[:user] == opts[:user]
+ end
+
+ test "with valid token(downcase), it assigns the user", %{conn: conn} = opts do
+ conn =
+ conn
+ |> put_req_header("authorization", "bearer #{opts[:token]}")
+ |> OAuthPlug.call(%{})
+
+ assert conn.assigns[:user] == opts[:user]
+ end
+
+ test "with valid token(downcase) in url parameters, it assigns the user", opts do
+ conn =
+ :get
+ |> build_conn("/?access_token=#{opts[:token]}")
+ |> put_req_header("content-type", "application/json")
+ |> fetch_query_params()
+ |> OAuthPlug.call(%{})
+
+ assert conn.assigns[:user] == opts[:user]
+ end
+
+ test "with valid token(downcase) in body parameters, it assigns the user", opts do
+ conn =
+ :post
+ |> build_conn("/api/v1/statuses", access_token: opts[:token], status: "test")
+ |> OAuthPlug.call(%{})
+
+ assert conn.assigns[:user] == opts[:user]
+ end
+
+ test "with invalid token, it not assigns the user", %{conn: conn} do
+ conn =
+ conn
+ |> put_req_header("authorization", "bearer TTTTT")
+ |> OAuthPlug.call(%{})
+
+ refute conn.assigns[:user]
+ end
+
+ test "when token is missed but token in session, it assigns the user", %{conn: conn} = opts do
+ conn =
+ conn
+ |> Plug.Session.call(Plug.Session.init(@session_opts))
+ |> fetch_session()
+ |> put_session(:oauth_token, opts[:token])
+ |> OAuthPlug.call(%{})
+
+ assert conn.assigns[:user] == opts[:user]
+ end
+end
diff --git a/test/pleroma/web/plugs/o_auth_scopes_plug_test.exs b/test/pleroma/web/plugs/o_auth_scopes_plug_test.exs
@@ -0,0 +1,210 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.Plugs.OAuthScopesPlugTest do
+ use Pleroma.Web.ConnCase
+
+ alias Pleroma.Plugs.OAuthScopesPlug
+ alias Pleroma.Repo
+
+ import Mock
+ import Pleroma.Factory
+
+ test "is not performed if marked as skipped", %{conn: conn} do
+ with_mock OAuthScopesPlug, [:passthrough], perform: &passthrough([&1, &2]) do
+ conn =
+ conn
+ |> OAuthScopesPlug.skip_plug()
+ |> OAuthScopesPlug.call(%{scopes: ["random_scope"]})
+
+ refute called(OAuthScopesPlug.perform(:_, :_))
+ refute conn.halted
+ end
+ end
+
+ test "if `token.scopes` fulfills specified 'any of' conditions, " <>
+ "proceeds with no op",
+ %{conn: conn} do
+ token = insert(:oauth_token, scopes: ["read", "write"]) |> Repo.preload(:user)
+
+ conn =
+ conn
+ |> assign(:user, token.user)
+ |> assign(:token, token)
+ |> OAuthScopesPlug.call(%{scopes: ["read"]})
+
+ refute conn.halted
+ assert conn.assigns[:user]
+ end
+
+ test "if `token.scopes` fulfills specified 'all of' conditions, " <>
+ "proceeds with no op",
+ %{conn: conn} do
+ token = insert(:oauth_token, scopes: ["scope1", "scope2", "scope3"]) |> Repo.preload(:user)
+
+ conn =
+ conn
+ |> assign(:user, token.user)
+ |> assign(:token, token)
+ |> OAuthScopesPlug.call(%{scopes: ["scope2", "scope3"], op: :&})
+
+ refute conn.halted
+ assert conn.assigns[:user]
+ end
+
+ describe "with `fallback: :proceed_unauthenticated` option, " do
+ test "if `token.scopes` doesn't fulfill specified conditions, " <>
+ "clears :user and :token assigns",
+ %{conn: conn} do
+ user = insert(:user)
+ token1 = insert(:oauth_token, scopes: ["read", "write"], user: user)
+
+ for token <- [token1, nil], op <- [:|, :&] do
+ ret_conn =
+ conn
+ |> assign(:user, user)
+ |> assign(:token, token)
+ |> OAuthScopesPlug.call(%{
+ scopes: ["follow"],
+ op: op,
+ fallback: :proceed_unauthenticated
+ })
+
+ refute ret_conn.halted
+ refute ret_conn.assigns[:user]
+ refute ret_conn.assigns[:token]
+ end
+ end
+ end
+
+ describe "without :fallback option, " do
+ test "if `token.scopes` does not fulfill specified 'any of' conditions, " <>
+ "returns 403 and halts",
+ %{conn: conn} do
+ for token <- [insert(:oauth_token, scopes: ["read", "write"]), nil] do
+ any_of_scopes = ["follow", "push"]
+
+ ret_conn =
+ conn
+ |> assign(:token, token)
+ |> OAuthScopesPlug.call(%{scopes: any_of_scopes})
+
+ assert ret_conn.halted
+ assert 403 == ret_conn.status
+
+ expected_error = "Insufficient permissions: #{Enum.join(any_of_scopes, " | ")}."
+ assert Jason.encode!(%{error: expected_error}) == ret_conn.resp_body
+ end
+ end
+
+ test "if `token.scopes` does not fulfill specified 'all of' conditions, " <>
+ "returns 403 and halts",
+ %{conn: conn} do
+ for token <- [insert(:oauth_token, scopes: ["read", "write"]), nil] do
+ token_scopes = (token && token.scopes) || []
+ all_of_scopes = ["write", "follow"]
+
+ conn =
+ conn
+ |> assign(:token, token)
+ |> OAuthScopesPlug.call(%{scopes: all_of_scopes, op: :&})
+
+ assert conn.halted
+ assert 403 == conn.status
+
+ expected_error =
+ "Insufficient permissions: #{Enum.join(all_of_scopes -- token_scopes, " & ")}."
+
+ assert Jason.encode!(%{error: expected_error}) == conn.resp_body
+ end
+ end
+ end
+
+ describe "with hierarchical scopes, " do
+ test "if `token.scopes` fulfills specified 'any of' conditions, " <>
+ "proceeds with no op",
+ %{conn: conn} do
+ token = insert(:oauth_token, scopes: ["read", "write"]) |> Repo.preload(:user)
+
+ conn =
+ conn
+ |> assign(:user, token.user)
+ |> assign(:token, token)
+ |> OAuthScopesPlug.call(%{scopes: ["read:something"]})
+
+ refute conn.halted
+ assert conn.assigns[:user]
+ end
+
+ test "if `token.scopes` fulfills specified 'all of' conditions, " <>
+ "proceeds with no op",
+ %{conn: conn} do
+ token = insert(:oauth_token, scopes: ["scope1", "scope2", "scope3"]) |> Repo.preload(:user)
+
+ conn =
+ conn
+ |> assign(:user, token.user)
+ |> assign(:token, token)
+ |> OAuthScopesPlug.call(%{scopes: ["scope1:subscope", "scope2:subscope"], op: :&})
+
+ refute conn.halted
+ assert conn.assigns[:user]
+ end
+ end
+
+ describe "filter_descendants/2" do
+ test "filters scopes which directly match or are ancestors of supported scopes" do
+ f = fn scopes, supported_scopes ->
+ OAuthScopesPlug.filter_descendants(scopes, supported_scopes)
+ end
+
+ assert f.(["read", "follow"], ["write", "read"]) == ["read"]
+
+ assert f.(["read", "write:something", "follow"], ["write", "read"]) ==
+ ["read", "write:something"]
+
+ assert f.(["admin:read"], ["write", "read"]) == []
+
+ assert f.(["admin:read"], ["write", "admin"]) == ["admin:read"]
+ end
+ end
+
+ describe "transform_scopes/2" do
+ setup do: clear_config([:auth, :enforce_oauth_admin_scope_usage])
+
+ setup do
+ {:ok, %{f: &OAuthScopesPlug.transform_scopes/2}}
+ end
+
+ test "with :admin option, prefixes all requested scopes with `admin:` " <>
+ "and [optionally] keeps only prefixed scopes, " <>
+ "depending on `[:auth, :enforce_oauth_admin_scope_usage]` setting",
+ %{f: f} do
+ Pleroma.Config.put([:auth, :enforce_oauth_admin_scope_usage], false)
+
+ assert f.(["read"], %{admin: true}) == ["admin:read", "read"]
+
+ assert f.(["read", "write"], %{admin: true}) == [
+ "admin:read",
+ "read",
+ "admin:write",
+ "write"
+ ]
+
+ Pleroma.Config.put([:auth, :enforce_oauth_admin_scope_usage], true)
+
+ assert f.(["read:accounts"], %{admin: true}) == ["admin:read:accounts"]
+
+ assert f.(["read", "write:reports"], %{admin: true}) == [
+ "admin:read",
+ "admin:write:reports"
+ ]
+ end
+
+ test "with no supported options, returns unmodified scopes", %{f: f} do
+ assert f.(["read"], %{}) == ["read"]
+ assert f.(["read", "write"], %{}) == ["read", "write"]
+ end
+ end
+end
diff --git a/test/pleroma/web/plugs/plug_helper_test.exs b/test/pleroma/web/plugs/plug_helper_test.exs
@@ -0,0 +1,91 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.Plugs.PlugHelperTest do
+ @moduledoc "Tests for the functionality added via `use Pleroma.Web, :plug`"
+
+ alias Pleroma.Plugs.ExpectAuthenticatedCheckPlug
+ alias Pleroma.Plugs.ExpectPublicOrAuthenticatedCheckPlug
+ alias Pleroma.Plugs.PlugHelper
+
+ import Mock
+
+ use Pleroma.Web.ConnCase
+
+ describe "when plug is skipped, " do
+ setup_with_mocks(
+ [
+ {ExpectPublicOrAuthenticatedCheckPlug, [:passthrough], []}
+ ],
+ %{conn: conn}
+ ) do
+ conn = ExpectPublicOrAuthenticatedCheckPlug.skip_plug(conn)
+ %{conn: conn}
+ end
+
+ test "it neither adds plug to called plugs list nor calls `perform/2`, " <>
+ "regardless of :if_func / :unless_func options",
+ %{conn: conn} do
+ for opts <- [%{}, %{if_func: fn _ -> true end}, %{unless_func: fn _ -> false end}] do
+ ret_conn = ExpectPublicOrAuthenticatedCheckPlug.call(conn, opts)
+
+ refute called(ExpectPublicOrAuthenticatedCheckPlug.perform(:_, :_))
+ refute PlugHelper.plug_called?(ret_conn, ExpectPublicOrAuthenticatedCheckPlug)
+ end
+ end
+ end
+
+ describe "when plug is NOT skipped, " do
+ setup_with_mocks([{ExpectAuthenticatedCheckPlug, [:passthrough], []}]) do
+ :ok
+ end
+
+ test "with no pre-run checks, adds plug to called plugs list and calls `perform/2`", %{
+ conn: conn
+ } do
+ ret_conn = ExpectAuthenticatedCheckPlug.call(conn, %{})
+
+ assert called(ExpectAuthenticatedCheckPlug.perform(ret_conn, :_))
+ assert PlugHelper.plug_called?(ret_conn, ExpectAuthenticatedCheckPlug)
+ end
+
+ test "when :if_func option is given, calls the plug only if provided function evals tru-ish",
+ %{conn: conn} do
+ ret_conn = ExpectAuthenticatedCheckPlug.call(conn, %{if_func: fn _ -> false end})
+
+ refute called(ExpectAuthenticatedCheckPlug.perform(:_, :_))
+ refute PlugHelper.plug_called?(ret_conn, ExpectAuthenticatedCheckPlug)
+
+ ret_conn = ExpectAuthenticatedCheckPlug.call(conn, %{if_func: fn _ -> true end})
+
+ assert called(ExpectAuthenticatedCheckPlug.perform(ret_conn, :_))
+ assert PlugHelper.plug_called?(ret_conn, ExpectAuthenticatedCheckPlug)
+ end
+
+ test "if :unless_func option is given, calls the plug only if provided function evals falsy",
+ %{conn: conn} do
+ ret_conn = ExpectAuthenticatedCheckPlug.call(conn, %{unless_func: fn _ -> true end})
+
+ refute called(ExpectAuthenticatedCheckPlug.perform(:_, :_))
+ refute PlugHelper.plug_called?(ret_conn, ExpectAuthenticatedCheckPlug)
+
+ ret_conn = ExpectAuthenticatedCheckPlug.call(conn, %{unless_func: fn _ -> false end})
+
+ assert called(ExpectAuthenticatedCheckPlug.perform(ret_conn, :_))
+ assert PlugHelper.plug_called?(ret_conn, ExpectAuthenticatedCheckPlug)
+ end
+
+ test "allows a plug to be called multiple times (even if it's in called plugs list)", %{
+ conn: conn
+ } do
+ conn = ExpectAuthenticatedCheckPlug.call(conn, %{an_option: :value1})
+ assert called(ExpectAuthenticatedCheckPlug.perform(conn, %{an_option: :value1}))
+
+ assert PlugHelper.plug_called?(conn, ExpectAuthenticatedCheckPlug)
+
+ conn = ExpectAuthenticatedCheckPlug.call(conn, %{an_option: :value2})
+ assert called(ExpectAuthenticatedCheckPlug.perform(conn, %{an_option: :value2}))
+ end
+ end
+end
diff --git a/test/pleroma/web/plugs/rate_limiter_test.exs b/test/pleroma/web/plugs/rate_limiter_test.exs
@@ -0,0 +1,263 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.Plugs.RateLimiterTest do
+ use Pleroma.Web.ConnCase
+
+ alias Phoenix.ConnTest
+ alias Pleroma.Config
+ alias Pleroma.Plugs.RateLimiter
+ alias Plug.Conn
+
+ import Pleroma.Factory
+ import Pleroma.Tests.Helpers, only: [clear_config: 1, clear_config: 2]
+
+ # Note: each example must work with separate buckets in order to prevent concurrency issues
+ setup do: clear_config([Pleroma.Web.Endpoint, :http, :ip])
+ setup do: clear_config(:rate_limit)
+
+ describe "config" do
+ @limiter_name :test_init
+ setup do: clear_config([Pleroma.Plugs.RemoteIp, :enabled])
+
+ test "config is required for plug to work" do
+ Config.put([:rate_limit, @limiter_name], {1, 1})
+ Config.put([Pleroma.Web.Endpoint, :http, :ip], {8, 8, 8, 8})
+
+ assert %{limits: {1, 1}, name: :test_init, opts: [name: :test_init]} ==
+ [name: @limiter_name]
+ |> RateLimiter.init()
+ |> RateLimiter.action_settings()
+
+ assert nil ==
+ [name: :nonexisting_limiter]
+ |> RateLimiter.init()
+ |> RateLimiter.action_settings()
+ end
+ end
+
+ test "it is disabled if it remote ip plug is enabled but no remote ip is found" do
+ assert RateLimiter.disabled?(Conn.assign(build_conn(), :remote_ip_found, false))
+ end
+
+ test "it is enabled if remote ip found" do
+ refute RateLimiter.disabled?(Conn.assign(build_conn(), :remote_ip_found, true))
+ end
+
+ test "it is enabled if remote_ip_found flag doesn't exist" do
+ refute RateLimiter.disabled?(build_conn())
+ end
+
+ test "it restricts based on config values" do
+ limiter_name = :test_plug_opts
+ scale = 80
+ limit = 5
+
+ Config.put([Pleroma.Web.Endpoint, :http, :ip], {8, 8, 8, 8})
+ Config.put([:rate_limit, limiter_name], {scale, limit})
+
+ plug_opts = RateLimiter.init(name: limiter_name)
+ conn = build_conn(:get, "/")
+
+ for i <- 1..5 do
+ conn = RateLimiter.call(conn, plug_opts)
+ assert {^i, _} = RateLimiter.inspect_bucket(conn, limiter_name, plug_opts)
+ Process.sleep(10)
+ end
+
+ conn = RateLimiter.call(conn, plug_opts)
+ assert %{"error" => "Throttled"} = ConnTest.json_response(conn, :too_many_requests)
+ assert conn.halted
+
+ Process.sleep(50)
+
+ conn = build_conn(:get, "/")
+
+ conn = RateLimiter.call(conn, plug_opts)
+ assert {1, 4} = RateLimiter.inspect_bucket(conn, limiter_name, plug_opts)
+
+ refute conn.status == Conn.Status.code(:too_many_requests)
+ refute conn.resp_body
+ refute conn.halted
+ end
+
+ describe "options" do
+ test "`bucket_name` option overrides default bucket name" do
+ limiter_name = :test_bucket_name
+
+ Config.put([:rate_limit, limiter_name], {1000, 5})
+ Config.put([Pleroma.Web.Endpoint, :http, :ip], {8, 8, 8, 8})
+
+ base_bucket_name = "#{limiter_name}:group1"
+ plug_opts = RateLimiter.init(name: limiter_name, bucket_name: base_bucket_name)
+
+ conn = build_conn(:get, "/")
+
+ RateLimiter.call(conn, plug_opts)
+ assert {1, 4} = RateLimiter.inspect_bucket(conn, base_bucket_name, plug_opts)
+ assert {:error, :not_found} = RateLimiter.inspect_bucket(conn, limiter_name, plug_opts)
+ end
+
+ test "`params` option allows different queries to be tracked independently" do
+ limiter_name = :test_params
+ Config.put([:rate_limit, limiter_name], {1000, 5})
+ Config.put([Pleroma.Web.Endpoint, :http, :ip], {8, 8, 8, 8})
+
+ plug_opts = RateLimiter.init(name: limiter_name, params: ["id"])
+
+ conn = build_conn(:get, "/?id=1")
+ conn = Conn.fetch_query_params(conn)
+ conn_2 = build_conn(:get, "/?id=2")
+
+ RateLimiter.call(conn, plug_opts)
+ assert {1, 4} = RateLimiter.inspect_bucket(conn, limiter_name, plug_opts)
+ assert {0, 5} = RateLimiter.inspect_bucket(conn_2, limiter_name, plug_opts)
+ end
+
+ test "it supports combination of options modifying bucket name" do
+ limiter_name = :test_options_combo
+ Config.put([:rate_limit, limiter_name], {1000, 5})
+ Config.put([Pleroma.Web.Endpoint, :http, :ip], {8, 8, 8, 8})
+
+ base_bucket_name = "#{limiter_name}:group1"
+
+ plug_opts =
+ RateLimiter.init(name: limiter_name, bucket_name: base_bucket_name, params: ["id"])
+
+ id = "100"
+
+ conn = build_conn(:get, "/?id=#{id}")
+ conn = Conn.fetch_query_params(conn)
+ conn_2 = build_conn(:get, "/?id=#{101}")
+
+ RateLimiter.call(conn, plug_opts)
+ assert {1, 4} = RateLimiter.inspect_bucket(conn, base_bucket_name, plug_opts)
+ assert {0, 5} = RateLimiter.inspect_bucket(conn_2, base_bucket_name, plug_opts)
+ end
+ end
+
+ describe "unauthenticated users" do
+ test "are restricted based on remote IP" do
+ limiter_name = :test_unauthenticated
+ Config.put([:rate_limit, limiter_name], [{1000, 5}, {1, 10}])
+ Config.put([Pleroma.Web.Endpoint, :http, :ip], {8, 8, 8, 8})
+
+ plug_opts = RateLimiter.init(name: limiter_name)
+
+ conn = %{build_conn(:get, "/") | remote_ip: {127, 0, 0, 2}}
+ conn_2 = %{build_conn(:get, "/") | remote_ip: {127, 0, 0, 3}}
+
+ for i <- 1..5 do
+ conn = RateLimiter.call(conn, plug_opts)
+ assert {^i, _} = RateLimiter.inspect_bucket(conn, limiter_name, plug_opts)
+ refute conn.halted
+ end
+
+ conn = RateLimiter.call(conn, plug_opts)
+
+ assert %{"error" => "Throttled"} = ConnTest.json_response(conn, :too_many_requests)
+ assert conn.halted
+
+ conn_2 = RateLimiter.call(conn_2, plug_opts)
+ assert {1, 4} = RateLimiter.inspect_bucket(conn_2, limiter_name, plug_opts)
+
+ refute conn_2.status == Conn.Status.code(:too_many_requests)
+ refute conn_2.resp_body
+ refute conn_2.halted
+ end
+ end
+
+ describe "authenticated users" do
+ setup do
+ Ecto.Adapters.SQL.Sandbox.checkout(Pleroma.Repo)
+
+ :ok
+ end
+
+ test "can have limits separate from unauthenticated connections" do
+ limiter_name = :test_authenticated1
+
+ scale = 50
+ limit = 5
+ Config.put([Pleroma.Web.Endpoint, :http, :ip], {8, 8, 8, 8})
+ Config.put([:rate_limit, limiter_name], [{1000, 1}, {scale, limit}])
+
+ plug_opts = RateLimiter.init(name: limiter_name)
+
+ user = insert(:user)
+ conn = build_conn(:get, "/") |> assign(:user, user)
+
+ for i <- 1..5 do
+ conn = RateLimiter.call(conn, plug_opts)
+ assert {^i, _} = RateLimiter.inspect_bucket(conn, limiter_name, plug_opts)
+ refute conn.halted
+ end
+
+ conn = RateLimiter.call(conn, plug_opts)
+
+ assert %{"error" => "Throttled"} = ConnTest.json_response(conn, :too_many_requests)
+ assert conn.halted
+ end
+
+ test "different users are counted independently" do
+ limiter_name = :test_authenticated2
+ Config.put([:rate_limit, limiter_name], [{1, 10}, {1000, 5}])
+ Config.put([Pleroma.Web.Endpoint, :http, :ip], {8, 8, 8, 8})
+
+ plug_opts = RateLimiter.init(name: limiter_name)
+
+ user = insert(:user)
+ conn = build_conn(:get, "/") |> assign(:user, user)
+
+ user_2 = insert(:user)
+ conn_2 = build_conn(:get, "/") |> assign(:user, user_2)
+
+ for i <- 1..5 do
+ conn = RateLimiter.call(conn, plug_opts)
+ assert {^i, _} = RateLimiter.inspect_bucket(conn, limiter_name, plug_opts)
+ end
+
+ conn = RateLimiter.call(conn, plug_opts)
+ assert %{"error" => "Throttled"} = ConnTest.json_response(conn, :too_many_requests)
+ assert conn.halted
+
+ conn_2 = RateLimiter.call(conn_2, plug_opts)
+ assert {1, 4} = RateLimiter.inspect_bucket(conn_2, limiter_name, plug_opts)
+ refute conn_2.status == Conn.Status.code(:too_many_requests)
+ refute conn_2.resp_body
+ refute conn_2.halted
+ end
+ end
+
+ test "doesn't crash due to a race condition when multiple requests are made at the same time and the bucket is not yet initialized" do
+ limiter_name = :test_race_condition
+ Pleroma.Config.put([:rate_limit, limiter_name], {1000, 5})
+ Pleroma.Config.put([Pleroma.Web.Endpoint, :http, :ip], {8, 8, 8, 8})
+
+ opts = RateLimiter.init(name: limiter_name)
+
+ conn = build_conn(:get, "/")
+ conn_2 = build_conn(:get, "/")
+
+ %Task{pid: pid1} =
+ task1 =
+ Task.async(fn ->
+ receive do
+ :process2_up ->
+ RateLimiter.call(conn, opts)
+ end
+ end)
+
+ task2 =
+ Task.async(fn ->
+ send(pid1, :process2_up)
+ RateLimiter.call(conn_2, opts)
+ end)
+
+ Task.await(task1)
+ Task.await(task2)
+
+ refute {:err, :not_found} == RateLimiter.inspect_bucket(conn, limiter_name, opts)
+ end
+end
diff --git a/test/pleroma/web/plugs/remote_ip_test.exs b/test/pleroma/web/plugs/remote_ip_test.exs
@@ -0,0 +1,108 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.Plugs.RemoteIpTest do
+ use ExUnit.Case
+ use Plug.Test
+
+ alias Pleroma.Plugs.RemoteIp
+
+ import Pleroma.Tests.Helpers, only: [clear_config: 2]
+
+ setup do:
+ clear_config(RemoteIp,
+ enabled: true,
+ headers: ["x-forwarded-for"],
+ proxies: [],
+ reserved: [
+ "127.0.0.0/8",
+ "::1/128",
+ "fc00::/7",
+ "10.0.0.0/8",
+ "172.16.0.0/12",
+ "192.168.0.0/16"
+ ]
+ )
+
+ test "disabled" do
+ Pleroma.Config.put(RemoteIp, enabled: false)
+
+ %{remote_ip: remote_ip} = conn(:get, "/")
+
+ conn =
+ conn(:get, "/")
+ |> put_req_header("x-forwarded-for", "1.1.1.1")
+ |> RemoteIp.call(nil)
+
+ assert conn.remote_ip == remote_ip
+ end
+
+ test "enabled" do
+ conn =
+ conn(:get, "/")
+ |> put_req_header("x-forwarded-for", "1.1.1.1")
+ |> RemoteIp.call(nil)
+
+ assert conn.remote_ip == {1, 1, 1, 1}
+ end
+
+ test "custom headers" do
+ Pleroma.Config.put(RemoteIp, enabled: true, headers: ["cf-connecting-ip"])
+
+ conn =
+ conn(:get, "/")
+ |> put_req_header("x-forwarded-for", "1.1.1.1")
+ |> RemoteIp.call(nil)
+
+ refute conn.remote_ip == {1, 1, 1, 1}
+
+ conn =
+ conn(:get, "/")
+ |> put_req_header("cf-connecting-ip", "1.1.1.1")
+ |> RemoteIp.call(nil)
+
+ assert conn.remote_ip == {1, 1, 1, 1}
+ end
+
+ test "custom proxies" do
+ conn =
+ conn(:get, "/")
+ |> put_req_header("x-forwarded-for", "173.245.48.1, 1.1.1.1, 173.245.48.2")
+ |> RemoteIp.call(nil)
+
+ refute conn.remote_ip == {1, 1, 1, 1}
+
+ Pleroma.Config.put([RemoteIp, :proxies], ["173.245.48.0/20"])
+
+ conn =
+ conn(:get, "/")
+ |> put_req_header("x-forwarded-for", "173.245.48.1, 1.1.1.1, 173.245.48.2")
+ |> RemoteIp.call(nil)
+
+ assert conn.remote_ip == {1, 1, 1, 1}
+ end
+
+ test "proxies set without CIDR format" do
+ Pleroma.Config.put([RemoteIp, :proxies], ["173.245.48.1"])
+
+ conn =
+ conn(:get, "/")
+ |> put_req_header("x-forwarded-for", "173.245.48.1, 1.1.1.1")
+ |> RemoteIp.call(nil)
+
+ assert conn.remote_ip == {1, 1, 1, 1}
+ end
+
+ test "proxies set `nonsensical` CIDR" do
+ Pleroma.Config.put([RemoteIp, :reserved], ["127.0.0.0/8"])
+ Pleroma.Config.put([RemoteIp, :proxies], ["10.0.0.3/24"])
+
+ conn =
+ conn(:get, "/")
+ |> put_req_header("x-forwarded-for", "10.0.0.3, 1.1.1.1")
+ |> RemoteIp.call(nil)
+
+ assert conn.remote_ip == {1, 1, 1, 1}
+ end
+end
diff --git a/test/pleroma/web/plugs/session_authentication_plug_test.exs b/test/pleroma/web/plugs/session_authentication_plug_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.Plugs.SessionAuthenticationPlugTest do
+ use Pleroma.Web.ConnCase, async: true
+
+ alias Pleroma.Plugs.SessionAuthenticationPlug
+ alias Pleroma.User
+
+ setup %{conn: conn} do
+ session_opts = [
+ store: :cookie,
+ key: "_test",
+ signing_salt: "cooldude"
+ ]
+
+ conn =
+ conn
+ |> Plug.Session.call(Plug.Session.init(session_opts))
+ |> fetch_session
+ |> assign(:auth_user, %User{id: 1})
+
+ %{conn: conn}
+ end
+
+ test "it does nothing if a user is assigned", %{conn: conn} do
+ conn =
+ conn
+ |> assign(:user, %User{})
+
+ ret_conn =
+ conn
+ |> SessionAuthenticationPlug.call(%{})
+
+ assert ret_conn == conn
+ end
+
+ test "if the auth_user has the same id as the user_id in the session, it assigns the user", %{
+ conn: conn
+ } do
+ conn =
+ conn
+ |> put_session(:user_id, conn.assigns.auth_user.id)
+ |> SessionAuthenticationPlug.call(%{})
+
+ assert conn.assigns.user == conn.assigns.auth_user
+ end
+
+ test "if the auth_user has a different id as the user_id in the session, it does nothing", %{
+ conn: conn
+ } do
+ conn =
+ conn
+ |> put_session(:user_id, -1)
+
+ ret_conn =
+ conn
+ |> SessionAuthenticationPlug.call(%{})
+
+ assert ret_conn == conn
+ end
+end
diff --git a/test/pleroma/web/plugs/set_format_plug_test.exs b/test/pleroma/web/plugs/set_format_plug_test.exs
@@ -0,0 +1,38 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.Plugs.SetFormatPlugTest do
+ use ExUnit.Case, async: true
+ use Plug.Test
+
+ alias Pleroma.Plugs.SetFormatPlug
+
+ test "set format from params" do
+ conn =
+ :get
+ |> conn("/cofe?_format=json")
+ |> SetFormatPlug.call([])
+
+ assert %{format: "json"} == conn.assigns
+ end
+
+ test "set format from header" do
+ conn =
+ :get
+ |> conn("/cofe")
+ |> put_private(:phoenix_format, "xml")
+ |> SetFormatPlug.call([])
+
+ assert %{format: "xml"} == conn.assigns
+ end
+
+ test "doesn't set format" do
+ conn =
+ :get
+ |> conn("/cofe")
+ |> SetFormatPlug.call([])
+
+ refute conn.assigns[:format]
+ end
+end
diff --git a/test/pleroma/web/plugs/set_locale_plug_test.exs b/test/pleroma/web/plugs/set_locale_plug_test.exs
@@ -0,0 +1,46 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.Plugs.SetLocalePlugTest do
+ use ExUnit.Case, async: true
+ use Plug.Test
+
+ alias Pleroma.Plugs.SetLocalePlug
+ alias Plug.Conn
+
+ test "default locale is `en`" do
+ conn =
+ :get
+ |> conn("/cofe")
+ |> SetLocalePlug.call([])
+
+ assert "en" == Gettext.get_locale()
+ assert %{locale: "en"} == conn.assigns
+ end
+
+ test "use supported locale from `accept-language`" do
+ conn =
+ :get
+ |> conn("/cofe")
+ |> Conn.put_req_header(
+ "accept-language",
+ "ru, fr-CH, fr;q=0.9, en;q=0.8, *;q=0.5"
+ )
+ |> SetLocalePlug.call([])
+
+ assert "ru" == Gettext.get_locale()
+ assert %{locale: "ru"} == conn.assigns
+ end
+
+ test "use default locale if locale from `accept-language` is not supported" do
+ conn =
+ :get
+ |> conn("/cofe")
+ |> Conn.put_req_header("accept-language", "tlh")
+ |> SetLocalePlug.call([])
+
+ assert "en" == Gettext.get_locale()
+ assert %{locale: "en"} == conn.assigns
+ end
+end
diff --git a/test/pleroma/web/plugs/set_user_session_id_plug_test.exs b/test/pleroma/web/plugs/set_user_session_id_plug_test.exs
@@ -0,0 +1,45 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.Plugs.SetUserSessionIdPlugTest do
+ use Pleroma.Web.ConnCase, async: true
+
+ alias Pleroma.Plugs.SetUserSessionIdPlug
+ alias Pleroma.User
+
+ setup %{conn: conn} do
+ session_opts = [
+ store: :cookie,
+ key: "_test",
+ signing_salt: "cooldude"
+ ]
+
+ conn =
+ conn
+ |> Plug.Session.call(Plug.Session.init(session_opts))
+ |> fetch_session
+
+ %{conn: conn}
+ end
+
+ test "doesn't do anything if the user isn't set", %{conn: conn} do
+ ret_conn =
+ conn
+ |> SetUserSessionIdPlug.call(%{})
+
+ assert ret_conn == conn
+ end
+
+ test "sets the user_id in the session to the user id of the user assign", %{conn: conn} do
+ Code.ensure_compiled(Pleroma.User)
+
+ conn =
+ conn
+ |> assign(:user, %User{id: 1})
+ |> SetUserSessionIdPlug.call(%{})
+
+ id = get_session(conn, :user_id)
+ assert id == 1
+ end
+end
diff --git a/test/pleroma/web/plugs/uploaded_media_plug_test.exs b/test/pleroma/web/plugs/uploaded_media_plug_test.exs
@@ -0,0 +1,43 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.Plugs.UploadedMediaPlugTest do
+ use Pleroma.Web.ConnCase
+ alias Pleroma.Upload
+
+ defp upload_file(context) do
+ Pleroma.DataCase.ensure_local_uploader(context)
+ File.cp!("test/fixtures/image.jpg", "test/fixtures/image_tmp.jpg")
+
+ file = %Plug.Upload{
+ content_type: "image/jpg",
+ path: Path.absname("test/fixtures/image_tmp.jpg"),
+ filename: "nice_tf.jpg"
+ }
+
+ {:ok, data} = Upload.store(file)
+ [%{"href" => attachment_url} | _] = data["url"]
+ [attachment_url: attachment_url]
+ end
+
+ setup_all :upload_file
+
+ test "does not send Content-Disposition header when name param is not set", %{
+ attachment_url: attachment_url
+ } do
+ conn = get(build_conn(), attachment_url)
+ refute Enum.any?(conn.resp_headers, &(elem(&1, 0) == "content-disposition"))
+ end
+
+ test "sends Content-Disposition header when name param is set", %{
+ attachment_url: attachment_url
+ } do
+ conn = get(build_conn(), attachment_url <> "?name=\"cofe\".gif")
+
+ assert Enum.any?(
+ conn.resp_headers,
+ &(&1 == {"content-disposition", "filename=\"\\\"cofe\\\".gif\""})
+ )
+ end
+end
diff --git a/test/pleroma/web/plugs/user_enabled_plug_test.exs b/test/pleroma/web/plugs/user_enabled_plug_test.exs
@@ -0,0 +1,59 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.Plugs.UserEnabledPlugTest do
+ use Pleroma.Web.ConnCase, async: true
+
+ alias Pleroma.Plugs.UserEnabledPlug
+ import Pleroma.Factory
+
+ setup do: clear_config([:instance, :account_activation_required])
+
+ test "doesn't do anything if the user isn't set", %{conn: conn} do
+ ret_conn =
+ conn
+ |> UserEnabledPlug.call(%{})
+
+ assert ret_conn == conn
+ end
+
+ test "with a user that's not confirmed and a config requiring confirmation, it removes that user",
+ %{conn: conn} do
+ Pleroma.Config.put([:instance, :account_activation_required], true)
+
+ user = insert(:user, confirmation_pending: true)
+
+ conn =
+ conn
+ |> assign(:user, user)
+ |> UserEnabledPlug.call(%{})
+
+ assert conn.assigns.user == nil
+ end
+
+ test "with a user that is deactivated, it removes that user", %{conn: conn} do
+ user = insert(:user, deactivated: true)
+
+ conn =
+ conn
+ |> assign(:user, user)
+ |> UserEnabledPlug.call(%{})
+
+ assert conn.assigns.user == nil
+ end
+
+ test "with a user that is not deactivated, it does nothing", %{conn: conn} do
+ user = insert(:user)
+
+ conn =
+ conn
+ |> assign(:user, user)
+
+ ret_conn =
+ conn
+ |> UserEnabledPlug.call(%{})
+
+ assert conn == ret_conn
+ end
+end
diff --git a/test/pleroma/web/plugs/user_fetcher_plug_test.exs b/test/pleroma/web/plugs/user_fetcher_plug_test.exs
@@ -0,0 +1,41 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.Plugs.UserFetcherPlugTest do
+ use Pleroma.Web.ConnCase, async: true
+
+ alias Pleroma.Plugs.UserFetcherPlug
+ import Pleroma.Factory
+
+ setup do
+ user = insert(:user)
+ %{user: user}
+ end
+
+ test "if an auth_credentials assign is present, it tries to fetch the user and assigns it", %{
+ conn: conn,
+ user: user
+ } do
+ conn =
+ conn
+ |> assign(:auth_credentials, %{
+ username: user.nickname,
+ password: nil
+ })
+
+ conn =
+ conn
+ |> UserFetcherPlug.call(%{})
+
+ assert conn.assigns[:auth_user] == user
+ end
+
+ test "without a credential assign it doesn't do anything", %{conn: conn} do
+ ret_conn =
+ conn
+ |> UserFetcherPlug.call(%{})
+
+ assert conn == ret_conn
+ end
+end
diff --git a/test/pleroma/web/plugs/user_is_admin_plug_test.exs b/test/pleroma/web/plugs/user_is_admin_plug_test.exs
@@ -0,0 +1,37 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.Plugs.UserIsAdminPlugTest do
+ use Pleroma.Web.ConnCase, async: true
+
+ alias Pleroma.Plugs.UserIsAdminPlug
+ import Pleroma.Factory
+
+ test "accepts a user that is an admin" do
+ user = insert(:user, is_admin: true)
+
+ conn = assign(build_conn(), :user, user)
+
+ ret_conn = UserIsAdminPlug.call(conn, %{})
+
+ assert conn == ret_conn
+ end
+
+ test "denies a user that isn't an admin" do
+ user = insert(:user)
+
+ conn =
+ build_conn()
+ |> assign(:user, user)
+ |> UserIsAdminPlug.call(%{})
+
+ assert conn.status == 403
+ end
+
+ test "denies when a user isn't set" do
+ conn = UserIsAdminPlug.call(build_conn(), %{})
+
+ assert conn.status == 403
+ end
+end
diff --git a/test/pleroma/web/push/impl_test.exs b/test/pleroma/web/push/impl_test.exs
@@ -0,0 +1,344 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.Push.ImplTest do
+ use Pleroma.DataCase
+
+ import Pleroma.Factory
+
+ alias Pleroma.Notification
+ alias Pleroma.Object
+ alias Pleroma.User
+ alias Pleroma.Web.ActivityPub.ActivityPub
+ alias Pleroma.Web.CommonAPI
+ alias Pleroma.Web.Push.Impl
+ alias Pleroma.Web.Push.Subscription
+
+ setup do
+ Tesla.Mock.mock(fn
+ %{method: :post, url: "https://example.com/example/1234"} ->
+ %Tesla.Env{status: 200}
+
+ %{method: :post, url: "https://example.com/example/not_found"} ->
+ %Tesla.Env{status: 400}
+
+ %{method: :post, url: "https://example.com/example/bad"} ->
+ %Tesla.Env{status: 100}
+ end)
+
+ :ok
+ end
+
+ @sub %{
+ endpoint: "https://example.com/example/1234",
+ keys: %{
+ auth: "8eDyX_uCN0XRhSbY5hs7Hg==",
+ p256dh:
+ "BCIWgsnyXDv1VkhqL2P7YRBvdeuDnlwAPT2guNhdIoW3IP7GmHh1SMKPLxRf7x8vJy6ZFK3ol2ohgn_-0yP7QQA="
+ }
+ }
+ @api_key "BASgACIHpN1GYgzSRp"
+ @message "@Bob: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce sagittis fini..."
+
+ test "performs sending notifications" do
+ user = insert(:user)
+ user2 = insert(:user)
+ insert(:push_subscription, user: user, data: %{alerts: %{"mention" => true}})
+ insert(:push_subscription, user: user2, data: %{alerts: %{"mention" => true}})
+
+ insert(:push_subscription,
+ user: user,
+ data: %{alerts: %{"follow" => true, "mention" => true}}
+ )
+
+ insert(:push_subscription,
+ user: user,
+ data: %{alerts: %{"follow" => true, "mention" => false}}
+ )
+
+ {:ok, activity} = CommonAPI.post(user, %{status: "<Lorem ipsum dolor sit amet."})
+
+ notif =
+ insert(:notification,
+ user: user,
+ activity: activity,
+ type: "mention"
+ )
+
+ assert Impl.perform(notif) == {:ok, [:ok, :ok]}
+ end
+
+ @tag capture_log: true
+ test "returns error if notif does not match " do
+ assert Impl.perform(%{}) == {:error, :unknown_type}
+ end
+
+ test "successful message sending" do
+ assert Impl.push_message(@message, @sub, @api_key, %Subscription{}) == :ok
+ end
+
+ @tag capture_log: true
+ test "fail message sending" do
+ assert Impl.push_message(
+ @message,
+ Map.merge(@sub, %{endpoint: "https://example.com/example/bad"}),
+ @api_key,
+ %Subscription{}
+ ) == :error
+ end
+
+ test "delete subscription if result send message between 400..500" do
+ subscription = insert(:push_subscription)
+
+ assert Impl.push_message(
+ @message,
+ Map.merge(@sub, %{endpoint: "https://example.com/example/not_found"}),
+ @api_key,
+ subscription
+ ) == :ok
+
+ refute Pleroma.Repo.get(Subscription, subscription.id)
+ end
+
+ test "deletes subscription when token has been deleted" do
+ subscription = insert(:push_subscription)
+
+ Pleroma.Repo.delete(subscription.token)
+
+ refute Pleroma.Repo.get(Subscription, subscription.id)
+ end
+
+ test "renders title and body for create activity" do
+ user = insert(:user, nickname: "Bob")
+
+ {:ok, activity} =
+ CommonAPI.post(user, %{
+ status:
+ "<span>Lorem ipsum dolor sit amet</span>, consectetur :firefox: adipiscing elit. Fusce sagittis finibus turpis."
+ })
+
+ object = Object.normalize(activity)
+
+ assert Impl.format_body(
+ %{
+ activity: activity
+ },
+ user,
+ object
+ ) ==
+ "@Bob: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce sagittis fini..."
+
+ assert Impl.format_title(%{activity: activity, type: "mention"}) ==
+ "New Mention"
+ end
+
+ test "renders title and body for follow activity" do
+ user = insert(:user, nickname: "Bob")
+ other_user = insert(:user)
+ {:ok, _, _, activity} = CommonAPI.follow(user, other_user)
+ object = Object.normalize(activity, false)
+
+ assert Impl.format_body(%{activity: activity, type: "follow"}, user, object) ==
+ "@Bob has followed you"
+
+ assert Impl.format_title(%{activity: activity, type: "follow"}) ==
+ "New Follower"
+ end
+
+ test "renders title and body for announce activity" do
+ user = insert(:user)
+
+ {:ok, activity} =
+ CommonAPI.post(user, %{
+ status:
+ "<span>Lorem ipsum dolor sit amet</span>, consectetur :firefox: adipiscing elit. Fusce sagittis finibus turpis."
+ })
+
+ {:ok, announce_activity} = CommonAPI.repeat(activity.id, user)
+ object = Object.normalize(activity)
+
+ assert Impl.format_body(%{activity: announce_activity}, user, object) ==
+ "@#{user.nickname} repeated: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce sagittis fini..."
+
+ assert Impl.format_title(%{activity: announce_activity, type: "reblog"}) ==
+ "New Repeat"
+ end
+
+ test "renders title and body for like activity" do
+ user = insert(:user, nickname: "Bob")
+
+ {:ok, activity} =
+ CommonAPI.post(user, %{
+ status:
+ "<span>Lorem ipsum dolor sit amet</span>, consectetur :firefox: adipiscing elit. Fusce sagittis finibus turpis."
+ })
+
+ {:ok, activity} = CommonAPI.favorite(user, activity.id)
+ object = Object.normalize(activity)
+
+ assert Impl.format_body(%{activity: activity, type: "favourite"}, user, object) ==
+ "@Bob has favorited your post"
+
+ assert Impl.format_title(%{activity: activity, type: "favourite"}) ==
+ "New Favorite"
+ end
+
+ test "renders title for create activity with direct visibility" do
+ user = insert(:user, nickname: "Bob")
+
+ {:ok, activity} =
+ CommonAPI.post(user, %{
+ visibility: "direct",
+ status: "This is just between you and me, pal"
+ })
+
+ assert Impl.format_title(%{activity: activity}) ==
+ "New Direct Message"
+ end
+
+ describe "build_content/3" do
+ test "builds content for chat messages" do
+ user = insert(:user)
+ recipient = insert(:user)
+
+ {:ok, chat} = CommonAPI.post_chat_message(user, recipient, "hey")
+ object = Object.normalize(chat, false)
+ [notification] = Notification.for_user(recipient)
+
+ res = Impl.build_content(notification, user, object)
+
+ assert res == %{
+ body: "@#{user.nickname}: hey",
+ title: "New Chat Message"
+ }
+ end
+
+ test "builds content for chat messages with no content" do
+ user = insert(:user)
+ recipient = insert(:user)
+
+ file = %Plug.Upload{
+ content_type: "image/jpg",
+ path: Path.absname("test/fixtures/image.jpg"),
+ filename: "an_image.jpg"
+ }
+
+ {:ok, upload} = ActivityPub.upload(file, actor: user.ap_id)
+
+ {:ok, chat} = CommonAPI.post_chat_message(user, recipient, nil, media_id: upload.id)
+ object = Object.normalize(chat, false)
+ [notification] = Notification.for_user(recipient)
+
+ res = Impl.build_content(notification, user, object)
+
+ assert res == %{
+ body: "@#{user.nickname}: (Attachment)",
+ title: "New Chat Message"
+ }
+ end
+
+ test "hides contents of notifications when option enabled" do
+ user = insert(:user, nickname: "Bob")
+
+ user2 =
+ insert(:user, nickname: "Rob", notification_settings: %{hide_notification_contents: true})
+
+ {:ok, activity} =
+ CommonAPI.post(user, %{
+ visibility: "direct",
+ status: "<Lorem ipsum dolor sit amet."
+ })
+
+ notif = insert(:notification, user: user2, activity: activity)
+
+ actor = User.get_cached_by_ap_id(notif.activity.data["actor"])
+ object = Object.normalize(activity)
+
+ assert Impl.build_content(notif, actor, object) == %{
+ body: "New Direct Message"
+ }
+
+ {:ok, activity} =
+ CommonAPI.post(user, %{
+ visibility: "public",
+ status: "<Lorem ipsum dolor sit amet."
+ })
+
+ notif = insert(:notification, user: user2, activity: activity, type: "mention")
+
+ actor = User.get_cached_by_ap_id(notif.activity.data["actor"])
+ object = Object.normalize(activity)
+
+ assert Impl.build_content(notif, actor, object) == %{
+ body: "New Mention"
+ }
+
+ {:ok, activity} = CommonAPI.favorite(user, activity.id)
+
+ notif = insert(:notification, user: user2, activity: activity, type: "favourite")
+
+ actor = User.get_cached_by_ap_id(notif.activity.data["actor"])
+ object = Object.normalize(activity)
+
+ assert Impl.build_content(notif, actor, object) == %{
+ body: "New Favorite"
+ }
+ end
+
+ test "returns regular content when hiding contents option disabled" do
+ user = insert(:user, nickname: "Bob")
+
+ user2 =
+ insert(:user, nickname: "Rob", notification_settings: %{hide_notification_contents: false})
+
+ {:ok, activity} =
+ CommonAPI.post(user, %{
+ visibility: "direct",
+ status:
+ "<span>Lorem ipsum dolor sit amet</span>, consectetur :firefox: adipiscing elit. Fusce sagittis finibus turpis."
+ })
+
+ notif = insert(:notification, user: user2, activity: activity)
+
+ actor = User.get_cached_by_ap_id(notif.activity.data["actor"])
+ object = Object.normalize(activity)
+
+ assert Impl.build_content(notif, actor, object) == %{
+ body:
+ "@Bob: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce sagittis fini...",
+ title: "New Direct Message"
+ }
+
+ {:ok, activity} =
+ CommonAPI.post(user, %{
+ visibility: "public",
+ status:
+ "<span>Lorem ipsum dolor sit amet</span>, consectetur :firefox: adipiscing elit. Fusce sagittis finibus turpis."
+ })
+
+ notif = insert(:notification, user: user2, activity: activity, type: "mention")
+
+ actor = User.get_cached_by_ap_id(notif.activity.data["actor"])
+ object = Object.normalize(activity)
+
+ assert Impl.build_content(notif, actor, object) == %{
+ body:
+ "@Bob: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce sagittis fini...",
+ title: "New Mention"
+ }
+
+ {:ok, activity} = CommonAPI.favorite(user, activity.id)
+
+ notif = insert(:notification, user: user2, activity: activity, type: "favourite")
+
+ actor = User.get_cached_by_ap_id(notif.activity.data["actor"])
+ object = Object.normalize(activity)
+
+ assert Impl.build_content(notif, actor, object) == %{
+ body: "@Bob has favorited your post",
+ title: "New Favorite"
+ }
+ end
+ end
+end
diff --git a/test/pleroma/web/rel_me_test.exs b/test/pleroma/web/rel_me_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.RelMeTest do
+ use ExUnit.Case
+
+ setup_all do
+ Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end)
+ :ok
+ end
+
+ test "parse/1" do
+ hrefs = ["https://social.example.org/users/lain"]
+
+ assert Pleroma.Web.RelMe.parse("http://example.com/rel_me/null") == {:ok, []}
+
+ assert {:ok, %Tesla.Env{status: 404}} =
+ Pleroma.Web.RelMe.parse("http://example.com/rel_me/error")
+
+ assert Pleroma.Web.RelMe.parse("http://example.com/rel_me/link") == {:ok, hrefs}
+ assert Pleroma.Web.RelMe.parse("http://example.com/rel_me/anchor") == {:ok, hrefs}
+ assert Pleroma.Web.RelMe.parse("http://example.com/rel_me/anchor_nofollow") == {:ok, hrefs}
+ end
+
+ test "maybe_put_rel_me/2" do
+ profile_urls = ["https://social.example.org/users/lain"]
+ attr = "me"
+ fallback = nil
+
+ assert Pleroma.Web.RelMe.maybe_put_rel_me("http://example.com/rel_me/null", profile_urls) ==
+ fallback
+
+ assert Pleroma.Web.RelMe.maybe_put_rel_me("http://example.com/rel_me/error", profile_urls) ==
+ fallback
+
+ assert Pleroma.Web.RelMe.maybe_put_rel_me("http://example.com/rel_me/anchor", profile_urls) ==
+ attr
+
+ assert Pleroma.Web.RelMe.maybe_put_rel_me(
+ "http://example.com/rel_me/anchor_nofollow",
+ profile_urls
+ ) == attr
+
+ assert Pleroma.Web.RelMe.maybe_put_rel_me("http://example.com/rel_me/link", profile_urls) ==
+ attr
+ end
+end
diff --git a/test/pleroma/web/rich_media/helpers_test.exs b/test/pleroma/web/rich_media/helpers_test.exs
@@ -0,0 +1,86 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.RichMedia.HelpersTest do
+ use Pleroma.DataCase
+
+ alias Pleroma.Config
+ alias Pleroma.Object
+ alias Pleroma.Web.CommonAPI
+ alias Pleroma.Web.RichMedia.Helpers
+
+ import Pleroma.Factory
+ import Tesla.Mock
+
+ setup do
+ mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
+
+ :ok
+ end
+
+ setup do: clear_config([:rich_media, :enabled])
+
+ test "refuses to crawl incomplete URLs" do
+ user = insert(:user)
+
+ {:ok, activity} =
+ CommonAPI.post(user, %{
+ status: "[test](example.com/ogp)",
+ content_type: "text/markdown"
+ })
+
+ Config.put([:rich_media, :enabled], true)
+
+ assert %{} == Pleroma.Web.RichMedia.Helpers.fetch_data_for_activity(activity)
+ end
+
+ test "refuses to crawl malformed URLs" do
+ user = insert(:user)
+
+ {:ok, activity} =
+ CommonAPI.post(user, %{
+ status: "[test](example.com[]/ogp)",
+ content_type: "text/markdown"
+ })
+
+ Config.put([:rich_media, :enabled], true)
+
+ assert %{} == Pleroma.Web.RichMedia.Helpers.fetch_data_for_activity(activity)
+ end
+
+ test "crawls valid, complete URLs" do
+ user = insert(:user)
+
+ {:ok, activity} =
+ CommonAPI.post(user, %{
+ status: "[test](https://example.com/ogp)",
+ content_type: "text/markdown"
+ })
+
+ Config.put([:rich_media, :enabled], true)
+
+ assert %{page_url: "https://example.com/ogp", rich_media: _} =
+ Pleroma.Web.RichMedia.Helpers.fetch_data_for_activity(activity)
+ end
+
+ test "refuses to crawl URLs of private network from posts" do
+ user = insert(:user)
+
+ {:ok, activity} =
+ CommonAPI.post(user, %{status: "http://127.0.0.1:4000/notice/9kCP7VNyPJXFOXDrgO"})
+
+ {:ok, activity2} = CommonAPI.post(user, %{status: "https://10.111.10.1/notice/9kCP7V"})
+ {:ok, activity3} = CommonAPI.post(user, %{status: "https://172.16.32.40/notice/9kCP7V"})
+ {:ok, activity4} = CommonAPI.post(user, %{status: "https://192.168.10.40/notice/9kCP7V"})
+ {:ok, activity5} = CommonAPI.post(user, %{status: "https://pleroma.local/notice/9kCP7V"})
+
+ Config.put([:rich_media, :enabled], true)
+
+ assert %{} = Helpers.fetch_data_for_activity(activity)
+ assert %{} = Helpers.fetch_data_for_activity(activity2)
+ assert %{} = Helpers.fetch_data_for_activity(activity3)
+ assert %{} = Helpers.fetch_data_for_activity(activity4)
+ assert %{} = Helpers.fetch_data_for_activity(activity5)
+ end
+end
diff --git a/test/pleroma/web/rich_media/parser_test.exs b/test/pleroma/web/rich_media/parser_test.exs
@@ -0,0 +1,176 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.RichMedia.ParserTest do
+ use ExUnit.Case, async: true
+
+ alias Pleroma.Web.RichMedia.Parser
+
+ setup do
+ Tesla.Mock.mock(fn
+ %{
+ method: :get,
+ url: "http://example.com/ogp"
+ } ->
+ %Tesla.Env{status: 200, body: File.read!("test/fixtures/rich_media/ogp.html")}
+
+ %{
+ method: :get,
+ url: "http://example.com/non-ogp"
+ } ->
+ %Tesla.Env{status: 200, body: File.read!("test/fixtures/rich_media/non_ogp_embed.html")}
+
+ %{
+ method: :get,
+ url: "http://example.com/ogp-missing-title"
+ } ->
+ %Tesla.Env{
+ status: 200,
+ body: File.read!("test/fixtures/rich_media/ogp-missing-title.html")
+ }
+
+ %{
+ method: :get,
+ url: "http://example.com/twitter-card"
+ } ->
+ %Tesla.Env{status: 200, body: File.read!("test/fixtures/rich_media/twitter_card.html")}
+
+ %{
+ method: :get,
+ url: "http://example.com/oembed"
+ } ->
+ %Tesla.Env{status: 200, body: File.read!("test/fixtures/rich_media/oembed.html")}
+
+ %{
+ method: :get,
+ url: "http://example.com/oembed.json"
+ } ->
+ %Tesla.Env{status: 200, body: File.read!("test/fixtures/rich_media/oembed.json")}
+
+ %{method: :get, url: "http://example.com/empty"} ->
+ %Tesla.Env{status: 200, body: "hello"}
+
+ %{method: :get, url: "http://example.com/malformed"} ->
+ %Tesla.Env{status: 200, body: File.read!("test/fixtures/rich_media/malformed-data.html")}
+
+ %{method: :get, url: "http://example.com/error"} ->
+ {:error, :overload}
+
+ %{
+ method: :head,
+ url: "http://example.com/huge-page"
+ } ->
+ %Tesla.Env{
+ status: 200,
+ headers: [{"content-length", "2000001"}, {"content-type", "text/html"}]
+ }
+
+ %{
+ method: :head,
+ url: "http://example.com/pdf-file"
+ } ->
+ %Tesla.Env{
+ status: 200,
+ headers: [{"content-length", "1000000"}, {"content-type", "application/pdf"}]
+ }
+
+ %{method: :head} ->
+ %Tesla.Env{status: 404, body: "", headers: []}
+ end)
+
+ :ok
+ end
+
+ test "returns error when no metadata present" do
+ assert {:error, _} = Parser.parse("http://example.com/empty")
+ end
+
+ test "doesn't just add a title" do
+ assert {:error, {:invalid_metadata, _}} = Parser.parse("http://example.com/non-ogp")
+ end
+
+ test "parses ogp" do
+ assert Parser.parse("http://example.com/ogp") ==
+ {:ok,
+ %{
+ "image" => "http://ia.media-imdb.com/images/rock.jpg",
+ "title" => "The Rock",
+ "description" =>
+ "Directed by Michael Bay. With Sean Connery, Nicolas Cage, Ed Harris, John Spencer.",
+ "type" => "video.movie",
+ "url" => "http://example.com/ogp"
+ }}
+ end
+
+ test "falls back to <title> when ogp:title is missing" do
+ assert Parser.parse("http://example.com/ogp-missing-title") ==
+ {:ok,
+ %{
+ "image" => "http://ia.media-imdb.com/images/rock.jpg",
+ "title" => "The Rock (1996)",
+ "description" =>
+ "Directed by Michael Bay. With Sean Connery, Nicolas Cage, Ed Harris, John Spencer.",
+ "type" => "video.movie",
+ "url" => "http://example.com/ogp-missing-title"
+ }}
+ end
+
+ test "parses twitter card" do
+ assert Parser.parse("http://example.com/twitter-card") ==
+ {:ok,
+ %{
+ "card" => "summary",
+ "site" => "@flickr",
+ "image" => "https://farm6.staticflickr.com/5510/14338202952_93595258ff_z.jpg",
+ "title" => "Small Island Developing States Photo Submission",
+ "description" => "View the album on Flickr.",
+ "url" => "http://example.com/twitter-card"
+ }}
+ end
+
+ test "parses OEmbed" do
+ assert Parser.parse("http://example.com/oembed") ==
+ {:ok,
+ %{
+ "author_name" => "bees",
+ "author_url" => "https://www.flickr.com/photos/bees/",
+ "cache_age" => 3600,
+ "flickr_type" => "photo",
+ "height" => "768",
+ "html" =>
+ "<a data-flickr-embed=\"true\" href=\"https://www.flickr.com/photos/bees/2362225867/\" title=\"Bacon Lollys by bees, on Flickr\"><img src=\"https://farm4.staticflickr.com/3040/2362225867_4a87ab8baf_b.jpg\" width=\"1024\" height=\"768\" alt=\"Bacon Lollys\"></a><script async src=\"https://embedr.flickr.com/assets/client-code.js\" charset=\"utf-8\"></script>",
+ "license" => "All Rights Reserved",
+ "license_id" => 0,
+ "provider_name" => "Flickr",
+ "provider_url" => "https://www.flickr.com/",
+ "thumbnail_height" => 150,
+ "thumbnail_url" =>
+ "https://farm4.staticflickr.com/3040/2362225867_4a87ab8baf_q.jpg",
+ "thumbnail_width" => 150,
+ "title" => "Bacon Lollys",
+ "type" => "photo",
+ "url" => "http://example.com/oembed",
+ "version" => "1.0",
+ "web_page" => "https://www.flickr.com/photos/bees/2362225867/",
+ "web_page_short_url" => "https://flic.kr/p/4AK2sc",
+ "width" => "1024"
+ }}
+ end
+
+ test "rejects invalid OGP data" do
+ assert {:error, _} = Parser.parse("http://example.com/malformed")
+ end
+
+ test "returns error if getting page was not successful" do
+ assert {:error, :overload} = Parser.parse("http://example.com/error")
+ end
+
+ test "does a HEAD request to check if the body is too large" do
+ assert {:error, :body_too_large} = Parser.parse("http://example.com/huge-page")
+ end
+
+ test "does a HEAD request to check if the body is html" do
+ assert {:error, {:content_type, _}} = Parser.parse("http://example.com/pdf-file")
+ end
+end
diff --git a/test/pleroma/web/rich_media/parsers/ttl/aws_signed_url_test.exs b/test/pleroma/web/rich_media/parsers/ttl/aws_signed_url_test.exs
@@ -0,0 +1,82 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.RichMedia.Parsers.TTL.AwsSignedUrlTest do
+ use ExUnit.Case, async: true
+
+ test "s3 signed url is parsed correct for expiration time" do
+ url = "https://pleroma.social/amz"
+
+ {:ok, timestamp} =
+ Timex.now()
+ |> DateTime.truncate(:second)
+ |> Timex.format("{ISO:Basic:Z}")
+
+ # in seconds
+ valid_till = 30
+
+ metadata = construct_metadata(timestamp, valid_till, url)
+
+ expire_time =
+ Timex.parse!(timestamp, "{ISO:Basic:Z}") |> Timex.to_unix() |> Kernel.+(valid_till)
+
+ assert {:ok, expire_time} == Pleroma.Web.RichMedia.Parser.TTL.AwsSignedUrl.ttl(metadata, url)
+ end
+
+ test "s3 signed url is parsed and correct ttl is set for rich media" do
+ url = "https://pleroma.social/amz"
+
+ {:ok, timestamp} =
+ Timex.now()
+ |> DateTime.truncate(:second)
+ |> Timex.format("{ISO:Basic:Z}")
+
+ # in seconds
+ valid_till = 30
+
+ metadata = construct_metadata(timestamp, valid_till, url)
+
+ body = """
+ <meta name="twitter:card" content="Pleroma" />
+ <meta name="twitter:site" content="Pleroma" />
+ <meta name="twitter:title" content="Pleroma" />
+ <meta name="twitter:description" content="Pleroma" />
+ <meta name="twitter:image" content="#{Map.get(metadata, :image)}" />
+ """
+
+ Tesla.Mock.mock(fn
+ %{
+ method: :get,
+ url: "https://pleroma.social/amz"
+ } ->
+ %Tesla.Env{status: 200, body: body}
+ end)
+
+ Cachex.put(:rich_media_cache, url, metadata)
+
+ Pleroma.Web.RichMedia.Parser.set_ttl_based_on_image(metadata, url)
+
+ {:ok, cache_ttl} = Cachex.ttl(:rich_media_cache, url)
+
+ # as there is delay in setting and pulling the data from cache we ignore 1 second
+ # make it 2 seconds for flakyness
+ assert_in_delta(valid_till * 1000, cache_ttl, 2000)
+ end
+
+ defp construct_s3_url(timestamp, valid_till) do
+ "https://pleroma.s3.ap-southeast-1.amazonaws.com/sachin%20%281%29%20_a%20-%25%2Aasdasd%20BNN%20bnnn%20.png?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAIBLWWK6RGDQXDLJQ%2F20190716%2Fap-southeast-1%2Fs3%2Faws4_request&X-Amz-Date=#{
+ timestamp
+ }&X-Amz-Expires=#{valid_till}&X-Amz-Signature=04ffd6b98634f4b1bbabc62e0fac4879093cd54a6eed24fe8eb38e8369526bbf&X-Amz-SignedHeaders=host"
+ end
+
+ defp construct_metadata(timestamp, valid_till, url) do
+ %{
+ image: construct_s3_url(timestamp, valid_till),
+ site: "Pleroma",
+ title: "Pleroma",
+ description: "Pleroma",
+ url: url
+ }
+ end
+end
diff --git a/test/pleroma/web/rich_media/parsers/twitter_card_test.exs b/test/pleroma/web/rich_media/parsers/twitter_card_test.exs
@@ -0,0 +1,127 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.RichMedia.Parsers.TwitterCardTest do
+ use ExUnit.Case, async: true
+ alias Pleroma.Web.RichMedia.Parsers.TwitterCard
+
+ test "returns error when html not contains twitter card" do
+ assert TwitterCard.parse([{"html", [], [{"head", [], []}, {"body", [], []}]}], %{}) == %{}
+ end
+
+ test "parses twitter card with only name attributes" do
+ html =
+ File.read!("test/fixtures/nypd-facial-recognition-children-teenagers3.html")
+ |> Floki.parse_document!()
+
+ assert TwitterCard.parse(html, %{}) ==
+ %{
+ "app:id:googleplay" => "com.nytimes.android",
+ "app:name:googleplay" => "NYTimes",
+ "app:url:googleplay" => "nytimes://reader/id/100000006583622",
+ "site" => nil,
+ "description" =>
+ "With little oversight, the N.Y.P.D. has been using powerful surveillance technology on photos of children and teenagers.",
+ "image" =>
+ "https://static01.nyt.com/images/2019/08/01/nyregion/01nypd-juveniles-promo/01nypd-juveniles-promo-facebookJumbo.jpg",
+ "type" => "article",
+ "url" =>
+ "https://www.nytimes.com/2019/08/01/nyregion/nypd-facial-recognition-children-teenagers.html",
+ "title" =>
+ "She Was Arrested at 14. Then Her Photo Went to a Facial Recognition Database."
+ }
+ end
+
+ test "parses twitter card with only property attributes" do
+ html =
+ File.read!("test/fixtures/nypd-facial-recognition-children-teenagers2.html")
+ |> Floki.parse_document!()
+
+ assert TwitterCard.parse(html, %{}) ==
+ %{
+ "card" => "summary_large_image",
+ "description" =>
+ "With little oversight, the N.Y.P.D. has been using powerful surveillance technology on photos of children and teenagers.",
+ "image" =>
+ "https://static01.nyt.com/images/2019/08/01/nyregion/01nypd-juveniles-promo/01nypd-juveniles-promo-videoSixteenByNineJumbo1600.jpg",
+ "image:alt" => "",
+ "title" =>
+ "She Was Arrested at 14. Then Her Photo Went to a Facial Recognition Database.",
+ "url" =>
+ "https://www.nytimes.com/2019/08/01/nyregion/nypd-facial-recognition-children-teenagers.html",
+ "type" => "article"
+ }
+ end
+
+ test "parses twitter card with name & property attributes" do
+ html =
+ File.read!("test/fixtures/nypd-facial-recognition-children-teenagers.html")
+ |> Floki.parse_document!()
+
+ assert TwitterCard.parse(html, %{}) ==
+ %{
+ "app:id:googleplay" => "com.nytimes.android",
+ "app:name:googleplay" => "NYTimes",
+ "app:url:googleplay" => "nytimes://reader/id/100000006583622",
+ "card" => "summary_large_image",
+ "description" =>
+ "With little oversight, the N.Y.P.D. has been using powerful surveillance technology on photos of children and teenagers.",
+ "image" =>
+ "https://static01.nyt.com/images/2019/08/01/nyregion/01nypd-juveniles-promo/01nypd-juveniles-promo-videoSixteenByNineJumbo1600.jpg",
+ "image:alt" => "",
+ "site" => nil,
+ "title" =>
+ "She Was Arrested at 14. Then Her Photo Went to a Facial Recognition Database.",
+ "url" =>
+ "https://www.nytimes.com/2019/08/01/nyregion/nypd-facial-recognition-children-teenagers.html",
+ "type" => "article"
+ }
+ end
+
+ test "respect only first title tag on the page" do
+ image_path =
+ "https://assets.atlasobscura.com/media/W1siZiIsInVwbG9hZHMvYXNzZXRzLzkwYzgyMzI4LThlMDUtNGRiNS05MDg3LTUzMGUxZTM5N2RmMmVkOTM5ZDM4MGM4OTIx" <>
+ "YTQ5MF9EQVIgZXhodW1hdGlvbiBvZiBNYXJnYXJldCBDb3JiaW4gZ3JhdmUgMTkyNi5qcGciXSxbInAiLCJjb252ZXJ0IiwiIl0sWyJwIiwiY29udmVydCIsIi1xdWFsaXR5IDgxIC1hdXRvLW9" <>
+ "yaWVudCJdLFsicCIsInRodW1iIiwiNjAweD4iXV0/DAR%20exhumation%20of%20Margaret%20Corbin%20grave%201926.jpg"
+
+ html =
+ File.read!("test/fixtures/margaret-corbin-grave-west-point.html") |> Floki.parse_document!()
+
+ assert TwitterCard.parse(html, %{}) ==
+ %{
+ "site" => "@atlasobscura",
+ "title" => "The Missing Grave of Margaret Corbin, Revolutionary War Veteran",
+ "card" => "summary_large_image",
+ "image" => image_path,
+ "description" =>
+ "She's the only woman veteran honored with a monument at West Point. But where was she buried?",
+ "site_name" => "Atlas Obscura",
+ "type" => "article",
+ "url" => "http://www.atlasobscura.com/articles/margaret-corbin-grave-west-point"
+ }
+ end
+
+ test "takes first founded title in html head if there is html markup error" do
+ html =
+ File.read!("test/fixtures/nypd-facial-recognition-children-teenagers4.html")
+ |> Floki.parse_document!()
+
+ assert TwitterCard.parse(html, %{}) ==
+ %{
+ "site" => nil,
+ "title" =>
+ "She Was Arrested at 14. Then Her Photo Went to a Facial Recognition Database.",
+ "app:id:googleplay" => "com.nytimes.android",
+ "app:name:googleplay" => "NYTimes",
+ "app:url:googleplay" => "nytimes://reader/id/100000006583622",
+ "description" =>
+ "With little oversight, the N.Y.P.D. has been using powerful surveillance technology on photos of children and teenagers.",
+ "image" =>
+ "https://static01.nyt.com/images/2019/08/01/nyregion/01nypd-juveniles-promo/01nypd-juveniles-promo-facebookJumbo.jpg",
+ "type" => "article",
+ "url" =>
+ "https://www.nytimes.com/2019/08/01/nyregion/nypd-facial-recognition-children-teenagers.html"
+ }
+ end
+end
diff --git a/test/pleroma/web/static_fe/static_fe_controller_test.exs b/test/pleroma/web/static_fe/static_fe_controller_test.exs
@@ -0,0 +1,196 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.StaticFE.StaticFEControllerTest do
+ use Pleroma.Web.ConnCase
+
+ alias Pleroma.Activity
+ alias Pleroma.Config
+ alias Pleroma.Web.ActivityPub.Transmogrifier
+ alias Pleroma.Web.CommonAPI
+
+ import Pleroma.Factory
+
+ setup_all do: clear_config([:static_fe, :enabled], true)
+ setup do: clear_config([:instance, :federating], true)
+
+ setup %{conn: conn} do
+ conn = put_req_header(conn, "accept", "text/html")
+ user = insert(:user)
+
+ %{conn: conn, user: user}
+ end
+
+ describe "user profile html" do
+ test "just the profile as HTML", %{conn: conn, user: user} do
+ conn = get(conn, "/users/#{user.nickname}")
+
+ assert html_response(conn, 200) =~ user.nickname
+ end
+
+ test "404 when user not found", %{conn: conn} do
+ conn = get(conn, "/users/limpopo")
+
+ assert html_response(conn, 404) =~ "not found"
+ end
+
+ test "profile does not include private messages", %{conn: conn, user: user} do
+ CommonAPI.post(user, %{status: "public"})
+ CommonAPI.post(user, %{status: "private", visibility: "private"})
+
+ conn = get(conn, "/users/#{user.nickname}")
+
+ html = html_response(conn, 200)
+
+ assert html =~ ">public<"
+ refute html =~ ">private<"
+ end
+
+ test "pagination", %{conn: conn, user: user} do
+ Enum.map(1..30, fn i -> CommonAPI.post(user, %{status: "test#{i}"}) end)
+
+ conn = get(conn, "/users/#{user.nickname}")
+
+ html = html_response(conn, 200)
+
+ assert html =~ ">test30<"
+ assert html =~ ">test11<"
+ refute html =~ ">test10<"
+ refute html =~ ">test1<"
+ end
+
+ test "pagination, page 2", %{conn: conn, user: user} do
+ activities = Enum.map(1..30, fn i -> CommonAPI.post(user, %{status: "test#{i}"}) end)
+ {:ok, a11} = Enum.at(activities, 11)
+
+ conn = get(conn, "/users/#{user.nickname}?max_id=#{a11.id}")
+
+ html = html_response(conn, 200)
+
+ assert html =~ ">test1<"
+ assert html =~ ">test10<"
+ refute html =~ ">test20<"
+ refute html =~ ">test29<"
+ end
+
+ test "it requires authentication if instance is NOT federating", %{conn: conn, user: user} do
+ ensure_federating_or_authenticated(conn, "/users/#{user.nickname}", user)
+ end
+ end
+
+ describe "notice html" do
+ test "single notice page", %{conn: conn, user: user} do
+ {:ok, activity} = CommonAPI.post(user, %{status: "testing a thing!"})
+
+ conn = get(conn, "/notice/#{activity.id}")
+
+ html = html_response(conn, 200)
+ assert html =~ "<header>"
+ assert html =~ user.nickname
+ 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>"})
+
+ conn =
+ conn
+ |> put_req_header("accept", "text/html")
+ |> get("/notice/#{activity.id}")
+
+ html = html_response(conn, 200)
+ assert html =~ ~s[<script>alert('xss')</script>]
+ end
+
+ test "shows the whole thread", %{conn: conn, user: user} do
+ {:ok, activity} = CommonAPI.post(user, %{status: "space: the final frontier"})
+
+ CommonAPI.post(user, %{
+ status: "these are the voyages or something",
+ in_reply_to_status_id: activity.id
+ })
+
+ conn = get(conn, "/notice/#{activity.id}")
+
+ html = html_response(conn, 200)
+ assert html =~ "the final frontier"
+ assert html =~ "voyages"
+ end
+
+ test "redirect by AP object ID", %{conn: conn, user: user} do
+ {:ok, %Activity{data: %{"object" => object_url}}} =
+ CommonAPI.post(user, %{status: "beam me up"})
+
+ conn = get(conn, URI.parse(object_url).path)
+
+ assert html_response(conn, 302) =~ "redirected"
+ end
+
+ test "redirect by activity ID", %{conn: conn, user: user} do
+ {:ok, %Activity{data: %{"id" => id}}} =
+ CommonAPI.post(user, %{status: "I'm a doctor, not a devops!"})
+
+ conn = get(conn, URI.parse(id).path)
+
+ assert html_response(conn, 302) =~ "redirected"
+ end
+
+ test "404 when notice not found", %{conn: conn} do
+ conn = get(conn, "/notice/88c9c317")
+
+ assert html_response(conn, 404) =~ "not found"
+ end
+
+ test "404 for private status", %{conn: conn, user: user} do
+ {:ok, activity} = CommonAPI.post(user, %{status: "don't show me!", visibility: "private"})
+
+ conn = get(conn, "/notice/#{activity.id}")
+
+ assert html_response(conn, 404) =~ "not found"
+ end
+
+ test "302 for remote cached status", %{conn: conn, user: user} do
+ message = %{
+ "@context" => "https://www.w3.org/ns/activitystreams",
+ "to" => user.follower_address,
+ "cc" => "https://www.w3.org/ns/activitystreams#Public",
+ "type" => "Create",
+ "object" => %{
+ "content" => "blah blah blah",
+ "type" => "Note",
+ "attributedTo" => user.ap_id,
+ "inReplyTo" => nil
+ },
+ "actor" => user.ap_id
+ }
+
+ assert {:ok, activity} = Transmogrifier.handle_incoming(message)
+
+ conn = get(conn, "/notice/#{activity.id}")
+
+ assert html_response(conn, 302) =~ "redirected"
+ end
+
+ test "it requires authentication if instance is NOT federating", %{conn: conn, user: user} do
+ {:ok, activity} = CommonAPI.post(user, %{status: "testing a thing!"})
+
+ ensure_federating_or_authenticated(conn, "/notice/#{activity.id}", user)
+ end
+ end
+end
diff --git a/test/pleroma/web/streamer_test.exs b/test/pleroma/web/streamer_test.exs
@@ -0,0 +1,767 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.StreamerTest do
+ use Pleroma.DataCase
+
+ import Pleroma.Factory
+
+ alias Pleroma.Chat
+ alias Pleroma.Chat.MessageReference
+ alias Pleroma.Conversation.Participation
+ alias Pleroma.List
+ alias Pleroma.Object
+ alias Pleroma.User
+ alias Pleroma.Web.CommonAPI
+ alias Pleroma.Web.Streamer
+ alias Pleroma.Web.StreamerView
+
+ @moduletag needs_streamer: true, capture_log: true
+
+ setup do: clear_config([:instance, :skip_thread_containment])
+
+ describe "get_topic/_ (unauthenticated)" do
+ test "allows public" do
+ assert {:ok, "public"} = Streamer.get_topic("public", nil, nil)
+ assert {:ok, "public:local"} = Streamer.get_topic("public:local", nil, nil)
+ assert {:ok, "public:media"} = Streamer.get_topic("public:media", nil, nil)
+ assert {:ok, "public:local:media"} = Streamer.get_topic("public:local:media", nil, nil)
+ end
+
+ test "allows hashtag streams" do
+ assert {:ok, "hashtag:cofe"} = Streamer.get_topic("hashtag", nil, nil, %{"tag" => "cofe"})
+ end
+
+ test "disallows user streams" do
+ assert {:error, _} = Streamer.get_topic("user", nil, nil)
+ assert {:error, _} = Streamer.get_topic("user:notification", nil, nil)
+ assert {:error, _} = Streamer.get_topic("direct", nil, nil)
+ end
+
+ test "disallows list streams" do
+ assert {:error, _} = Streamer.get_topic("list", nil, nil, %{"list" => 42})
+ end
+ end
+
+ describe "get_topic/_ (authenticated)" do
+ setup do: oauth_access(["read"])
+
+ test "allows public streams (regardless of OAuth token scopes)", %{
+ user: user,
+ token: read_oauth_token
+ } do
+ with oauth_token <- [nil, read_oauth_token] do
+ assert {:ok, "public"} = Streamer.get_topic("public", user, oauth_token)
+ assert {:ok, "public:local"} = Streamer.get_topic("public:local", user, oauth_token)
+ assert {:ok, "public:media"} = Streamer.get_topic("public:media", user, oauth_token)
+
+ assert {:ok, "public:local:media"} =
+ Streamer.get_topic("public:local:media", user, oauth_token)
+ end
+ end
+
+ test "allows user streams (with proper OAuth token scopes)", %{
+ user: user,
+ token: read_oauth_token
+ } do
+ %{token: read_notifications_token} = oauth_access(["read:notifications"], user: user)
+ %{token: read_statuses_token} = oauth_access(["read:statuses"], user: user)
+ %{token: badly_scoped_token} = oauth_access(["irrelevant:scope"], user: user)
+
+ expected_user_topic = "user:#{user.id}"
+ expected_notification_topic = "user:notification:#{user.id}"
+ expected_direct_topic = "direct:#{user.id}"
+ expected_pleroma_chat_topic = "user:pleroma_chat:#{user.id}"
+
+ for valid_user_token <- [read_oauth_token, read_statuses_token] do
+ assert {:ok, ^expected_user_topic} = Streamer.get_topic("user", user, valid_user_token)
+
+ assert {:ok, ^expected_direct_topic} =
+ Streamer.get_topic("direct", user, valid_user_token)
+
+ assert {:ok, ^expected_pleroma_chat_topic} =
+ Streamer.get_topic("user:pleroma_chat", user, valid_user_token)
+ end
+
+ for invalid_user_token <- [read_notifications_token, badly_scoped_token],
+ user_topic <- ["user", "direct", "user:pleroma_chat"] do
+ assert {:error, :unauthorized} = Streamer.get_topic(user_topic, user, invalid_user_token)
+ end
+
+ for valid_notification_token <- [read_oauth_token, read_notifications_token] do
+ assert {:ok, ^expected_notification_topic} =
+ Streamer.get_topic("user:notification", user, valid_notification_token)
+ end
+
+ for invalid_notification_token <- [read_statuses_token, badly_scoped_token] do
+ assert {:error, :unauthorized} =
+ Streamer.get_topic("user:notification", user, invalid_notification_token)
+ end
+ end
+
+ test "allows hashtag streams (regardless of OAuth token scopes)", %{
+ user: user,
+ token: read_oauth_token
+ } do
+ for oauth_token <- [nil, read_oauth_token] do
+ assert {:ok, "hashtag:cofe"} =
+ Streamer.get_topic("hashtag", user, oauth_token, %{"tag" => "cofe"})
+ end
+ end
+
+ test "disallows registering to another user's stream", %{user: user, token: read_oauth_token} do
+ another_user = insert(:user)
+ assert {:error, _} = Streamer.get_topic("user:#{another_user.id}", user, read_oauth_token)
+
+ assert {:error, _} =
+ Streamer.get_topic("user:notification:#{another_user.id}", user, read_oauth_token)
+
+ assert {:error, _} = Streamer.get_topic("direct:#{another_user.id}", user, read_oauth_token)
+ end
+
+ test "allows list stream that are owned by the user (with `read` or `read:lists` scopes)", %{
+ user: user,
+ token: read_oauth_token
+ } do
+ %{token: read_lists_token} = oauth_access(["read:lists"], user: user)
+ %{token: invalid_token} = oauth_access(["irrelevant:scope"], user: user)
+ {:ok, list} = List.create("Test", user)
+
+ assert {:error, _} = Streamer.get_topic("list:#{list.id}", user, read_oauth_token)
+
+ for valid_token <- [read_oauth_token, read_lists_token] do
+ assert {:ok, _} = Streamer.get_topic("list", user, valid_token, %{"list" => list.id})
+ end
+
+ assert {:error, _} = Streamer.get_topic("list", user, invalid_token, %{"list" => list.id})
+ end
+
+ test "disallows list stream that are not owned by the user", %{user: user, token: oauth_token} do
+ another_user = insert(:user)
+ {:ok, list} = List.create("Test", another_user)
+
+ assert {:error, _} = Streamer.get_topic("list:#{list.id}", user, oauth_token)
+ assert {:error, _} = Streamer.get_topic("list", user, oauth_token, %{"list" => list.id})
+ end
+ end
+
+ describe "user streams" do
+ setup do
+ %{user: user, token: token} = oauth_access(["read"])
+ notify = insert(:notification, user: user, activity: build(:note_activity))
+ {:ok, %{user: user, notify: notify, token: token}}
+ end
+
+ test "it streams the user's post in the 'user' stream", %{user: user, token: oauth_token} do
+ Streamer.get_topic_and_add_socket("user", user, oauth_token)
+ {:ok, activity} = CommonAPI.post(user, %{status: "hey"})
+
+ assert_receive {:render_with_user, _, _, ^activity}
+ refute Streamer.filtered_by_user?(user, activity)
+ end
+
+ test "it streams boosts of the user in the 'user' stream", %{user: user, token: oauth_token} do
+ Streamer.get_topic_and_add_socket("user", user, oauth_token)
+
+ other_user = insert(:user)
+ {:ok, activity} = CommonAPI.post(other_user, %{status: "hey"})
+ {:ok, announce} = CommonAPI.repeat(activity.id, user)
+
+ assert_receive {:render_with_user, Pleroma.Web.StreamerView, "update.json", ^announce}
+ 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,
+ token: oauth_token
+ } do
+ Streamer.get_topic_and_add_socket("user", user, oauth_token)
+
+ 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,
+ token: oauth_token
+ } do
+ Streamer.get_topic_and_add_socket("user", user, oauth_token)
+
+ 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,
+ token: oauth_token
+ } do
+ Streamer.get_topic_and_add_socket("user", user, oauth_token)
+
+ other_user = insert(:user)
+ {:ok, activity} = CommonAPI.post(other_user, %{status: "hey"})
+
+ data =
+ File.read!("test/fixtures/mastodon-announce.json")
+ |> Poison.decode!()
+ |> Map.put("object", activity.data["object"])
+ |> Map.put("actor", user.ap_id)
+
+ {:ok, %Pleroma.Activity{data: _data, local: false} = announce} =
+ Pleroma.Web.ActivityPub.Transmogrifier.handle_incoming(data)
+
+ assert_receive {:render_with_user, Pleroma.Web.StreamerView, "update.json", ^announce}
+ refute Streamer.filtered_by_user?(user, announce)
+ end
+
+ test "it sends notify to in the 'user' stream", %{
+ user: user,
+ token: oauth_token,
+ notify: notify
+ } do
+ Streamer.get_topic_and_add_socket("user", user, oauth_token)
+ Streamer.stream("user", notify)
+
+ assert_receive {:render_with_user, _, _, ^notify}
+ refute Streamer.filtered_by_user?(user, notify)
+ end
+
+ test "it sends notify to in the 'user:notification' stream", %{
+ user: user,
+ token: oauth_token,
+ notify: notify
+ } do
+ Streamer.get_topic_and_add_socket("user:notification", user, oauth_token)
+ Streamer.stream("user:notification", notify)
+
+ assert_receive {:render_with_user, _, _, ^notify}
+ refute Streamer.filtered_by_user?(user, notify)
+ end
+
+ test "it sends chat messages to the 'user:pleroma_chat' stream", %{
+ user: user,
+ token: oauth_token
+ } do
+ other_user = insert(:user)
+
+ {:ok, create_activity} = CommonAPI.post_chat_message(other_user, user, "hey cirno")
+ object = Object.normalize(create_activity, false)
+ chat = Chat.get(user.id, other_user.ap_id)
+ cm_ref = MessageReference.for_chat_and_object(chat, object)
+ cm_ref = %{cm_ref | chat: chat, object: object}
+
+ Streamer.get_topic_and_add_socket("user:pleroma_chat", user, oauth_token)
+ Streamer.stream("user:pleroma_chat", {user, cm_ref})
+
+ text = StreamerView.render("chat_update.json", %{chat_message_reference: cm_ref})
+
+ assert text =~ "hey cirno"
+ assert_receive {:text, ^text}
+ end
+
+ test "it sends chat messages to the 'user' stream", %{user: user, token: oauth_token} do
+ other_user = insert(:user)
+
+ {:ok, create_activity} = CommonAPI.post_chat_message(other_user, user, "hey cirno")
+ object = Object.normalize(create_activity, false)
+ chat = Chat.get(user.id, other_user.ap_id)
+ cm_ref = MessageReference.for_chat_and_object(chat, object)
+ cm_ref = %{cm_ref | chat: chat, object: object}
+
+ Streamer.get_topic_and_add_socket("user", user, oauth_token)
+ Streamer.stream("user", {user, cm_ref})
+
+ text = StreamerView.render("chat_update.json", %{chat_message_reference: cm_ref})
+
+ assert text =~ "hey cirno"
+ assert_receive {:text, ^text}
+ end
+
+ test "it sends chat message notifications to the 'user:notification' stream", %{
+ user: user,
+ token: oauth_token
+ } do
+ other_user = insert(:user)
+
+ {:ok, create_activity} = CommonAPI.post_chat_message(other_user, user, "hey")
+
+ notify =
+ Repo.get_by(Pleroma.Notification, user_id: user.id, activity_id: create_activity.id)
+ |> Repo.preload(:activity)
+
+ Streamer.get_topic_and_add_socket("user:notification", user, oauth_token)
+ Streamer.stream("user:notification", notify)
+
+ assert_receive {:render_with_user, _, _, ^notify}
+ refute Streamer.filtered_by_user?(user, notify)
+ end
+
+ test "it doesn't send notify to the 'user:notification' stream when a user is blocked", %{
+ user: user,
+ token: oauth_token
+ } do
+ blocked = insert(:user)
+ {:ok, _user_relationship} = User.block(user, blocked)
+
+ Streamer.get_topic_and_add_socket("user:notification", user, oauth_token)
+
+ {:ok, activity} = CommonAPI.post(user, %{status: ":("})
+ {:ok, _} = CommonAPI.favorite(blocked, activity.id)
+
+ refute_receive _
+ end
+
+ test "it doesn't send notify to the 'user:notification' stream when a thread is muted", %{
+ user: user,
+ token: oauth_token
+ } do
+ user2 = insert(:user)
+
+ {:ok, activity} = CommonAPI.post(user, %{status: "super hot take"})
+ {:ok, _} = CommonAPI.add_mute(user, activity)
+
+ Streamer.get_topic_and_add_socket("user:notification", user, oauth_token)
+
+ {:ok, favorite_activity} = CommonAPI.favorite(user2, activity.id)
+
+ refute_receive _
+ assert Streamer.filtered_by_user?(user, favorite_activity)
+ end
+
+ test "it sends favorite to 'user:notification' stream'", %{
+ user: user,
+ token: oauth_token
+ } do
+ user2 = insert(:user, %{ap_id: "https://hecking-lewd-place.com/user/meanie"})
+
+ {:ok, activity} = CommonAPI.post(user, %{status: "super hot take"})
+ Streamer.get_topic_and_add_socket("user:notification", user, oauth_token)
+ {:ok, favorite_activity} = CommonAPI.favorite(user2, activity.id)
+
+ assert_receive {:render_with_user, _, "notification.json", notif}
+ assert notif.activity.id == favorite_activity.id
+ refute Streamer.filtered_by_user?(user, notif)
+ end
+
+ test "it doesn't send the 'user:notification' stream' when a domain is blocked", %{
+ user: user,
+ token: oauth_token
+ } do
+ user2 = insert(:user, %{ap_id: "https://hecking-lewd-place.com/user/meanie"})
+
+ {:ok, user} = User.block_domain(user, "hecking-lewd-place.com")
+ {:ok, activity} = CommonAPI.post(user, %{status: "super hot take"})
+ Streamer.get_topic_and_add_socket("user:notification", user, oauth_token)
+ {:ok, favorite_activity} = CommonAPI.favorite(user2, activity.id)
+
+ refute_receive _
+ assert Streamer.filtered_by_user?(user, favorite_activity)
+ end
+
+ test "it sends follow activities to the 'user:notification' stream", %{
+ user: user,
+ token: oauth_token
+ } do
+ user_url = user.ap_id
+ user2 = insert(:user)
+
+ body =
+ File.read!("test/fixtures/users_mock/localhost.json")
+ |> String.replace("{{nickname}}", user.nickname)
+ |> Jason.encode!()
+
+ Tesla.Mock.mock_global(fn
+ %{method: :get, url: ^user_url} ->
+ %Tesla.Env{status: 200, body: body}
+ end)
+
+ Streamer.get_topic_and_add_socket("user:notification", user, oauth_token)
+ {:ok, _follower, _followed, follow_activity} = CommonAPI.follow(user2, user)
+
+ assert_receive {:render_with_user, _, "notification.json", notif}
+ assert notif.activity.id == follow_activity.id
+ refute Streamer.filtered_by_user?(user, notif)
+ end
+ end
+
+ describe "public streams" do
+ test "it sends to public (authenticated)" do
+ %{user: user, token: oauth_token} = oauth_access(["read"])
+ other_user = insert(:user)
+
+ Streamer.get_topic_and_add_socket("public", user, oauth_token)
+
+ {:ok, activity} = CommonAPI.post(other_user, %{status: "Test"})
+ assert_receive {:render_with_user, _, _, ^activity}
+ refute Streamer.filtered_by_user?(other_user, activity)
+ end
+
+ test "it sends to public (unauthenticated)" do
+ user = insert(:user)
+
+ Streamer.get_topic_and_add_socket("public", nil, nil)
+
+ {:ok, activity} = CommonAPI.post(user, %{status: "Test"})
+ activity_id = activity.id
+ assert_receive {:text, event}
+ assert %{"event" => "update", "payload" => payload} = Jason.decode!(event)
+ assert %{"id" => ^activity_id} = Jason.decode!(payload)
+
+ {:ok, _} = CommonAPI.delete(activity.id, user)
+ assert_receive {:text, event}
+ assert %{"event" => "delete", "payload" => ^activity_id} = Jason.decode!(event)
+ end
+
+ test "handles deletions" do
+ %{user: user, token: oauth_token} = oauth_access(["read"])
+ other_user = insert(:user)
+ {:ok, activity} = CommonAPI.post(other_user, %{status: "Test"})
+
+ Streamer.get_topic_and_add_socket("public", user, oauth_token)
+
+ {:ok, _} = CommonAPI.delete(activity.id, other_user)
+ activity_id = activity.id
+ assert_receive {:text, event}
+ assert %{"event" => "delete", "payload" => ^activity_id} = Jason.decode!(event)
+ end
+ end
+
+ describe "thread_containment/2" do
+ test "it filters to user if recipients invalid and thread containment is enabled" do
+ Pleroma.Config.put([:instance, :skip_thread_containment], false)
+ author = insert(:user)
+ %{user: user, token: oauth_token} = oauth_access(["read"])
+ User.follow(user, author, :follow_accept)
+
+ activity =
+ insert(:note_activity,
+ note:
+ insert(:note,
+ user: author,
+ data: %{"to" => ["TEST-FFF"]}
+ )
+ )
+
+ Streamer.get_topic_and_add_socket("public", user, oauth_token)
+ Streamer.stream("public", activity)
+ assert_receive {:render_with_user, _, _, ^activity}
+ assert Streamer.filtered_by_user?(user, activity)
+ end
+
+ test "it sends message if recipients invalid and thread containment is disabled" do
+ Pleroma.Config.put([:instance, :skip_thread_containment], true)
+ author = insert(:user)
+ %{user: user, token: oauth_token} = oauth_access(["read"])
+ User.follow(user, author, :follow_accept)
+
+ activity =
+ insert(:note_activity,
+ note:
+ insert(:note,
+ user: author,
+ data: %{"to" => ["TEST-FFF"]}
+ )
+ )
+
+ Streamer.get_topic_and_add_socket("public", user, oauth_token)
+ Streamer.stream("public", activity)
+
+ assert_receive {:render_with_user, _, _, ^activity}
+ refute Streamer.filtered_by_user?(user, activity)
+ end
+
+ test "it sends message if recipients invalid and thread containment is enabled but user's thread containment is disabled" do
+ Pleroma.Config.put([:instance, :skip_thread_containment], false)
+ author = insert(:user)
+ user = insert(:user, skip_thread_containment: true)
+ %{token: oauth_token} = oauth_access(["read"], user: user)
+ User.follow(user, author, :follow_accept)
+
+ activity =
+ insert(:note_activity,
+ note:
+ insert(:note,
+ user: author,
+ data: %{"to" => ["TEST-FFF"]}
+ )
+ )
+
+ Streamer.get_topic_and_add_socket("public", user, oauth_token)
+ Streamer.stream("public", activity)
+
+ assert_receive {:render_with_user, _, _, ^activity}
+ refute Streamer.filtered_by_user?(user, activity)
+ end
+ end
+
+ describe "blocks" do
+ setup do: oauth_access(["read"])
+
+ test "it filters messages involving blocked users", %{user: user, token: oauth_token} do
+ blocked_user = insert(:user)
+ {:ok, _user_relationship} = User.block(user, blocked_user)
+
+ Streamer.get_topic_and_add_socket("public", user, oauth_token)
+ {:ok, activity} = CommonAPI.post(blocked_user, %{status: "Test"})
+ assert_receive {:render_with_user, _, _, ^activity}
+ assert Streamer.filtered_by_user?(user, activity)
+ end
+
+ test "it filters messages transitively involving blocked users", %{
+ user: blocker,
+ token: blocker_token
+ } do
+ blockee = insert(:user)
+ friend = insert(:user)
+
+ Streamer.get_topic_and_add_socket("public", blocker, blocker_token)
+
+ {:ok, _user_relationship} = User.block(blocker, blockee)
+
+ {:ok, activity_one} = CommonAPI.post(friend, %{status: "hey! @#{blockee.nickname}"})
+
+ assert_receive {:render_with_user, _, _, ^activity_one}
+ assert Streamer.filtered_by_user?(blocker, activity_one)
+
+ {:ok, activity_two} = CommonAPI.post(blockee, %{status: "hey! @#{friend.nickname}"})
+
+ assert_receive {:render_with_user, _, _, ^activity_two}
+ assert Streamer.filtered_by_user?(blocker, activity_two)
+
+ {:ok, activity_three} = CommonAPI.post(blockee, %{status: "hey! @#{blocker.nickname}"})
+
+ assert_receive {:render_with_user, _, _, ^activity_three}
+ assert Streamer.filtered_by_user?(blocker, activity_three)
+ end
+ end
+
+ describe "lists" do
+ setup do: oauth_access(["read"])
+
+ test "it doesn't send unwanted DMs to list", %{user: user_a, token: user_a_token} do
+ user_b = insert(:user)
+ user_c = insert(:user)
+
+ {:ok, user_a} = User.follow(user_a, user_b)
+
+ {:ok, list} = List.create("Test", user_a)
+ {:ok, list} = List.follow(list, user_b)
+
+ Streamer.get_topic_and_add_socket("list", user_a, user_a_token, %{"list" => list.id})
+
+ {:ok, _activity} =
+ CommonAPI.post(user_b, %{
+ status: "@#{user_c.nickname} Test",
+ visibility: "direct"
+ })
+
+ refute_receive _
+ end
+
+ test "it doesn't send unwanted private posts to list", %{user: user_a, token: user_a_token} do
+ user_b = insert(:user)
+
+ {:ok, list} = List.create("Test", user_a)
+ {:ok, list} = List.follow(list, user_b)
+
+ Streamer.get_topic_and_add_socket("list", user_a, user_a_token, %{"list" => list.id})
+
+ {:ok, _activity} =
+ CommonAPI.post(user_b, %{
+ status: "Test",
+ visibility: "private"
+ })
+
+ refute_receive _
+ end
+
+ test "it sends wanted private posts to list", %{user: user_a, token: user_a_token} do
+ user_b = insert(:user)
+
+ {:ok, user_a} = User.follow(user_a, user_b)
+
+ {:ok, list} = List.create("Test", user_a)
+ {:ok, list} = List.follow(list, user_b)
+
+ Streamer.get_topic_and_add_socket("list", user_a, user_a_token, %{"list" => list.id})
+
+ {:ok, activity} =
+ CommonAPI.post(user_b, %{
+ status: "Test",
+ visibility: "private"
+ })
+
+ assert_receive {:render_with_user, _, _, ^activity}
+ refute Streamer.filtered_by_user?(user_a, activity)
+ end
+ end
+
+ describe "muted reblogs" do
+ setup do: oauth_access(["read"])
+
+ test "it filters muted reblogs", %{user: user1, token: user1_token} do
+ user2 = insert(:user)
+ user3 = insert(:user)
+ CommonAPI.follow(user1, user2)
+ CommonAPI.hide_reblogs(user1, user2)
+
+ {:ok, create_activity} = CommonAPI.post(user3, %{status: "I'm kawen"})
+
+ Streamer.get_topic_and_add_socket("user", user1, user1_token)
+ {:ok, announce_activity} = CommonAPI.repeat(create_activity.id, user2)
+ assert_receive {:render_with_user, _, _, ^announce_activity}
+ assert Streamer.filtered_by_user?(user1, announce_activity)
+ end
+
+ test "it filters reblog notification for reblog-muted actors", %{
+ user: user1,
+ token: user1_token
+ } do
+ user2 = insert(:user)
+ CommonAPI.follow(user1, user2)
+ CommonAPI.hide_reblogs(user1, user2)
+
+ {:ok, create_activity} = CommonAPI.post(user1, %{status: "I'm kawen"})
+ Streamer.get_topic_and_add_socket("user", user1, user1_token)
+ {:ok, _announce_activity} = CommonAPI.repeat(create_activity.id, user2)
+
+ assert_receive {:render_with_user, _, "notification.json", notif}
+ assert Streamer.filtered_by_user?(user1, notif)
+ end
+
+ test "it send non-reblog notification for reblog-muted actors", %{
+ user: user1,
+ token: user1_token
+ } do
+ user2 = insert(:user)
+ CommonAPI.follow(user1, user2)
+ CommonAPI.hide_reblogs(user1, user2)
+
+ {:ok, create_activity} = CommonAPI.post(user1, %{status: "I'm kawen"})
+ Streamer.get_topic_and_add_socket("user", user1, user1_token)
+ {:ok, _favorite_activity} = CommonAPI.favorite(user2, create_activity.id)
+
+ assert_receive {:render_with_user, _, "notification.json", notif}
+ refute Streamer.filtered_by_user?(user1, notif)
+ end
+ end
+
+ describe "muted threads" do
+ test "it filters posts from muted threads" do
+ user = insert(:user)
+ %{user: user2, token: user2_token} = oauth_access(["read"])
+ Streamer.get_topic_and_add_socket("user", user2, user2_token)
+
+ {:ok, user2, user, _activity} = CommonAPI.follow(user2, user)
+ {:ok, activity} = CommonAPI.post(user, %{status: "super hot take"})
+ {:ok, _} = CommonAPI.add_mute(user2, activity)
+
+ assert_receive {:render_with_user, _, _, ^activity}
+ assert Streamer.filtered_by_user?(user2, activity)
+ end
+ end
+
+ describe "direct streams" do
+ setup do: oauth_access(["read"])
+
+ test "it sends conversation update to the 'direct' stream", %{user: user, token: oauth_token} do
+ another_user = insert(:user)
+
+ Streamer.get_topic_and_add_socket("direct", user, oauth_token)
+
+ {:ok, _create_activity} =
+ CommonAPI.post(another_user, %{
+ status: "hey @#{user.nickname}",
+ visibility: "direct"
+ })
+
+ assert_receive {:text, received_event}
+
+ assert %{"event" => "conversation", "payload" => received_payload} =
+ Jason.decode!(received_event)
+
+ assert %{"last_status" => last_status} = Jason.decode!(received_payload)
+ [participation] = Participation.for_user(user)
+ assert last_status["pleroma"]["direct_conversation_id"] == participation.id
+ end
+
+ test "it doesn't send conversation update to the 'direct' stream when the last message in the conversation is deleted",
+ %{user: user, token: oauth_token} do
+ another_user = insert(:user)
+
+ Streamer.get_topic_and_add_socket("direct", user, oauth_token)
+
+ {:ok, create_activity} =
+ CommonAPI.post(another_user, %{
+ status: "hi @#{user.nickname}",
+ visibility: "direct"
+ })
+
+ create_activity_id = create_activity.id
+ assert_receive {:render_with_user, _, _, ^create_activity}
+ assert_receive {:text, received_conversation1}
+ assert %{"event" => "conversation", "payload" => _} = Jason.decode!(received_conversation1)
+
+ {:ok, _} = CommonAPI.delete(create_activity_id, another_user)
+
+ assert_receive {:text, received_event}
+
+ assert %{"event" => "delete", "payload" => ^create_activity_id} =
+ Jason.decode!(received_event)
+
+ refute_receive _
+ end
+
+ test "it sends conversation update to the 'direct' stream when a message is deleted", %{
+ user: user,
+ token: oauth_token
+ } do
+ another_user = insert(:user)
+ Streamer.get_topic_and_add_socket("direct", user, oauth_token)
+
+ {:ok, create_activity} =
+ CommonAPI.post(another_user, %{
+ status: "hi @#{user.nickname}",
+ visibility: "direct"
+ })
+
+ {:ok, create_activity2} =
+ CommonAPI.post(another_user, %{
+ status: "hi @#{user.nickname} 2",
+ in_reply_to_status_id: create_activity.id,
+ visibility: "direct"
+ })
+
+ assert_receive {:render_with_user, _, _, ^create_activity}
+ assert_receive {:render_with_user, _, _, ^create_activity2}
+ assert_receive {:text, received_conversation1}
+ assert %{"event" => "conversation", "payload" => _} = Jason.decode!(received_conversation1)
+ assert_receive {:text, received_conversation1}
+ assert %{"event" => "conversation", "payload" => _} = Jason.decode!(received_conversation1)
+
+ {:ok, _} = CommonAPI.delete(create_activity2.id, another_user)
+
+ assert_receive {:text, received_event}
+ assert %{"event" => "delete", "payload" => _} = Jason.decode!(received_event)
+
+ assert_receive {:text, received_event}
+
+ assert %{"event" => "conversation", "payload" => received_payload} =
+ Jason.decode!(received_event)
+
+ assert %{"last_status" => last_status} = Jason.decode!(received_payload)
+ assert last_status["id"] == to_string(create_activity.id)
+ end
+ end
+end
diff --git a/test/pleroma/web/twitter_api/controller_test.exs b/test/pleroma/web/twitter_api/controller_test.exs
@@ -0,0 +1,138 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.TwitterAPI.ControllerTest do
+ use Pleroma.Web.ConnCase
+
+ alias Pleroma.Builders.ActivityBuilder
+ alias Pleroma.Repo
+ alias Pleroma.User
+ alias Pleroma.Web.OAuth.Token
+
+ import Pleroma.Factory
+
+ describe "POST /api/qvitter/statuses/notifications/read" do
+ test "without valid credentials", %{conn: conn} do
+ conn = post(conn, "/api/qvitter/statuses/notifications/read", %{"latest_id" => 1_234_567})
+ assert json_response(conn, 403) == %{"error" => "Invalid credentials."}
+ end
+
+ test "with credentials, without any params" do
+ %{conn: conn} = oauth_access(["write:notifications"])
+
+ conn = post(conn, "/api/qvitter/statuses/notifications/read")
+
+ assert json_response(conn, 400) == %{
+ "error" => "You need to specify latest_id",
+ "request" => "/api/qvitter/statuses/notifications/read"
+ }
+ end
+
+ test "with credentials, with params" do
+ %{user: current_user, conn: conn} =
+ oauth_access(["read:notifications", "write:notifications"])
+
+ other_user = insert(:user)
+
+ {:ok, _activity} =
+ ActivityBuilder.insert(%{"to" => [current_user.ap_id]}, %{user: other_user})
+
+ response_conn =
+ conn
+ |> assign(:user, current_user)
+ |> get("/api/v1/notifications")
+
+ [notification] = response = json_response(response_conn, 200)
+
+ assert length(response) == 1
+
+ assert notification["pleroma"]["is_seen"] == false
+
+ response_conn =
+ conn
+ |> assign(:user, current_user)
+ |> post("/api/qvitter/statuses/notifications/read", %{"latest_id" => notification["id"]})
+
+ [notification] = response = json_response(response_conn, 200)
+
+ assert length(response) == 1
+
+ assert notification["pleroma"]["is_seen"] == true
+ end
+ end
+
+ describe "GET /api/account/confirm_email/:id/:token" do
+ setup do
+ {:ok, user} =
+ insert(:user)
+ |> User.confirmation_changeset(need_confirmation: true)
+ |> Repo.update()
+
+ assert user.confirmation_pending
+
+ [user: user]
+ end
+
+ test "it redirects to root url", %{conn: conn, user: user} do
+ conn = get(conn, "/api/account/confirm_email/#{user.id}/#{user.confirmation_token}")
+
+ assert 302 == conn.status
+ end
+
+ test "it confirms the user account", %{conn: conn, user: user} do
+ get(conn, "/api/account/confirm_email/#{user.id}/#{user.confirmation_token}")
+
+ user = User.get_cached_by_id(user.id)
+
+ refute user.confirmation_pending
+ refute user.confirmation_token
+ end
+
+ test "it returns 500 if user cannot be found by id", %{conn: conn, user: user} do
+ conn = get(conn, "/api/account/confirm_email/0/#{user.confirmation_token}")
+
+ assert 500 == conn.status
+ end
+
+ test "it returns 500 if token is invalid", %{conn: conn, user: user} do
+ conn = get(conn, "/api/account/confirm_email/#{user.id}/wrong_token")
+
+ assert 500 == conn.status
+ end
+ end
+
+ describe "GET /api/oauth_tokens" do
+ setup do
+ token = insert(:oauth_token) |> Repo.preload(:user)
+
+ %{token: token}
+ end
+
+ test "renders list", %{token: token} do
+ response =
+ build_conn()
+ |> assign(:user, token.user)
+ |> get("/api/oauth_tokens")
+
+ keys =
+ json_response(response, 200)
+ |> hd()
+ |> Map.keys()
+
+ assert keys -- ["id", "app_name", "valid_until"] == []
+ end
+
+ test "revoke token", %{token: token} do
+ response =
+ build_conn()
+ |> assign(:user, token.user)
+ |> delete("/api/oauth_tokens/#{token.id}")
+
+ tokens = Token.get_user_tokens(token.user)
+
+ assert tokens == []
+ assert response.status == 201
+ end
+ end
+end
diff --git a/test/pleroma/web/twitter_api/password_controller_test.exs b/test/pleroma/web/twitter_api/password_controller_test.exs
@@ -0,0 +1,81 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.TwitterAPI.PasswordControllerTest do
+ use Pleroma.Web.ConnCase
+
+ alias Pleroma.PasswordResetToken
+ alias Pleroma.User
+ alias Pleroma.Web.OAuth.Token
+ import Pleroma.Factory
+
+ describe "GET /api/pleroma/password_reset/token" do
+ test "it returns error when token invalid", %{conn: conn} do
+ response =
+ conn
+ |> get("/api/pleroma/password_reset/token")
+ |> html_response(:ok)
+
+ assert response =~ "<h2>Invalid Token</h2>"
+ end
+
+ test "it shows password reset form", %{conn: conn} do
+ user = insert(:user)
+ {:ok, token} = PasswordResetToken.create_token(user)
+
+ response =
+ conn
+ |> get("/api/pleroma/password_reset/#{token.token}")
+ |> html_response(:ok)
+
+ assert response =~ "<h2>Password Reset for #{user.nickname}</h2>"
+ end
+ end
+
+ describe "POST /api/pleroma/password_reset" do
+ test "it returns HTTP 200", %{conn: conn} do
+ user = insert(:user)
+ {:ok, token} = PasswordResetToken.create_token(user)
+ {:ok, _access_token} = Token.create(insert(:oauth_app), user, %{})
+
+ params = %{
+ "password" => "test",
+ password_confirmation: "test",
+ token: token.token
+ }
+
+ response =
+ conn
+ |> assign(:user, user)
+ |> post("/api/pleroma/password_reset", %{data: params})
+ |> html_response(:ok)
+
+ assert response =~ "<h2>Password changed!</h2>"
+
+ user = refresh_record(user)
+ assert Pbkdf2.verify_pass("test", user.password_hash)
+ assert Enum.empty?(Token.get_user_tokens(user))
+ end
+
+ test "it sets password_reset_pending to false", %{conn: conn} do
+ user = insert(:user, password_reset_pending: true)
+
+ {:ok, token} = PasswordResetToken.create_token(user)
+ {:ok, _access_token} = Token.create(insert(:oauth_app), user, %{})
+
+ params = %{
+ "password" => "test",
+ password_confirmation: "test",
+ token: token.token
+ }
+
+ conn
+ |> assign(:user, user)
+ |> post("/api/pleroma/password_reset", %{data: params})
+ |> html_response(:ok)
+
+ assert User.get_by_id(user.id).password_reset_pending == false
+ end
+ end
+end
diff --git a/test/pleroma/web/twitter_api/remote_follow_controller_test.exs b/test/pleroma/web/twitter_api/remote_follow_controller_test.exs
@@ -0,0 +1,350 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.TwitterAPI.RemoteFollowControllerTest do
+ use Pleroma.Web.ConnCase
+
+ alias Pleroma.Config
+ alias Pleroma.MFA
+ alias Pleroma.MFA.TOTP
+ alias Pleroma.User
+ alias Pleroma.Web.CommonAPI
+
+ import ExUnit.CaptureLog
+ import Pleroma.Factory
+ import Ecto.Query
+
+ setup do
+ Tesla.Mock.mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
+ :ok
+ end
+
+ setup_all do: clear_config([:instance, :federating], true)
+ setup do: clear_config([:instance])
+ setup do: clear_config([:frontend_configurations, :pleroma_fe])
+ setup do: clear_config([:user, :deny_follow_blocked])
+
+ describe "GET /ostatus_subscribe - remote_follow/2" do
+ test "adds status to pleroma instance if the `acct` is a status", %{conn: conn} do
+ assert conn
+ |> get(
+ remote_follow_path(conn, :follow, %{
+ acct: "https://mastodon.social/users/emelie/statuses/101849165031453009"
+ })
+ )
+ |> redirected_to() =~ "/notice/"
+ end
+
+ test "show follow account page if the `acct` is a account link", %{conn: conn} do
+ response =
+ conn
+ |> get(remote_follow_path(conn, :follow, %{acct: "https://mastodon.social/users/emelie"}))
+ |> html_response(200)
+
+ assert response =~ "Log in to follow"
+ end
+
+ test "show follow page if the `acct` is a account link", %{conn: conn} do
+ user = insert(:user)
+
+ response =
+ conn
+ |> assign(:user, user)
+ |> get(remote_follow_path(conn, :follow, %{acct: "https://mastodon.social/users/emelie"}))
+ |> html_response(200)
+
+ assert response =~ "Remote follow"
+ end
+
+ test "show follow page with error when user cannot fecth by `acct` link", %{conn: conn} do
+ user = insert(:user)
+
+ assert capture_log(fn ->
+ response =
+ conn
+ |> assign(:user, user)
+ |> get(
+ remote_follow_path(conn, :follow, %{
+ acct: "https://mastodon.social/users/not_found"
+ })
+ )
+ |> html_response(200)
+
+ assert response =~ "Error fetching user"
+ end) =~ "Object has been deleted"
+ end
+ end
+
+ describe "POST /ostatus_subscribe - do_follow/2 with assigned user " do
+ test "required `follow | write:follows` scope", %{conn: conn} do
+ user = insert(:user)
+ user2 = insert(:user)
+ read_token = insert(:oauth_token, user: user, scopes: ["read"])
+
+ assert capture_log(fn ->
+ response =
+ conn
+ |> assign(:user, user)
+ |> assign(:token, read_token)
+ |> post(remote_follow_path(conn, :do_follow), %{"user" => %{"id" => user2.id}})
+ |> response(200)
+
+ assert response =~ "Error following account"
+ end) =~ "Insufficient permissions: follow | write:follows."
+ end
+
+ test "follows user", %{conn: conn} do
+ user = insert(:user)
+ user2 = insert(:user)
+
+ conn =
+ conn
+ |> assign(:user, user)
+ |> assign(:token, insert(:oauth_token, user: user, scopes: ["write:follows"]))
+ |> post(remote_follow_path(conn, :do_follow), %{"user" => %{"id" => user2.id}})
+
+ assert redirected_to(conn) == "/users/#{user2.id}"
+ end
+
+ test "returns error when user is deactivated", %{conn: conn} do
+ user = insert(:user, deactivated: true)
+ user2 = insert(:user)
+
+ response =
+ conn
+ |> assign(:user, user)
+ |> post(remote_follow_path(conn, :do_follow), %{"user" => %{"id" => user2.id}})
+ |> response(200)
+
+ assert response =~ "Error following account"
+ end
+
+ test "returns error when user is blocked", %{conn: conn} do
+ Pleroma.Config.put([:user, :deny_follow_blocked], true)
+ user = insert(:user)
+ user2 = insert(:user)
+
+ {:ok, _user_block} = Pleroma.User.block(user2, user)
+
+ response =
+ conn
+ |> assign(:user, user)
+ |> post(remote_follow_path(conn, :do_follow), %{"user" => %{"id" => user2.id}})
+ |> response(200)
+
+ assert response =~ "Error following account"
+ end
+
+ test "returns error when followee not found", %{conn: conn} do
+ user = insert(:user)
+
+ response =
+ conn
+ |> assign(:user, user)
+ |> post(remote_follow_path(conn, :do_follow), %{"user" => %{"id" => "jimm"}})
+ |> response(200)
+
+ assert response =~ "Error following account"
+ end
+
+ test "returns success result when user already in followers", %{conn: conn} do
+ user = insert(:user)
+ user2 = insert(:user)
+ {:ok, _, _, _} = CommonAPI.follow(user, user2)
+
+ conn =
+ conn
+ |> assign(:user, refresh_record(user))
+ |> assign(:token, insert(:oauth_token, user: user, scopes: ["write:follows"]))
+ |> post(remote_follow_path(conn, :do_follow), %{"user" => %{"id" => user2.id}})
+
+ assert redirected_to(conn) == "/users/#{user2.id}"
+ end
+ end
+
+ describe "POST /ostatus_subscribe - follow/2 with enabled Two-Factor Auth " do
+ test "render the MFA login form", %{conn: conn} do
+ otp_secret = TOTP.generate_secret()
+
+ user =
+ insert(:user,
+ multi_factor_authentication_settings: %MFA.Settings{
+ enabled: true,
+ totp: %MFA.Settings.TOTP{secret: otp_secret, confirmed: true}
+ }
+ )
+
+ user2 = insert(:user)
+
+ response =
+ conn
+ |> post(remote_follow_path(conn, :do_follow), %{
+ "authorization" => %{"name" => user.nickname, "password" => "test", "id" => user2.id}
+ })
+ |> response(200)
+
+ mfa_token = Pleroma.Repo.one(from(q in Pleroma.MFA.Token, where: q.user_id == ^user.id))
+
+ assert response =~ "Two-factor authentication"
+ assert response =~ "Authentication code"
+ assert response =~ mfa_token.token
+ refute user2.follower_address in User.following(user)
+ end
+
+ test "returns error when password is incorrect", %{conn: conn} do
+ otp_secret = TOTP.generate_secret()
+
+ user =
+ insert(:user,
+ multi_factor_authentication_settings: %MFA.Settings{
+ enabled: true,
+ totp: %MFA.Settings.TOTP{secret: otp_secret, confirmed: true}
+ }
+ )
+
+ user2 = insert(:user)
+
+ response =
+ conn
+ |> post(remote_follow_path(conn, :do_follow), %{
+ "authorization" => %{"name" => user.nickname, "password" => "test1", "id" => user2.id}
+ })
+ |> response(200)
+
+ assert response =~ "Wrong username or password"
+ refute user2.follower_address in User.following(user)
+ end
+
+ test "follows", %{conn: conn} do
+ otp_secret = TOTP.generate_secret()
+
+ user =
+ insert(:user,
+ multi_factor_authentication_settings: %MFA.Settings{
+ enabled: true,
+ totp: %MFA.Settings.TOTP{secret: otp_secret, confirmed: true}
+ }
+ )
+
+ {:ok, %{token: token}} = MFA.Token.create(user)
+
+ user2 = insert(:user)
+ otp_token = TOTP.generate_token(otp_secret)
+
+ conn =
+ conn
+ |> post(
+ remote_follow_path(conn, :do_follow),
+ %{
+ "mfa" => %{"code" => otp_token, "token" => token, "id" => user2.id}
+ }
+ )
+
+ assert redirected_to(conn) == "/users/#{user2.id}"
+ assert user2.follower_address in User.following(user)
+ end
+
+ test "returns error when auth code is incorrect", %{conn: conn} do
+ otp_secret = TOTP.generate_secret()
+
+ user =
+ insert(:user,
+ multi_factor_authentication_settings: %MFA.Settings{
+ enabled: true,
+ totp: %MFA.Settings.TOTP{secret: otp_secret, confirmed: true}
+ }
+ )
+
+ {:ok, %{token: token}} = MFA.Token.create(user)
+
+ user2 = insert(:user)
+ otp_token = TOTP.generate_token(TOTP.generate_secret())
+
+ response =
+ conn
+ |> post(
+ remote_follow_path(conn, :do_follow),
+ %{
+ "mfa" => %{"code" => otp_token, "token" => token, "id" => user2.id}
+ }
+ )
+ |> response(200)
+
+ assert response =~ "Wrong authentication code"
+ refute user2.follower_address in User.following(user)
+ end
+ end
+
+ describe "POST /ostatus_subscribe - follow/2 without assigned user " do
+ test "follows", %{conn: conn} do
+ user = insert(:user)
+ user2 = insert(:user)
+
+ conn =
+ conn
+ |> post(remote_follow_path(conn, :do_follow), %{
+ "authorization" => %{"name" => user.nickname, "password" => "test", "id" => user2.id}
+ })
+
+ assert redirected_to(conn) == "/users/#{user2.id}"
+ assert user2.follower_address in User.following(user)
+ end
+
+ test "returns error when followee not found", %{conn: conn} do
+ user = insert(:user)
+
+ response =
+ conn
+ |> post(remote_follow_path(conn, :do_follow), %{
+ "authorization" => %{"name" => user.nickname, "password" => "test", "id" => "jimm"}
+ })
+ |> response(200)
+
+ assert response =~ "Error following account"
+ end
+
+ test "returns error when login invalid", %{conn: conn} do
+ user = insert(:user)
+
+ response =
+ conn
+ |> post(remote_follow_path(conn, :do_follow), %{
+ "authorization" => %{"name" => "jimm", "password" => "test", "id" => user.id}
+ })
+ |> response(200)
+
+ assert response =~ "Wrong username or password"
+ end
+
+ test "returns error when password invalid", %{conn: conn} do
+ user = insert(:user)
+ user2 = insert(:user)
+
+ response =
+ conn
+ |> post(remote_follow_path(conn, :do_follow), %{
+ "authorization" => %{"name" => user.nickname, "password" => "42", "id" => user2.id}
+ })
+ |> response(200)
+
+ assert response =~ "Wrong username or password"
+ end
+
+ test "returns error when user is blocked", %{conn: conn} do
+ Pleroma.Config.put([:user, :deny_follow_blocked], true)
+ user = insert(:user)
+ user2 = insert(:user)
+ {:ok, _user_block} = Pleroma.User.block(user2, user)
+
+ response =
+ conn
+ |> post(remote_follow_path(conn, :do_follow), %{
+ "authorization" => %{"name" => user.nickname, "password" => "test", "id" => user2.id}
+ })
+ |> response(200)
+
+ assert response =~ "Error following account"
+ end
+ end
+end
diff --git a/test/pleroma/web/twitter_api/twitter_api_test.exs b/test/pleroma/web/twitter_api/twitter_api_test.exs
@@ -0,0 +1,432 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do
+ use Pleroma.DataCase
+ import Pleroma.Factory
+ alias Pleroma.Repo
+ alias Pleroma.Tests.ObanHelpers
+ alias Pleroma.User
+ alias Pleroma.UserInviteToken
+ alias Pleroma.Web.TwitterAPI.TwitterAPI
+
+ setup_all do
+ Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end)
+ :ok
+ end
+
+ test "it registers a new user and returns the user." do
+ data = %{
+ :username => "lain",
+ :email => "lain@wired.jp",
+ :fullname => "lain iwakura",
+ :password => "bear",
+ :confirm => "bear"
+ }
+
+ {:ok, user} = TwitterAPI.register_user(data)
+
+ assert user == User.get_cached_by_nickname("lain")
+ end
+
+ test "it registers a new user with empty string in bio and returns the user" do
+ data = %{
+ :username => "lain",
+ :email => "lain@wired.jp",
+ :fullname => "lain iwakura",
+ :bio => "",
+ :password => "bear",
+ :confirm => "bear"
+ }
+
+ {:ok, user} = TwitterAPI.register_user(data)
+
+ assert user == User.get_cached_by_nickname("lain")
+ end
+
+ test "it sends confirmation email if :account_activation_required is specified in instance config" do
+ setting = Pleroma.Config.get([:instance, :account_activation_required])
+
+ unless setting do
+ Pleroma.Config.put([:instance, :account_activation_required], true)
+ on_exit(fn -> Pleroma.Config.put([:instance, :account_activation_required], setting) end)
+ end
+
+ data = %{
+ :username => "lain",
+ :email => "lain@wired.jp",
+ :fullname => "lain iwakura",
+ :bio => "",
+ :password => "bear",
+ :confirm => "bear"
+ }
+
+ {:ok, user} = TwitterAPI.register_user(data)
+ ObanHelpers.perform_all()
+
+ assert user.confirmation_pending
+
+ email = Pleroma.Emails.UserEmail.account_confirmation_email(user)
+
+ notify_email = Pleroma.Config.get([:instance, :notify_email])
+ instance_name = Pleroma.Config.get([:instance, :name])
+
+ Swoosh.TestAssertions.assert_email_sent(
+ from: {instance_name, notify_email},
+ to: {user.name, user.email},
+ html_body: email.html_body
+ )
+ end
+
+ test "it sends an admin email if :account_approval_required is specified in instance config" do
+ admin = insert(:user, is_admin: true)
+ setting = Pleroma.Config.get([:instance, :account_approval_required])
+
+ unless setting do
+ Pleroma.Config.put([:instance, :account_approval_required], true)
+ on_exit(fn -> Pleroma.Config.put([:instance, :account_approval_required], setting) end)
+ end
+
+ data = %{
+ :username => "lain",
+ :email => "lain@wired.jp",
+ :fullname => "lain iwakura",
+ :bio => "",
+ :password => "bear",
+ :confirm => "bear",
+ :reason => "I love anime"
+ }
+
+ {:ok, user} = TwitterAPI.register_user(data)
+ ObanHelpers.perform_all()
+
+ assert user.approval_pending
+
+ email = Pleroma.Emails.AdminEmail.new_unapproved_registration(admin, user)
+
+ notify_email = Pleroma.Config.get([:instance, :notify_email])
+ instance_name = Pleroma.Config.get([:instance, :name])
+
+ Swoosh.TestAssertions.assert_email_sent(
+ from: {instance_name, notify_email},
+ to: {admin.name, admin.email},
+ html_body: email.html_body
+ )
+ end
+
+ test "it registers a new user and parses mentions in the bio" do
+ data1 = %{
+ :username => "john",
+ :email => "john@gmail.com",
+ :fullname => "John Doe",
+ :bio => "test",
+ :password => "bear",
+ :confirm => "bear"
+ }
+
+ {:ok, user1} = TwitterAPI.register_user(data1)
+
+ data2 = %{
+ :username => "lain",
+ :email => "lain@wired.jp",
+ :fullname => "lain iwakura",
+ :bio => "@john test",
+ :password => "bear",
+ :confirm => "bear"
+ }
+
+ {:ok, user2} = TwitterAPI.register_user(data2)
+
+ expected_text =
+ ~s(<span class="h-card"><a class="u-url mention" data-user="#{user1.id}" href="#{
+ user1.ap_id
+ }" rel="ugc">@<span>john</span></a></span> test)
+
+ assert user2.bio == expected_text
+ end
+
+ describe "register with one time token" do
+ setup do: clear_config([:instance, :registrations_open], false)
+
+ test "returns user on success" do
+ {:ok, invite} = UserInviteToken.create_invite()
+
+ data = %{
+ :username => "vinny",
+ :email => "pasta@pizza.vs",
+ :fullname => "Vinny Vinesauce",
+ :bio => "streamer",
+ :password => "hiptofbees",
+ :confirm => "hiptofbees",
+ :token => invite.token
+ }
+
+ {:ok, user} = TwitterAPI.register_user(data)
+
+ assert user == User.get_cached_by_nickname("vinny")
+
+ invite = Repo.get_by(UserInviteToken, token: invite.token)
+ assert invite.used == true
+ end
+
+ test "returns error on invalid token" do
+ data = %{
+ :username => "GrimReaper",
+ :email => "death@reapers.afterlife",
+ :fullname => "Reaper Grim",
+ :bio => "Your time has come",
+ :password => "scythe",
+ :confirm => "scythe",
+ :token => "DudeLetMeInImAFairy"
+ }
+
+ {:error, msg} = TwitterAPI.register_user(data)
+
+ assert msg == "Invalid token"
+ refute User.get_cached_by_nickname("GrimReaper")
+ end
+
+ test "returns error on expired token" do
+ {:ok, invite} = UserInviteToken.create_invite()
+ UserInviteToken.update_invite!(invite, used: true)
+
+ data = %{
+ :username => "GrimReaper",
+ :email => "death@reapers.afterlife",
+ :fullname => "Reaper Grim",
+ :bio => "Your time has come",
+ :password => "scythe",
+ :confirm => "scythe",
+ :token => invite.token
+ }
+
+ {:error, msg} = TwitterAPI.register_user(data)
+
+ assert msg == "Expired token"
+ refute User.get_cached_by_nickname("GrimReaper")
+ end
+ end
+
+ describe "registers with date limited token" do
+ setup do: clear_config([:instance, :registrations_open], false)
+
+ setup do
+ data = %{
+ :username => "vinny",
+ :email => "pasta@pizza.vs",
+ :fullname => "Vinny Vinesauce",
+ :bio => "streamer",
+ :password => "hiptofbees",
+ :confirm => "hiptofbees"
+ }
+
+ check_fn = fn invite ->
+ data = Map.put(data, :token, invite.token)
+ {:ok, user} = TwitterAPI.register_user(data)
+
+ assert user == User.get_cached_by_nickname("vinny")
+ end
+
+ {:ok, data: data, check_fn: check_fn}
+ end
+
+ test "returns user on success", %{check_fn: check_fn} do
+ {:ok, invite} = UserInviteToken.create_invite(%{expires_at: Date.utc_today()})
+
+ check_fn.(invite)
+
+ invite = Repo.get_by(UserInviteToken, token: invite.token)
+
+ refute invite.used
+ end
+
+ test "returns user on token which expired tomorrow", %{check_fn: check_fn} do
+ {:ok, invite} = UserInviteToken.create_invite(%{expires_at: Date.add(Date.utc_today(), 1)})
+
+ check_fn.(invite)
+
+ invite = Repo.get_by(UserInviteToken, token: invite.token)
+
+ refute invite.used
+ end
+
+ test "returns an error on overdue date", %{data: data} do
+ {:ok, invite} = UserInviteToken.create_invite(%{expires_at: Date.add(Date.utc_today(), -1)})
+
+ data = Map.put(data, "token", invite.token)
+
+ {:error, msg} = TwitterAPI.register_user(data)
+
+ assert msg == "Expired token"
+ refute User.get_cached_by_nickname("vinny")
+ invite = Repo.get_by(UserInviteToken, token: invite.token)
+
+ refute invite.used
+ end
+ end
+
+ describe "registers with reusable token" do
+ setup do: clear_config([:instance, :registrations_open], false)
+
+ test "returns user on success, after him registration fails" do
+ {:ok, invite} = UserInviteToken.create_invite(%{max_use: 100})
+
+ UserInviteToken.update_invite!(invite, uses: 99)
+
+ data = %{
+ :username => "vinny",
+ :email => "pasta@pizza.vs",
+ :fullname => "Vinny Vinesauce",
+ :bio => "streamer",
+ :password => "hiptofbees",
+ :confirm => "hiptofbees",
+ :token => invite.token
+ }
+
+ {:ok, user} = TwitterAPI.register_user(data)
+ assert user == User.get_cached_by_nickname("vinny")
+
+ invite = Repo.get_by(UserInviteToken, token: invite.token)
+ assert invite.used == true
+
+ data = %{
+ :username => "GrimReaper",
+ :email => "death@reapers.afterlife",
+ :fullname => "Reaper Grim",
+ :bio => "Your time has come",
+ :password => "scythe",
+ :confirm => "scythe",
+ :token => invite.token
+ }
+
+ {:error, msg} = TwitterAPI.register_user(data)
+
+ assert msg == "Expired token"
+ refute User.get_cached_by_nickname("GrimReaper")
+ end
+ end
+
+ describe "registers with reusable date limited token" do
+ setup do: clear_config([:instance, :registrations_open], false)
+
+ test "returns user on success" do
+ {:ok, invite} = UserInviteToken.create_invite(%{expires_at: Date.utc_today(), max_use: 100})
+
+ data = %{
+ :username => "vinny",
+ :email => "pasta@pizza.vs",
+ :fullname => "Vinny Vinesauce",
+ :bio => "streamer",
+ :password => "hiptofbees",
+ :confirm => "hiptofbees",
+ :token => invite.token
+ }
+
+ {:ok, user} = TwitterAPI.register_user(data)
+ assert user == User.get_cached_by_nickname("vinny")
+
+ invite = Repo.get_by(UserInviteToken, token: invite.token)
+ refute invite.used
+ end
+
+ test "error after max uses" do
+ {:ok, invite} = UserInviteToken.create_invite(%{expires_at: Date.utc_today(), max_use: 100})
+
+ UserInviteToken.update_invite!(invite, uses: 99)
+
+ data = %{
+ :username => "vinny",
+ :email => "pasta@pizza.vs",
+ :fullname => "Vinny Vinesauce",
+ :bio => "streamer",
+ :password => "hiptofbees",
+ :confirm => "hiptofbees",
+ :token => invite.token
+ }
+
+ {:ok, user} = TwitterAPI.register_user(data)
+ assert user == User.get_cached_by_nickname("vinny")
+
+ invite = Repo.get_by(UserInviteToken, token: invite.token)
+ assert invite.used == true
+
+ data = %{
+ :username => "GrimReaper",
+ :email => "death@reapers.afterlife",
+ :fullname => "Reaper Grim",
+ :bio => "Your time has come",
+ :password => "scythe",
+ :confirm => "scythe",
+ :token => invite.token
+ }
+
+ {:error, msg} = TwitterAPI.register_user(data)
+
+ assert msg == "Expired token"
+ refute User.get_cached_by_nickname("GrimReaper")
+ end
+
+ test "returns error on overdue date" do
+ {:ok, invite} =
+ UserInviteToken.create_invite(%{expires_at: Date.add(Date.utc_today(), -1), max_use: 100})
+
+ data = %{
+ :username => "GrimReaper",
+ :email => "death@reapers.afterlife",
+ :fullname => "Reaper Grim",
+ :bio => "Your time has come",
+ :password => "scythe",
+ :confirm => "scythe",
+ :token => invite.token
+ }
+
+ {:error, msg} = TwitterAPI.register_user(data)
+
+ assert msg == "Expired token"
+ refute User.get_cached_by_nickname("GrimReaper")
+ end
+
+ test "returns error on with overdue date and after max" do
+ {:ok, invite} =
+ UserInviteToken.create_invite(%{expires_at: Date.add(Date.utc_today(), -1), max_use: 100})
+
+ UserInviteToken.update_invite!(invite, uses: 100)
+
+ data = %{
+ :username => "GrimReaper",
+ :email => "death@reapers.afterlife",
+ :fullname => "Reaper Grim",
+ :bio => "Your time has come",
+ :password => "scythe",
+ :confirm => "scythe",
+ :token => invite.token
+ }
+
+ {:error, msg} = TwitterAPI.register_user(data)
+
+ assert msg == "Expired token"
+ refute User.get_cached_by_nickname("GrimReaper")
+ end
+ end
+
+ test "it returns the error on registration problems" do
+ data = %{
+ :username => "lain",
+ :email => "lain@wired.jp",
+ :fullname => "lain iwakura",
+ :bio => "close the world."
+ }
+
+ {:error, error} = TwitterAPI.register_user(data)
+
+ assert is_binary(error)
+ refute User.get_cached_by_nickname("lain")
+ end
+
+ setup do
+ Supervisor.terminate_child(Pleroma.Supervisor, Cachex)
+ Supervisor.restart_child(Pleroma.Supervisor, Cachex)
+ :ok
+ end
+end
diff --git a/test/pleroma/web/twitter_api/util_controller_test.exs b/test/pleroma/web/twitter_api/util_controller_test.exs
@@ -0,0 +1,437 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.TwitterAPI.UtilControllerTest do
+ use Pleroma.Web.ConnCase
+ use Oban.Testing, repo: Pleroma.Repo
+
+ alias Pleroma.Config
+ alias Pleroma.Tests.ObanHelpers
+ alias Pleroma.User
+
+ import Pleroma.Factory
+ import Mock
+
+ setup do
+ Tesla.Mock.mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
+ :ok
+ end
+
+ setup do: clear_config([:instance])
+ setup do: clear_config([:frontend_configurations, :pleroma_fe])
+
+ describe "PUT /api/pleroma/notification_settings" do
+ setup do: oauth_access(["write:accounts"])
+
+ test "it updates notification settings", %{user: user, conn: conn} do
+ conn
+ |> put("/api/pleroma/notification_settings", %{
+ "block_from_strangers" => true,
+ "bar" => 1
+ })
+ |> json_response(:ok)
+
+ user = refresh_record(user)
+
+ assert %Pleroma.User.NotificationSetting{
+ block_from_strangers: true,
+ hide_notification_contents: false
+ } == user.notification_settings
+ end
+
+ test "it updates notification settings to enable hiding contents", %{user: user, conn: conn} do
+ conn
+ |> put("/api/pleroma/notification_settings", %{"hide_notification_contents" => "1"})
+ |> json_response(:ok)
+
+ user = refresh_record(user)
+
+ assert %Pleroma.User.NotificationSetting{
+ block_from_strangers: false,
+ hide_notification_contents: true
+ } == user.notification_settings
+ end
+ end
+
+ describe "GET /api/pleroma/frontend_configurations" do
+ test "returns everything in :pleroma, :frontend_configurations", %{conn: conn} do
+ config = [
+ frontend_a: %{
+ x: 1,
+ y: 2
+ },
+ frontend_b: %{
+ z: 3
+ }
+ ]
+
+ Config.put(:frontend_configurations, config)
+
+ response =
+ conn
+ |> get("/api/pleroma/frontend_configurations")
+ |> json_response(:ok)
+
+ assert response == Jason.encode!(config |> Enum.into(%{})) |> Jason.decode!()
+ end
+ end
+
+ describe "/api/pleroma/emoji" do
+ test "returns json with custom emoji with tags", %{conn: conn} do
+ emoji =
+ conn
+ |> get("/api/pleroma/emoji")
+ |> json_response(200)
+
+ assert Enum.all?(emoji, fn
+ {_key,
+ %{
+ "image_url" => url,
+ "tags" => tags
+ }} ->
+ is_binary(url) and is_list(tags)
+ end)
+ end
+ end
+
+ describe "GET /api/pleroma/healthcheck" do
+ setup do: clear_config([:instance, :healthcheck])
+
+ test "returns 503 when healthcheck disabled", %{conn: conn} do
+ Config.put([:instance, :healthcheck], false)
+
+ response =
+ conn
+ |> get("/api/pleroma/healthcheck")
+ |> json_response(503)
+
+ assert response == %{}
+ end
+
+ test "returns 200 when healthcheck enabled and all ok", %{conn: conn} do
+ Config.put([:instance, :healthcheck], true)
+
+ with_mock Pleroma.Healthcheck,
+ system_info: fn -> %Pleroma.Healthcheck{healthy: true} end do
+ response =
+ conn
+ |> get("/api/pleroma/healthcheck")
+ |> json_response(200)
+
+ assert %{
+ "active" => _,
+ "healthy" => true,
+ "idle" => _,
+ "memory_used" => _,
+ "pool_size" => _
+ } = response
+ end
+ end
+
+ test "returns 503 when healthcheck enabled and health is false", %{conn: conn} do
+ Config.put([:instance, :healthcheck], true)
+
+ with_mock Pleroma.Healthcheck,
+ system_info: fn -> %Pleroma.Healthcheck{healthy: false} end do
+ response =
+ conn
+ |> get("/api/pleroma/healthcheck")
+ |> json_response(503)
+
+ assert %{
+ "active" => _,
+ "healthy" => false,
+ "idle" => _,
+ "memory_used" => _,
+ "pool_size" => _
+ } = response
+ end
+ end
+ end
+
+ describe "POST /api/pleroma/disable_account" do
+ setup do: oauth_access(["write:accounts"])
+
+ test "with valid permissions and password, it disables the account", %{conn: conn, user: user} do
+ response =
+ conn
+ |> post("/api/pleroma/disable_account", %{"password" => "test"})
+ |> json_response(:ok)
+
+ assert response == %{"status" => "success"}
+ ObanHelpers.perform_all()
+
+ user = User.get_cached_by_id(user.id)
+
+ assert user.deactivated == true
+ end
+
+ test "with valid permissions and invalid password, it returns an error", %{conn: conn} do
+ user = insert(:user)
+
+ response =
+ conn
+ |> post("/api/pleroma/disable_account", %{"password" => "test1"})
+ |> json_response(:ok)
+
+ assert response == %{"error" => "Invalid password."}
+ user = User.get_cached_by_id(user.id)
+
+ refute user.deactivated
+ end
+ end
+
+ describe "POST /main/ostatus - remote_subscribe/2" do
+ setup do: clear_config([:instance, :federating], true)
+
+ test "renders subscribe form", %{conn: conn} do
+ user = insert(:user)
+
+ response =
+ conn
+ |> post("/main/ostatus", %{"nickname" => user.nickname, "profile" => ""})
+ |> response(:ok)
+
+ refute response =~ "Could not find user"
+ assert response =~ "Remotely follow #{user.nickname}"
+ end
+
+ test "renders subscribe form with error when user not found", %{conn: conn} do
+ response =
+ conn
+ |> post("/main/ostatus", %{"nickname" => "nickname", "profile" => ""})
+ |> response(:ok)
+
+ assert response =~ "Could not find user"
+ refute response =~ "Remotely follow"
+ end
+
+ test "it redirect to webfinger url", %{conn: conn} do
+ user = insert(:user)
+ user2 = insert(:user, ap_id: "shp@social.heldscal.la")
+
+ conn =
+ conn
+ |> post("/main/ostatus", %{
+ "user" => %{"nickname" => user.nickname, "profile" => user2.ap_id}
+ })
+
+ assert redirected_to(conn) ==
+ "https://social.heldscal.la/main/ostatussub?profile=#{user.ap_id}"
+ end
+
+ test "it renders form with error when user not found", %{conn: conn} do
+ user2 = insert(:user, ap_id: "shp@social.heldscal.la")
+
+ response =
+ conn
+ |> post("/main/ostatus", %{"user" => %{"nickname" => "jimm", "profile" => user2.ap_id}})
+ |> response(:ok)
+
+ assert response =~ "Something went wrong."
+ end
+ end
+
+ test "it returns new captcha", %{conn: conn} do
+ with_mock Pleroma.Captcha,
+ new: fn -> "test_captcha" end do
+ resp =
+ conn
+ |> get("/api/pleroma/captcha")
+ |> response(200)
+
+ assert resp == "\"test_captcha\""
+ assert called(Pleroma.Captcha.new())
+ end
+ end
+
+ describe "POST /api/pleroma/change_email" do
+ setup do: oauth_access(["write:accounts"])
+
+ test "without permissions", %{conn: conn} do
+ conn =
+ conn
+ |> assign(:token, nil)
+ |> post("/api/pleroma/change_email")
+
+ assert json_response(conn, 403) == %{"error" => "Insufficient permissions: write:accounts."}
+ end
+
+ test "with proper permissions and invalid password", %{conn: conn} do
+ conn =
+ post(conn, "/api/pleroma/change_email", %{
+ "password" => "hi",
+ "email" => "test@test.com"
+ })
+
+ assert json_response(conn, 200) == %{"error" => "Invalid password."}
+ end
+
+ test "with proper permissions, valid password and invalid email", %{
+ conn: conn
+ } do
+ conn =
+ post(conn, "/api/pleroma/change_email", %{
+ "password" => "test",
+ "email" => "foobar"
+ })
+
+ assert json_response(conn, 200) == %{"error" => "Email has invalid format."}
+ end
+
+ test "with proper permissions, valid password and no email", %{
+ conn: conn
+ } do
+ conn =
+ post(conn, "/api/pleroma/change_email", %{
+ "password" => "test"
+ })
+
+ assert json_response(conn, 200) == %{"error" => "Email can't be blank."}
+ end
+
+ test "with proper permissions, valid password and blank email", %{
+ conn: conn
+ } do
+ conn =
+ post(conn, "/api/pleroma/change_email", %{
+ "password" => "test",
+ "email" => ""
+ })
+
+ assert json_response(conn, 200) == %{"error" => "Email can't be blank."}
+ end
+
+ test "with proper permissions, valid password and non unique email", %{
+ conn: conn
+ } do
+ user = insert(:user)
+
+ conn =
+ post(conn, "/api/pleroma/change_email", %{
+ "password" => "test",
+ "email" => user.email
+ })
+
+ assert json_response(conn, 200) == %{"error" => "Email has already been taken."}
+ end
+
+ test "with proper permissions, valid password and valid email", %{
+ conn: conn
+ } do
+ conn =
+ post(conn, "/api/pleroma/change_email", %{
+ "password" => "test",
+ "email" => "cofe@foobar.com"
+ })
+
+ assert json_response(conn, 200) == %{"status" => "success"}
+ end
+ end
+
+ describe "POST /api/pleroma/change_password" do
+ setup do: oauth_access(["write:accounts"])
+
+ test "without permissions", %{conn: conn} do
+ conn =
+ conn
+ |> assign(:token, nil)
+ |> post("/api/pleroma/change_password")
+
+ assert json_response(conn, 403) == %{"error" => "Insufficient permissions: write:accounts."}
+ end
+
+ test "with proper permissions and invalid password", %{conn: conn} do
+ conn =
+ post(conn, "/api/pleroma/change_password", %{
+ "password" => "hi",
+ "new_password" => "newpass",
+ "new_password_confirmation" => "newpass"
+ })
+
+ assert json_response(conn, 200) == %{"error" => "Invalid password."}
+ end
+
+ test "with proper permissions, valid password and new password and confirmation not matching",
+ %{
+ conn: conn
+ } do
+ conn =
+ post(conn, "/api/pleroma/change_password", %{
+ "password" => "test",
+ "new_password" => "newpass",
+ "new_password_confirmation" => "notnewpass"
+ })
+
+ assert json_response(conn, 200) == %{
+ "error" => "New password does not match confirmation."
+ }
+ end
+
+ test "with proper permissions, valid password and invalid new password", %{
+ conn: conn
+ } do
+ conn =
+ post(conn, "/api/pleroma/change_password", %{
+ "password" => "test",
+ "new_password" => "",
+ "new_password_confirmation" => ""
+ })
+
+ assert json_response(conn, 200) == %{
+ "error" => "New password can't be blank."
+ }
+ end
+
+ test "with proper permissions, valid password and matching new password and confirmation", %{
+ conn: conn,
+ user: user
+ } do
+ conn =
+ post(conn, "/api/pleroma/change_password", %{
+ "password" => "test",
+ "new_password" => "newpass",
+ "new_password_confirmation" => "newpass"
+ })
+
+ assert json_response(conn, 200) == %{"status" => "success"}
+ fetched_user = User.get_cached_by_id(user.id)
+ assert Pbkdf2.verify_pass("newpass", fetched_user.password_hash) == true
+ end
+ end
+
+ describe "POST /api/pleroma/delete_account" do
+ setup do: oauth_access(["write:accounts"])
+
+ test "without permissions", %{conn: conn} do
+ conn =
+ conn
+ |> assign(:token, nil)
+ |> post("/api/pleroma/delete_account")
+
+ assert json_response(conn, 403) ==
+ %{"error" => "Insufficient permissions: write:accounts."}
+ end
+
+ test "with proper permissions and wrong or missing password", %{conn: conn} do
+ for params <- [%{"password" => "hi"}, %{}] do
+ ret_conn = post(conn, "/api/pleroma/delete_account", params)
+
+ assert json_response(ret_conn, 200) == %{"error" => "Invalid password."}
+ end
+ end
+
+ test "with proper permissions and valid password", %{conn: conn, user: user} do
+ conn = post(conn, "/api/pleroma/delete_account", %{"password" => "test"})
+ ObanHelpers.perform_all()
+ assert json_response(conn, 200) == %{"status" => "success"}
+
+ user = User.get_by_id(user.id)
+ assert user.deactivated == true
+ assert user.name == nil
+ assert user.bio == ""
+ assert user.password_hash == nil
+ end
+ end
+end
diff --git a/test/pleroma/web/uploader_controller_test.exs b/test/pleroma/web/uploader_controller_test.exs
@@ -0,0 +1,43 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.UploaderControllerTest do
+ use Pleroma.Web.ConnCase
+ alias Pleroma.Uploaders.Uploader
+
+ describe "callback/2" do
+ test "it returns 400 response when process callback isn't alive", %{conn: conn} do
+ res =
+ conn
+ |> post(uploader_path(conn, :callback, "test-path"))
+
+ assert res.status == 400
+ assert res.resp_body == "{\"error\":\"bad request\"}"
+ end
+
+ test "it returns success result", %{conn: conn} do
+ task =
+ Task.async(fn ->
+ receive do
+ {Uploader, pid, conn, _params} ->
+ conn =
+ conn
+ |> put_status(:ok)
+ |> Phoenix.Controller.json(%{upload_path: "test-path"})
+
+ send(pid, {Uploader, conn})
+ end
+ end)
+
+ :global.register_name({Uploader, "test-path"}, task.pid)
+
+ res =
+ conn
+ |> post(uploader_path(conn, :callback, "test-path"))
+ |> json_response(200)
+
+ assert res == %{"upload_path" => "test-path"}
+ end
+ end
+end
diff --git a/test/pleroma/web/views/error_view_test.exs b/test/pleroma/web/views/error_view_test.exs
@@ -0,0 +1,36 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.ErrorViewTest do
+ use Pleroma.Web.ConnCase, async: true
+ import ExUnit.CaptureLog
+
+ # Bring render/3 and render_to_string/3 for testing custom views
+ import Phoenix.View
+
+ test "renders 404.json" do
+ assert render(Pleroma.Web.ErrorView, "404.json", []) == %{errors: %{detail: "Page not found"}}
+ end
+
+ test "render 500.json" do
+ assert capture_log(fn ->
+ assert render(Pleroma.Web.ErrorView, "500.json", []) ==
+ %{errors: %{detail: "Internal server error", reason: "nil"}}
+ end) =~ "[error] Internal server error: nil"
+ end
+
+ test "render any other" do
+ assert capture_log(fn ->
+ assert render(Pleroma.Web.ErrorView, "505.json", []) ==
+ %{errors: %{detail: "Internal server error", reason: "nil"}}
+ end) =~ "[error] Internal server error: nil"
+ end
+
+ test "render 500.json with reason" do
+ assert capture_log(fn ->
+ assert render(Pleroma.Web.ErrorView, "500.json", reason: "test reason") ==
+ %{errors: %{detail: "Internal server error", reason: "\"test reason\""}}
+ end) =~ "[error] Internal server error: \"test reason\""
+ end
+end
diff --git a/test/pleroma/web/web_finger/web_finger_controller_test.exs b/test/pleroma/web/web_finger/web_finger_controller_test.exs
@@ -0,0 +1,94 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.WebFinger.WebFingerControllerTest do
+ use Pleroma.Web.ConnCase
+
+ import ExUnit.CaptureLog
+ import Pleroma.Factory
+ import Tesla.Mock
+
+ setup do
+ mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
+ :ok
+ end
+
+ setup_all do: clear_config([:instance, :federating], true)
+
+ test "GET host-meta" do
+ response =
+ build_conn()
+ |> get("/.well-known/host-meta")
+
+ assert response.status == 200
+
+ assert response.resp_body ==
+ ~s(<?xml version="1.0" encoding="UTF-8"?><XRD xmlns="http://docs.oasis-open.org/ns/xri/xrd-1.0"><Link rel="lrdd" template="#{
+ Pleroma.Web.base_url()
+ }/.well-known/webfinger?resource={uri}" type="application/xrd+xml" /></XRD>)
+ end
+
+ test "Webfinger JRD" do
+ user = insert(:user)
+
+ response =
+ build_conn()
+ |> put_req_header("accept", "application/jrd+json")
+ |> get("/.well-known/webfinger?resource=acct:#{user.nickname}@localhost")
+
+ assert json_response(response, 200)["subject"] == "acct:#{user.nickname}@localhost"
+ end
+
+ test "it returns 404 when user isn't found (JSON)" do
+ result =
+ build_conn()
+ |> put_req_header("accept", "application/jrd+json")
+ |> get("/.well-known/webfinger?resource=acct:jimm@localhost")
+ |> json_response(404)
+
+ assert result == "Couldn't find user"
+ end
+
+ test "Webfinger XML" do
+ user = insert(:user)
+
+ response =
+ build_conn()
+ |> put_req_header("accept", "application/xrd+xml")
+ |> get("/.well-known/webfinger?resource=acct:#{user.nickname}@localhost")
+
+ assert response(response, 200)
+ end
+
+ test "it returns 404 when user isn't found (XML)" do
+ result =
+ build_conn()
+ |> put_req_header("accept", "application/xrd+xml")
+ |> get("/.well-known/webfinger?resource=acct:jimm@localhost")
+ |> response(404)
+
+ assert result == "Couldn't find user"
+ end
+
+ test "Sends a 404 when invalid format" do
+ user = insert(:user)
+
+ assert capture_log(fn ->
+ assert_raise Phoenix.NotAcceptableError, fn ->
+ build_conn()
+ |> put_req_header("accept", "text/html")
+ |> get("/.well-known/webfinger?resource=acct:#{user.nickname}@localhost")
+ end
+ end) =~ "no supported media type in accept header"
+ end
+
+ test "Sends a 400 when resource param is missing" do
+ response =
+ build_conn()
+ |> put_req_header("accept", "application/xrd+xml,application/jrd+json")
+ |> get("/.well-known/webfinger")
+
+ assert response(response, 400)
+ end
+end
diff --git a/test/pleroma/web/web_finger_test.exs b/test/pleroma/web/web_finger_test.exs
@@ -0,0 +1,116 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.WebFingerTest do
+ use Pleroma.DataCase
+ alias Pleroma.Web.WebFinger
+ import Pleroma.Factory
+ import Tesla.Mock
+
+ setup do
+ mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
+ :ok
+ end
+
+ describe "host meta" do
+ test "returns a link to the xml lrdd" do
+ host_info = WebFinger.host_meta()
+
+ assert String.contains?(host_info, Pleroma.Web.base_url())
+ end
+ end
+
+ describe "incoming webfinger request" do
+ test "works for fqns" do
+ user = insert(:user)
+
+ {:ok, result} =
+ WebFinger.webfinger("#{user.nickname}@#{Pleroma.Web.Endpoint.host()}", "XML")
+
+ assert is_binary(result)
+ end
+
+ test "works for ap_ids" do
+ user = insert(:user)
+
+ {:ok, result} = WebFinger.webfinger(user.ap_id, "XML")
+ assert is_binary(result)
+ end
+ end
+
+ describe "fingering" do
+ test "returns error for nonsensical input" do
+ assert {:error, _} = WebFinger.finger("bliblablu")
+ assert {:error, _} = WebFinger.finger("pleroma.social")
+ end
+
+ test "returns error when fails parse xml or json" do
+ user = "invalid_content@social.heldscal.la"
+ assert {:error, %Jason.DecodeError{}} = WebFinger.finger(user)
+ end
+
+ test "returns the ActivityPub actor URI for an ActivityPub user" do
+ user = "framasoft@framatube.org"
+
+ {:ok, _data} = WebFinger.finger(user)
+ end
+
+ test "returns the ActivityPub actor URI for an ActivityPub user with the ld+json mimetype" do
+ user = "kaniini@gerzilla.de"
+
+ {:ok, data} = WebFinger.finger(user)
+
+ assert data["ap_id"] == "https://gerzilla.de/channel/kaniini"
+ end
+
+ test "it work for AP-only user" do
+ user = "kpherox@mstdn.jp"
+
+ {:ok, data} = WebFinger.finger(user)
+
+ assert data["magic_key"] == nil
+ assert data["salmon"] == nil
+
+ assert data["topic"] == nil
+ assert data["subject"] == "acct:kPherox@mstdn.jp"
+ assert data["ap_id"] == "https://mstdn.jp/users/kPherox"
+ assert data["subscribe_address"] == "https://mstdn.jp/authorize_interaction?acct={uri}"
+ end
+
+ test "it works for friendica" do
+ user = "lain@squeet.me"
+
+ {:ok, _data} = WebFinger.finger(user)
+ end
+
+ test "it gets the xrd endpoint" do
+ {:ok, template} = WebFinger.find_lrdd_template("social.heldscal.la")
+
+ assert template == "https://social.heldscal.la/.well-known/webfinger?resource={uri}"
+ end
+
+ test "it gets the xrd endpoint for hubzilla" do
+ {:ok, template} = WebFinger.find_lrdd_template("macgirvin.com")
+
+ assert template == "https://macgirvin.com/xrd/?uri={uri}"
+ end
+
+ test "it gets the xrd endpoint for statusnet" do
+ {:ok, template} = WebFinger.find_lrdd_template("status.alpicola.com")
+
+ assert template == "http://status.alpicola.com/main/xrd?uri={uri}"
+ end
+
+ test "it works with idna domains as nickname" do
+ nickname = "lain@" <> to_string(:idna.encode("zetsubou.みんな"))
+
+ {:ok, _data} = WebFinger.finger(nickname)
+ end
+
+ test "it works with idna domains as link" do
+ ap_id = "https://" <> to_string(:idna.encode("zetsubou.みんな")) <> "/users/lain"
+ {:ok, _data} = WebFinger.finger(ap_id)
+ end
+ end
+end
diff --git a/test/pleroma/workers/cron/digest_emails_worker_test.exs b/test/pleroma/workers/cron/digest_emails_worker_test.exs
@@ -0,0 +1,54 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Workers.Cron.DigestEmailsWorkerTest do
+ use Pleroma.DataCase
+
+ import Pleroma.Factory
+
+ alias Pleroma.Tests.ObanHelpers
+ alias Pleroma.User
+ alias Pleroma.Web.CommonAPI
+
+ setup do: clear_config([:email_notifications, :digest])
+
+ setup do
+ Pleroma.Config.put([:email_notifications, :digest], %{
+ active: true,
+ inactivity_threshold: 7,
+ interval: 7
+ })
+
+ user = insert(:user)
+
+ date =
+ Timex.now()
+ |> Timex.shift(days: -10)
+ |> Timex.to_naive_datetime()
+
+ user2 = insert(:user, last_digest_emailed_at: date)
+ {:ok, _} = User.switch_email_notifications(user2, "digest", true)
+ CommonAPI.post(user, %{status: "hey @#{user2.nickname}!"})
+
+ {:ok, user2: user2}
+ end
+
+ test "it sends digest emails", %{user2: user2} do
+ Pleroma.Workers.Cron.DigestEmailsWorker.perform(%Oban.Job{})
+ # Performing job(s) enqueued at previous step
+ ObanHelpers.perform_all()
+
+ assert_received {:email, email}
+ assert email.to == [{user2.name, user2.email}]
+ assert email.subject == "Your digest from #{Pleroma.Config.get(:instance)[:name]}"
+ end
+
+ test "it doesn't fail when a user has no email", %{user2: user2} do
+ {:ok, _} = user2 |> Ecto.Changeset.change(%{email: nil}) |> Pleroma.Repo.update()
+
+ Pleroma.Workers.Cron.DigestEmailsWorker.perform(%Oban.Job{})
+ # Performing job(s) enqueued at previous step
+ ObanHelpers.perform_all()
+ end
+end
diff --git a/test/pleroma/workers/cron/new_users_digest_worker_test.exs b/test/pleroma/workers/cron/new_users_digest_worker_test.exs
@@ -0,0 +1,45 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Workers.Cron.NewUsersDigestWorkerTest do
+ use Pleroma.DataCase
+ import Pleroma.Factory
+
+ alias Pleroma.Tests.ObanHelpers
+ alias Pleroma.Web.CommonAPI
+ alias Pleroma.Workers.Cron.NewUsersDigestWorker
+
+ test "it sends new users digest emails" do
+ yesterday = NaiveDateTime.utc_now() |> Timex.shift(days: -1)
+ admin = insert(:user, %{is_admin: true})
+ user = insert(:user, %{inserted_at: yesterday})
+ user2 = insert(:user, %{inserted_at: yesterday})
+ CommonAPI.post(user, %{status: "cofe"})
+
+ NewUsersDigestWorker.perform(%Oban.Job{})
+ ObanHelpers.perform_all()
+
+ assert_received {:email, email}
+ assert email.to == [{admin.name, admin.email}]
+ assert email.subject == "#{Pleroma.Config.get([:instance, :name])} New Users"
+
+ refute email.html_body =~ admin.nickname
+ assert email.html_body =~ user.nickname
+ assert email.html_body =~ user2.nickname
+ assert email.html_body =~ "cofe"
+ assert email.html_body =~ "#{Pleroma.Web.Endpoint.url()}/static/logo.png"
+ end
+
+ test "it doesn't fail when admin has no email" do
+ yesterday = NaiveDateTime.utc_now() |> Timex.shift(days: -1)
+ insert(:user, %{is_admin: true, email: nil})
+ insert(:user, %{inserted_at: yesterday})
+ user = insert(:user, %{inserted_at: yesterday})
+
+ CommonAPI.post(user, %{status: "cofe"})
+
+ NewUsersDigestWorker.perform(%Oban.Job{})
+ ObanHelpers.perform_all()
+ end
+end
diff --git a/test/pleroma/workers/scheduled_activity_worker_test.exs b/test/pleroma/workers/scheduled_activity_worker_test.exs
@@ -0,0 +1,49 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Workers.ScheduledActivityWorkerTest do
+ use Pleroma.DataCase
+
+ alias Pleroma.ScheduledActivity
+ alias Pleroma.Workers.ScheduledActivityWorker
+
+ import Pleroma.Factory
+ import ExUnit.CaptureLog
+
+ setup do: clear_config([ScheduledActivity, :enabled])
+
+ test "creates a status from the scheduled activity" do
+ Pleroma.Config.put([ScheduledActivity, :enabled], true)
+ user = insert(:user)
+
+ naive_datetime =
+ NaiveDateTime.add(
+ NaiveDateTime.utc_now(),
+ -:timer.minutes(2),
+ :millisecond
+ )
+
+ scheduled_activity =
+ insert(
+ :scheduled_activity,
+ scheduled_at: naive_datetime,
+ user: user,
+ params: %{status: "hi"}
+ )
+
+ ScheduledActivityWorker.perform(%Oban.Job{args: %{"activity_id" => scheduled_activity.id}})
+
+ refute Repo.get(ScheduledActivity, scheduled_activity.id)
+ activity = Repo.all(Pleroma.Activity) |> Enum.find(&(&1.actor == user.ap_id))
+ assert Pleroma.Object.normalize(activity).data["content"] == "hi"
+ end
+
+ test "adds log message if ScheduledActivity isn't find" do
+ Pleroma.Config.put([ScheduledActivity, :enabled], true)
+
+ assert capture_log([level: :error], fn ->
+ ScheduledActivityWorker.perform(%Oban.Job{args: %{"activity_id" => 42}})
+ end) =~ "Couldn't find scheduled activity"
+ end
+end
diff --git a/test/pleroma/xml_builder_test.exs b/test/pleroma/xml_builder_test.exs
@@ -0,0 +1,65 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.XmlBuilderTest do
+ use Pleroma.DataCase
+ alias Pleroma.XmlBuilder
+
+ test "Build a basic xml string from a tuple" do
+ data = {:feed, %{xmlns: "http://www.w3.org/2005/Atom"}, "Some content"}
+
+ expected_xml = "<feed xmlns=\"http://www.w3.org/2005/Atom\">Some content</feed>"
+
+ assert XmlBuilder.to_xml(data) == expected_xml
+ end
+
+ test "returns a complete document" do
+ data = {:feed, %{xmlns: "http://www.w3.org/2005/Atom"}, "Some content"}
+
+ expected_xml =
+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?><feed xmlns=\"http://www.w3.org/2005/Atom\">Some content</feed>"
+
+ assert XmlBuilder.to_doc(data) == expected_xml
+ end
+
+ test "Works without attributes" do
+ data = {
+ :feed,
+ "Some content"
+ }
+
+ expected_xml = "<feed>Some content</feed>"
+
+ assert XmlBuilder.to_xml(data) == expected_xml
+ end
+
+ test "It works with nested tuples" do
+ data = {
+ :feed,
+ [
+ {:guy, "brush"},
+ {:lament, %{configuration: "puzzle"}, "pinhead"}
+ ]
+ }
+
+ expected_xml =
+ ~s[<feed><guy>brush</guy><lament configuration="puzzle">pinhead</lament></feed>]
+
+ assert XmlBuilder.to_xml(data) == expected_xml
+ end
+
+ test "Represents NaiveDateTime as iso8601" do
+ assert XmlBuilder.to_xml(~N[2000-01-01 13:13:33]) == "2000-01-01T13:13:33"
+ end
+
+ test "Uses self-closing tags when no content is giving" do
+ data = {
+ :link,
+ %{rel: "self"}
+ }
+
+ expected_xml = ~s[<link rel="self" />]
+ assert XmlBuilder.to_xml(data) == expected_xml
+ end
+end
diff --git a/test/plugs/admin_secret_authentication_plug_test.exs b/test/plugs/admin_secret_authentication_plug_test.exs
@@ -1,75 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Plugs.AdminSecretAuthenticationPlugTest do
- use Pleroma.Web.ConnCase
-
- import Mock
- import Pleroma.Factory
-
- alias Pleroma.Plugs.AdminSecretAuthenticationPlug
- alias Pleroma.Plugs.OAuthScopesPlug
- alias Pleroma.Plugs.PlugHelper
- alias Pleroma.Plugs.RateLimiter
-
- test "does nothing if a user is assigned", %{conn: conn} do
- user = insert(:user)
-
- conn =
- conn
- |> assign(:user, user)
-
- ret_conn =
- conn
- |> AdminSecretAuthenticationPlug.call(%{})
-
- assert conn == ret_conn
- end
-
- describe "when secret set it assigns an admin user" do
- setup do: clear_config([:admin_token])
-
- setup_with_mocks([{RateLimiter, [:passthrough], []}]) do
- :ok
- end
-
- test "with `admin_token` query parameter", %{conn: conn} do
- Pleroma.Config.put(:admin_token, "password123")
-
- conn =
- %{conn | params: %{"admin_token" => "wrong_password"}}
- |> AdminSecretAuthenticationPlug.call(%{})
-
- refute conn.assigns[:user]
- assert called(RateLimiter.call(conn, name: :authentication))
-
- conn =
- %{conn | params: %{"admin_token" => "password123"}}
- |> AdminSecretAuthenticationPlug.call(%{})
-
- assert conn.assigns[:user].is_admin
- assert PlugHelper.plug_skipped?(conn, OAuthScopesPlug)
- end
-
- test "with `x-admin-token` HTTP header", %{conn: conn} do
- Pleroma.Config.put(:admin_token, "☕️")
-
- conn =
- conn
- |> put_req_header("x-admin-token", "🥛")
- |> AdminSecretAuthenticationPlug.call(%{})
-
- refute conn.assigns[:user]
- assert called(RateLimiter.call(conn, name: :authentication))
-
- conn =
- conn
- |> put_req_header("x-admin-token", "☕️")
- |> AdminSecretAuthenticationPlug.call(%{})
-
- assert conn.assigns[:user].is_admin
- assert PlugHelper.plug_skipped?(conn, OAuthScopesPlug)
- end
- end
-end
diff --git a/test/plugs/authentication_plug_test.exs b/test/plugs/authentication_plug_test.exs
@@ -1,125 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Plugs.AuthenticationPlugTest do
- use Pleroma.Web.ConnCase, async: true
-
- alias Pleroma.Plugs.AuthenticationPlug
- alias Pleroma.Plugs.OAuthScopesPlug
- alias Pleroma.Plugs.PlugHelper
- alias Pleroma.User
-
- import ExUnit.CaptureLog
- import Pleroma.Factory
-
- setup %{conn: conn} do
- user = %User{
- id: 1,
- name: "dude",
- password_hash: Pbkdf2.hash_pwd_salt("guy")
- }
-
- conn =
- conn
- |> assign(:auth_user, user)
-
- %{user: user, conn: conn}
- end
-
- test "it does nothing if a user is assigned", %{conn: conn} do
- conn =
- conn
- |> assign(:user, %User{})
-
- ret_conn =
- conn
- |> AuthenticationPlug.call(%{})
-
- assert ret_conn == conn
- end
-
- test "with a correct password in the credentials, " <>
- "it assigns the auth_user and marks OAuthScopesPlug as skipped",
- %{conn: conn} do
- conn =
- conn
- |> assign(:auth_credentials, %{password: "guy"})
- |> AuthenticationPlug.call(%{})
-
- assert conn.assigns.user == conn.assigns.auth_user
- assert PlugHelper.plug_skipped?(conn, OAuthScopesPlug)
- end
-
- test "with a bcrypt hash, it updates to a pkbdf2 hash", %{conn: conn} do
- user = insert(:user, password_hash: Bcrypt.hash_pwd_salt("123"))
- assert "$2" <> _ = user.password_hash
-
- conn =
- conn
- |> assign(:auth_user, user)
- |> assign(:auth_credentials, %{password: "123"})
- |> AuthenticationPlug.call(%{})
-
- assert conn.assigns.user.id == conn.assigns.auth_user.id
- assert PlugHelper.plug_skipped?(conn, OAuthScopesPlug)
-
- user = User.get_by_id(user.id)
- assert "$pbkdf2" <> _ = user.password_hash
- end
-
- @tag :skip_on_mac
- test "with a crypt hash, it updates to a pkbdf2 hash", %{conn: conn} do
- user =
- insert(:user,
- password_hash:
- "$6$9psBWV8gxkGOZWBz$PmfCycChoxeJ3GgGzwvhlgacb9mUoZ.KUXNCssekER4SJ7bOK53uXrHNb2e4i8yPFgSKyzaW9CcmrDXWIEMtD1"
- )
-
- conn =
- conn
- |> assign(:auth_user, user)
- |> assign(:auth_credentials, %{password: "password"})
- |> AuthenticationPlug.call(%{})
-
- assert conn.assigns.user.id == conn.assigns.auth_user.id
- assert PlugHelper.plug_skipped?(conn, OAuthScopesPlug)
-
- user = User.get_by_id(user.id)
- assert "$pbkdf2" <> _ = user.password_hash
- end
-
- describe "checkpw/2" do
- test "check pbkdf2 hash" do
- hash =
- "$pbkdf2-sha512$160000$loXqbp8GYls43F0i6lEfIw$AY.Ep.2pGe57j2hAPY635sI/6w7l9Q9u9Bp02PkPmF3OrClDtJAI8bCiivPr53OKMF7ph6iHhN68Rom5nEfC2A"
-
- assert AuthenticationPlug.checkpw("test-password", hash)
- refute AuthenticationPlug.checkpw("test-password1", hash)
- end
-
- @tag :skip_on_mac
- test "check sha512-crypt hash" do
- hash =
- "$6$9psBWV8gxkGOZWBz$PmfCycChoxeJ3GgGzwvhlgacb9mUoZ.KUXNCssekER4SJ7bOK53uXrHNb2e4i8yPFgSKyzaW9CcmrDXWIEMtD1"
-
- assert AuthenticationPlug.checkpw("password", hash)
- end
-
- test "check bcrypt hash" do
- hash = "$2a$10$uyhC/R/zoE1ndwwCtMusK.TLVzkQ/Ugsbqp3uXI.CTTz0gBw.24jS"
-
- assert AuthenticationPlug.checkpw("password", hash)
- refute AuthenticationPlug.checkpw("password1", hash)
- end
-
- test "it returns false when hash invalid" do
- hash =
- "psBWV8gxkGOZWBz$PmfCycChoxeJ3GgGzwvhlgacb9mUoZ.KUXNCssekER4SJ7bOK53uXrHNb2e4i8yPFgSKyzaW9CcmrDXWIEMtD1"
-
- assert capture_log(fn ->
- refute Pleroma.Plugs.AuthenticationPlug.checkpw("password", hash)
- end) =~ "[error] Password hash not recognized"
- end
- end
-end
diff --git a/test/plugs/basic_auth_decoder_plug_test.exs b/test/plugs/basic_auth_decoder_plug_test.exs
@@ -1,35 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Plugs.BasicAuthDecoderPlugTest do
- use Pleroma.Web.ConnCase, async: true
-
- alias Pleroma.Plugs.BasicAuthDecoderPlug
-
- defp basic_auth_enc(username, password) do
- "Basic " <> Base.encode64("#{username}:#{password}")
- end
-
- test "it puts the decoded credentials into the assigns", %{conn: conn} do
- header = basic_auth_enc("moonman", "iloverobek")
-
- conn =
- conn
- |> put_req_header("authorization", header)
- |> BasicAuthDecoderPlug.call(%{})
-
- assert conn.assigns[:auth_credentials] == %{
- username: "moonman",
- password: "iloverobek"
- }
- end
-
- test "without a authorization header it doesn't do anything", %{conn: conn} do
- ret_conn =
- conn
- |> BasicAuthDecoderPlug.call(%{})
-
- assert conn == ret_conn
- end
-end
diff --git a/test/plugs/cache_control_test.exs b/test/plugs/cache_control_test.exs
@@ -1,20 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Web.CacheControlTest do
- use Pleroma.Web.ConnCase
- alias Plug.Conn
-
- test "Verify Cache-Control header on static assets", %{conn: conn} do
- conn = get(conn, "/index.html")
-
- assert Conn.get_resp_header(conn, "cache-control") == ["public, no-cache"]
- end
-
- test "Verify Cache-Control header on the API", %{conn: conn} do
- conn = get(conn, "/api/v1/instance")
-
- assert Conn.get_resp_header(conn, "cache-control") == ["max-age=0, private, must-revalidate"]
- end
-end
diff --git a/test/plugs/cache_test.exs b/test/plugs/cache_test.exs
@@ -1,186 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Plugs.CacheTest do
- use ExUnit.Case, async: true
- use Plug.Test
-
- alias Pleroma.Plugs.Cache
-
- @miss_resp {200,
- [
- {"cache-control", "max-age=0, private, must-revalidate"},
- {"content-type", "cofe/hot; charset=utf-8"},
- {"x-cache", "MISS from Pleroma"}
- ], "cofe"}
-
- @hit_resp {200,
- [
- {"cache-control", "max-age=0, private, must-revalidate"},
- {"content-type", "cofe/hot; charset=utf-8"},
- {"x-cache", "HIT from Pleroma"}
- ], "cofe"}
-
- @ttl 5
-
- setup do
- Cachex.clear(:web_resp_cache)
- :ok
- end
-
- test "caches a response" do
- assert @miss_resp ==
- conn(:get, "/")
- |> Cache.call(%{query_params: false, ttl: nil})
- |> put_resp_content_type("cofe/hot")
- |> send_resp(:ok, "cofe")
- |> sent_resp()
-
- assert_raise(Plug.Conn.AlreadySentError, fn ->
- conn(:get, "/")
- |> Cache.call(%{query_params: false, ttl: nil})
- |> put_resp_content_type("cofe/hot")
- |> send_resp(:ok, "cofe")
- |> sent_resp()
- end)
-
- assert @hit_resp ==
- conn(:get, "/")
- |> Cache.call(%{query_params: false, ttl: nil})
- |> sent_resp()
- end
-
- test "ttl is set" do
- assert @miss_resp ==
- conn(:get, "/")
- |> Cache.call(%{query_params: false, ttl: @ttl})
- |> put_resp_content_type("cofe/hot")
- |> send_resp(:ok, "cofe")
- |> sent_resp()
-
- assert @hit_resp ==
- conn(:get, "/")
- |> Cache.call(%{query_params: false, ttl: @ttl})
- |> sent_resp()
-
- :timer.sleep(@ttl + 1)
-
- assert @miss_resp ==
- conn(:get, "/")
- |> Cache.call(%{query_params: false, ttl: @ttl})
- |> put_resp_content_type("cofe/hot")
- |> send_resp(:ok, "cofe")
- |> sent_resp()
- end
-
- test "set ttl via conn.assigns" do
- assert @miss_resp ==
- conn(:get, "/")
- |> Cache.call(%{query_params: false, ttl: nil})
- |> put_resp_content_type("cofe/hot")
- |> assign(:cache_ttl, @ttl)
- |> send_resp(:ok, "cofe")
- |> sent_resp()
-
- assert @hit_resp ==
- conn(:get, "/")
- |> Cache.call(%{query_params: false, ttl: nil})
- |> sent_resp()
-
- :timer.sleep(@ttl + 1)
-
- assert @miss_resp ==
- conn(:get, "/")
- |> Cache.call(%{query_params: false, ttl: nil})
- |> put_resp_content_type("cofe/hot")
- |> send_resp(:ok, "cofe")
- |> sent_resp()
- end
-
- test "ignore query string when `query_params` is false" do
- assert @miss_resp ==
- conn(:get, "/?cofe")
- |> Cache.call(%{query_params: false, ttl: nil})
- |> put_resp_content_type("cofe/hot")
- |> send_resp(:ok, "cofe")
- |> sent_resp()
-
- assert @hit_resp ==
- conn(:get, "/?cofefe")
- |> Cache.call(%{query_params: false, ttl: nil})
- |> sent_resp()
- end
-
- test "take query string into account when `query_params` is true" do
- assert @miss_resp ==
- conn(:get, "/?cofe")
- |> Cache.call(%{query_params: true, ttl: nil})
- |> put_resp_content_type("cofe/hot")
- |> send_resp(:ok, "cofe")
- |> sent_resp()
-
- assert @miss_resp ==
- conn(:get, "/?cofefe")
- |> Cache.call(%{query_params: true, ttl: nil})
- |> put_resp_content_type("cofe/hot")
- |> send_resp(:ok, "cofe")
- |> sent_resp()
- end
-
- test "take specific query params into account when `query_params` is list" do
- assert @miss_resp ==
- conn(:get, "/?a=1&b=2&c=3&foo=bar")
- |> fetch_query_params()
- |> Cache.call(%{query_params: ["a", "b", "c"], ttl: nil})
- |> put_resp_content_type("cofe/hot")
- |> send_resp(:ok, "cofe")
- |> sent_resp()
-
- assert @hit_resp ==
- conn(:get, "/?bar=foo&c=3&b=2&a=1")
- |> fetch_query_params()
- |> Cache.call(%{query_params: ["a", "b", "c"], ttl: nil})
- |> sent_resp()
-
- assert @miss_resp ==
- conn(:get, "/?bar=foo&c=3&b=2&a=2")
- |> fetch_query_params()
- |> Cache.call(%{query_params: ["a", "b", "c"], ttl: nil})
- |> put_resp_content_type("cofe/hot")
- |> send_resp(:ok, "cofe")
- |> sent_resp()
- end
-
- test "ignore not GET requests" do
- expected =
- {200,
- [
- {"cache-control", "max-age=0, private, must-revalidate"},
- {"content-type", "cofe/hot; charset=utf-8"}
- ], "cofe"}
-
- assert expected ==
- conn(:post, "/")
- |> Cache.call(%{query_params: true, ttl: nil})
- |> put_resp_content_type("cofe/hot")
- |> send_resp(:ok, "cofe")
- |> sent_resp()
- end
-
- test "ignore non-successful responses" do
- expected =
- {418,
- [
- {"cache-control", "max-age=0, private, must-revalidate"},
- {"content-type", "tea/iced; charset=utf-8"}
- ], "🥤"}
-
- assert expected ==
- conn(:get, "/cofe")
- |> Cache.call(%{query_params: true, ttl: nil})
- |> put_resp_content_type("tea/iced")
- |> send_resp(:im_a_teapot, "🥤")
- |> sent_resp()
- end
-end
diff --git a/test/plugs/ensure_authenticated_plug_test.exs b/test/plugs/ensure_authenticated_plug_test.exs
@@ -1,96 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Plugs.EnsureAuthenticatedPlugTest do
- use Pleroma.Web.ConnCase, async: true
-
- alias Pleroma.Plugs.EnsureAuthenticatedPlug
- alias Pleroma.User
-
- describe "without :if_func / :unless_func options" do
- test "it halts if user is NOT assigned", %{conn: conn} do
- conn = EnsureAuthenticatedPlug.call(conn, %{})
-
- assert conn.status == 403
- assert conn.halted == true
- end
-
- test "it continues if a user is assigned", %{conn: conn} do
- conn = assign(conn, :user, %User{})
- ret_conn = EnsureAuthenticatedPlug.call(conn, %{})
-
- refute ret_conn.halted
- end
- end
-
- test "it halts if user is assigned and MFA enabled", %{conn: conn} do
- conn =
- conn
- |> assign(:user, %User{multi_factor_authentication_settings: %{enabled: true}})
- |> assign(:auth_credentials, %{password: "xd-42"})
- |> EnsureAuthenticatedPlug.call(%{})
-
- assert conn.status == 403
- assert conn.halted == true
-
- assert conn.resp_body ==
- "{\"error\":\"Two-factor authentication enabled, you must use a access token.\"}"
- end
-
- test "it continues if user is assigned and MFA disabled", %{conn: conn} do
- conn =
- conn
- |> assign(:user, %User{multi_factor_authentication_settings: %{enabled: false}})
- |> assign(:auth_credentials, %{password: "xd-42"})
- |> EnsureAuthenticatedPlug.call(%{})
-
- refute conn.status == 403
- refute conn.halted
- end
-
- describe "with :if_func / :unless_func options" do
- setup do
- %{
- true_fn: fn _conn -> true end,
- false_fn: fn _conn -> false end
- }
- end
-
- test "it continues if a user is assigned", %{conn: conn, true_fn: true_fn, false_fn: false_fn} do
- conn = assign(conn, :user, %User{})
- refute EnsureAuthenticatedPlug.call(conn, if_func: true_fn).halted
- refute EnsureAuthenticatedPlug.call(conn, if_func: false_fn).halted
- refute EnsureAuthenticatedPlug.call(conn, unless_func: true_fn).halted
- refute EnsureAuthenticatedPlug.call(conn, unless_func: false_fn).halted
- end
-
- test "it continues if a user is NOT assigned but :if_func evaluates to `false`",
- %{conn: conn, false_fn: false_fn} do
- ret_conn = EnsureAuthenticatedPlug.call(conn, if_func: false_fn)
- refute ret_conn.halted
- end
-
- test "it continues if a user is NOT assigned but :unless_func evaluates to `true`",
- %{conn: conn, true_fn: true_fn} do
- ret_conn = EnsureAuthenticatedPlug.call(conn, unless_func: true_fn)
- refute ret_conn.halted
- end
-
- test "it halts if a user is NOT assigned and :if_func evaluates to `true`",
- %{conn: conn, true_fn: true_fn} do
- conn = EnsureAuthenticatedPlug.call(conn, if_func: true_fn)
-
- assert conn.status == 403
- assert conn.halted == true
- end
-
- test "it halts if a user is NOT assigned and :unless_func evaluates to `false`",
- %{conn: conn, false_fn: false_fn} do
- conn = EnsureAuthenticatedPlug.call(conn, unless_func: false_fn)
-
- assert conn.status == 403
- assert conn.halted == true
- end
- end
-end
diff --git a/test/plugs/ensure_public_or_authenticated_plug_test.exs b/test/plugs/ensure_public_or_authenticated_plug_test.exs
@@ -1,48 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Plugs.EnsurePublicOrAuthenticatedPlugTest do
- use Pleroma.Web.ConnCase, async: true
-
- alias Pleroma.Config
- alias Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug
- alias Pleroma.User
-
- setup do: clear_config([:instance, :public])
-
- test "it halts if not public and no user is assigned", %{conn: conn} do
- Config.put([:instance, :public], false)
-
- conn =
- conn
- |> EnsurePublicOrAuthenticatedPlug.call(%{})
-
- assert conn.status == 403
- assert conn.halted == true
- end
-
- test "it continues if public", %{conn: conn} do
- Config.put([:instance, :public], true)
-
- ret_conn =
- conn
- |> EnsurePublicOrAuthenticatedPlug.call(%{})
-
- refute ret_conn.halted
- end
-
- test "it continues if a user is assigned, even if not public", %{conn: conn} do
- Config.put([:instance, :public], false)
-
- conn =
- conn
- |> assign(:user, %User{})
-
- ret_conn =
- conn
- |> EnsurePublicOrAuthenticatedPlug.call(%{})
-
- refute ret_conn.halted
- end
-end
diff --git a/test/plugs/ensure_user_key_plug_test.exs b/test/plugs/ensure_user_key_plug_test.exs
@@ -1,29 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Plugs.EnsureUserKeyPlugTest do
- use Pleroma.Web.ConnCase, async: true
-
- alias Pleroma.Plugs.EnsureUserKeyPlug
-
- test "if the conn has a user key set, it does nothing", %{conn: conn} do
- conn =
- conn
- |> assign(:user, 1)
-
- ret_conn =
- conn
- |> EnsureUserKeyPlug.call(%{})
-
- assert conn == ret_conn
- end
-
- test "if the conn has no key set, it sets it to nil", %{conn: conn} do
- conn =
- conn
- |> EnsureUserKeyPlug.call(%{})
-
- assert Map.has_key?(conn.assigns, :user)
- end
-end
diff --git a/test/plugs/idempotency_plug_test.exs b/test/plugs/idempotency_plug_test.exs
@@ -1,110 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Plugs.IdempotencyPlugTest do
- use ExUnit.Case, async: true
- use Plug.Test
-
- alias Pleroma.Plugs.IdempotencyPlug
- alias Plug.Conn
-
- test "returns result from cache" do
- key = "test1"
- orig_request_id = "test1"
- second_request_id = "test2"
- body = "testing"
- status = 200
-
- :post
- |> conn("/cofe")
- |> put_req_header("idempotency-key", key)
- |> Conn.put_resp_header("x-request-id", orig_request_id)
- |> Conn.put_resp_content_type("application/json")
- |> IdempotencyPlug.call([])
- |> Conn.send_resp(status, body)
-
- conn =
- :post
- |> conn("/cofe")
- |> put_req_header("idempotency-key", key)
- |> Conn.put_resp_header("x-request-id", second_request_id)
- |> Conn.put_resp_content_type("application/json")
- |> IdempotencyPlug.call([])
-
- assert_raise Conn.AlreadySentError, fn ->
- Conn.send_resp(conn, :im_a_teapot, "no cofe")
- end
-
- assert conn.resp_body == body
- assert conn.status == status
-
- assert [^second_request_id] = Conn.get_resp_header(conn, "x-request-id")
- assert [^orig_request_id] = Conn.get_resp_header(conn, "x-original-request-id")
- assert [^key] = Conn.get_resp_header(conn, "idempotency-key")
- assert ["true"] = Conn.get_resp_header(conn, "idempotent-replayed")
- assert ["application/json; charset=utf-8"] = Conn.get_resp_header(conn, "content-type")
- end
-
- test "pass conn downstream if the cache not found" do
- key = "test2"
- orig_request_id = "test3"
- body = "testing"
- status = 200
-
- conn =
- :post
- |> conn("/cofe")
- |> put_req_header("idempotency-key", key)
- |> Conn.put_resp_header("x-request-id", orig_request_id)
- |> Conn.put_resp_content_type("application/json")
- |> IdempotencyPlug.call([])
- |> Conn.send_resp(status, body)
-
- assert conn.resp_body == body
- assert conn.status == status
-
- assert [] = Conn.get_resp_header(conn, "idempotent-replayed")
- assert [^key] = Conn.get_resp_header(conn, "idempotency-key")
- end
-
- test "passes conn downstream if idempotency is not present in headers" do
- orig_request_id = "test4"
- body = "testing"
- status = 200
-
- conn =
- :post
- |> conn("/cofe")
- |> Conn.put_resp_header("x-request-id", orig_request_id)
- |> Conn.put_resp_content_type("application/json")
- |> IdempotencyPlug.call([])
- |> Conn.send_resp(status, body)
-
- assert [] = Conn.get_resp_header(conn, "idempotency-key")
- end
-
- test "doesn't work with GET/DELETE" do
- key = "test3"
- body = "testing"
- status = 200
-
- conn =
- :get
- |> conn("/cofe")
- |> put_req_header("idempotency-key", key)
- |> IdempotencyPlug.call([])
- |> Conn.send_resp(status, body)
-
- assert [] = Conn.get_resp_header(conn, "idempotency-key")
-
- conn =
- :delete
- |> conn("/cofe")
- |> put_req_header("idempotency-key", key)
- |> IdempotencyPlug.call([])
- |> Conn.send_resp(status, body)
-
- assert [] = Conn.get_resp_header(conn, "idempotency-key")
- end
-end
diff --git a/test/plugs/instance_static_test.exs b/test/plugs/instance_static_test.exs
@@ -1,65 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Web.InstanceStaticPlugTest do
- use Pleroma.Web.ConnCase
-
- @dir "test/tmp/instance_static"
-
- setup do
- File.mkdir_p!(@dir)
- on_exit(fn -> File.rm_rf(@dir) end)
- end
-
- setup do: clear_config([:instance, :static_dir], @dir)
-
- test "overrides index" do
- bundled_index = get(build_conn(), "/")
- refute html_response(bundled_index, 200) == "hello world"
-
- File.write!(@dir <> "/index.html", "hello world")
-
- index = get(build_conn(), "/")
- assert html_response(index, 200) == "hello world"
- end
-
- test "also overrides frontend files", %{conn: conn} do
- name = "pelmora"
- ref = "uguu"
-
- clear_config([:frontends, :primary], %{"name" => name, "ref" => ref})
-
- bundled_index = get(conn, "/")
- refute html_response(bundled_index, 200) == "from frontend plug"
-
- path = "#{@dir}/frontends/#{name}/#{ref}"
- File.mkdir_p!(path)
- File.write!("#{path}/index.html", "from frontend plug")
-
- index = get(conn, "/")
- assert html_response(index, 200) == "from frontend plug"
-
- File.write!(@dir <> "/index.html", "from instance static")
-
- index = get(conn, "/")
- assert html_response(index, 200) == "from instance static"
- end
-
- test "overrides any file in static/static" do
- bundled_index = get(build_conn(), "/static/terms-of-service.html")
-
- assert html_response(bundled_index, 200) ==
- File.read!("priv/static/static/terms-of-service.html")
-
- File.mkdir!(@dir <> "/static")
- File.write!(@dir <> "/static/terms-of-service.html", "plz be kind")
-
- index = get(build_conn(), "/static/terms-of-service.html")
- assert html_response(index, 200) == "plz be kind"
-
- File.write!(@dir <> "/static/kaniini.html", "<h1>rabbit hugs as a service</h1>")
- index = get(build_conn(), "/static/kaniini.html")
- assert html_response(index, 200) == "<h1>rabbit hugs as a service</h1>"
- end
-end
diff --git a/test/plugs/legacy_authentication_plug_test.exs b/test/plugs/legacy_authentication_plug_test.exs
@@ -1,82 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Plugs.LegacyAuthenticationPlugTest do
- use Pleroma.Web.ConnCase
-
- import Pleroma.Factory
-
- alias Pleroma.Plugs.LegacyAuthenticationPlug
- alias Pleroma.Plugs.OAuthScopesPlug
- alias Pleroma.Plugs.PlugHelper
- alias Pleroma.User
-
- setup do
- user =
- insert(:user,
- password: "password",
- password_hash:
- "$6$9psBWV8gxkGOZWBz$PmfCycChoxeJ3GgGzwvhlgacb9mUoZ.KUXNCssekER4SJ7bOK53uXrHNb2e4i8yPFgSKyzaW9CcmrDXWIEMtD1"
- )
-
- %{user: user}
- end
-
- test "it does nothing if a user is assigned", %{conn: conn, user: user} do
- conn =
- conn
- |> assign(:auth_credentials, %{username: "dude", password: "password"})
- |> assign(:auth_user, user)
- |> assign(:user, %User{})
-
- ret_conn =
- conn
- |> LegacyAuthenticationPlug.call(%{})
-
- assert ret_conn == conn
- end
-
- @tag :skip_on_mac
- test "if `auth_user` is present and password is correct, " <>
- "it authenticates the user, resets the password, marks OAuthScopesPlug as skipped",
- %{
- conn: conn,
- user: user
- } do
- conn =
- conn
- |> assign(:auth_credentials, %{username: "dude", password: "password"})
- |> assign(:auth_user, user)
-
- conn = LegacyAuthenticationPlug.call(conn, %{})
-
- assert conn.assigns.user.id == user.id
- assert PlugHelper.plug_skipped?(conn, OAuthScopesPlug)
- end
-
- @tag :skip_on_mac
- test "it does nothing if the password is wrong", %{
- conn: conn,
- user: user
- } do
- conn =
- conn
- |> assign(:auth_credentials, %{username: "dude", password: "wrong_password"})
- |> assign(:auth_user, user)
-
- ret_conn =
- conn
- |> LegacyAuthenticationPlug.call(%{})
-
- assert conn == ret_conn
- end
-
- test "with no credentials or user it does nothing", %{conn: conn} do
- ret_conn =
- conn
- |> LegacyAuthenticationPlug.call(%{})
-
- assert ret_conn == conn
- end
-end
diff --git a/test/plugs/oauth_plug_test.exs b/test/plugs/oauth_plug_test.exs
@@ -1,80 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Plugs.OAuthPlugTest do
- use Pleroma.Web.ConnCase, async: true
-
- alias Pleroma.Plugs.OAuthPlug
- import Pleroma.Factory
-
- @session_opts [
- store: :cookie,
- key: "_test",
- signing_salt: "cooldude"
- ]
-
- setup %{conn: conn} do
- user = insert(:user)
- {:ok, %{token: token}} = Pleroma.Web.OAuth.Token.create(insert(:oauth_app), user)
- %{user: user, token: token, conn: conn}
- end
-
- test "with valid token(uppercase), it assigns the user", %{conn: conn} = opts do
- conn =
- conn
- |> put_req_header("authorization", "BEARER #{opts[:token]}")
- |> OAuthPlug.call(%{})
-
- assert conn.assigns[:user] == opts[:user]
- end
-
- test "with valid token(downcase), it assigns the user", %{conn: conn} = opts do
- conn =
- conn
- |> put_req_header("authorization", "bearer #{opts[:token]}")
- |> OAuthPlug.call(%{})
-
- assert conn.assigns[:user] == opts[:user]
- end
-
- test "with valid token(downcase) in url parameters, it assigns the user", opts do
- conn =
- :get
- |> build_conn("/?access_token=#{opts[:token]}")
- |> put_req_header("content-type", "application/json")
- |> fetch_query_params()
- |> OAuthPlug.call(%{})
-
- assert conn.assigns[:user] == opts[:user]
- end
-
- test "with valid token(downcase) in body parameters, it assigns the user", opts do
- conn =
- :post
- |> build_conn("/api/v1/statuses", access_token: opts[:token], status: "test")
- |> OAuthPlug.call(%{})
-
- assert conn.assigns[:user] == opts[:user]
- end
-
- test "with invalid token, it not assigns the user", %{conn: conn} do
- conn =
- conn
- |> put_req_header("authorization", "bearer TTTTT")
- |> OAuthPlug.call(%{})
-
- refute conn.assigns[:user]
- end
-
- test "when token is missed but token in session, it assigns the user", %{conn: conn} = opts do
- conn =
- conn
- |> Plug.Session.call(Plug.Session.init(@session_opts))
- |> fetch_session()
- |> put_session(:oauth_token, opts[:token])
- |> OAuthPlug.call(%{})
-
- assert conn.assigns[:user] == opts[:user]
- end
-end
diff --git a/test/plugs/oauth_scopes_plug_test.exs b/test/plugs/oauth_scopes_plug_test.exs
@@ -1,210 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Plugs.OAuthScopesPlugTest do
- use Pleroma.Web.ConnCase
-
- alias Pleroma.Plugs.OAuthScopesPlug
- alias Pleroma.Repo
-
- import Mock
- import Pleroma.Factory
-
- test "is not performed if marked as skipped", %{conn: conn} do
- with_mock OAuthScopesPlug, [:passthrough], perform: &passthrough([&1, &2]) do
- conn =
- conn
- |> OAuthScopesPlug.skip_plug()
- |> OAuthScopesPlug.call(%{scopes: ["random_scope"]})
-
- refute called(OAuthScopesPlug.perform(:_, :_))
- refute conn.halted
- end
- end
-
- test "if `token.scopes` fulfills specified 'any of' conditions, " <>
- "proceeds with no op",
- %{conn: conn} do
- token = insert(:oauth_token, scopes: ["read", "write"]) |> Repo.preload(:user)
-
- conn =
- conn
- |> assign(:user, token.user)
- |> assign(:token, token)
- |> OAuthScopesPlug.call(%{scopes: ["read"]})
-
- refute conn.halted
- assert conn.assigns[:user]
- end
-
- test "if `token.scopes` fulfills specified 'all of' conditions, " <>
- "proceeds with no op",
- %{conn: conn} do
- token = insert(:oauth_token, scopes: ["scope1", "scope2", "scope3"]) |> Repo.preload(:user)
-
- conn =
- conn
- |> assign(:user, token.user)
- |> assign(:token, token)
- |> OAuthScopesPlug.call(%{scopes: ["scope2", "scope3"], op: :&})
-
- refute conn.halted
- assert conn.assigns[:user]
- end
-
- describe "with `fallback: :proceed_unauthenticated` option, " do
- test "if `token.scopes` doesn't fulfill specified conditions, " <>
- "clears :user and :token assigns",
- %{conn: conn} do
- user = insert(:user)
- token1 = insert(:oauth_token, scopes: ["read", "write"], user: user)
-
- for token <- [token1, nil], op <- [:|, :&] do
- ret_conn =
- conn
- |> assign(:user, user)
- |> assign(:token, token)
- |> OAuthScopesPlug.call(%{
- scopes: ["follow"],
- op: op,
- fallback: :proceed_unauthenticated
- })
-
- refute ret_conn.halted
- refute ret_conn.assigns[:user]
- refute ret_conn.assigns[:token]
- end
- end
- end
-
- describe "without :fallback option, " do
- test "if `token.scopes` does not fulfill specified 'any of' conditions, " <>
- "returns 403 and halts",
- %{conn: conn} do
- for token <- [insert(:oauth_token, scopes: ["read", "write"]), nil] do
- any_of_scopes = ["follow", "push"]
-
- ret_conn =
- conn
- |> assign(:token, token)
- |> OAuthScopesPlug.call(%{scopes: any_of_scopes})
-
- assert ret_conn.halted
- assert 403 == ret_conn.status
-
- expected_error = "Insufficient permissions: #{Enum.join(any_of_scopes, " | ")}."
- assert Jason.encode!(%{error: expected_error}) == ret_conn.resp_body
- end
- end
-
- test "if `token.scopes` does not fulfill specified 'all of' conditions, " <>
- "returns 403 and halts",
- %{conn: conn} do
- for token <- [insert(:oauth_token, scopes: ["read", "write"]), nil] do
- token_scopes = (token && token.scopes) || []
- all_of_scopes = ["write", "follow"]
-
- conn =
- conn
- |> assign(:token, token)
- |> OAuthScopesPlug.call(%{scopes: all_of_scopes, op: :&})
-
- assert conn.halted
- assert 403 == conn.status
-
- expected_error =
- "Insufficient permissions: #{Enum.join(all_of_scopes -- token_scopes, " & ")}."
-
- assert Jason.encode!(%{error: expected_error}) == conn.resp_body
- end
- end
- end
-
- describe "with hierarchical scopes, " do
- test "if `token.scopes` fulfills specified 'any of' conditions, " <>
- "proceeds with no op",
- %{conn: conn} do
- token = insert(:oauth_token, scopes: ["read", "write"]) |> Repo.preload(:user)
-
- conn =
- conn
- |> assign(:user, token.user)
- |> assign(:token, token)
- |> OAuthScopesPlug.call(%{scopes: ["read:something"]})
-
- refute conn.halted
- assert conn.assigns[:user]
- end
-
- test "if `token.scopes` fulfills specified 'all of' conditions, " <>
- "proceeds with no op",
- %{conn: conn} do
- token = insert(:oauth_token, scopes: ["scope1", "scope2", "scope3"]) |> Repo.preload(:user)
-
- conn =
- conn
- |> assign(:user, token.user)
- |> assign(:token, token)
- |> OAuthScopesPlug.call(%{scopes: ["scope1:subscope", "scope2:subscope"], op: :&})
-
- refute conn.halted
- assert conn.assigns[:user]
- end
- end
-
- describe "filter_descendants/2" do
- test "filters scopes which directly match or are ancestors of supported scopes" do
- f = fn scopes, supported_scopes ->
- OAuthScopesPlug.filter_descendants(scopes, supported_scopes)
- end
-
- assert f.(["read", "follow"], ["write", "read"]) == ["read"]
-
- assert f.(["read", "write:something", "follow"], ["write", "read"]) ==
- ["read", "write:something"]
-
- assert f.(["admin:read"], ["write", "read"]) == []
-
- assert f.(["admin:read"], ["write", "admin"]) == ["admin:read"]
- end
- end
-
- describe "transform_scopes/2" do
- setup do: clear_config([:auth, :enforce_oauth_admin_scope_usage])
-
- setup do
- {:ok, %{f: &OAuthScopesPlug.transform_scopes/2}}
- end
-
- test "with :admin option, prefixes all requested scopes with `admin:` " <>
- "and [optionally] keeps only prefixed scopes, " <>
- "depending on `[:auth, :enforce_oauth_admin_scope_usage]` setting",
- %{f: f} do
- Pleroma.Config.put([:auth, :enforce_oauth_admin_scope_usage], false)
-
- assert f.(["read"], %{admin: true}) == ["admin:read", "read"]
-
- assert f.(["read", "write"], %{admin: true}) == [
- "admin:read",
- "read",
- "admin:write",
- "write"
- ]
-
- Pleroma.Config.put([:auth, :enforce_oauth_admin_scope_usage], true)
-
- assert f.(["read:accounts"], %{admin: true}) == ["admin:read:accounts"]
-
- assert f.(["read", "write:reports"], %{admin: true}) == [
- "admin:read",
- "admin:write:reports"
- ]
- end
-
- test "with no supported options, returns unmodified scopes", %{f: f} do
- assert f.(["read"], %{}) == ["read"]
- assert f.(["read", "write"], %{}) == ["read", "write"]
- end
- end
-end
diff --git a/test/plugs/rate_limiter_test.exs b/test/plugs/rate_limiter_test.exs
@@ -1,263 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Plugs.RateLimiterTest do
- use Pleroma.Web.ConnCase
-
- alias Phoenix.ConnTest
- alias Pleroma.Config
- alias Pleroma.Plugs.RateLimiter
- alias Plug.Conn
-
- import Pleroma.Factory
- import Pleroma.Tests.Helpers, only: [clear_config: 1, clear_config: 2]
-
- # Note: each example must work with separate buckets in order to prevent concurrency issues
- setup do: clear_config([Pleroma.Web.Endpoint, :http, :ip])
- setup do: clear_config(:rate_limit)
-
- describe "config" do
- @limiter_name :test_init
- setup do: clear_config([Pleroma.Plugs.RemoteIp, :enabled])
-
- test "config is required for plug to work" do
- Config.put([:rate_limit, @limiter_name], {1, 1})
- Config.put([Pleroma.Web.Endpoint, :http, :ip], {8, 8, 8, 8})
-
- assert %{limits: {1, 1}, name: :test_init, opts: [name: :test_init]} ==
- [name: @limiter_name]
- |> RateLimiter.init()
- |> RateLimiter.action_settings()
-
- assert nil ==
- [name: :nonexisting_limiter]
- |> RateLimiter.init()
- |> RateLimiter.action_settings()
- end
- end
-
- test "it is disabled if it remote ip plug is enabled but no remote ip is found" do
- assert RateLimiter.disabled?(Conn.assign(build_conn(), :remote_ip_found, false))
- end
-
- test "it is enabled if remote ip found" do
- refute RateLimiter.disabled?(Conn.assign(build_conn(), :remote_ip_found, true))
- end
-
- test "it is enabled if remote_ip_found flag doesn't exist" do
- refute RateLimiter.disabled?(build_conn())
- end
-
- test "it restricts based on config values" do
- limiter_name = :test_plug_opts
- scale = 80
- limit = 5
-
- Config.put([Pleroma.Web.Endpoint, :http, :ip], {8, 8, 8, 8})
- Config.put([:rate_limit, limiter_name], {scale, limit})
-
- plug_opts = RateLimiter.init(name: limiter_name)
- conn = build_conn(:get, "/")
-
- for i <- 1..5 do
- conn = RateLimiter.call(conn, plug_opts)
- assert {^i, _} = RateLimiter.inspect_bucket(conn, limiter_name, plug_opts)
- Process.sleep(10)
- end
-
- conn = RateLimiter.call(conn, plug_opts)
- assert %{"error" => "Throttled"} = ConnTest.json_response(conn, :too_many_requests)
- assert conn.halted
-
- Process.sleep(50)
-
- conn = build_conn(:get, "/")
-
- conn = RateLimiter.call(conn, plug_opts)
- assert {1, 4} = RateLimiter.inspect_bucket(conn, limiter_name, plug_opts)
-
- refute conn.status == Conn.Status.code(:too_many_requests)
- refute conn.resp_body
- refute conn.halted
- end
-
- describe "options" do
- test "`bucket_name` option overrides default bucket name" do
- limiter_name = :test_bucket_name
-
- Config.put([:rate_limit, limiter_name], {1000, 5})
- Config.put([Pleroma.Web.Endpoint, :http, :ip], {8, 8, 8, 8})
-
- base_bucket_name = "#{limiter_name}:group1"
- plug_opts = RateLimiter.init(name: limiter_name, bucket_name: base_bucket_name)
-
- conn = build_conn(:get, "/")
-
- RateLimiter.call(conn, plug_opts)
- assert {1, 4} = RateLimiter.inspect_bucket(conn, base_bucket_name, plug_opts)
- assert {:error, :not_found} = RateLimiter.inspect_bucket(conn, limiter_name, plug_opts)
- end
-
- test "`params` option allows different queries to be tracked independently" do
- limiter_name = :test_params
- Config.put([:rate_limit, limiter_name], {1000, 5})
- Config.put([Pleroma.Web.Endpoint, :http, :ip], {8, 8, 8, 8})
-
- plug_opts = RateLimiter.init(name: limiter_name, params: ["id"])
-
- conn = build_conn(:get, "/?id=1")
- conn = Conn.fetch_query_params(conn)
- conn_2 = build_conn(:get, "/?id=2")
-
- RateLimiter.call(conn, plug_opts)
- assert {1, 4} = RateLimiter.inspect_bucket(conn, limiter_name, plug_opts)
- assert {0, 5} = RateLimiter.inspect_bucket(conn_2, limiter_name, plug_opts)
- end
-
- test "it supports combination of options modifying bucket name" do
- limiter_name = :test_options_combo
- Config.put([:rate_limit, limiter_name], {1000, 5})
- Config.put([Pleroma.Web.Endpoint, :http, :ip], {8, 8, 8, 8})
-
- base_bucket_name = "#{limiter_name}:group1"
-
- plug_opts =
- RateLimiter.init(name: limiter_name, bucket_name: base_bucket_name, params: ["id"])
-
- id = "100"
-
- conn = build_conn(:get, "/?id=#{id}")
- conn = Conn.fetch_query_params(conn)
- conn_2 = build_conn(:get, "/?id=#{101}")
-
- RateLimiter.call(conn, plug_opts)
- assert {1, 4} = RateLimiter.inspect_bucket(conn, base_bucket_name, plug_opts)
- assert {0, 5} = RateLimiter.inspect_bucket(conn_2, base_bucket_name, plug_opts)
- end
- end
-
- describe "unauthenticated users" do
- test "are restricted based on remote IP" do
- limiter_name = :test_unauthenticated
- Config.put([:rate_limit, limiter_name], [{1000, 5}, {1, 10}])
- Config.put([Pleroma.Web.Endpoint, :http, :ip], {8, 8, 8, 8})
-
- plug_opts = RateLimiter.init(name: limiter_name)
-
- conn = %{build_conn(:get, "/") | remote_ip: {127, 0, 0, 2}}
- conn_2 = %{build_conn(:get, "/") | remote_ip: {127, 0, 0, 3}}
-
- for i <- 1..5 do
- conn = RateLimiter.call(conn, plug_opts)
- assert {^i, _} = RateLimiter.inspect_bucket(conn, limiter_name, plug_opts)
- refute conn.halted
- end
-
- conn = RateLimiter.call(conn, plug_opts)
-
- assert %{"error" => "Throttled"} = ConnTest.json_response(conn, :too_many_requests)
- assert conn.halted
-
- conn_2 = RateLimiter.call(conn_2, plug_opts)
- assert {1, 4} = RateLimiter.inspect_bucket(conn_2, limiter_name, plug_opts)
-
- refute conn_2.status == Conn.Status.code(:too_many_requests)
- refute conn_2.resp_body
- refute conn_2.halted
- end
- end
-
- describe "authenticated users" do
- setup do
- Ecto.Adapters.SQL.Sandbox.checkout(Pleroma.Repo)
-
- :ok
- end
-
- test "can have limits separate from unauthenticated connections" do
- limiter_name = :test_authenticated1
-
- scale = 50
- limit = 5
- Config.put([Pleroma.Web.Endpoint, :http, :ip], {8, 8, 8, 8})
- Config.put([:rate_limit, limiter_name], [{1000, 1}, {scale, limit}])
-
- plug_opts = RateLimiter.init(name: limiter_name)
-
- user = insert(:user)
- conn = build_conn(:get, "/") |> assign(:user, user)
-
- for i <- 1..5 do
- conn = RateLimiter.call(conn, plug_opts)
- assert {^i, _} = RateLimiter.inspect_bucket(conn, limiter_name, plug_opts)
- refute conn.halted
- end
-
- conn = RateLimiter.call(conn, plug_opts)
-
- assert %{"error" => "Throttled"} = ConnTest.json_response(conn, :too_many_requests)
- assert conn.halted
- end
-
- test "different users are counted independently" do
- limiter_name = :test_authenticated2
- Config.put([:rate_limit, limiter_name], [{1, 10}, {1000, 5}])
- Config.put([Pleroma.Web.Endpoint, :http, :ip], {8, 8, 8, 8})
-
- plug_opts = RateLimiter.init(name: limiter_name)
-
- user = insert(:user)
- conn = build_conn(:get, "/") |> assign(:user, user)
-
- user_2 = insert(:user)
- conn_2 = build_conn(:get, "/") |> assign(:user, user_2)
-
- for i <- 1..5 do
- conn = RateLimiter.call(conn, plug_opts)
- assert {^i, _} = RateLimiter.inspect_bucket(conn, limiter_name, plug_opts)
- end
-
- conn = RateLimiter.call(conn, plug_opts)
- assert %{"error" => "Throttled"} = ConnTest.json_response(conn, :too_many_requests)
- assert conn.halted
-
- conn_2 = RateLimiter.call(conn_2, plug_opts)
- assert {1, 4} = RateLimiter.inspect_bucket(conn_2, limiter_name, plug_opts)
- refute conn_2.status == Conn.Status.code(:too_many_requests)
- refute conn_2.resp_body
- refute conn_2.halted
- end
- end
-
- test "doesn't crash due to a race condition when multiple requests are made at the same time and the bucket is not yet initialized" do
- limiter_name = :test_race_condition
- Pleroma.Config.put([:rate_limit, limiter_name], {1000, 5})
- Pleroma.Config.put([Pleroma.Web.Endpoint, :http, :ip], {8, 8, 8, 8})
-
- opts = RateLimiter.init(name: limiter_name)
-
- conn = build_conn(:get, "/")
- conn_2 = build_conn(:get, "/")
-
- %Task{pid: pid1} =
- task1 =
- Task.async(fn ->
- receive do
- :process2_up ->
- RateLimiter.call(conn, opts)
- end
- end)
-
- task2 =
- Task.async(fn ->
- send(pid1, :process2_up)
- RateLimiter.call(conn_2, opts)
- end)
-
- Task.await(task1)
- Task.await(task2)
-
- refute {:err, :not_found} == RateLimiter.inspect_bucket(conn, limiter_name, opts)
- end
-end
diff --git a/test/plugs/remote_ip_test.exs b/test/plugs/remote_ip_test.exs
@@ -1,108 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Plugs.RemoteIpTest do
- use ExUnit.Case
- use Plug.Test
-
- alias Pleroma.Plugs.RemoteIp
-
- import Pleroma.Tests.Helpers, only: [clear_config: 2]
-
- setup do:
- clear_config(RemoteIp,
- enabled: true,
- headers: ["x-forwarded-for"],
- proxies: [],
- reserved: [
- "127.0.0.0/8",
- "::1/128",
- "fc00::/7",
- "10.0.0.0/8",
- "172.16.0.0/12",
- "192.168.0.0/16"
- ]
- )
-
- test "disabled" do
- Pleroma.Config.put(RemoteIp, enabled: false)
-
- %{remote_ip: remote_ip} = conn(:get, "/")
-
- conn =
- conn(:get, "/")
- |> put_req_header("x-forwarded-for", "1.1.1.1")
- |> RemoteIp.call(nil)
-
- assert conn.remote_ip == remote_ip
- end
-
- test "enabled" do
- conn =
- conn(:get, "/")
- |> put_req_header("x-forwarded-for", "1.1.1.1")
- |> RemoteIp.call(nil)
-
- assert conn.remote_ip == {1, 1, 1, 1}
- end
-
- test "custom headers" do
- Pleroma.Config.put(RemoteIp, enabled: true, headers: ["cf-connecting-ip"])
-
- conn =
- conn(:get, "/")
- |> put_req_header("x-forwarded-for", "1.1.1.1")
- |> RemoteIp.call(nil)
-
- refute conn.remote_ip == {1, 1, 1, 1}
-
- conn =
- conn(:get, "/")
- |> put_req_header("cf-connecting-ip", "1.1.1.1")
- |> RemoteIp.call(nil)
-
- assert conn.remote_ip == {1, 1, 1, 1}
- end
-
- test "custom proxies" do
- conn =
- conn(:get, "/")
- |> put_req_header("x-forwarded-for", "173.245.48.1, 1.1.1.1, 173.245.48.2")
- |> RemoteIp.call(nil)
-
- refute conn.remote_ip == {1, 1, 1, 1}
-
- Pleroma.Config.put([RemoteIp, :proxies], ["173.245.48.0/20"])
-
- conn =
- conn(:get, "/")
- |> put_req_header("x-forwarded-for", "173.245.48.1, 1.1.1.1, 173.245.48.2")
- |> RemoteIp.call(nil)
-
- assert conn.remote_ip == {1, 1, 1, 1}
- end
-
- test "proxies set without CIDR format" do
- Pleroma.Config.put([RemoteIp, :proxies], ["173.245.48.1"])
-
- conn =
- conn(:get, "/")
- |> put_req_header("x-forwarded-for", "173.245.48.1, 1.1.1.1")
- |> RemoteIp.call(nil)
-
- assert conn.remote_ip == {1, 1, 1, 1}
- end
-
- test "proxies set `nonsensical` CIDR" do
- Pleroma.Config.put([RemoteIp, :reserved], ["127.0.0.0/8"])
- Pleroma.Config.put([RemoteIp, :proxies], ["10.0.0.3/24"])
-
- conn =
- conn(:get, "/")
- |> put_req_header("x-forwarded-for", "10.0.0.3, 1.1.1.1")
- |> RemoteIp.call(nil)
-
- assert conn.remote_ip == {1, 1, 1, 1}
- end
-end
diff --git a/test/plugs/session_authentication_plug_test.exs b/test/plugs/session_authentication_plug_test.exs
@@ -1,63 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Plugs.SessionAuthenticationPlugTest do
- use Pleroma.Web.ConnCase, async: true
-
- alias Pleroma.Plugs.SessionAuthenticationPlug
- alias Pleroma.User
-
- setup %{conn: conn} do
- session_opts = [
- store: :cookie,
- key: "_test",
- signing_salt: "cooldude"
- ]
-
- conn =
- conn
- |> Plug.Session.call(Plug.Session.init(session_opts))
- |> fetch_session
- |> assign(:auth_user, %User{id: 1})
-
- %{conn: conn}
- end
-
- test "it does nothing if a user is assigned", %{conn: conn} do
- conn =
- conn
- |> assign(:user, %User{})
-
- ret_conn =
- conn
- |> SessionAuthenticationPlug.call(%{})
-
- assert ret_conn == conn
- end
-
- test "if the auth_user has the same id as the user_id in the session, it assigns the user", %{
- conn: conn
- } do
- conn =
- conn
- |> put_session(:user_id, conn.assigns.auth_user.id)
- |> SessionAuthenticationPlug.call(%{})
-
- assert conn.assigns.user == conn.assigns.auth_user
- end
-
- test "if the auth_user has a different id as the user_id in the session, it does nothing", %{
- conn: conn
- } do
- conn =
- conn
- |> put_session(:user_id, -1)
-
- ret_conn =
- conn
- |> SessionAuthenticationPlug.call(%{})
-
- assert ret_conn == conn
- end
-end
diff --git a/test/plugs/set_format_plug_test.exs b/test/plugs/set_format_plug_test.exs
@@ -1,38 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Plugs.SetFormatPlugTest do
- use ExUnit.Case, async: true
- use Plug.Test
-
- alias Pleroma.Plugs.SetFormatPlug
-
- test "set format from params" do
- conn =
- :get
- |> conn("/cofe?_format=json")
- |> SetFormatPlug.call([])
-
- assert %{format: "json"} == conn.assigns
- end
-
- test "set format from header" do
- conn =
- :get
- |> conn("/cofe")
- |> put_private(:phoenix_format, "xml")
- |> SetFormatPlug.call([])
-
- assert %{format: "xml"} == conn.assigns
- end
-
- test "doesn't set format" do
- conn =
- :get
- |> conn("/cofe")
- |> SetFormatPlug.call([])
-
- refute conn.assigns[:format]
- end
-end
diff --git a/test/plugs/set_locale_plug_test.exs b/test/plugs/set_locale_plug_test.exs
@@ -1,46 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Plugs.SetLocalePlugTest do
- use ExUnit.Case, async: true
- use Plug.Test
-
- alias Pleroma.Plugs.SetLocalePlug
- alias Plug.Conn
-
- test "default locale is `en`" do
- conn =
- :get
- |> conn("/cofe")
- |> SetLocalePlug.call([])
-
- assert "en" == Gettext.get_locale()
- assert %{locale: "en"} == conn.assigns
- end
-
- test "use supported locale from `accept-language`" do
- conn =
- :get
- |> conn("/cofe")
- |> Conn.put_req_header(
- "accept-language",
- "ru, fr-CH, fr;q=0.9, en;q=0.8, *;q=0.5"
- )
- |> SetLocalePlug.call([])
-
- assert "ru" == Gettext.get_locale()
- assert %{locale: "ru"} == conn.assigns
- end
-
- test "use default locale if locale from `accept-language` is not supported" do
- conn =
- :get
- |> conn("/cofe")
- |> Conn.put_req_header("accept-language", "tlh")
- |> SetLocalePlug.call([])
-
- assert "en" == Gettext.get_locale()
- assert %{locale: "en"} == conn.assigns
- end
-end
diff --git a/test/plugs/set_user_session_id_plug_test.exs b/test/plugs/set_user_session_id_plug_test.exs
@@ -1,45 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Plugs.SetUserSessionIdPlugTest do
- use Pleroma.Web.ConnCase, async: true
-
- alias Pleroma.Plugs.SetUserSessionIdPlug
- alias Pleroma.User
-
- setup %{conn: conn} do
- session_opts = [
- store: :cookie,
- key: "_test",
- signing_salt: "cooldude"
- ]
-
- conn =
- conn
- |> Plug.Session.call(Plug.Session.init(session_opts))
- |> fetch_session
-
- %{conn: conn}
- end
-
- test "doesn't do anything if the user isn't set", %{conn: conn} do
- ret_conn =
- conn
- |> SetUserSessionIdPlug.call(%{})
-
- assert ret_conn == conn
- end
-
- test "sets the user_id in the session to the user id of the user assign", %{conn: conn} do
- Code.ensure_compiled(Pleroma.User)
-
- conn =
- conn
- |> assign(:user, %User{id: 1})
- |> SetUserSessionIdPlug.call(%{})
-
- id = get_session(conn, :user_id)
- assert id == 1
- end
-end
diff --git a/test/plugs/uploaded_media_plug_test.exs b/test/plugs/uploaded_media_plug_test.exs
@@ -1,43 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Web.UploadedMediaPlugTest do
- use Pleroma.Web.ConnCase
- alias Pleroma.Upload
-
- defp upload_file(context) do
- Pleroma.DataCase.ensure_local_uploader(context)
- File.cp!("test/fixtures/image.jpg", "test/fixtures/image_tmp.jpg")
-
- file = %Plug.Upload{
- content_type: "image/jpg",
- path: Path.absname("test/fixtures/image_tmp.jpg"),
- filename: "nice_tf.jpg"
- }
-
- {:ok, data} = Upload.store(file)
- [%{"href" => attachment_url} | _] = data["url"]
- [attachment_url: attachment_url]
- end
-
- setup_all :upload_file
-
- test "does not send Content-Disposition header when name param is not set", %{
- attachment_url: attachment_url
- } do
- conn = get(build_conn(), attachment_url)
- refute Enum.any?(conn.resp_headers, &(elem(&1, 0) == "content-disposition"))
- end
-
- test "sends Content-Disposition header when name param is set", %{
- attachment_url: attachment_url
- } do
- conn = get(build_conn(), attachment_url <> "?name=\"cofe\".gif")
-
- assert Enum.any?(
- conn.resp_headers,
- &(&1 == {"content-disposition", "filename=\"\\\"cofe\\\".gif\""})
- )
- end
-end
diff --git a/test/plugs/user_enabled_plug_test.exs b/test/plugs/user_enabled_plug_test.exs
@@ -1,59 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Plugs.UserEnabledPlugTest do
- use Pleroma.Web.ConnCase, async: true
-
- alias Pleroma.Plugs.UserEnabledPlug
- import Pleroma.Factory
-
- setup do: clear_config([:instance, :account_activation_required])
-
- test "doesn't do anything if the user isn't set", %{conn: conn} do
- ret_conn =
- conn
- |> UserEnabledPlug.call(%{})
-
- assert ret_conn == conn
- end
-
- test "with a user that's not confirmed and a config requiring confirmation, it removes that user",
- %{conn: conn} do
- Pleroma.Config.put([:instance, :account_activation_required], true)
-
- user = insert(:user, confirmation_pending: true)
-
- conn =
- conn
- |> assign(:user, user)
- |> UserEnabledPlug.call(%{})
-
- assert conn.assigns.user == nil
- end
-
- test "with a user that is deactivated, it removes that user", %{conn: conn} do
- user = insert(:user, deactivated: true)
-
- conn =
- conn
- |> assign(:user, user)
- |> UserEnabledPlug.call(%{})
-
- assert conn.assigns.user == nil
- end
-
- test "with a user that is not deactivated, it does nothing", %{conn: conn} do
- user = insert(:user)
-
- conn =
- conn
- |> assign(:user, user)
-
- ret_conn =
- conn
- |> UserEnabledPlug.call(%{})
-
- assert conn == ret_conn
- end
-end
diff --git a/test/plugs/user_fetcher_plug_test.exs b/test/plugs/user_fetcher_plug_test.exs
@@ -1,41 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Plugs.UserFetcherPlugTest do
- use Pleroma.Web.ConnCase, async: true
-
- alias Pleroma.Plugs.UserFetcherPlug
- import Pleroma.Factory
-
- setup do
- user = insert(:user)
- %{user: user}
- end
-
- test "if an auth_credentials assign is present, it tries to fetch the user and assigns it", %{
- conn: conn,
- user: user
- } do
- conn =
- conn
- |> assign(:auth_credentials, %{
- username: user.nickname,
- password: nil
- })
-
- conn =
- conn
- |> UserFetcherPlug.call(%{})
-
- assert conn.assigns[:auth_user] == user
- end
-
- test "without a credential assign it doesn't do anything", %{conn: conn} do
- ret_conn =
- conn
- |> UserFetcherPlug.call(%{})
-
- assert conn == ret_conn
- end
-end
diff --git a/test/plugs/user_is_admin_plug_test.exs b/test/plugs/user_is_admin_plug_test.exs
@@ -1,37 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Plugs.UserIsAdminPlugTest do
- use Pleroma.Web.ConnCase, async: true
-
- alias Pleroma.Plugs.UserIsAdminPlug
- import Pleroma.Factory
-
- test "accepts a user that is an admin" do
- user = insert(:user, is_admin: true)
-
- conn = assign(build_conn(), :user, user)
-
- ret_conn = UserIsAdminPlug.call(conn, %{})
-
- assert conn == ret_conn
- end
-
- test "denies a user that isn't an admin" do
- user = insert(:user)
-
- conn =
- build_conn()
- |> assign(:user, user)
- |> UserIsAdminPlug.call(%{})
-
- assert conn.status == 403
- end
-
- test "denies when a user isn't set" do
- conn = UserIsAdminPlug.call(build_conn(), %{})
-
- assert conn.status == 403
- end
-end
diff --git a/test/runtime_test.exs b/test/runtime_test.exs
@@ -1,11 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.RuntimeTest do
- use ExUnit.Case, async: true
-
- test "it loads custom runtime modules" do
- assert {:module, RuntimeModule} == Code.ensure_compiled(RuntimeModule)
- end
-end
diff --git a/test/support/captcha_mock.ex b/test/support/captcha/mock.ex
diff --git a/test/web/activity_pub/object_validators/types/date_time_test.exs b/test/web/activity_pub/object_validators/types/date_time_test.exs
@@ -1,36 +0,0 @@
-# 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.Types.DateTimeTest do
- alias Pleroma.EctoType.ActivityPub.ObjectValidators.DateTime
- use Pleroma.DataCase
-
- test "it validates an xsd:Datetime" do
- valid_strings = [
- "2004-04-12T13:20:00",
- "2004-04-12T13:20:15.5",
- "2004-04-12T13:20:00-05:00",
- "2004-04-12T13:20:00Z"
- ]
-
- invalid_strings = [
- "2004-04-12T13:00",
- "2004-04-1213:20:00",
- "99-04-12T13:00",
- "2004-04-12"
- ]
-
- assert {:ok, "2004-04-01T12:00:00Z"} == DateTime.cast("2004-04-01T12:00:00Z")
-
- Enum.each(valid_strings, fn date_time ->
- result = DateTime.cast(date_time)
- assert {:ok, _} = result
- end)
-
- Enum.each(invalid_strings, fn date_time ->
- result = DateTime.cast(date_time)
- assert :error == result
- end)
- end
-end
diff --git a/test/web/activity_pub/object_validators/types/object_id_test.exs b/test/web/activity_pub/object_validators/types/object_id_test.exs
@@ -1,41 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Web.ObjectValidators.Types.ObjectIDTest do
- alias Pleroma.EctoType.ActivityPub.ObjectValidators.ObjectID
- use Pleroma.DataCase
-
- @uris [
- "http://lain.com/users/lain",
- "http://lain.com",
- "https://lain.com/object/1"
- ]
-
- @non_uris [
- "https://",
- "rin",
- 1,
- :x,
- %{"1" => 2}
- ]
-
- test "it accepts http uris" do
- Enum.each(@uris, fn uri ->
- assert {:ok, uri} == ObjectID.cast(uri)
- end)
- end
-
- test "it accepts an object with a nested uri id" do
- Enum.each(@uris, fn uri ->
- assert {:ok, uri} == ObjectID.cast(%{"id" => uri})
- end)
- end
-
- test "it rejects non-uri strings" do
- Enum.each(@non_uris, fn non_uri ->
- assert :error == ObjectID.cast(non_uri)
- assert :error == ObjectID.cast(%{"id" => non_uri})
- end)
- end
-end
diff --git a/test/web/activity_pub/object_validators/types/recipients_test.exs b/test/web/activity_pub/object_validators/types/recipients_test.exs
@@ -1,31 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Web.ObjectValidators.Types.RecipientsTest do
- alias Pleroma.EctoType.ActivityPub.ObjectValidators.Recipients
- use Pleroma.DataCase
-
- test "it asserts that all elements of the list are object ids" do
- list = ["https://lain.com/users/lain", "invalid"]
-
- assert :error == Recipients.cast(list)
- end
-
- test "it works with a list" do
- list = ["https://lain.com/users/lain"]
- assert {:ok, list} == Recipients.cast(list)
- end
-
- test "it works with a list with whole objects" do
- list = ["https://lain.com/users/lain", %{"id" => "https://gensokyo.2hu/users/raymoo"}]
- resulting_list = ["https://gensokyo.2hu/users/raymoo", "https://lain.com/users/lain"]
- assert {:ok, resulting_list} == Recipients.cast(list)
- end
-
- test "it turns a single string into a list" do
- recipient = "https://lain.com/users/lain"
-
- assert {:ok, [recipient]} == Recipients.cast(recipient)
- end
-end
diff --git a/test/web/activity_pub/object_validators/types/safe_text_test.exs b/test/web/activity_pub/object_validators/types/safe_text_test.exs
@@ -1,30 +0,0 @@
-# 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.Types.SafeTextTest do
- use Pleroma.DataCase
-
- alias Pleroma.EctoType.ActivityPub.ObjectValidators.SafeText
-
- test "it lets normal text go through" do
- text = "hey how are you"
- assert {:ok, text} == SafeText.cast(text)
- end
-
- test "it removes html tags from text" do
- text = "hey look xss <script>alert('foo')</script>"
- assert {:ok, "hey look xss alert('foo')"} == SafeText.cast(text)
- end
-
- test "it keeps basic html tags" do
- text = "hey <a href='http://gensokyo.2hu'>look</a> xss <script>alert('foo')</script>"
-
- assert {:ok, "hey <a href=\"http://gensokyo.2hu\">look</a> xss alert('foo')"} ==
- SafeText.cast(text)
- end
-
- test "errors for non-text" do
- assert :error == SafeText.cast(1)
- end
-end
diff --git a/test/web/auth/auth_test_controller_test.exs b/test/web/auth/auth_test_controller_test.exs
@@ -1,242 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Tests.AuthTestControllerTest do
- use Pleroma.Web.ConnCase
-
- import Pleroma.Factory
-
- describe "do_oauth_check" do
- test "serves with proper OAuth token (fulfilling requested scopes)" do
- %{conn: good_token_conn, user: user} = oauth_access(["read"])
-
- assert %{"user_id" => user.id} ==
- good_token_conn
- |> get("/test/authenticated_api/do_oauth_check")
- |> json_response(200)
-
- # Unintended usage (:api) — use with :authenticated_api instead
- assert %{"user_id" => user.id} ==
- good_token_conn
- |> get("/test/api/do_oauth_check")
- |> json_response(200)
- end
-
- test "fails on no token / missing scope(s)" do
- %{conn: bad_token_conn} = oauth_access(["irrelevant_scope"])
-
- bad_token_conn
- |> get("/test/authenticated_api/do_oauth_check")
- |> json_response(403)
-
- bad_token_conn
- |> assign(:token, nil)
- |> get("/test/api/do_oauth_check")
- |> json_response(403)
- end
- end
-
- describe "fallback_oauth_check" do
- test "serves with proper OAuth token (fulfilling requested scopes)" do
- %{conn: good_token_conn, user: user} = oauth_access(["read"])
-
- assert %{"user_id" => user.id} ==
- good_token_conn
- |> get("/test/api/fallback_oauth_check")
- |> json_response(200)
-
- # Unintended usage (:authenticated_api) — use with :api instead
- assert %{"user_id" => user.id} ==
- good_token_conn
- |> get("/test/authenticated_api/fallback_oauth_check")
- |> json_response(200)
- end
-
- test "for :api on public instance, drops :user and renders on no token / missing scope(s)" do
- clear_config([:instance, :public], true)
-
- %{conn: bad_token_conn} = oauth_access(["irrelevant_scope"])
-
- assert %{"user_id" => nil} ==
- bad_token_conn
- |> get("/test/api/fallback_oauth_check")
- |> json_response(200)
-
- assert %{"user_id" => nil} ==
- bad_token_conn
- |> assign(:token, nil)
- |> get("/test/api/fallback_oauth_check")
- |> json_response(200)
- end
-
- test "for :api on private instance, fails on no token / missing scope(s)" do
- clear_config([:instance, :public], false)
-
- %{conn: bad_token_conn} = oauth_access(["irrelevant_scope"])
-
- bad_token_conn
- |> get("/test/api/fallback_oauth_check")
- |> json_response(403)
-
- bad_token_conn
- |> assign(:token, nil)
- |> get("/test/api/fallback_oauth_check")
- |> json_response(403)
- end
- end
-
- describe "skip_oauth_check" do
- test "for :authenticated_api, serves if :user is set (regardless of token / token scopes)" do
- user = insert(:user)
-
- assert %{"user_id" => user.id} ==
- build_conn()
- |> assign(:user, user)
- |> get("/test/authenticated_api/skip_oauth_check")
- |> json_response(200)
-
- %{conn: bad_token_conn, user: user} = oauth_access(["irrelevant_scope"])
-
- assert %{"user_id" => user.id} ==
- bad_token_conn
- |> get("/test/authenticated_api/skip_oauth_check")
- |> json_response(200)
- end
-
- test "serves via :api on public instance if :user is not set" do
- clear_config([:instance, :public], true)
-
- assert %{"user_id" => nil} ==
- build_conn()
- |> get("/test/api/skip_oauth_check")
- |> json_response(200)
-
- build_conn()
- |> get("/test/authenticated_api/skip_oauth_check")
- |> json_response(403)
- end
-
- test "fails on private instance if :user is not set" do
- clear_config([:instance, :public], false)
-
- build_conn()
- |> get("/test/api/skip_oauth_check")
- |> json_response(403)
-
- build_conn()
- |> get("/test/authenticated_api/skip_oauth_check")
- |> json_response(403)
- end
- end
-
- describe "fallback_oauth_skip_publicity_check" do
- test "serves with proper OAuth token (fulfilling requested scopes)" do
- %{conn: good_token_conn, user: user} = oauth_access(["read"])
-
- assert %{"user_id" => user.id} ==
- good_token_conn
- |> get("/test/api/fallback_oauth_skip_publicity_check")
- |> json_response(200)
-
- # Unintended usage (:authenticated_api)
- assert %{"user_id" => user.id} ==
- good_token_conn
- |> get("/test/authenticated_api/fallback_oauth_skip_publicity_check")
- |> json_response(200)
- end
-
- test "for :api on private / public instance, drops :user and renders on token issue" do
- %{conn: bad_token_conn} = oauth_access(["irrelevant_scope"])
-
- for is_public <- [true, false] do
- clear_config([:instance, :public], is_public)
-
- assert %{"user_id" => nil} ==
- bad_token_conn
- |> get("/test/api/fallback_oauth_skip_publicity_check")
- |> json_response(200)
-
- assert %{"user_id" => nil} ==
- bad_token_conn
- |> assign(:token, nil)
- |> get("/test/api/fallback_oauth_skip_publicity_check")
- |> json_response(200)
- end
- end
- end
-
- describe "skip_oauth_skip_publicity_check" do
- test "for :authenticated_api, serves if :user is set (regardless of token / token scopes)" do
- user = insert(:user)
-
- assert %{"user_id" => user.id} ==
- build_conn()
- |> assign(:user, user)
- |> get("/test/authenticated_api/skip_oauth_skip_publicity_check")
- |> json_response(200)
-
- %{conn: bad_token_conn, user: user} = oauth_access(["irrelevant_scope"])
-
- assert %{"user_id" => user.id} ==
- bad_token_conn
- |> get("/test/authenticated_api/skip_oauth_skip_publicity_check")
- |> json_response(200)
- end
-
- test "for :api, serves on private and public instances regardless of whether :user is set" do
- user = insert(:user)
-
- for is_public <- [true, false] do
- clear_config([:instance, :public], is_public)
-
- assert %{"user_id" => nil} ==
- build_conn()
- |> get("/test/api/skip_oauth_skip_publicity_check")
- |> json_response(200)
-
- assert %{"user_id" => user.id} ==
- build_conn()
- |> assign(:user, user)
- |> get("/test/api/skip_oauth_skip_publicity_check")
- |> json_response(200)
- end
- end
- end
-
- describe "missing_oauth_check_definition" do
- def test_missing_oauth_check_definition_failure(endpoint, expected_error) do
- %{conn: conn} = oauth_access(["read", "write", "follow", "push", "admin"])
-
- assert %{"error" => expected_error} ==
- conn
- |> get(endpoint)
- |> json_response(403)
- end
-
- test "fails if served via :authenticated_api" do
- test_missing_oauth_check_definition_failure(
- "/test/authenticated_api/missing_oauth_check_definition",
- "Security violation: OAuth scopes check was neither handled nor explicitly skipped."
- )
- end
-
- test "fails if served via :api and the instance is private" do
- clear_config([:instance, :public], false)
-
- test_missing_oauth_check_definition_failure(
- "/test/api/missing_oauth_check_definition",
- "This resource requires authentication."
- )
- end
-
- test "succeeds with dropped :user if served via :api on public instance" do
- %{conn: conn} = oauth_access(["read", "write", "follow", "push", "admin"])
-
- assert %{"user_id" => nil} ==
- conn
- |> get("/test/api/missing_oauth_check_definition")
- |> json_response(200)
- end
- end
-end
diff --git a/test/web/masto_fe_controller_test.exs b/test/web/masto_fe_controller_test.exs
@@ -1,85 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Web.MastodonAPI.MastoFEController do
- use Pleroma.Web.ConnCase
-
- alias Pleroma.Config
- alias Pleroma.User
-
- import Pleroma.Factory
-
- setup do: clear_config([:instance, :public])
-
- test "put settings", %{conn: conn} do
- user = insert(:user)
-
- conn =
- conn
- |> assign(:user, user)
- |> assign(:token, insert(:oauth_token, user: user, scopes: ["write:accounts"]))
- |> put("/api/web/settings", %{"data" => %{"programming" => "socks"}})
-
- assert _result = json_response(conn, 200)
-
- user = User.get_cached_by_ap_id(user.ap_id)
- assert user.mastofe_settings == %{"programming" => "socks"}
- end
-
- describe "index/2 redirections" do
- setup %{conn: conn} do
- session_opts = [
- store: :cookie,
- key: "_test",
- signing_salt: "cooldude"
- ]
-
- conn =
- conn
- |> Plug.Session.call(Plug.Session.init(session_opts))
- |> fetch_session()
-
- test_path = "/web/statuses/test"
- %{conn: conn, path: test_path}
- end
-
- test "redirects not logged-in users to the login page", %{conn: conn, path: path} do
- conn = get(conn, path)
-
- assert conn.status == 302
- assert redirected_to(conn) == "/web/login"
- end
-
- test "redirects not logged-in users to the login page on private instances", %{
- conn: conn,
- path: path
- } do
- Config.put([:instance, :public], false)
-
- conn = get(conn, path)
-
- assert conn.status == 302
- assert redirected_to(conn) == "/web/login"
- end
-
- test "does not redirect logged in users to the login page", %{conn: conn, path: path} do
- token = insert(:oauth_token, scopes: ["read"])
-
- conn =
- conn
- |> assign(:user, token.user)
- |> assign(:token, token)
- |> get(path)
-
- assert conn.status == 200
- end
-
- test "saves referer path to session", %{conn: conn, path: path} do
- conn = get(conn, path)
- return_to = Plug.Conn.get_session(conn, :return_to)
-
- assert return_to == path
- end
- end
-end
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
@@ -1,529 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Web.MastodonAPI.MastodonAPIController.UpdateCredentialsTest do
- alias Pleroma.Repo
- alias Pleroma.User
-
- use Pleroma.Web.ConnCase
-
- import Mock
- import Pleroma.Factory
-
- setup do: clear_config([:instance, :max_account_fields])
-
- describe "updating credentials" do
- setup do: oauth_access(["write:accounts"])
- setup :request_content_type
-
- test "sets user settings in a generic way", %{conn: conn} do
- res_conn =
- patch(conn, "/api/v1/accounts/update_credentials", %{
- "pleroma_settings_store" => %{
- pleroma_fe: %{
- theme: "bla"
- }
- }
- })
-
- assert user_data = json_response_and_validate_schema(res_conn, 200)
- assert user_data["pleroma"]["settings_store"] == %{"pleroma_fe" => %{"theme" => "bla"}}
-
- user = Repo.get(User, user_data["id"])
-
- res_conn =
- conn
- |> assign(:user, user)
- |> patch("/api/v1/accounts/update_credentials", %{
- "pleroma_settings_store" => %{
- masto_fe: %{
- theme: "bla"
- }
- }
- })
-
- assert user_data = json_response_and_validate_schema(res_conn, 200)
-
- assert user_data["pleroma"]["settings_store"] ==
- %{
- "pleroma_fe" => %{"theme" => "bla"},
- "masto_fe" => %{"theme" => "bla"}
- }
-
- user = Repo.get(User, user_data["id"])
-
- clear_config([:instance, :federating], true)
-
- with_mock Pleroma.Web.Federator,
- publish: fn _activity -> :ok end do
- res_conn =
- conn
- |> assign(:user, user)
- |> patch("/api/v1/accounts/update_credentials", %{
- "pleroma_settings_store" => %{
- masto_fe: %{
- theme: "blub"
- }
- }
- })
-
- assert user_data = json_response_and_validate_schema(res_conn, 200)
-
- assert user_data["pleroma"]["settings_store"] ==
- %{
- "pleroma_fe" => %{"theme" => "bla"},
- "masto_fe" => %{"theme" => "blub"}
- }
-
- assert_called(Pleroma.Web.Federator.publish(:_))
- end
- end
-
- test "updates the user's bio", %{conn: conn} do
- user2 = insert(:user)
-
- raw_bio = "I drink #cofe with @#{user2.nickname}\n\nsuya.."
-
- conn = patch(conn, "/api/v1/accounts/update_credentials", %{"note" => raw_bio})
-
- assert user_data = json_response_and_validate_schema(conn, 200)
-
- assert user_data["note"] ==
- ~s(I drink <a class="hashtag" data-tag="cofe" href="http://localhost:4001/tag/cofe">#cofe</a> with <span class="h-card"><a class="u-url mention" data-user="#{
- user2.id
- }" href="#{user2.ap_id}" rel="ugc">@<span>#{user2.nickname}</span></a></span><br/><br/>suya..)
-
- assert user_data["source"]["note"] == raw_bio
-
- user = Repo.get(User, user_data["id"])
-
- assert user.raw_bio == raw_bio
- end
-
- test "updates the user's locking status", %{conn: conn} do
- conn = patch(conn, "/api/v1/accounts/update_credentials", %{locked: "true"})
-
- assert user_data = json_response_and_validate_schema(conn, 200)
- 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
-
- conn = patch(conn, "/api/v1/accounts/update_credentials", %{allow_following_move: "false"})
-
- assert refresh_record(user).allow_following_move == false
- assert user_data = json_response_and_validate_schema(conn, 200)
- assert user_data["pleroma"]["allow_following_move"] == false
- end
-
- test "updates the user's default scope", %{conn: conn} do
- conn = patch(conn, "/api/v1/accounts/update_credentials", %{default_scope: "unlisted"})
-
- assert user_data = json_response_and_validate_schema(conn, 200)
- assert user_data["source"]["privacy"] == "unlisted"
- end
-
- test "updates the user's privacy", %{conn: conn} do
- conn = patch(conn, "/api/v1/accounts/update_credentials", %{source: %{privacy: "unlisted"}})
-
- assert user_data = json_response_and_validate_schema(conn, 200)
- assert user_data["source"]["privacy"] == "unlisted"
- end
-
- test "updates the user's hide_followers status", %{conn: conn} do
- conn = patch(conn, "/api/v1/accounts/update_credentials", %{hide_followers: "true"})
-
- assert user_data = json_response_and_validate_schema(conn, 200)
- assert user_data["pleroma"]["hide_followers"] == true
- end
-
- test "updates the user's discoverable status", %{conn: conn} do
- assert %{"source" => %{"pleroma" => %{"discoverable" => true}}} =
- conn
- |> patch("/api/v1/accounts/update_credentials", %{discoverable: "true"})
- |> json_response_and_validate_schema(:ok)
-
- assert %{"source" => %{"pleroma" => %{"discoverable" => false}}} =
- conn
- |> patch("/api/v1/accounts/update_credentials", %{discoverable: "false"})
- |> json_response_and_validate_schema(:ok)
- end
-
- test "updates the user's hide_followers_count and hide_follows_count", %{conn: conn} do
- conn =
- patch(conn, "/api/v1/accounts/update_credentials", %{
- hide_followers_count: "true",
- hide_follows_count: "true"
- })
-
- assert user_data = json_response_and_validate_schema(conn, 200)
- assert user_data["pleroma"]["hide_followers_count"] == true
- assert user_data["pleroma"]["hide_follows_count"] == true
- end
-
- test "updates the user's skip_thread_containment option", %{user: user, conn: conn} do
- response =
- conn
- |> patch("/api/v1/accounts/update_credentials", %{skip_thread_containment: "true"})
- |> json_response_and_validate_schema(200)
-
- assert response["pleroma"]["skip_thread_containment"] == true
- assert refresh_record(user).skip_thread_containment
- end
-
- test "updates the user's hide_follows status", %{conn: conn} do
- conn = patch(conn, "/api/v1/accounts/update_credentials", %{hide_follows: "true"})
-
- assert user_data = json_response_and_validate_schema(conn, 200)
- assert user_data["pleroma"]["hide_follows"] == true
- end
-
- test "updates the user's hide_favorites status", %{conn: conn} do
- conn = patch(conn, "/api/v1/accounts/update_credentials", %{hide_favorites: "true"})
-
- assert user_data = json_response_and_validate_schema(conn, 200)
- assert user_data["pleroma"]["hide_favorites"] == true
- end
-
- test "updates the user's show_role status", %{conn: conn} do
- conn = patch(conn, "/api/v1/accounts/update_credentials", %{show_role: "false"})
-
- assert user_data = json_response_and_validate_schema(conn, 200)
- assert user_data["source"]["pleroma"]["show_role"] == false
- end
-
- test "updates the user's no_rich_text status", %{conn: conn} do
- conn = patch(conn, "/api/v1/accounts/update_credentials", %{no_rich_text: "true"})
-
- assert user_data = json_response_and_validate_schema(conn, 200)
- assert user_data["source"]["pleroma"]["no_rich_text"] == true
- end
-
- test "updates the user's name", %{conn: conn} do
- conn =
- patch(conn, "/api/v1/accounts/update_credentials", %{"display_name" => "markorepairs"})
-
- assert user_data = json_response_and_validate_schema(conn, 200)
- assert user_data["display_name"] == "markorepairs"
-
- update_activity = Repo.one(Pleroma.Activity)
- assert update_activity.data["type"] == "Update"
- assert update_activity.data["object"]["name"] == "markorepairs"
- end
-
- test "updates the user's avatar", %{user: user, conn: conn} do
- new_avatar = %Plug.Upload{
- content_type: "image/jpg",
- path: Path.absname("test/fixtures/image.jpg"),
- filename: "an_image.jpg"
- }
-
- assert user.avatar == %{}
-
- 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
- new_header = %Plug.Upload{
- content_type: "image/jpg",
- path: Path.absname("test/fixtures/image.jpg"),
- filename: "an_image.jpg"
- }
-
- res = patch(conn, "/api/v1/accounts/update_credentials", %{"header" => new_header})
-
- 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, user: user} do
- new_header = %Plug.Upload{
- content_type: "image/jpg",
- path: Path.absname("test/fixtures/image.jpg"),
- filename: "an_image.jpg"
- }
-
- res =
- patch(conn, "/api/v1/accounts/update_credentials", %{
- "pleroma_background_image" => new_header
- })
-
- 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
- token1 = insert(:oauth_token, scopes: ["read"])
- token2 = insert(:oauth_token, scopes: ["write", "follow"])
-
- for token <- [token1, token2] do
- conn =
- build_conn()
- |> put_req_header("content-type", "multipart/form-data")
- |> put_req_header("authorization", "Bearer #{token.token}")
- |> patch("/api/v1/accounts/update_credentials", %{})
-
- if token == token1 do
- assert %{"error" => "Insufficient permissions: write:accounts."} ==
- json_response_and_validate_schema(conn, 403)
- else
- assert json_response_and_validate_schema(conn, 200)
- end
- end
- end
-
- test "updates profile emojos", %{user: user, conn: conn} do
- note = "*sips :blank:*"
- name = "I am :firefox:"
-
- ret_conn =
- patch(conn, "/api/v1/accounts/update_credentials", %{
- "note" => note,
- "display_name" => name
- })
-
- assert json_response_and_validate_schema(ret_conn, 200)
-
- conn = get(conn, "/api/v1/accounts/#{user.id}")
-
- assert user_data = json_response_and_validate_schema(conn, 200)
-
- assert user_data["note"] == note
- assert user_data["display_name"] == name
- assert [%{"shortcode" => "blank"}, %{"shortcode" => "firefox"}] = user_data["emojis"]
- end
-
- test "update fields", %{conn: conn} do
- fields = [
- %{"name" => "<a href=\"http://google.com\">foo</a>", "value" => "<script>bar</script>"},
- %{"name" => "link.io", "value" => "cofe.io"}
- ]
-
- account_data =
- conn
- |> patch("/api/v1/accounts/update_credentials", %{"fields_attributes" => fields})
- |> json_response_and_validate_schema(200)
-
- assert account_data["fields"] == [
- %{"name" => "<a href=\"http://google.com\">foo</a>", "value" => "bar"},
- %{
- "name" => "link.io",
- "value" => ~S(<a href="http://cofe.io" rel="ugc">cofe.io</a>)
- }
- ]
-
- assert account_data["source"]["fields"] == [
- %{
- "name" => "<a href=\"http://google.com\">foo</a>",
- "value" => "<script>bar</script>"
- },
- %{"name" => "link.io", "value" => "cofe.io"}
- ]
- 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 =
- [
- "fields_attributes[1][name]=link",
- "fields_attributes[1][value]=http://cofe.io",
- "fields_attributes[0][name]=foo",
- "fields_attributes[0][value]=bar"
- ]
- |> Enum.join("&")
-
- account =
- conn
- |> put_req_header("content-type", "application/x-www-form-urlencoded")
- |> patch("/api/v1/accounts/update_credentials", fields)
- |> json_response_and_validate_schema(200)
-
- assert account["fields"] == [
- %{"name" => "foo", "value" => "bar"},
- %{
- "name" => "link",
- "value" => ~S(<a href="http://cofe.io" rel="ugc">http://cofe.io</a>)
- }
- ]
-
- assert account["source"]["fields"] == [
- %{"name" => "foo", "value" => "bar"},
- %{"name" => "link", "value" => "http://cofe.io"}
- ]
- end
-
- test "update fields with empty name", %{conn: conn} do
- fields = [
- %{"name" => "foo", "value" => ""},
- %{"name" => "", "value" => "bar"}
- ]
-
- account =
- conn
- |> patch("/api/v1/accounts/update_credentials", %{"fields_attributes" => fields})
- |> json_response_and_validate_schema(200)
-
- assert account["fields"] == [
- %{"name" => "foo", "value" => ""}
- ]
- end
-
- test "update fields when invalid request", %{conn: conn} do
- name_limit = Pleroma.Config.get([:instance, :account_field_name_length])
- value_limit = Pleroma.Config.get([:instance, :account_field_value_length])
-
- long_name = Enum.map(0..name_limit, fn _ -> "x" end) |> Enum.join()
- long_value = Enum.map(0..value_limit, fn _ -> "x" end) |> Enum.join()
-
- fields = [%{"name" => "foo", "value" => long_value}]
-
- assert %{"error" => "Invalid request"} ==
- conn
- |> patch("/api/v1/accounts/update_credentials", %{"fields_attributes" => fields})
- |> json_response_and_validate_schema(403)
-
- fields = [%{"name" => long_name, "value" => "bar"}]
-
- assert %{"error" => "Invalid request"} ==
- conn
- |> patch("/api/v1/accounts/update_credentials", %{"fields_attributes" => fields})
- |> json_response_and_validate_schema(403)
-
- Pleroma.Config.put([:instance, :max_account_fields], 1)
-
- fields = [
- %{"name" => "foo", "value" => "bar"},
- %{"name" => "link", "value" => "cofe.io"}
- ]
-
- assert %{"error" => "Invalid request"} ==
- conn
- |> patch("/api/v1/accounts/update_credentials", %{"fields_attributes" => fields})
- |> json_response_and_validate_schema(403)
- end
- end
-
- describe "Mark account as bot" do
- setup do: oauth_access(["write:accounts"])
- setup :request_content_type
-
- test "changing actor_type to Service makes account a bot", %{conn: conn} do
- account =
- conn
- |> patch("/api/v1/accounts/update_credentials", %{actor_type: "Service"})
- |> json_response_and_validate_schema(200)
-
- assert account["bot"]
- assert account["source"]["pleroma"]["actor_type"] == "Service"
- end
-
- test "changing actor_type to Person makes account a human", %{conn: conn} do
- account =
- conn
- |> patch("/api/v1/accounts/update_credentials", %{actor_type: "Person"})
- |> json_response_and_validate_schema(200)
-
- refute account["bot"]
- assert account["source"]["pleroma"]["actor_type"] == "Person"
- end
-
- test "changing actor_type to Application causes error", %{conn: conn} do
- response =
- conn
- |> patch("/api/v1/accounts/update_credentials", %{actor_type: "Application"})
- |> json_response_and_validate_schema(403)
-
- assert %{"error" => "Invalid request"} == response
- end
-
- test "changing bot field to true changes actor_type to Service", %{conn: conn} do
- account =
- conn
- |> patch("/api/v1/accounts/update_credentials", %{bot: "true"})
- |> json_response_and_validate_schema(200)
-
- assert account["bot"]
- assert account["source"]["pleroma"]["actor_type"] == "Service"
- end
-
- test "changing bot field to false changes actor_type to Person", %{conn: conn} do
- account =
- conn
- |> patch("/api/v1/accounts/update_credentials", %{bot: "false"})
- |> json_response_and_validate_schema(200)
-
- refute account["bot"]
- assert account["source"]["pleroma"]["actor_type"] == "Person"
- end
-
- test "actor_type field has a higher priority than bot", %{conn: conn} do
- account =
- conn
- |> patch("/api/v1/accounts/update_credentials", %{
- actor_type: "Person",
- bot: "true"
- })
- |> json_response_and_validate_schema(200)
-
- refute account["bot"]
- assert account["source"]["pleroma"]["actor_type"] == "Person"
- end
- end
-end
diff --git a/test/web/media_proxy/invalidations/http_test.exs b/test/web/media_proxy/invalidations/http_test.exs
@@ -1,43 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Web.MediaProxy.Invalidation.HttpTest do
- use ExUnit.Case
- alias Pleroma.Web.MediaProxy.Invalidation
-
- import ExUnit.CaptureLog
- import Tesla.Mock
-
- setup do
- on_exit(fn -> Cachex.clear(:banned_urls_cache) end)
- end
-
- test "logs hasn't error message when request is valid" do
- mock(fn
- %{method: :purge, url: "http://example.com/media/example.jpg"} ->
- %Tesla.Env{status: 200}
- end)
-
- refute capture_log(fn ->
- assert Invalidation.Http.purge(
- ["http://example.com/media/example.jpg"],
- []
- ) == {:ok, ["http://example.com/media/example.jpg"]}
- end) =~ "Error while cache purge"
- end
-
- test "it write error message in logs when request invalid" do
- mock(fn
- %{method: :purge, url: "http://example.com/media/example1.jpg"} ->
- %Tesla.Env{status: 404}
- end)
-
- assert capture_log(fn ->
- assert Invalidation.Http.purge(
- ["http://example.com/media/example1.jpg"],
- []
- ) == {:ok, ["http://example.com/media/example1.jpg"]}
- end) =~ "Error while cache purge: url - http://example.com/media/example1.jpg"
- end
-end
diff --git a/test/web/media_proxy/invalidations/script_test.exs b/test/web/media_proxy/invalidations/script_test.exs
@@ -1,30 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Web.MediaProxy.Invalidation.ScriptTest do
- use ExUnit.Case
- alias Pleroma.Web.MediaProxy.Invalidation
-
- import ExUnit.CaptureLog
-
- setup do
- on_exit(fn -> Cachex.clear(:banned_urls_cache) end)
- end
-
- test "it logger error when script not found" do
- assert capture_log(fn ->
- assert Invalidation.Script.purge(
- ["http://example.com/media/example.jpg"],
- script_path: "./example"
- ) == {:error, "%ErlangError{original: :enoent}"}
- end) =~ "Error while cache purge: %ErlangError{original: :enoent}"
-
- capture_log(fn ->
- assert Invalidation.Script.purge(
- ["http://example.com/media/example.jpg"],
- []
- ) == {:error, "\"not found script path\""}
- end)
- end
-end
diff --git a/test/web/media_proxy/media_proxy_controller_test.exs b/test/web/media_proxy/media_proxy_controller_test.exs
@@ -1,342 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Web.MediaProxy.MediaProxyControllerTest do
- use Pleroma.Web.ConnCase
-
- import Mock
-
- alias Pleroma.Web.MediaProxy
- alias Plug.Conn
-
- setup do
- on_exit(fn -> Cachex.clear(:banned_urls_cache) end)
- end
-
- describe "Media Proxy" 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 "it returns 404 when disabled", %{conn: conn} do
- clear_config([:media_proxy, :enabled], false)
-
- assert %Conn{
- status: 404,
- resp_body: "Not Found"
- } = get(conn, "/proxy/hhgfh/eeeee")
-
- assert %Conn{
- status: 404,
- resp_body: "Not Found"
- } = get(conn, "/proxy/hhgfh/eeee/fff")
- 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 "redirects to 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
-
- 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
-
- describe "Media Preview Proxy" do
- def assert_dependencies_installed do
- missing_dependencies = Pleroma.Helpers.MediaHelper.missing_dependencies()
-
- assert missing_dependencies == [],
- "Error: missing dependencies (please refer to `docs/installation`): #{
- inspect(missing_dependencies)
- }"
- end
-
- setup do
- clear_config([:media_proxy, :enabled], true)
- clear_config([:media_preview_proxy, :enabled], true)
- clear_config([Pleroma.Web.Endpoint, :secret_key_base], "00000000000")
-
- original_url = "https://google.fn/test.png"
-
- [
- url: MediaProxy.encode_preview_url(original_url),
- media_proxy_url: MediaProxy.encode_url(original_url)
- ]
- end
-
- test "returns 404 when media proxy is disabled", %{conn: conn} do
- clear_config([:media_proxy, :enabled], false)
-
- assert %Conn{
- status: 404,
- resp_body: "Not Found"
- } = get(conn, "/proxy/preview/hhgfh/eeeee")
-
- assert %Conn{
- status: 404,
- resp_body: "Not Found"
- } = get(conn, "/proxy/preview/hhgfh/fff")
- end
-
- test "returns 404 when disabled", %{conn: conn} do
- clear_config([:media_preview_proxy, :enabled], false)
-
- assert %Conn{
- status: 404,
- resp_body: "Not Found"
- } = get(conn, "/proxy/preview/hhgfh/eeeee")
-
- assert %Conn{
- status: 404,
- resp_body: "Not Found"
- } = get(conn, "/proxy/preview/hhgfh/fff")
- 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/preview/hhgfh/eeee")
-
- assert %Conn{
- status: 403,
- resp_body: "Forbidden"
- } = get(conn, "/proxy/preview/hhgfh/eeee/fff")
- end
-
- test "redirects to 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
-
- test "responds with 424 Failed Dependency if HEAD request to media proxy fails", %{
- conn: conn,
- url: url,
- media_proxy_url: media_proxy_url
- } do
- Tesla.Mock.mock(fn
- %{method: "head", url: ^media_proxy_url} ->
- %Tesla.Env{status: 500, body: ""}
- end)
-
- response = get(conn, url)
- assert response.status == 424
- assert response.resp_body == "Can't fetch HTTP headers (HTTP 500)."
- end
-
- test "redirects to media proxy URI on unsupported content type", %{
- conn: conn,
- url: url,
- media_proxy_url: media_proxy_url
- } do
- Tesla.Mock.mock(fn
- %{method: "head", url: ^media_proxy_url} ->
- %Tesla.Env{status: 200, body: "", headers: [{"content-type", "application/pdf"}]}
- end)
-
- response = get(conn, url)
- assert response.status == 302
- assert redirected_to(response) == media_proxy_url
- end
-
- test "with `static=true` and GIF image preview requested, responds with JPEG image", %{
- conn: conn,
- url: url,
- media_proxy_url: media_proxy_url
- } do
- assert_dependencies_installed()
-
- # Setting a high :min_content_length to ensure this scenario is not affected by its logic
- clear_config([:media_preview_proxy, :min_content_length], 1_000_000_000)
-
- Tesla.Mock.mock(fn
- %{method: "head", url: ^media_proxy_url} ->
- %Tesla.Env{
- status: 200,
- body: "",
- headers: [{"content-type", "image/gif"}, {"content-length", "1001718"}]
- }
-
- %{method: :get, url: ^media_proxy_url} ->
- %Tesla.Env{status: 200, body: File.read!("test/fixtures/image.gif")}
- end)
-
- response = get(conn, url <> "?static=true")
-
- assert response.status == 200
- assert Conn.get_resp_header(response, "content-type") == ["image/jpeg"]
- assert response.resp_body != ""
- end
-
- test "with GIF image preview requested and no `static` param, redirects to media proxy URI",
- %{
- conn: conn,
- url: url,
- media_proxy_url: media_proxy_url
- } do
- Tesla.Mock.mock(fn
- %{method: "head", url: ^media_proxy_url} ->
- %Tesla.Env{status: 200, body: "", headers: [{"content-type", "image/gif"}]}
- end)
-
- response = get(conn, url)
-
- assert response.status == 302
- assert redirected_to(response) == media_proxy_url
- end
-
- test "with `static` param and non-GIF image preview requested, " <>
- "redirects to media preview proxy URI without `static` param",
- %{
- conn: conn,
- url: url,
- media_proxy_url: media_proxy_url
- } do
- Tesla.Mock.mock(fn
- %{method: "head", url: ^media_proxy_url} ->
- %Tesla.Env{status: 200, body: "", headers: [{"content-type", "image/jpeg"}]}
- end)
-
- response = get(conn, url <> "?static=true")
-
- assert response.status == 302
- assert redirected_to(response) == url
- end
-
- test "with :min_content_length setting not matched by Content-Length header, " <>
- "redirects to media proxy URI",
- %{
- conn: conn,
- url: url,
- media_proxy_url: media_proxy_url
- } do
- clear_config([:media_preview_proxy, :min_content_length], 100_000)
-
- Tesla.Mock.mock(fn
- %{method: "head", url: ^media_proxy_url} ->
- %Tesla.Env{
- status: 200,
- body: "",
- headers: [{"content-type", "image/gif"}, {"content-length", "5000"}]
- }
- end)
-
- response = get(conn, url)
-
- assert response.status == 302
- assert redirected_to(response) == media_proxy_url
- end
-
- test "thumbnails PNG images into PNG", %{
- conn: conn,
- url: url,
- media_proxy_url: media_proxy_url
- } do
- assert_dependencies_installed()
-
- Tesla.Mock.mock(fn
- %{method: "head", url: ^media_proxy_url} ->
- %Tesla.Env{status: 200, body: "", headers: [{"content-type", "image/png"}]}
-
- %{method: :get, url: ^media_proxy_url} ->
- %Tesla.Env{status: 200, body: File.read!("test/fixtures/image.png")}
- end)
-
- response = get(conn, url)
-
- assert response.status == 200
- assert Conn.get_resp_header(response, "content-type") == ["image/png"]
- assert response.resp_body != ""
- end
-
- test "thumbnails JPEG images into JPEG", %{
- conn: conn,
- url: url,
- media_proxy_url: media_proxy_url
- } do
- assert_dependencies_installed()
-
- Tesla.Mock.mock(fn
- %{method: "head", url: ^media_proxy_url} ->
- %Tesla.Env{status: 200, body: "", headers: [{"content-type", "image/jpeg"}]}
-
- %{method: :get, url: ^media_proxy_url} ->
- %Tesla.Env{status: 200, body: File.read!("test/fixtures/image.jpg")}
- end)
-
- response = get(conn, url)
-
- assert response.status == 200
- assert Conn.get_resp_header(response, "content-type") == ["image/jpeg"]
- assert response.resp_body != ""
- end
-
- test "redirects to media proxy URI in case of thumbnailing error", %{
- conn: conn,
- url: url,
- media_proxy_url: media_proxy_url
- } do
- Tesla.Mock.mock(fn
- %{method: "head", url: ^media_proxy_url} ->
- %Tesla.Env{status: 200, body: "", headers: [{"content-type", "image/jpeg"}]}
-
- %{method: :get, url: ^media_proxy_url} ->
- %Tesla.Env{status: 200, body: "<html><body>error</body></html>"}
- end)
-
- response = get(conn, url)
-
- assert response.status == 302
- assert redirected_to(response) == media_proxy_url
- end
- end
-end
diff --git a/test/web/media_proxy/media_proxy_test.exs b/test/web/media_proxy/media_proxy_test.exs
@@ -1,234 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Web.MediaProxyTest do
- use ExUnit.Case
- use Pleroma.Tests.Helpers
-
- alias Pleroma.Config
- alias Pleroma.Web.Endpoint
- alias Pleroma.Web.MediaProxy
-
- defp decode_result(encoded) do
- [_, "proxy", sig, base64 | _] = URI.parse(encoded).path |> String.split("/")
- {:ok, decoded} = MediaProxy.decode_url(sig, base64)
- decoded
- end
-
- describe "when enabled" do
- setup do: clear_config([:media_proxy, :enabled], true)
-
- test "ignores invalid url" do
- assert MediaProxy.url(nil) == nil
- assert MediaProxy.url("") == nil
- end
-
- test "ignores relative url" do
- assert MediaProxy.url("/local") == "/local"
- assert MediaProxy.url("/") == "/"
- end
-
- test "ignores local url" do
- 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 = MediaProxy.url(url)
-
- assert String.starts_with?(
- encoded,
- Config.get([:media_proxy, :base_url], Pleroma.Web.base_url())
- )
-
- assert String.ends_with?(encoded, "/logo.png")
-
- assert decode_result(encoded) == url
- end
-
- test "encodes and decodes URL without a path" do
- url = "https://pleroma.soykaf.com"
- 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 = 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 = MediaProxy.url(url)
- assert String.ends_with?(encoded, "/logo.png")
- assert decode_result(encoded) == url
- end
-
- test "validates signature" do
- encoded = MediaProxy.url("https://pleroma.social")
-
- clear_config(
- [Endpoint, :secret_key_base],
- "00000000000000000000000000000000000000000000000"
- )
-
- [_, "proxy", sig, base64 | _] = URI.parse(encoded).path |> String.split("/")
- assert MediaProxy.decode_url(sig, base64) == {:error, :invalid_signature}
- end
-
- def test_verify_request_path_and_url(request_path, url, expected_result) do
- assert MediaProxy.verify_request_path_and_url(request_path, url) == expected_result
-
- assert MediaProxy.verify_request_path_and_url(
- %Plug.Conn{
- params: %{"filename" => Path.basename(request_path)},
- request_path: request_path
- },
- url
- ) == expected_result
- end
-
- test "if first arg of `verify_request_path_and_url/2` is a Plug.Conn without \"filename\" " <>
- "parameter, `verify_request_path_and_url/2` returns :ok " do
- assert MediaProxy.verify_request_path_and_url(
- %Plug.Conn{params: %{}, request_path: "/some/path"},
- "https://instance.com/file.jpg"
- ) == :ok
-
- assert MediaProxy.verify_request_path_and_url(
- %Plug.Conn{params: %{}, request_path: "/path/to/file.jpg"},
- "https://instance.com/file.jpg"
- ) == :ok
- end
-
- test "`verify_request_path_and_url/2` preserves the encoded or decoded path" do
- test_verify_request_path_and_url(
- "/Hello world.jpg",
- "http://pleroma.social/Hello world.jpg",
- :ok
- )
-
- test_verify_request_path_and_url(
- "/Hello%20world.jpg",
- "http://pleroma.social/Hello%20world.jpg",
- :ok
- )
-
- test_verify_request_path_and_url(
- "/my%2Flong%2Furl%2F2019%2F07%2FS.jpg",
- "http://pleroma.social/my%2Flong%2Furl%2F2019%2F07%2FS.jpg",
- :ok
- )
-
- test_verify_request_path_and_url(
- # Note: `conn.request_path` returns encoded url
- "/ANALYSE-DAI-_-LE-STABLECOIN-100-D%C3%89CENTRALIS%C3%89-BQ.jpg",
- "https://mydomain.com/uploads/2019/07/ANALYSE-DAI-_-LE-STABLECOIN-100-DÉCENTRALISÉ-BQ.jpg",
- :ok
- )
-
- test_verify_request_path_and_url(
- "/my%2Flong%2Furl%2F2019%2F07%2FS",
- "http://pleroma.social/my%2Flong%2Furl%2F2019%2F07%2FS.jpg",
- {:wrong_filename, "my%2Flong%2Furl%2F2019%2F07%2FS.jpg"}
- )
- end
-
- test "uses the configured base_url" do
- base_url = "https://cache.pleroma.social"
- clear_config([:media_proxy, :base_url], base_url)
-
- url = "https://pleroma.soykaf.com/static/logo.png"
- encoded = MediaProxy.url(url)
-
- assert String.starts_with?(encoded, base_url)
- end
-
- # Some sites expect ASCII encoded characters in the URL to be preserved even if
- # unnecessary.
- # Issues: https://git.pleroma.social/pleroma/pleroma/issues/580
- # https://git.pleroma.social/pleroma/pleroma/issues/1055
- test "preserve ASCII encoding" 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 = MediaProxy.url(url)
- assert decode_result(encoded) == url
- end
-
- # This includes unsafe/reserved characters which are not interpreted as part of the URL
- # and would otherwise have to be ASCII encoded. It is our role to ensure the proxied URL
- # is unmodified, so we are testing these characters anyway.
- test "preserve non-unicode characters per RFC3986" do
- url =
- "https://pleroma.com/ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890-._~:/?#[]@!$&'()*+,;=|^`{}"
-
- encoded = MediaProxy.url(url)
- assert decode_result(encoded) == url
- end
-
- test "preserve unicode characters" do
- url = "https://ko.wikipedia.org/wiki/위키백과:대문"
-
- encoded = MediaProxy.url(url)
- assert decode_result(encoded) == url
- end
- end
-
- describe "when disabled" do
- setup do: clear_config([:media_proxy, :enabled], false)
-
- test "does not encode remote urls" do
- assert MediaProxy.url("https://google.fr") == "https://google.fr"
- end
- end
-
- describe "whitelist" do
- setup do: clear_config([:media_proxy, :enabled], true)
-
- test "mediaproxy whitelist" do
- 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 = MediaProxy.url(url)
- assert unencoded == url
- end
-
- test "does not change whitelisted urls" do
- clear_config([:media_proxy, :whitelist], ["mycdn.akamai.com"])
- clear_config([:media_proxy, :base_url], "https://cache.pleroma.social")
-
- media_url = "https://mycdn.akamai.com"
-
- url = "#{media_url}/static/logo.png"
- 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"
- clear_config([Pleroma.Upload, :base_url], media_url)
-
- url = "#{media_url}/static/logo.png"
- encoded = MediaProxy.url(url)
-
- assert String.starts_with?(encoded, media_url)
- end
- end
-end
diff --git a/test/web/metadata/feed_test.exs b/test/web/metadata/feed_test.exs
@@ -1,18 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Web.Metadata.Providers.FeedTest do
- use Pleroma.DataCase
- import Pleroma.Factory
- alias Pleroma.Web.Metadata.Providers.Feed
-
- test "it renders a link to user's atom feed" do
- user = insert(:user, nickname: "lain")
-
- assert Feed.build_tags(%{user: user}) == [
- {:link,
- [rel: "alternate", type: "application/atom+xml", href: "/users/lain/feed.atom"], []}
- ]
- end
-end
diff --git a/test/web/metadata/metadata_test.exs b/test/web/metadata/metadata_test.exs
@@ -1,49 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Web.MetadataTest do
- use Pleroma.DataCase, async: true
-
- import Pleroma.Factory
-
- describe "restrict indexing remote users" do
- test "for remote user" do
- user = insert(:user, local: false)
-
- assert Pleroma.Web.Metadata.build_tags(%{user: user}) =~
- "<meta content=\"noindex, noarchive\" name=\"robots\">"
- end
-
- test "for local user" do
- user = insert(:user, discoverable: false)
-
- assert Pleroma.Web.Metadata.build_tags(%{user: user}) =~
- "<meta content=\"noindex, noarchive\" name=\"robots\">"
- end
-
- test "for local user set to discoverable" do
- user = insert(:user, discoverable: true)
-
- refute Pleroma.Web.Metadata.build_tags(%{user: user}) =~
- "<meta content=\"noindex, noarchive\" name=\"robots\">"
- end
- end
-
- describe "no metadata for private instances" do
- test "for local user set to discoverable" do
- clear_config([:instance, :public], false)
- user = insert(:user, bio: "This is my secret fedi account bio", discoverable: true)
-
- assert "" = Pleroma.Web.Metadata.build_tags(%{user: user})
- end
-
- test "search exclusion metadata is included" do
- clear_config([:instance, :public], false)
- user = insert(:user, bio: "This is my secret fedi account bio", discoverable: false)
-
- assert ~s(<meta content="noindex, noarchive" name="robots">) ==
- Pleroma.Web.Metadata.build_tags(%{user: user})
- end
- end
-end
diff --git a/test/web/metadata/opengraph_test.exs b/test/web/metadata/opengraph_test.exs
@@ -1,96 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Web.Metadata.Providers.OpenGraphTest do
- use Pleroma.DataCase
- import Pleroma.Factory
- alias Pleroma.Web.Metadata.Providers.OpenGraph
-
- setup do: clear_config([Pleroma.Web.Metadata, :unfurl_nsfw])
-
- test "it renders all supported types of attachments and skips unknown types" do
- user = insert(:user)
-
- note =
- insert(:note, %{
- data: %{
- "actor" => user.ap_id,
- "tag" => [],
- "id" => "https://pleroma.gov/objects/whatever",
- "content" => "pleroma in a nutshell",
- "attachment" => [
- %{
- "url" => [
- %{"mediaType" => "image/png", "href" => "https://pleroma.gov/tenshi.png"}
- ]
- },
- %{
- "url" => [
- %{
- "mediaType" => "application/octet-stream",
- "href" => "https://pleroma.gov/fqa/badapple.sfc"
- }
- ]
- },
- %{
- "url" => [
- %{"mediaType" => "video/webm", "href" => "https://pleroma.gov/about/juche.webm"}
- ]
- },
- %{
- "url" => [
- %{
- "mediaType" => "audio/basic",
- "href" => "http://www.gnu.org/music/free-software-song.au"
- }
- ]
- }
- ]
- }
- })
-
- result = OpenGraph.build_tags(%{object: note, url: note.data["id"], user: user})
-
- assert Enum.all?(
- [
- {:meta, [property: "og:image", content: "https://pleroma.gov/tenshi.png"], []},
- {:meta,
- [property: "og:audio", content: "http://www.gnu.org/music/free-software-song.au"],
- []},
- {:meta, [property: "og:video", content: "https://pleroma.gov/about/juche.webm"],
- []}
- ],
- fn element -> element in result end
- )
- end
-
- test "it does not render attachments if post is nsfw" do
- Pleroma.Config.put([Pleroma.Web.Metadata, :unfurl_nsfw], false)
- user = insert(:user, avatar: %{"url" => [%{"href" => "https://pleroma.gov/tenshi.png"}]})
-
- note =
- insert(:note, %{
- data: %{
- "actor" => user.ap_id,
- "id" => "https://pleroma.gov/objects/whatever",
- "content" => "#cuteposting #nsfw #hambaga",
- "tag" => ["cuteposting", "nsfw", "hambaga"],
- "sensitive" => true,
- "attachment" => [
- %{
- "url" => [
- %{"mediaType" => "image/png", "href" => "https://misskey.microsoft/corndog.png"}
- ]
- }
- ]
- }
- })
-
- result = OpenGraph.build_tags(%{object: note, url: note.data["id"], user: user})
-
- assert {:meta, [property: "og:image", content: "https://pleroma.gov/tenshi.png"], []} in result
-
- refute {:meta, [property: "og:image", content: "https://misskey.microsoft/corndog.png"], []} in result
- end
-end
diff --git a/test/web/metadata/player_view_test.exs b/test/web/metadata/player_view_test.exs
@@ -1,33 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Web.Metadata.PlayerViewTest do
- use Pleroma.DataCase
-
- alias Pleroma.Web.Metadata.PlayerView
-
- test "it renders audio tag" do
- res =
- PlayerView.render(
- "player.html",
- %{"mediaType" => "audio", "href" => "test-href"}
- )
- |> Phoenix.HTML.safe_to_string()
-
- assert res ==
- "<audio controls><source src=\"test-href\" type=\"audio\">Your browser does not support audio playback.</audio>"
- end
-
- test "it renders videos tag" do
- res =
- PlayerView.render(
- "player.html",
- %{"mediaType" => "video", "href" => "test-href"}
- )
- |> Phoenix.HTML.safe_to_string()
-
- assert res ==
- "<video controls loop><source src=\"test-href\" type=\"video\">Your browser does not support video playback.</video>"
- end
-end
diff --git a/test/web/metadata/rel_me_test.exs b/test/web/metadata/rel_me_test.exs
@@ -1,21 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Web.Metadata.Providers.RelMeTest do
- use Pleroma.DataCase
- import Pleroma.Factory
- alias Pleroma.Web.Metadata.Providers.RelMe
-
- test "it renders all links with rel='me' from user bio" do
- bio =
- ~s(<a href="https://some-link.com">https://some-link.com</a> <a rel="me" href="https://another-link.com">https://another-link.com</a> <link href="http://some.com"> <link rel="me" href="http://some3.com">)
-
- user = insert(:user, %{bio: bio})
-
- assert RelMe.build_tags(%{user: user}) == [
- {:link, [rel: "me", href: "http://some3.com"], []},
- {:link, [rel: "me", href: "https://another-link.com"], []}
- ]
- end
-end
diff --git a/test/web/metadata/restrict_indexing_test.exs b/test/web/metadata/restrict_indexing_test.exs
@@ -1,27 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Web.Metadata.Providers.RestrictIndexingTest do
- use ExUnit.Case, async: true
-
- describe "build_tags/1" do
- test "for remote user" do
- assert Pleroma.Web.Metadata.Providers.RestrictIndexing.build_tags(%{
- user: %Pleroma.User{local: false}
- }) == [{:meta, [name: "robots", content: "noindex, noarchive"], []}]
- end
-
- test "for local user" do
- assert Pleroma.Web.Metadata.Providers.RestrictIndexing.build_tags(%{
- user: %Pleroma.User{local: true, discoverable: true}
- }) == []
- end
-
- test "for local user when discoverable is false" do
- assert Pleroma.Web.Metadata.Providers.RestrictIndexing.build_tags(%{
- user: %Pleroma.User{local: true, discoverable: false}
- }) == [{:meta, [name: "robots", content: "noindex, noarchive"], []}]
- end
- end
-end
diff --git a/test/web/metadata/twitter_card_test.exs b/test/web/metadata/twitter_card_test.exs
@@ -1,150 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Web.Metadata.Providers.TwitterCardTest do
- use Pleroma.DataCase
- import Pleroma.Factory
-
- alias Pleroma.User
- alias Pleroma.Web.CommonAPI
- alias Pleroma.Web.Endpoint
- alias Pleroma.Web.Metadata.Providers.TwitterCard
- alias Pleroma.Web.Metadata.Utils
- alias Pleroma.Web.Router
-
- setup do: clear_config([Pleroma.Web.Metadata, :unfurl_nsfw])
-
- test "it renders twitter card for user info" do
- user = insert(:user, name: "Jimmy Hendriks", bio: "born 19 March 1994")
- avatar_url = Utils.attachment_url(User.avatar_url(user))
- res = TwitterCard.build_tags(%{user: user})
-
- assert res == [
- {:meta, [property: "twitter:title", content: Utils.user_name_string(user)], []},
- {:meta, [property: "twitter:description", content: "born 19 March 1994"], []},
- {:meta, [property: "twitter:image", content: avatar_url], []},
- {:meta, [property: "twitter:card", content: "summary"], []}
- ]
- end
-
- test "it uses summary twittercard if post has no attachment" do
- user = insert(:user, name: "Jimmy Hendriks", bio: "born 19 March 1994")
- {:ok, activity} = CommonAPI.post(user, %{status: "HI"})
-
- note =
- insert(:note, %{
- data: %{
- "actor" => user.ap_id,
- "tag" => [],
- "id" => "https://pleroma.gov/objects/whatever",
- "content" => "pleroma in a nutshell"
- }
- })
-
- result = TwitterCard.build_tags(%{object: note, user: user, activity_id: activity.id})
-
- assert [
- {:meta, [property: "twitter:title", content: Utils.user_name_string(user)], []},
- {:meta, [property: "twitter:description", content: "“pleroma in a nutshell”"], []},
- {:meta, [property: "twitter:image", content: "http://localhost:4001/images/avi.png"],
- []},
- {:meta, [property: "twitter:card", content: "summary"], []}
- ] == result
- end
-
- test "it renders avatar not attachment if post is nsfw and unfurl_nsfw is disabled" do
- Pleroma.Config.put([Pleroma.Web.Metadata, :unfurl_nsfw], false)
- user = insert(:user, name: "Jimmy Hendriks", bio: "born 19 March 1994")
- {:ok, activity} = CommonAPI.post(user, %{status: "HI"})
-
- note =
- insert(:note, %{
- data: %{
- "actor" => user.ap_id,
- "tag" => [],
- "id" => "https://pleroma.gov/objects/whatever",
- "content" => "pleroma in a nutshell",
- "sensitive" => true,
- "attachment" => [
- %{
- "url" => [%{"mediaType" => "image/png", "href" => "https://pleroma.gov/tenshi.png"}]
- },
- %{
- "url" => [
- %{
- "mediaType" => "application/octet-stream",
- "href" => "https://pleroma.gov/fqa/badapple.sfc"
- }
- ]
- },
- %{
- "url" => [
- %{"mediaType" => "video/webm", "href" => "https://pleroma.gov/about/juche.webm"}
- ]
- }
- ]
- }
- })
-
- result = TwitterCard.build_tags(%{object: note, user: user, activity_id: activity.id})
-
- assert [
- {:meta, [property: "twitter:title", content: Utils.user_name_string(user)], []},
- {:meta, [property: "twitter:description", content: "“pleroma in a nutshell”"], []},
- {:meta, [property: "twitter:image", content: "http://localhost:4001/images/avi.png"],
- []},
- {:meta, [property: "twitter:card", content: "summary"], []}
- ] == result
- end
-
- test "it renders supported types of attachments and skips unknown types" do
- user = insert(:user, name: "Jimmy Hendriks", bio: "born 19 March 1994")
- {:ok, activity} = CommonAPI.post(user, %{status: "HI"})
-
- note =
- insert(:note, %{
- data: %{
- "actor" => user.ap_id,
- "tag" => [],
- "id" => "https://pleroma.gov/objects/whatever",
- "content" => "pleroma in a nutshell",
- "attachment" => [
- %{
- "url" => [%{"mediaType" => "image/png", "href" => "https://pleroma.gov/tenshi.png"}]
- },
- %{
- "url" => [
- %{
- "mediaType" => "application/octet-stream",
- "href" => "https://pleroma.gov/fqa/badapple.sfc"
- }
- ]
- },
- %{
- "url" => [
- %{"mediaType" => "video/webm", "href" => "https://pleroma.gov/about/juche.webm"}
- ]
- }
- ]
- }
- })
-
- result = TwitterCard.build_tags(%{object: note, user: user, activity_id: activity.id})
-
- assert [
- {:meta, [property: "twitter:title", content: Utils.user_name_string(user)], []},
- {:meta, [property: "twitter:description", content: "“pleroma in a nutshell”"], []},
- {:meta, [property: "twitter:card", content: "summary_large_image"], []},
- {:meta, [property: "twitter:player", content: "https://pleroma.gov/tenshi.png"], []},
- {:meta, [property: "twitter:card", content: "player"], []},
- {:meta,
- [
- property: "twitter:player",
- content: Router.Helpers.o_status_url(Endpoint, :notice_player, activity.id)
- ], []},
- {:meta, [property: "twitter:player:width", content: "480"], []},
- {:meta, [property: "twitter:player:height", content: "480"], []}
- ] == result
- end
-end
diff --git a/test/web/metadata/utils_test.exs b/test/web/metadata/utils_test.exs
@@ -1,32 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Web.Metadata.UtilsTest do
- use Pleroma.DataCase
- import Pleroma.Factory
- alias Pleroma.Web.Metadata.Utils
-
- describe "scrub_html_and_truncate/1" do
- test "it returns text without encode HTML" do
- user = insert(:user)
-
- note =
- insert(:note, %{
- data: %{
- "actor" => user.ap_id,
- "id" => "https://pleroma.gov/objects/whatever",
- "content" => "Pleroma's really cool!"
- }
- })
-
- assert Utils.scrub_html_and_truncate(note) == "Pleroma's really cool!"
- end
- end
-
- describe "scrub_html_and_truncate/2" do
- test "it returns text without encode HTML" do
- assert Utils.scrub_html_and_truncate("Pleroma's really cool!") == "Pleroma's really cool!"
- end
- end
-end
diff --git a/test/web/mongooseim/mongoose_im_controller_test.exs b/test/web/mongooseim/mongoose_im_controller_test.exs
@@ -1,81 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Web.MongooseIMController do
- use Pleroma.Web.ConnCase
- import Pleroma.Factory
-
- test "/user_exists", %{conn: conn} do
- _user = insert(:user, nickname: "lain")
- _remote_user = insert(:user, nickname: "alice", local: false)
- _deactivated_user = insert(:user, nickname: "konata", deactivated: true)
-
- res =
- conn
- |> get(mongoose_im_path(conn, :user_exists), user: "lain")
- |> json_response(200)
-
- assert res == true
-
- res =
- conn
- |> get(mongoose_im_path(conn, :user_exists), user: "alice")
- |> json_response(404)
-
- assert res == false
-
- res =
- conn
- |> get(mongoose_im_path(conn, :user_exists), user: "bob")
- |> json_response(404)
-
- assert res == false
-
- res =
- conn
- |> get(mongoose_im_path(conn, :user_exists), user: "konata")
- |> json_response(404)
-
- assert res == false
- end
-
- test "/check_password", %{conn: conn} do
- user = insert(:user, password_hash: Pbkdf2.hash_pwd_salt("cool"))
-
- _deactivated_user =
- insert(:user,
- nickname: "konata",
- deactivated: true,
- password_hash: Pbkdf2.hash_pwd_salt("cool")
- )
-
- res =
- conn
- |> get(mongoose_im_path(conn, :check_password), user: user.nickname, pass: "cool")
- |> json_response(200)
-
- assert res == true
-
- res =
- conn
- |> get(mongoose_im_path(conn, :check_password), user: user.nickname, pass: "uncool")
- |> json_response(403)
-
- assert res == false
-
- res =
- conn
- |> get(mongoose_im_path(conn, :check_password), user: "konata", pass: "cool")
- |> json_response(404)
-
- assert res == false
-
- res =
- conn
- |> get(mongoose_im_path(conn, :check_password), user: "nobody", pass: "cool")
- |> json_response(404)
-
- assert res == false
- end
-end
diff --git a/test/web/node_info_test.exs b/test/web/node_info_test.exs
@@ -1,188 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Web.NodeInfoTest do
- use Pleroma.Web.ConnCase
-
- import Pleroma.Factory
-
- alias Pleroma.Config
-
- setup do: clear_config([:mrf_simple])
- setup do: clear_config(:instance)
-
- test "GET /.well-known/nodeinfo", %{conn: conn} do
- links =
- conn
- |> get("/.well-known/nodeinfo")
- |> json_response(200)
- |> Map.fetch!("links")
-
- Enum.each(links, fn link ->
- href = Map.fetch!(link, "href")
-
- conn
- |> get(href)
- |> json_response(200)
- end)
- end
-
- test "nodeinfo shows staff accounts", %{conn: conn} do
- moderator = insert(:user, local: true, is_moderator: true)
- admin = insert(:user, local: true, is_admin: true)
-
- conn =
- conn
- |> get("/nodeinfo/2.1.json")
-
- assert result = json_response(conn, 200)
-
- assert moderator.ap_id in result["metadata"]["staffAccounts"]
- assert admin.ap_id in result["metadata"]["staffAccounts"]
- end
-
- test "nodeinfo shows restricted nicknames", %{conn: conn} do
- conn =
- conn
- |> get("/nodeinfo/2.1.json")
-
- assert result = json_response(conn, 200)
-
- assert Config.get([Pleroma.User, :restricted_nicknames]) ==
- result["metadata"]["restrictedNicknames"]
- end
-
- test "returns software.repository field in nodeinfo 2.1", %{conn: conn} do
- conn
- |> get("/.well-known/nodeinfo")
- |> json_response(200)
-
- conn =
- conn
- |> get("/nodeinfo/2.1.json")
-
- assert result = json_response(conn, 200)
- assert Pleroma.Application.repository() == result["software"]["repository"]
- end
-
- test "returns fieldsLimits field", %{conn: conn} do
- clear_config([:instance, :max_account_fields], 10)
- clear_config([:instance, :max_remote_account_fields], 15)
- clear_config([:instance, :account_field_name_length], 255)
- clear_config([:instance, :account_field_value_length], 2048)
-
- response =
- conn
- |> get("/nodeinfo/2.1.json")
- |> json_response(:ok)
-
- assert response["metadata"]["fieldsLimits"]["maxFields"] == 10
- assert response["metadata"]["fieldsLimits"]["maxRemoteFields"] == 15
- assert response["metadata"]["fieldsLimits"]["nameLength"] == 255
- assert response["metadata"]["fieldsLimits"]["valueLength"] == 2048
- end
-
- test "it returns the safe_dm_mentions feature if enabled", %{conn: conn} do
- clear_config([:instance, :safe_dm_mentions], true)
-
- response =
- conn
- |> get("/nodeinfo/2.1.json")
- |> json_response(:ok)
-
- assert "safe_dm_mentions" in response["metadata"]["features"]
-
- Config.put([:instance, :safe_dm_mentions], false)
-
- response =
- conn
- |> get("/nodeinfo/2.1.json")
- |> json_response(:ok)
-
- refute "safe_dm_mentions" in response["metadata"]["features"]
- end
-
- describe "`metadata/federation/enabled`" do
- setup do: clear_config([:instance, :federating])
-
- test "it shows if federation is enabled/disabled", %{conn: conn} do
- Config.put([:instance, :federating], true)
-
- response =
- conn
- |> get("/nodeinfo/2.1.json")
- |> json_response(:ok)
-
- assert response["metadata"]["federation"]["enabled"] == true
-
- Config.put([:instance, :federating], false)
-
- response =
- conn
- |> get("/nodeinfo/2.1.json")
- |> json_response(:ok)
-
- assert response["metadata"]["federation"]["enabled"] == false
- end
- end
-
- test "it shows default features flags", %{conn: conn} do
- response =
- conn
- |> get("/nodeinfo/2.1.json")
- |> json_response(:ok)
-
- default_features = [
- "pleroma_api",
- "mastodon_api",
- "mastodon_api_streaming",
- "polls",
- "pleroma_explicit_addressing",
- "shareable_emoji_packs",
- "multifetch",
- "pleroma_emoji_reactions",
- "pleroma:api/v1/notifications:include_types_filter",
- "pleroma_chat_messages"
- ]
-
- assert MapSet.subset?(
- MapSet.new(default_features),
- MapSet.new(response["metadata"]["features"])
- )
- end
-
- test "it shows MRF transparency data if enabled", %{conn: conn} do
- clear_config([:mrf, :policies], [Pleroma.Web.ActivityPub.MRF.SimplePolicy])
- clear_config([:mrf, :transparency], true)
-
- simple_config = %{"reject" => ["example.com"]}
- clear_config(:mrf_simple, simple_config)
-
- response =
- conn
- |> get("/nodeinfo/2.1.json")
- |> json_response(:ok)
-
- assert response["metadata"]["federation"]["mrf_simple"] == simple_config
- end
-
- test "it performs exclusions from MRF transparency data if configured", %{conn: conn} do
- clear_config([:mrf, :policies], [Pleroma.Web.ActivityPub.MRF.SimplePolicy])
- clear_config([:mrf, :transparency], true)
- clear_config([:mrf, :transparency_exclusions], ["other.site"])
-
- simple_config = %{"reject" => ["example.com", "other.site"]}
- clear_config(:mrf_simple, simple_config)
-
- expected_config = %{"reject" => ["example.com"]}
-
- response =
- conn
- |> get("/nodeinfo/2.1.json")
- |> json_response(:ok)
-
- assert response["metadata"]["federation"]["mrf_simple"] == expected_config
- assert response["metadata"]["federation"]["exclusions"] == true
- end
-end
diff --git a/test/web/oauth/app_test.exs b/test/web/oauth/app_test.exs
@@ -1,44 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Web.OAuth.AppTest do
- use Pleroma.DataCase
-
- alias Pleroma.Web.OAuth.App
- import Pleroma.Factory
-
- describe "get_or_make/2" do
- test "gets exist app" do
- attrs = %{client_name: "Mastodon-Local", redirect_uris: "."}
- app = insert(:oauth_app, Map.merge(attrs, %{scopes: ["read", "write"]}))
- {:ok, %App{} = exist_app} = App.get_or_make(attrs, [])
- assert exist_app == app
- end
-
- test "make app" do
- attrs = %{client_name: "Mastodon-Local", redirect_uris: "."}
- {:ok, %App{} = app} = App.get_or_make(attrs, ["write"])
- assert app.scopes == ["write"]
- end
-
- test "gets exist app and updates scopes" do
- attrs = %{client_name: "Mastodon-Local", redirect_uris: "."}
- app = insert(:oauth_app, Map.merge(attrs, %{scopes: ["read", "write"]}))
- {:ok, %App{} = exist_app} = App.get_or_make(attrs, ["read", "write", "follow", "push"])
- assert exist_app.id == app.id
- assert exist_app.scopes == ["read", "write", "follow", "push"]
- end
-
- test "has unique client_id" do
- insert(:oauth_app, client_name: "", redirect_uris: "", client_id: "boop")
-
- error =
- catch_error(insert(:oauth_app, client_name: "", redirect_uris: "", client_id: "boop"))
-
- assert %Ecto.ConstraintError{} = error
- assert error.constraint == "apps_client_id_index"
- assert error.type == :unique
- end
- end
-end
diff --git a/test/web/oauth/authorization_test.exs b/test/web/oauth/authorization_test.exs
@@ -1,77 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Web.OAuth.AuthorizationTest do
- use Pleroma.DataCase
- alias Pleroma.Web.OAuth.App
- alias Pleroma.Web.OAuth.Authorization
- import Pleroma.Factory
-
- setup do
- {:ok, app} =
- Repo.insert(
- App.register_changeset(%App{}, %{
- client_name: "client",
- scopes: ["read", "write"],
- redirect_uris: "url"
- })
- )
-
- %{app: app}
- end
-
- test "create an authorization token for a valid app", %{app: app} do
- user = insert(:user)
-
- {:ok, auth1} = Authorization.create_authorization(app, user)
- assert auth1.scopes == app.scopes
-
- {:ok, auth2} = Authorization.create_authorization(app, user, ["read"])
- assert auth2.scopes == ["read"]
-
- for auth <- [auth1, auth2] do
- assert auth.user_id == user.id
- assert auth.app_id == app.id
- assert String.length(auth.token) > 10
- assert auth.used == false
- end
- end
-
- test "use up a token", %{app: app} do
- user = insert(:user)
-
- {:ok, auth} = Authorization.create_authorization(app, user)
-
- {:ok, auth} = Authorization.use_token(auth)
-
- assert auth.used == true
-
- assert {:error, "already used"} == Authorization.use_token(auth)
-
- expired_auth = %Authorization{
- user_id: user.id,
- app_id: app.id,
- valid_until: NaiveDateTime.add(NaiveDateTime.utc_now(), -10),
- token: "mytoken",
- used: false
- }
-
- {:ok, expired_auth} = Repo.insert(expired_auth)
-
- assert {:error, "token expired"} == Authorization.use_token(expired_auth)
- end
-
- test "delete authorizations", %{app: app} do
- user = insert(:user)
-
- {:ok, auth} = Authorization.create_authorization(app, user)
- {:ok, auth} = Authorization.use_token(auth)
-
- Authorization.delete_user_authorizations(user)
-
- {_, invalid} = Authorization.use_token(auth)
-
- assert auth != invalid
- end
-end
diff --git a/test/web/oauth/ldap_authorization_test.exs b/test/web/oauth/ldap_authorization_test.exs
@@ -1,135 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Web.OAuth.LDAPAuthorizationTest do
- use Pleroma.Web.ConnCase
- alias Pleroma.Repo
- alias Pleroma.Web.OAuth.Token
- import Pleroma.Factory
- import Mock
-
- @skip if !Code.ensure_loaded?(:eldap), do: :skip
-
- setup_all do: clear_config([:ldap, :enabled], true)
-
- setup_all do: clear_config(Pleroma.Web.Auth.Authenticator, Pleroma.Web.Auth.LDAPAuthenticator)
-
- @tag @skip
- test "authorizes the existing user using LDAP credentials" do
- password = "testpassword"
- user = insert(:user, password_hash: Pbkdf2.hash_pwd_salt(password))
- app = insert(:oauth_app, scopes: ["read", "write"])
-
- host = Pleroma.Config.get([:ldap, :host]) |> to_charlist
- port = Pleroma.Config.get([:ldap, :port])
-
- with_mocks [
- {:eldap, [],
- [
- open: fn [^host], [{:port, ^port}, {:ssl, false} | _] -> {:ok, self()} end,
- simple_bind: fn _connection, _dn, ^password -> :ok end,
- close: fn _connection ->
- send(self(), :close_connection)
- :ok
- end
- ]}
- ] do
- conn =
- build_conn()
- |> post("/oauth/token", %{
- "grant_type" => "password",
- "username" => user.nickname,
- "password" => password,
- "client_id" => app.client_id,
- "client_secret" => app.client_secret
- })
-
- assert %{"access_token" => token} = json_response(conn, 200)
-
- token = Repo.get_by(Token, token: token)
-
- assert token.user_id == user.id
- assert_received :close_connection
- end
- end
-
- @tag @skip
- test "creates a new user after successful LDAP authorization" do
- password = "testpassword"
- user = build(:user)
- app = insert(:oauth_app, scopes: ["read", "write"])
-
- host = Pleroma.Config.get([:ldap, :host]) |> to_charlist
- port = Pleroma.Config.get([:ldap, :port])
-
- with_mocks [
- {:eldap, [],
- [
- open: fn [^host], [{:port, ^port}, {:ssl, false} | _] -> {:ok, self()} end,
- simple_bind: fn _connection, _dn, ^password -> :ok end,
- equalityMatch: fn _type, _value -> :ok end,
- wholeSubtree: fn -> :ok end,
- search: fn _connection, _options ->
- {:ok, {:eldap_search_result, [{:eldap_entry, '', []}], []}}
- end,
- close: fn _connection ->
- send(self(), :close_connection)
- :ok
- end
- ]}
- ] do
- conn =
- build_conn()
- |> post("/oauth/token", %{
- "grant_type" => "password",
- "username" => user.nickname,
- "password" => password,
- "client_id" => app.client_id,
- "client_secret" => app.client_secret
- })
-
- assert %{"access_token" => token} = json_response(conn, 200)
-
- token = Repo.get_by(Token, token: token) |> Repo.preload(:user)
-
- assert token.user.nickname == user.nickname
- assert_received :close_connection
- end
- end
-
- @tag @skip
- test "disallow authorization for wrong LDAP credentials" do
- password = "testpassword"
- user = insert(:user, password_hash: Pbkdf2.hash_pwd_salt(password))
- app = insert(:oauth_app, scopes: ["read", "write"])
-
- host = Pleroma.Config.get([:ldap, :host]) |> to_charlist
- port = Pleroma.Config.get([:ldap, :port])
-
- with_mocks [
- {:eldap, [],
- [
- open: fn [^host], [{:port, ^port}, {:ssl, false} | _] -> {:ok, self()} end,
- simple_bind: fn _connection, _dn, ^password -> {:error, :invalidCredentials} end,
- close: fn _connection ->
- send(self(), :close_connection)
- :ok
- end
- ]}
- ] do
- conn =
- build_conn()
- |> post("/oauth/token", %{
- "grant_type" => "password",
- "username" => user.nickname,
- "password" => password,
- "client_id" => app.client_id,
- "client_secret" => app.client_secret
- })
-
- assert %{"error" => "Invalid credentials"} = json_response(conn, 400)
- assert_received :close_connection
- end
- end
-end
diff --git a/test/web/oauth/mfa_controller_test.exs b/test/web/oauth/mfa_controller_test.exs
@@ -1,306 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2018 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Web.OAuth.MFAControllerTest do
- use Pleroma.Web.ConnCase
- import Pleroma.Factory
-
- alias Pleroma.MFA
- alias Pleroma.MFA.BackupCodes
- alias Pleroma.MFA.TOTP
- alias Pleroma.Repo
- alias Pleroma.Web.OAuth.Authorization
- alias Pleroma.Web.OAuth.OAuthController
-
- setup %{conn: conn} do
- otp_secret = TOTP.generate_secret()
-
- user =
- insert(:user,
- multi_factor_authentication_settings: %MFA.Settings{
- enabled: true,
- backup_codes: [Pbkdf2.hash_pwd_salt("test-code")],
- totp: %MFA.Settings.TOTP{secret: otp_secret, confirmed: true}
- }
- )
-
- app = insert(:oauth_app)
- {:ok, conn: conn, user: user, app: app}
- end
-
- describe "show" do
- setup %{conn: conn, user: user, app: app} do
- mfa_token =
- insert(:mfa_token,
- user: user,
- authorization: build(:oauth_authorization, app: app, scopes: ["write"])
- )
-
- {:ok, conn: conn, mfa_token: mfa_token}
- end
-
- test "GET /oauth/mfa renders mfa forms", %{conn: conn, mfa_token: mfa_token} do
- conn =
- get(
- conn,
- "/oauth/mfa",
- %{
- "mfa_token" => mfa_token.token,
- "state" => "a_state",
- "redirect_uri" => "http://localhost:8080/callback"
- }
- )
-
- assert response = html_response(conn, 200)
- assert response =~ "Two-factor authentication"
- assert response =~ mfa_token.token
- assert response =~ "http://localhost:8080/callback"
- end
-
- test "GET /oauth/mfa renders mfa recovery forms", %{conn: conn, mfa_token: mfa_token} do
- conn =
- get(
- conn,
- "/oauth/mfa",
- %{
- "mfa_token" => mfa_token.token,
- "state" => "a_state",
- "redirect_uri" => "http://localhost:8080/callback",
- "challenge_type" => "recovery"
- }
- )
-
- assert response = html_response(conn, 200)
- assert response =~ "Two-factor recovery"
- assert response =~ mfa_token.token
- assert response =~ "http://localhost:8080/callback"
- end
- end
-
- describe "verify" do
- setup %{conn: conn, user: user, app: app} do
- mfa_token =
- insert(:mfa_token,
- user: user,
- authorization: build(:oauth_authorization, app: app, scopes: ["write"])
- )
-
- {:ok, conn: conn, user: user, mfa_token: mfa_token, app: app}
- end
-
- test "POST /oauth/mfa/verify, verify totp code", %{
- conn: conn,
- user: user,
- mfa_token: mfa_token,
- app: app
- } do
- otp_token = TOTP.generate_token(user.multi_factor_authentication_settings.totp.secret)
-
- conn =
- conn
- |> post("/oauth/mfa/verify", %{
- "mfa" => %{
- "mfa_token" => mfa_token.token,
- "challenge_type" => "totp",
- "code" => otp_token,
- "state" => "a_state",
- "redirect_uri" => OAuthController.default_redirect_uri(app)
- }
- })
-
- target = redirected_to(conn)
- target_url = %URI{URI.parse(target) | query: nil} |> URI.to_string()
- query = URI.parse(target).query |> URI.query_decoder() |> Map.new()
- assert %{"state" => "a_state", "code" => code} = query
- assert target_url == OAuthController.default_redirect_uri(app)
- auth = Repo.get_by(Authorization, token: code)
- assert auth.scopes == ["write"]
- end
-
- test "POST /oauth/mfa/verify, verify recovery code", %{
- conn: conn,
- mfa_token: mfa_token,
- app: app
- } do
- conn =
- conn
- |> post("/oauth/mfa/verify", %{
- "mfa" => %{
- "mfa_token" => mfa_token.token,
- "challenge_type" => "recovery",
- "code" => "test-code",
- "state" => "a_state",
- "redirect_uri" => OAuthController.default_redirect_uri(app)
- }
- })
-
- target = redirected_to(conn)
- target_url = %URI{URI.parse(target) | query: nil} |> URI.to_string()
- query = URI.parse(target).query |> URI.query_decoder() |> Map.new()
- assert %{"state" => "a_state", "code" => code} = query
- assert target_url == OAuthController.default_redirect_uri(app)
- auth = Repo.get_by(Authorization, token: code)
- assert auth.scopes == ["write"]
- end
- end
-
- describe "challenge/totp" do
- test "returns access token with valid code", %{conn: conn, user: user, app: app} do
- otp_token = TOTP.generate_token(user.multi_factor_authentication_settings.totp.secret)
-
- mfa_token =
- insert(:mfa_token,
- user: user,
- authorization: build(:oauth_authorization, app: app, scopes: ["write"])
- )
-
- response =
- conn
- |> post("/oauth/mfa/challenge", %{
- "mfa_token" => mfa_token.token,
- "challenge_type" => "totp",
- "code" => otp_token,
- "client_id" => app.client_id,
- "client_secret" => app.client_secret
- })
- |> json_response(:ok)
-
- ap_id = user.ap_id
-
- assert match?(
- %{
- "access_token" => _,
- "expires_in" => 600,
- "me" => ^ap_id,
- "refresh_token" => _,
- "scope" => "write",
- "token_type" => "Bearer"
- },
- response
- )
- end
-
- test "returns errors when mfa token invalid", %{conn: conn, user: user, app: app} do
- otp_token = TOTP.generate_token(user.multi_factor_authentication_settings.totp.secret)
-
- response =
- conn
- |> post("/oauth/mfa/challenge", %{
- "mfa_token" => "XXX",
- "challenge_type" => "totp",
- "code" => otp_token,
- "client_id" => app.client_id,
- "client_secret" => app.client_secret
- })
- |> json_response(400)
-
- assert response == %{"error" => "Invalid code"}
- end
-
- test "returns error when otp code is invalid", %{conn: conn, user: user, app: app} do
- mfa_token = insert(:mfa_token, user: user)
-
- response =
- conn
- |> post("/oauth/mfa/challenge", %{
- "mfa_token" => mfa_token.token,
- "challenge_type" => "totp",
- "code" => "XXX",
- "client_id" => app.client_id,
- "client_secret" => app.client_secret
- })
- |> json_response(400)
-
- assert response == %{"error" => "Invalid code"}
- end
-
- test "returns error when client credentails is wrong ", %{conn: conn, user: user} do
- otp_token = TOTP.generate_token(user.multi_factor_authentication_settings.totp.secret)
- mfa_token = insert(:mfa_token, user: user)
-
- response =
- conn
- |> post("/oauth/mfa/challenge", %{
- "mfa_token" => mfa_token.token,
- "challenge_type" => "totp",
- "code" => otp_token,
- "client_id" => "xxx",
- "client_secret" => "xxx"
- })
- |> json_response(400)
-
- assert response == %{"error" => "Invalid code"}
- end
- end
-
- describe "challenge/recovery" do
- setup %{conn: conn} do
- app = insert(:oauth_app)
- {:ok, conn: conn, app: app}
- end
-
- test "returns access token with valid code", %{conn: conn, app: app} do
- otp_secret = TOTP.generate_secret()
-
- [code | _] = backup_codes = BackupCodes.generate()
-
- hashed_codes =
- backup_codes
- |> Enum.map(&Pbkdf2.hash_pwd_salt(&1))
-
- user =
- insert(:user,
- multi_factor_authentication_settings: %MFA.Settings{
- enabled: true,
- backup_codes: hashed_codes,
- totp: %MFA.Settings.TOTP{secret: otp_secret, confirmed: true}
- }
- )
-
- mfa_token =
- insert(:mfa_token,
- user: user,
- authorization: build(:oauth_authorization, app: app, scopes: ["write"])
- )
-
- response =
- conn
- |> post("/oauth/mfa/challenge", %{
- "mfa_token" => mfa_token.token,
- "challenge_type" => "recovery",
- "code" => code,
- "client_id" => app.client_id,
- "client_secret" => app.client_secret
- })
- |> json_response(:ok)
-
- ap_id = user.ap_id
-
- assert match?(
- %{
- "access_token" => _,
- "expires_in" => 600,
- "me" => ^ap_id,
- "refresh_token" => _,
- "scope" => "write",
- "token_type" => "Bearer"
- },
- response
- )
-
- error_response =
- conn
- |> post("/oauth/mfa/challenge", %{
- "mfa_token" => mfa_token.token,
- "challenge_type" => "recovery",
- "code" => code,
- "client_id" => app.client_id,
- "client_secret" => app.client_secret
- })
- |> json_response(400)
-
- assert error_response == %{"error" => "Invalid code"}
- end
- end
-end
diff --git a/test/web/oauth/oauth_controller_test.exs b/test/web/oauth/oauth_controller_test.exs
@@ -1,1232 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Web.OAuth.OAuthControllerTest do
- use Pleroma.Web.ConnCase
- import Pleroma.Factory
-
- alias Pleroma.MFA
- alias Pleroma.MFA.TOTP
- alias Pleroma.Repo
- alias Pleroma.User
- alias Pleroma.Web.OAuth.Authorization
- alias Pleroma.Web.OAuth.OAuthController
- alias Pleroma.Web.OAuth.Token
-
- @session_opts [
- store: :cookie,
- key: "_test",
- signing_salt: "cooldude"
- ]
- setup do
- clear_config([:instance, :account_activation_required])
- clear_config([:instance, :account_approval_required])
- end
-
- describe "in OAuth consumer mode, " do
- setup do
- [
- app: insert(:oauth_app),
- conn:
- build_conn()
- |> Plug.Session.call(Plug.Session.init(@session_opts))
- |> fetch_session()
- ]
- end
-
- setup do: clear_config([:auth, :oauth_consumer_strategies], ~w(twitter facebook))
-
- test "GET /oauth/authorize renders auth forms, including OAuth consumer form", %{
- app: app,
- conn: conn
- } do
- conn =
- get(
- conn,
- "/oauth/authorize",
- %{
- "response_type" => "code",
- "client_id" => app.client_id,
- "redirect_uri" => OAuthController.default_redirect_uri(app),
- "scope" => "read"
- }
- )
-
- assert response = html_response(conn, 200)
- assert response =~ "Sign in with Twitter"
- assert response =~ o_auth_path(conn, :prepare_request)
- end
-
- test "GET /oauth/prepare_request encodes parameters as `state` and redirects", %{
- app: app,
- conn: conn
- } do
- conn =
- get(
- conn,
- "/oauth/prepare_request",
- %{
- "provider" => "twitter",
- "authorization" => %{
- "scope" => "read follow",
- "client_id" => app.client_id,
- "redirect_uri" => OAuthController.default_redirect_uri(app),
- "state" => "a_state"
- }
- }
- )
-
- assert response = html_response(conn, 302)
-
- redirect_query = URI.parse(redirected_to(conn)).query
- assert %{"state" => state_param} = URI.decode_query(redirect_query)
- assert {:ok, state_components} = Poison.decode(state_param)
-
- expected_client_id = app.client_id
- expected_redirect_uri = app.redirect_uris
-
- assert %{
- "scope" => "read follow",
- "client_id" => ^expected_client_id,
- "redirect_uri" => ^expected_redirect_uri,
- "state" => "a_state"
- } = state_components
- end
-
- test "with user-bound registration, GET /oauth/<provider>/callback redirects to `redirect_uri` with `code`",
- %{app: app, conn: conn} do
- registration = insert(:registration)
- redirect_uri = OAuthController.default_redirect_uri(app)
-
- state_params = %{
- "scope" => Enum.join(app.scopes, " "),
- "client_id" => app.client_id,
- "redirect_uri" => redirect_uri,
- "state" => ""
- }
-
- conn =
- conn
- |> assign(:ueberauth_auth, %{provider: registration.provider, uid: registration.uid})
- |> get(
- "/oauth/twitter/callback",
- %{
- "oauth_token" => "G-5a3AAAAAAAwMH9AAABaektfSM",
- "oauth_verifier" => "QZl8vUqNvXMTKpdmUnGejJxuHG75WWWs",
- "provider" => "twitter",
- "state" => Poison.encode!(state_params)
- }
- )
-
- assert response = html_response(conn, 302)
- assert redirected_to(conn) =~ ~r/#{redirect_uri}\?code=.+/
- end
-
- test "with user-unbound registration, GET /oauth/<provider>/callback renders registration_details page",
- %{app: app, conn: conn} do
- user = insert(:user)
-
- state_params = %{
- "scope" => "read write",
- "client_id" => app.client_id,
- "redirect_uri" => OAuthController.default_redirect_uri(app),
- "state" => "a_state"
- }
-
- conn =
- conn
- |> assign(:ueberauth_auth, %{
- provider: "twitter",
- uid: "171799000",
- info: %{nickname: user.nickname, email: user.email, name: user.name, description: nil}
- })
- |> get(
- "/oauth/twitter/callback",
- %{
- "oauth_token" => "G-5a3AAAAAAAwMH9AAABaektfSM",
- "oauth_verifier" => "QZl8vUqNvXMTKpdmUnGejJxuHG75WWWs",
- "provider" => "twitter",
- "state" => Poison.encode!(state_params)
- }
- )
-
- assert response = html_response(conn, 200)
- assert response =~ ~r/name="op" type="submit" value="register"/
- assert response =~ ~r/name="op" type="submit" value="connect"/
- assert response =~ user.email
- assert response =~ user.nickname
- end
-
- test "on authentication error, GET /oauth/<provider>/callback redirects to `redirect_uri`", %{
- app: app,
- conn: conn
- } do
- state_params = %{
- "scope" => Enum.join(app.scopes, " "),
- "client_id" => app.client_id,
- "redirect_uri" => OAuthController.default_redirect_uri(app),
- "state" => ""
- }
-
- conn =
- conn
- |> assign(:ueberauth_failure, %{errors: [%{message: "(error description)"}]})
- |> get(
- "/oauth/twitter/callback",
- %{
- "oauth_token" => "G-5a3AAAAAAAwMH9AAABaektfSM",
- "oauth_verifier" => "QZl8vUqNvXMTKpdmUnGejJxuHG75WWWs",
- "provider" => "twitter",
- "state" => Poison.encode!(state_params)
- }
- )
-
- assert response = html_response(conn, 302)
- assert redirected_to(conn) == app.redirect_uris
- assert get_flash(conn, :error) == "Failed to authenticate: (error description)."
- end
-
- test "GET /oauth/registration_details renders registration details form", %{
- app: app,
- conn: conn
- } do
- conn =
- get(
- conn,
- "/oauth/registration_details",
- %{
- "authorization" => %{
- "scopes" => app.scopes,
- "client_id" => app.client_id,
- "redirect_uri" => OAuthController.default_redirect_uri(app),
- "state" => "a_state",
- "nickname" => nil,
- "email" => "john@doe.com"
- }
- }
- )
-
- assert response = html_response(conn, 200)
- assert response =~ ~r/name="op" type="submit" value="register"/
- assert response =~ ~r/name="op" type="submit" value="connect"/
- end
-
- test "with valid params, POST /oauth/register?op=register redirects to `redirect_uri` with `code`",
- %{
- app: app,
- conn: conn
- } do
- registration = insert(:registration, user: nil, info: %{"nickname" => nil, "email" => nil})
- redirect_uri = OAuthController.default_redirect_uri(app)
-
- conn =
- conn
- |> put_session(:registration_id, registration.id)
- |> post(
- "/oauth/register",
- %{
- "op" => "register",
- "authorization" => %{
- "scopes" => app.scopes,
- "client_id" => app.client_id,
- "redirect_uri" => redirect_uri,
- "state" => "a_state",
- "nickname" => "availablenick",
- "email" => "available@email.com"
- }
- }
- )
-
- assert response = html_response(conn, 302)
- assert redirected_to(conn) =~ ~r/#{redirect_uri}\?code=.+/
- end
-
- test "with unlisted `redirect_uri`, POST /oauth/register?op=register results in HTTP 401",
- %{
- app: app,
- conn: conn
- } do
- registration = insert(:registration, user: nil, info: %{"nickname" => nil, "email" => nil})
- unlisted_redirect_uri = "http://cross-site-request.com"
-
- conn =
- conn
- |> put_session(:registration_id, registration.id)
- |> post(
- "/oauth/register",
- %{
- "op" => "register",
- "authorization" => %{
- "scopes" => app.scopes,
- "client_id" => app.client_id,
- "redirect_uri" => unlisted_redirect_uri,
- "state" => "a_state",
- "nickname" => "availablenick",
- "email" => "available@email.com"
- }
- }
- )
-
- assert response = html_response(conn, 401)
- end
-
- test "with invalid params, POST /oauth/register?op=register renders registration_details page",
- %{
- app: app,
- conn: conn
- } do
- another_user = insert(:user)
- registration = insert(:registration, user: nil, info: %{"nickname" => nil, "email" => nil})
-
- params = %{
- "op" => "register",
- "authorization" => %{
- "scopes" => app.scopes,
- "client_id" => app.client_id,
- "redirect_uri" => OAuthController.default_redirect_uri(app),
- "state" => "a_state",
- "nickname" => "availablenickname",
- "email" => "available@email.com"
- }
- }
-
- for {bad_param, bad_param_value} <-
- [{"nickname", another_user.nickname}, {"email", another_user.email}] do
- bad_registration_attrs = %{
- "authorization" => Map.put(params["authorization"], bad_param, bad_param_value)
- }
-
- bad_params = Map.merge(params, bad_registration_attrs)
-
- conn =
- conn
- |> put_session(:registration_id, registration.id)
- |> post("/oauth/register", bad_params)
-
- assert html_response(conn, 403) =~ ~r/name="op" type="submit" value="register"/
- assert get_flash(conn, :error) == "Error: #{bad_param} has already been taken."
- end
- end
-
- test "with valid params, POST /oauth/register?op=connect redirects to `redirect_uri` with `code`",
- %{
- app: app,
- conn: conn
- } do
- user = insert(:user, password_hash: Pbkdf2.hash_pwd_salt("testpassword"))
- registration = insert(:registration, user: nil)
- redirect_uri = OAuthController.default_redirect_uri(app)
-
- conn =
- conn
- |> put_session(:registration_id, registration.id)
- |> post(
- "/oauth/register",
- %{
- "op" => "connect",
- "authorization" => %{
- "scopes" => app.scopes,
- "client_id" => app.client_id,
- "redirect_uri" => redirect_uri,
- "state" => "a_state",
- "name" => user.nickname,
- "password" => "testpassword"
- }
- }
- )
-
- assert response = html_response(conn, 302)
- assert redirected_to(conn) =~ ~r/#{redirect_uri}\?code=.+/
- end
-
- test "with unlisted `redirect_uri`, POST /oauth/register?op=connect results in HTTP 401`",
- %{
- app: app,
- conn: conn
- } do
- user = insert(:user, password_hash: Pbkdf2.hash_pwd_salt("testpassword"))
- registration = insert(:registration, user: nil)
- unlisted_redirect_uri = "http://cross-site-request.com"
-
- conn =
- conn
- |> put_session(:registration_id, registration.id)
- |> post(
- "/oauth/register",
- %{
- "op" => "connect",
- "authorization" => %{
- "scopes" => app.scopes,
- "client_id" => app.client_id,
- "redirect_uri" => unlisted_redirect_uri,
- "state" => "a_state",
- "name" => user.nickname,
- "password" => "testpassword"
- }
- }
- )
-
- assert response = html_response(conn, 401)
- end
-
- test "with invalid params, POST /oauth/register?op=connect renders registration_details page",
- %{
- app: app,
- conn: conn
- } do
- user = insert(:user)
- registration = insert(:registration, user: nil)
-
- params = %{
- "op" => "connect",
- "authorization" => %{
- "scopes" => app.scopes,
- "client_id" => app.client_id,
- "redirect_uri" => OAuthController.default_redirect_uri(app),
- "state" => "a_state",
- "name" => user.nickname,
- "password" => "wrong password"
- }
- }
-
- conn =
- conn
- |> put_session(:registration_id, registration.id)
- |> post("/oauth/register", params)
-
- assert html_response(conn, 401) =~ ~r/name="op" type="submit" value="connect"/
- assert get_flash(conn, :error) == "Invalid Username/Password"
- end
- end
-
- describe "GET /oauth/authorize" do
- setup do
- [
- app: insert(:oauth_app, redirect_uris: "https://redirect.url"),
- conn:
- build_conn()
- |> Plug.Session.call(Plug.Session.init(@session_opts))
- |> fetch_session()
- ]
- end
-
- test "renders authentication page", %{app: app, conn: conn} do
- conn =
- get(
- conn,
- "/oauth/authorize",
- %{
- "response_type" => "code",
- "client_id" => app.client_id,
- "redirect_uri" => OAuthController.default_redirect_uri(app),
- "scope" => "read"
- }
- )
-
- assert html_response(conn, 200) =~ ~s(type="submit")
- end
-
- test "properly handles internal calls with `authorization`-wrapped params", %{
- app: app,
- conn: conn
- } do
- conn =
- get(
- conn,
- "/oauth/authorize",
- %{
- "authorization" => %{
- "response_type" => "code",
- "client_id" => app.client_id,
- "redirect_uri" => OAuthController.default_redirect_uri(app),
- "scope" => "read"
- }
- }
- )
-
- assert html_response(conn, 200) =~ ~s(type="submit")
- end
-
- test "renders authentication page if user is already authenticated but `force_login` is tru-ish",
- %{app: app, conn: conn} do
- token = insert(:oauth_token, app: app)
-
- conn =
- conn
- |> put_session(:oauth_token, token.token)
- |> get(
- "/oauth/authorize",
- %{
- "response_type" => "code",
- "client_id" => app.client_id,
- "redirect_uri" => OAuthController.default_redirect_uri(app),
- "scope" => "read",
- "force_login" => "true"
- }
- )
-
- assert html_response(conn, 200) =~ ~s(type="submit")
- end
-
- test "renders authentication page if user is already authenticated but user request with another client",
- %{
- app: app,
- conn: conn
- } do
- token = insert(:oauth_token, app: app)
-
- conn =
- conn
- |> put_session(:oauth_token, token.token)
- |> get(
- "/oauth/authorize",
- %{
- "response_type" => "code",
- "client_id" => "another_client_id",
- "redirect_uri" => OAuthController.default_redirect_uri(app),
- "scope" => "read"
- }
- )
-
- assert html_response(conn, 200) =~ ~s(type="submit")
- end
-
- test "with existing authentication and non-OOB `redirect_uri`, redirects to app with `token` and `state` params",
- %{
- app: app,
- conn: conn
- } do
- token = insert(:oauth_token, app: app)
-
- conn =
- conn
- |> put_session(:oauth_token, token.token)
- |> get(
- "/oauth/authorize",
- %{
- "response_type" => "code",
- "client_id" => app.client_id,
- "redirect_uri" => OAuthController.default_redirect_uri(app),
- "state" => "specific_client_state",
- "scope" => "read"
- }
- )
-
- assert URI.decode(redirected_to(conn)) ==
- "https://redirect.url?access_token=#{token.token}&state=specific_client_state"
- end
-
- test "with existing authentication and unlisted non-OOB `redirect_uri`, redirects without credentials",
- %{
- app: app,
- conn: conn
- } do
- unlisted_redirect_uri = "http://cross-site-request.com"
- token = insert(:oauth_token, app: app)
-
- conn =
- conn
- |> put_session(:oauth_token, token.token)
- |> get(
- "/oauth/authorize",
- %{
- "response_type" => "code",
- "client_id" => app.client_id,
- "redirect_uri" => unlisted_redirect_uri,
- "state" => "specific_client_state",
- "scope" => "read"
- }
- )
-
- assert redirected_to(conn) == unlisted_redirect_uri
- end
-
- test "with existing authentication and OOB `redirect_uri`, redirects to app with `token` and `state` params",
- %{
- app: app,
- conn: conn
- } do
- token = insert(:oauth_token, app: app)
-
- conn =
- conn
- |> put_session(:oauth_token, token.token)
- |> get(
- "/oauth/authorize",
- %{
- "response_type" => "code",
- "client_id" => app.client_id,
- "redirect_uri" => "urn:ietf:wg:oauth:2.0:oob",
- "scope" => "read"
- }
- )
-
- assert html_response(conn, 200) =~ "Authorization exists"
- end
- end
-
- describe "POST /oauth/authorize" do
- test "redirects with oauth authorization, " <>
- "granting requested app-supported scopes to both admin- and non-admin users" do
- app_scopes = ["read", "write", "admin", "secret_scope"]
- app = insert(:oauth_app, scopes: app_scopes)
- redirect_uri = OAuthController.default_redirect_uri(app)
-
- non_admin = insert(:user, is_admin: false)
- admin = insert(:user, is_admin: true)
- scopes_subset = ["read:subscope", "write", "admin"]
-
- # In case scope param is missing, expecting _all_ app-supported scopes to be granted
- for user <- [non_admin, admin],
- {requested_scopes, expected_scopes} <-
- %{scopes_subset => scopes_subset, nil: app_scopes} do
- conn =
- post(
- build_conn(),
- "/oauth/authorize",
- %{
- "authorization" => %{
- "name" => user.nickname,
- "password" => "test",
- "client_id" => app.client_id,
- "redirect_uri" => redirect_uri,
- "scope" => requested_scopes,
- "state" => "statepassed"
- }
- }
- )
-
- target = redirected_to(conn)
- assert target =~ redirect_uri
-
- query = URI.parse(target).query |> URI.query_decoder() |> Map.new()
-
- assert %{"state" => "statepassed", "code" => code} = query
- auth = Repo.get_by(Authorization, token: code)
- assert auth
- assert auth.scopes == expected_scopes
- end
- end
-
- test "redirect to on two-factor auth page" do
- otp_secret = TOTP.generate_secret()
-
- user =
- insert(:user,
- multi_factor_authentication_settings: %MFA.Settings{
- enabled: true,
- totp: %MFA.Settings.TOTP{secret: otp_secret, confirmed: true}
- }
- )
-
- app = insert(:oauth_app, scopes: ["read", "write", "follow"])
-
- conn =
- build_conn()
- |> post("/oauth/authorize", %{
- "authorization" => %{
- "name" => user.nickname,
- "password" => "test",
- "client_id" => app.client_id,
- "redirect_uri" => app.redirect_uris,
- "scope" => "read write",
- "state" => "statepassed"
- }
- })
-
- result = html_response(conn, 200)
-
- mfa_token = Repo.get_by(MFA.Token, user_id: user.id)
- assert result =~ app.redirect_uris
- assert result =~ "statepassed"
- assert result =~ mfa_token.token
- assert result =~ "Two-factor authentication"
- end
-
- test "returns 401 for wrong credentials", %{conn: conn} do
- user = insert(:user)
- app = insert(:oauth_app)
- redirect_uri = OAuthController.default_redirect_uri(app)
-
- result =
- conn
- |> post("/oauth/authorize", %{
- "authorization" => %{
- "name" => user.nickname,
- "password" => "wrong",
- "client_id" => app.client_id,
- "redirect_uri" => redirect_uri,
- "state" => "statepassed",
- "scope" => Enum.join(app.scopes, " ")
- }
- })
- |> html_response(:unauthorized)
-
- # Keep the details
- assert result =~ app.client_id
- assert result =~ redirect_uri
-
- # Error message
- assert result =~ "Invalid Username/Password"
- end
-
- test "returns 401 for missing scopes" do
- user = insert(:user, is_admin: false)
- app = insert(:oauth_app, scopes: ["read", "write", "admin"])
- redirect_uri = OAuthController.default_redirect_uri(app)
-
- result =
- build_conn()
- |> post("/oauth/authorize", %{
- "authorization" => %{
- "name" => user.nickname,
- "password" => "test",
- "client_id" => app.client_id,
- "redirect_uri" => redirect_uri,
- "state" => "statepassed",
- "scope" => ""
- }
- })
- |> html_response(:unauthorized)
-
- # Keep the details
- assert result =~ app.client_id
- assert result =~ redirect_uri
-
- # Error message
- assert result =~ "This action is outside the authorized scopes"
- end
-
- test "returns 401 for scopes beyond app scopes hierarchy", %{conn: conn} do
- user = insert(:user)
- app = insert(:oauth_app, scopes: ["read", "write"])
- redirect_uri = OAuthController.default_redirect_uri(app)
-
- result =
- conn
- |> post("/oauth/authorize", %{
- "authorization" => %{
- "name" => user.nickname,
- "password" => "test",
- "client_id" => app.client_id,
- "redirect_uri" => redirect_uri,
- "state" => "statepassed",
- "scope" => "read write follow"
- }
- })
- |> html_response(:unauthorized)
-
- # Keep the details
- assert result =~ app.client_id
- assert result =~ redirect_uri
-
- # Error message
- assert result =~ "This action is outside the authorized scopes"
- end
- end
-
- describe "POST /oauth/token" do
- test "issues a token for an all-body request" do
- user = insert(:user)
- app = insert(:oauth_app, scopes: ["read", "write"])
-
- {:ok, auth} = Authorization.create_authorization(app, user, ["write"])
-
- conn =
- build_conn()
- |> post("/oauth/token", %{
- "grant_type" => "authorization_code",
- "code" => auth.token,
- "redirect_uri" => OAuthController.default_redirect_uri(app),
- "client_id" => app.client_id,
- "client_secret" => app.client_secret
- })
-
- assert %{"access_token" => token, "me" => ap_id} = json_response(conn, 200)
-
- token = Repo.get_by(Token, token: token)
- assert token
- assert token.scopes == auth.scopes
- assert user.ap_id == ap_id
- end
-
- test "issues a token for `password` grant_type with valid credentials, with full permissions by default" do
- password = "testpassword"
- user = insert(:user, password_hash: Pbkdf2.hash_pwd_salt(password))
-
- app = insert(:oauth_app, scopes: ["read", "write"])
-
- # Note: "scope" param is intentionally omitted
- conn =
- build_conn()
- |> post("/oauth/token", %{
- "grant_type" => "password",
- "username" => user.nickname,
- "password" => password,
- "client_id" => app.client_id,
- "client_secret" => app.client_secret
- })
-
- assert %{"access_token" => token} = json_response(conn, 200)
-
- token = Repo.get_by(Token, token: token)
- assert token
- assert token.scopes == app.scopes
- end
-
- test "issues a mfa token for `password` grant_type, when MFA enabled" do
- password = "testpassword"
- otp_secret = TOTP.generate_secret()
-
- user =
- insert(:user,
- password_hash: Pbkdf2.hash_pwd_salt(password),
- multi_factor_authentication_settings: %MFA.Settings{
- enabled: true,
- totp: %MFA.Settings.TOTP{secret: otp_secret, confirmed: true}
- }
- )
-
- app = insert(:oauth_app, scopes: ["read", "write"])
-
- response =
- build_conn()
- |> post("/oauth/token", %{
- "grant_type" => "password",
- "username" => user.nickname,
- "password" => password,
- "client_id" => app.client_id,
- "client_secret" => app.client_secret
- })
- |> json_response(403)
-
- assert match?(
- %{
- "supported_challenge_types" => "totp",
- "mfa_token" => _,
- "error" => "mfa_required"
- },
- response
- )
-
- token = Repo.get_by(MFA.Token, token: response["mfa_token"])
- assert token.user_id == user.id
- assert token.authorization_id
- end
-
- test "issues a token for request with HTTP basic auth client credentials" do
- user = insert(:user)
- app = insert(:oauth_app, scopes: ["scope1", "scope2", "scope3"])
-
- {:ok, auth} = Authorization.create_authorization(app, user, ["scope1", "scope2"])
- assert auth.scopes == ["scope1", "scope2"]
-
- app_encoded =
- (URI.encode_www_form(app.client_id) <> ":" <> URI.encode_www_form(app.client_secret))
- |> Base.encode64()
-
- conn =
- build_conn()
- |> put_req_header("authorization", "Basic " <> app_encoded)
- |> post("/oauth/token", %{
- "grant_type" => "authorization_code",
- "code" => auth.token,
- "redirect_uri" => OAuthController.default_redirect_uri(app)
- })
-
- assert %{"access_token" => token, "scope" => scope} = json_response(conn, 200)
-
- assert scope == "scope1 scope2"
-
- token = Repo.get_by(Token, token: token)
- assert token
- assert token.scopes == ["scope1", "scope2"]
- end
-
- test "issue a token for client_credentials grant type" do
- app = insert(:oauth_app, scopes: ["read", "write"])
-
- conn =
- build_conn()
- |> post("/oauth/token", %{
- "grant_type" => "client_credentials",
- "client_id" => app.client_id,
- "client_secret" => app.client_secret
- })
-
- assert %{"access_token" => token, "refresh_token" => refresh, "scope" => scope} =
- json_response(conn, 200)
-
- assert token
- token_from_db = Repo.get_by(Token, token: token)
- assert token_from_db
- assert refresh
- assert scope == "read write"
- end
-
- test "rejects token exchange with invalid client credentials" do
- user = insert(:user)
- app = insert(:oauth_app)
-
- {:ok, auth} = Authorization.create_authorization(app, user)
-
- conn =
- build_conn()
- |> put_req_header("authorization", "Basic JTIxOiVGMCU5RiVBNCVCNwo=")
- |> post("/oauth/token", %{
- "grant_type" => "authorization_code",
- "code" => auth.token,
- "redirect_uri" => OAuthController.default_redirect_uri(app)
- })
-
- assert resp = json_response(conn, 400)
- assert %{"error" => _} = resp
- refute Map.has_key?(resp, "access_token")
- end
-
- test "rejects token exchange for valid credentials belonging to unconfirmed user and confirmation is required" do
- Pleroma.Config.put([:instance, :account_activation_required], true)
- password = "testpassword"
-
- {:ok, user} =
- insert(:user, password_hash: Pbkdf2.hash_pwd_salt(password))
- |> User.confirmation_changeset(need_confirmation: true)
- |> User.update_and_set_cache()
-
- refute Pleroma.User.account_status(user) == :active
-
- app = insert(:oauth_app)
-
- conn =
- build_conn()
- |> post("/oauth/token", %{
- "grant_type" => "password",
- "username" => user.nickname,
- "password" => password,
- "client_id" => app.client_id,
- "client_secret" => app.client_secret
- })
-
- assert resp = json_response(conn, 403)
- assert %{"error" => _} = resp
- refute Map.has_key?(resp, "access_token")
- end
-
- test "rejects token exchange for valid credentials belonging to deactivated user" do
- password = "testpassword"
-
- user =
- insert(:user,
- password_hash: Pbkdf2.hash_pwd_salt(password),
- deactivated: true
- )
-
- app = insert(:oauth_app)
-
- resp =
- build_conn()
- |> post("/oauth/token", %{
- "grant_type" => "password",
- "username" => user.nickname,
- "password" => password,
- "client_id" => app.client_id,
- "client_secret" => app.client_secret
- })
- |> json_response(403)
-
- assert resp == %{
- "error" => "Your account is currently disabled",
- "identifier" => "account_is_disabled"
- }
- end
-
- test "rejects token exchange for user with password_reset_pending set to true" do
- password = "testpassword"
-
- user =
- insert(:user,
- password_hash: Pbkdf2.hash_pwd_salt(password),
- password_reset_pending: true
- )
-
- app = insert(:oauth_app, scopes: ["read", "write"])
-
- resp =
- build_conn()
- |> post("/oauth/token", %{
- "grant_type" => "password",
- "username" => user.nickname,
- "password" => password,
- "client_id" => app.client_id,
- "client_secret" => app.client_secret
- })
- |> json_response(403)
-
- assert resp == %{
- "error" => "Password reset is required",
- "identifier" => "password_reset_required"
- }
- end
-
- test "rejects token exchange for user with confirmation_pending set to true" do
- Pleroma.Config.put([:instance, :account_activation_required], true)
- password = "testpassword"
-
- user =
- insert(:user,
- password_hash: Pbkdf2.hash_pwd_salt(password),
- confirmation_pending: true
- )
-
- app = insert(:oauth_app, scopes: ["read", "write"])
-
- resp =
- build_conn()
- |> post("/oauth/token", %{
- "grant_type" => "password",
- "username" => user.nickname,
- "password" => password,
- "client_id" => app.client_id,
- "client_secret" => app.client_secret
- })
- |> json_response(403)
-
- assert resp == %{
- "error" => "Your login is missing a confirmed e-mail address",
- "identifier" => "missing_confirmed_email"
- }
- end
-
- test "rejects token exchange for valid credentials belonging to an unapproved user" do
- password = "testpassword"
-
- user = insert(:user, password_hash: Pbkdf2.hash_pwd_salt(password), approval_pending: true)
-
- refute Pleroma.User.account_status(user) == :active
-
- app = insert(:oauth_app)
-
- conn =
- build_conn()
- |> post("/oauth/token", %{
- "grant_type" => "password",
- "username" => user.nickname,
- "password" => password,
- "client_id" => app.client_id,
- "client_secret" => app.client_secret
- })
-
- assert resp = json_response(conn, 403)
- assert %{"error" => _} = resp
- refute Map.has_key?(resp, "access_token")
- end
-
- test "rejects an invalid authorization code" do
- app = insert(:oauth_app)
-
- conn =
- build_conn()
- |> post("/oauth/token", %{
- "grant_type" => "authorization_code",
- "code" => "Imobviouslyinvalid",
- "redirect_uri" => OAuthController.default_redirect_uri(app),
- "client_id" => app.client_id,
- "client_secret" => app.client_secret
- })
-
- assert resp = json_response(conn, 400)
- assert %{"error" => _} = json_response(conn, 400)
- refute Map.has_key?(resp, "access_token")
- end
- end
-
- describe "POST /oauth/token - refresh token" do
- setup do: clear_config([:oauth2, :issue_new_refresh_token])
-
- test "issues a new access token with keep fresh token" do
- Pleroma.Config.put([:oauth2, :issue_new_refresh_token], true)
- user = insert(:user)
- app = insert(:oauth_app, scopes: ["read", "write"])
-
- {:ok, auth} = Authorization.create_authorization(app, user, ["write"])
- {:ok, token} = Token.exchange_token(app, auth)
-
- response =
- build_conn()
- |> post("/oauth/token", %{
- "grant_type" => "refresh_token",
- "refresh_token" => token.refresh_token,
- "client_id" => app.client_id,
- "client_secret" => app.client_secret
- })
- |> json_response(200)
-
- ap_id = user.ap_id
-
- assert match?(
- %{
- "scope" => "write",
- "token_type" => "Bearer",
- "expires_in" => 600,
- "access_token" => _,
- "refresh_token" => _,
- "me" => ^ap_id
- },
- response
- )
-
- refute Repo.get_by(Token, token: token.token)
- new_token = Repo.get_by(Token, token: response["access_token"])
- assert new_token.refresh_token == token.refresh_token
- assert new_token.scopes == auth.scopes
- assert new_token.user_id == user.id
- assert new_token.app_id == app.id
- end
-
- test "issues a new access token with new fresh token" do
- Pleroma.Config.put([:oauth2, :issue_new_refresh_token], false)
- user = insert(:user)
- app = insert(:oauth_app, scopes: ["read", "write"])
-
- {:ok, auth} = Authorization.create_authorization(app, user, ["write"])
- {:ok, token} = Token.exchange_token(app, auth)
-
- response =
- build_conn()
- |> post("/oauth/token", %{
- "grant_type" => "refresh_token",
- "refresh_token" => token.refresh_token,
- "client_id" => app.client_id,
- "client_secret" => app.client_secret
- })
- |> json_response(200)
-
- ap_id = user.ap_id
-
- assert match?(
- %{
- "scope" => "write",
- "token_type" => "Bearer",
- "expires_in" => 600,
- "access_token" => _,
- "refresh_token" => _,
- "me" => ^ap_id
- },
- response
- )
-
- refute Repo.get_by(Token, token: token.token)
- new_token = Repo.get_by(Token, token: response["access_token"])
- refute new_token.refresh_token == token.refresh_token
- assert new_token.scopes == auth.scopes
- assert new_token.user_id == user.id
- assert new_token.app_id == app.id
- end
-
- test "returns 400 if we try use access token" do
- user = insert(:user)
- app = insert(:oauth_app, scopes: ["read", "write"])
-
- {:ok, auth} = Authorization.create_authorization(app, user, ["write"])
- {:ok, token} = Token.exchange_token(app, auth)
-
- response =
- build_conn()
- |> post("/oauth/token", %{
- "grant_type" => "refresh_token",
- "refresh_token" => token.token,
- "client_id" => app.client_id,
- "client_secret" => app.client_secret
- })
- |> json_response(400)
-
- assert %{"error" => "Invalid credentials"} == response
- end
-
- test "returns 400 if refresh_token invalid" do
- app = insert(:oauth_app, scopes: ["read", "write"])
-
- response =
- build_conn()
- |> post("/oauth/token", %{
- "grant_type" => "refresh_token",
- "refresh_token" => "token.refresh_token",
- "client_id" => app.client_id,
- "client_secret" => app.client_secret
- })
- |> json_response(400)
-
- assert %{"error" => "Invalid credentials"} == response
- end
-
- test "issues a new token if token expired" do
- user = insert(:user)
- app = insert(:oauth_app, scopes: ["read", "write"])
-
- {:ok, auth} = Authorization.create_authorization(app, user, ["write"])
- {:ok, token} = Token.exchange_token(app, auth)
-
- change =
- Ecto.Changeset.change(
- token,
- %{valid_until: NaiveDateTime.add(NaiveDateTime.utc_now(), -86_400 * 30)}
- )
-
- {:ok, access_token} = Repo.update(change)
-
- response =
- build_conn()
- |> post("/oauth/token", %{
- "grant_type" => "refresh_token",
- "refresh_token" => access_token.refresh_token,
- "client_id" => app.client_id,
- "client_secret" => app.client_secret
- })
- |> json_response(200)
-
- ap_id = user.ap_id
-
- assert match?(
- %{
- "scope" => "write",
- "token_type" => "Bearer",
- "expires_in" => 600,
- "access_token" => _,
- "refresh_token" => _,
- "me" => ^ap_id
- },
- response
- )
-
- refute Repo.get_by(Token, token: token.token)
- token = Repo.get_by(Token, token: response["access_token"])
- assert token
- assert token.scopes == auth.scopes
- assert token.user_id == user.id
- assert token.app_id == app.id
- end
- end
-
- describe "POST /oauth/token - bad request" do
- test "returns 500" do
- response =
- build_conn()
- |> post("/oauth/token", %{})
- |> json_response(500)
-
- assert %{"error" => "Bad request"} == response
- end
- end
-
- describe "POST /oauth/revoke - bad request" do
- test "returns 500" do
- response =
- build_conn()
- |> post("/oauth/revoke", %{})
- |> json_response(500)
-
- assert %{"error" => "Bad request"} == response
- end
- end
-end
diff --git a/test/web/oauth/token/utils_test.exs b/test/web/oauth/token/utils_test.exs
@@ -1,53 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Web.OAuth.Token.UtilsTest do
- use Pleroma.DataCase
- alias Pleroma.Web.OAuth.Token.Utils
- import Pleroma.Factory
-
- describe "fetch_app/1" do
- test "returns error when credentials is invalid" do
- assert {:error, :not_found} =
- Utils.fetch_app(%Plug.Conn{params: %{"client_id" => 1, "client_secret" => "x"}})
- end
-
- test "returns App by params credentails" do
- app = insert(:oauth_app)
-
- assert {:ok, load_app} =
- Utils.fetch_app(%Plug.Conn{
- params: %{"client_id" => app.client_id, "client_secret" => app.client_secret}
- })
-
- assert load_app == app
- end
-
- test "returns App by header credentails" do
- app = insert(:oauth_app)
- header = "Basic " <> Base.encode64("#{app.client_id}:#{app.client_secret}")
-
- conn =
- %Plug.Conn{}
- |> Plug.Conn.put_req_header("authorization", header)
-
- assert {:ok, load_app} = Utils.fetch_app(conn)
- assert load_app == app
- end
- end
-
- describe "format_created_at/1" do
- test "returns formatted created at" do
- token = insert(:oauth_token)
- date = Utils.format_created_at(token)
-
- token_date =
- token.inserted_at
- |> DateTime.from_naive!("Etc/UTC")
- |> DateTime.to_unix()
-
- assert token_date == date
- end
- end
-end
diff --git a/test/web/oauth/token_test.exs b/test/web/oauth/token_test.exs
@@ -1,72 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Web.OAuth.TokenTest do
- use Pleroma.DataCase
- alias Pleroma.Repo
- alias Pleroma.Web.OAuth.App
- alias Pleroma.Web.OAuth.Authorization
- alias Pleroma.Web.OAuth.Token
-
- import Pleroma.Factory
-
- test "exchanges a auth token for an access token, preserving `scopes`" do
- {:ok, app} =
- Repo.insert(
- App.register_changeset(%App{}, %{
- client_name: "client",
- scopes: ["read", "write"],
- redirect_uris: "url"
- })
- )
-
- user = insert(:user)
-
- {:ok, auth} = Authorization.create_authorization(app, user, ["read"])
- assert auth.scopes == ["read"]
-
- {:ok, token} = Token.exchange_token(app, auth)
-
- assert token.app_id == app.id
- assert token.user_id == user.id
- assert token.scopes == auth.scopes
- assert String.length(token.token) > 10
- assert String.length(token.refresh_token) > 10
-
- auth = Repo.get(Authorization, auth.id)
- {:error, "already used"} = Token.exchange_token(app, auth)
- end
-
- test "deletes all tokens of a user" do
- {:ok, app1} =
- Repo.insert(
- App.register_changeset(%App{}, %{
- client_name: "client1",
- scopes: ["scope"],
- redirect_uris: "url"
- })
- )
-
- {:ok, app2} =
- Repo.insert(
- App.register_changeset(%App{}, %{
- client_name: "client2",
- scopes: ["scope"],
- redirect_uris: "url"
- })
- )
-
- user = insert(:user)
-
- {:ok, auth1} = Authorization.create_authorization(app1, user)
- {:ok, auth2} = Authorization.create_authorization(app2, user)
-
- {:ok, _token1} = Token.exchange_token(app1, auth1)
- {:ok, _token2} = Token.exchange_token(app2, auth2)
-
- {tokens, _} = Token.delete_user_tokens(user)
-
- assert tokens == 2
- end
-end
diff --git a/test/web/ostatus/ostatus_controller_test.exs b/test/web/ostatus/ostatus_controller_test.exs
@@ -1,338 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Web.OStatus.OStatusControllerTest do
- use Pleroma.Web.ConnCase
-
- import Pleroma.Factory
-
- alias Pleroma.Config
- alias Pleroma.Object
- alias Pleroma.User
- alias Pleroma.Web.ActivityPub.ActivityPub
- alias Pleroma.Web.CommonAPI
- alias Pleroma.Web.Endpoint
-
- require Pleroma.Constants
-
- setup_all do
- Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end)
- :ok
- end
-
- setup do: clear_config([:instance, :federating], true)
-
- describe "Mastodon compatibility routes" do
- setup %{conn: conn} do
- conn = put_req_header(conn, "accept", "text/html")
-
- {:ok, object} =
- %{
- "type" => "Note",
- "content" => "hey",
- "id" => Endpoint.url() <> "/users/raymoo/statuses/999999999",
- "actor" => Endpoint.url() <> "/users/raymoo",
- "to" => [Pleroma.Constants.as_public()]
- }
- |> Object.create()
-
- {:ok, activity, _} =
- %{
- "id" => object.data["id"] <> "/activity",
- "type" => "Create",
- "object" => object.data["id"],
- "actor" => object.data["actor"],
- "to" => object.data["to"]
- }
- |> ActivityPub.persist(local: true)
-
- %{conn: conn, activity: activity}
- end
-
- test "redirects to /notice/:id for html format", %{conn: conn, activity: activity} do
- conn = get(conn, "/users/raymoo/statuses/999999999")
- assert redirected_to(conn) == "/notice/#{activity.id}"
- end
-
- test "redirects to /notice/:id for html format for activity", %{
- conn: conn,
- activity: activity
- } do
- conn = get(conn, "/users/raymoo/statuses/999999999/activity")
- assert redirected_to(conn) == "/notice/#{activity.id}"
- end
- end
-
- # Note: see ActivityPubControllerTest for JSON format tests
- describe "GET /objects/:uuid (text/html)" do
- setup %{conn: conn} do
- conn = put_req_header(conn, "accept", "text/html")
- %{conn: conn}
- end
-
- test "redirects to /notice/id for html format", %{conn: conn} do
- note_activity = insert(:note_activity)
- object = Object.normalize(note_activity)
- [_, uuid] = hd(Regex.scan(~r/.+\/([\w-]+)$/, object.data["id"]))
- url = "/objects/#{uuid}"
-
- conn = get(conn, url)
- assert redirected_to(conn) == "/notice/#{note_activity.id}"
- end
-
- test "404s on private objects", %{conn: conn} do
- note_activity = insert(:direct_note_activity)
- object = Object.normalize(note_activity)
- [_, uuid] = hd(Regex.scan(~r/.+\/([\w-]+)$/, object.data["id"]))
-
- conn
- |> get("/objects/#{uuid}")
- |> response(404)
- end
-
- test "404s on non-existing objects", %{conn: conn} do
- conn
- |> get("/objects/123")
- |> response(404)
- end
- end
-
- # Note: see ActivityPubControllerTest for JSON format tests
- describe "GET /activities/:uuid (text/html)" do
- setup %{conn: conn} do
- conn = put_req_header(conn, "accept", "text/html")
- %{conn: conn}
- end
-
- test "redirects to /notice/id for html format", %{conn: conn} do
- note_activity = insert(:note_activity)
- [_, uuid] = hd(Regex.scan(~r/.+\/([\w-]+)$/, note_activity.data["id"]))
-
- conn = get(conn, "/activities/#{uuid}")
- assert redirected_to(conn) == "/notice/#{note_activity.id}"
- end
-
- test "404s on private activities", %{conn: conn} do
- note_activity = insert(:direct_note_activity)
- [_, uuid] = hd(Regex.scan(~r/.+\/([\w-]+)$/, note_activity.data["id"]))
-
- conn
- |> get("/activities/#{uuid}")
- |> response(404)
- end
-
- test "404s on nonexistent activities", %{conn: conn} do
- conn
- |> get("/activities/123")
- |> response(404)
- end
- end
-
- describe "GET notice/2" do
- test "redirects to a proper object URL when json requested and the object is local", %{
- conn: conn
- } do
- note_activity = insert(:note_activity)
- expected_redirect_url = Object.normalize(note_activity).data["id"]
-
- redirect_url =
- conn
- |> put_req_header("accept", "application/activity+json")
- |> get("/notice/#{note_activity.id}")
- |> redirected_to()
-
- assert redirect_url == expected_redirect_url
- end
-
- test "returns a 404 on remote notice when json requested", %{conn: conn} do
- note_activity = insert(:note_activity, local: false)
-
- conn
- |> put_req_header("accept", "application/activity+json")
- |> get("/notice/#{note_activity.id}")
- |> response(404)
- end
-
- test "500s when actor not found", %{conn: conn} do
- note_activity = insert(:note_activity)
- user = User.get_cached_by_ap_id(note_activity.data["actor"])
- User.invalidate_cache(user)
- Pleroma.Repo.delete(user)
-
- conn =
- conn
- |> get("/notice/#{note_activity.id}")
-
- assert response(conn, 500) == ~S({"error":"Something went wrong"})
- end
-
- test "render html for redirect for html format", %{conn: conn} do
- note_activity = insert(:note_activity)
-
- resp =
- conn
- |> put_req_header("accept", "text/html")
- |> get("/notice/#{note_activity.id}")
- |> response(200)
-
- assert resp =~
- "<meta content=\"#{Pleroma.Web.base_url()}/notice/#{note_activity.id}\" property=\"og:url\">"
-
- user = insert(:user)
-
- {:ok, like_activity} = CommonAPI.favorite(user, note_activity.id)
-
- assert like_activity.data["type"] == "Like"
-
- resp =
- conn
- |> put_req_header("accept", "text/html")
- |> get("/notice/#{like_activity.id}")
- |> response(200)
-
- assert resp =~ "<!--server-generated-meta-->"
- end
-
- test "404s a private notice", %{conn: conn} do
- note_activity = insert(:direct_note_activity)
- url = "/notice/#{note_activity.id}"
-
- conn =
- conn
- |> get(url)
-
- assert response(conn, 404)
- end
-
- test "404s a non-existing notice", %{conn: conn} do
- url = "/notice/123"
-
- conn =
- conn
- |> get(url)
-
- assert response(conn, 404)
- end
-
- test "it requires authentication if instance is NOT federating", %{
- conn: conn
- } do
- user = insert(:user)
- note_activity = insert(:note_activity)
-
- conn = put_req_header(conn, "accept", "text/html")
-
- ensure_federating_or_authenticated(conn, "/notice/#{note_activity.id}", user)
- end
- end
-
- describe "GET /notice/:id/embed_player" do
- setup do
- note_activity = insert(:note_activity)
- object = Pleroma.Object.normalize(note_activity)
-
- object_data =
- Map.put(object.data, "attachment", [
- %{
- "url" => [
- %{
- "href" =>
- "https://peertube.moe/static/webseed/df5f464b-be8d-46fb-ad81-2d4c2d1630e3-480.mp4",
- "mediaType" => "video/mp4",
- "type" => "Link"
- }
- ]
- }
- ])
-
- object
- |> Ecto.Changeset.change(data: object_data)
- |> Pleroma.Repo.update()
-
- %{note_activity: note_activity}
- end
-
- test "renders embed player", %{conn: conn, note_activity: note_activity} do
- conn = get(conn, "/notice/#{note_activity.id}/embed_player")
-
- assert Plug.Conn.get_resp_header(conn, "x-frame-options") == ["ALLOW"]
-
- assert Plug.Conn.get_resp_header(
- conn,
- "content-security-policy"
- ) == [
- "default-src 'none';style-src 'self' 'unsafe-inline';img-src 'self' data: https:; media-src 'self' https:;"
- ]
-
- assert response(conn, 200) =~
- "<video controls loop><source src=\"https://peertube.moe/static/webseed/df5f464b-be8d-46fb-ad81-2d4c2d1630e3-480.mp4\" type=\"video/mp4\">Your browser does not support video/mp4 playback.</video>"
- end
-
- test "404s when activity isn't create", %{conn: conn} do
- note_activity = insert(:note_activity, data_attrs: %{"type" => "Like"})
-
- assert conn
- |> get("/notice/#{note_activity.id}/embed_player")
- |> response(404)
- end
-
- test "404s when activity is direct message", %{conn: conn} do
- note_activity = insert(:note_activity, data_attrs: %{"directMessage" => true})
-
- assert conn
- |> get("/notice/#{note_activity.id}/embed_player")
- |> response(404)
- end
-
- test "404s when attachment is empty", %{conn: conn} do
- note_activity = insert(:note_activity)
- object = Pleroma.Object.normalize(note_activity)
- object_data = Map.put(object.data, "attachment", [])
-
- object
- |> Ecto.Changeset.change(data: object_data)
- |> Pleroma.Repo.update()
-
- assert conn
- |> get("/notice/#{note_activity.id}/embed_player")
- |> response(404)
- end
-
- test "404s when attachment isn't audio or video", %{conn: conn} do
- note_activity = insert(:note_activity)
- object = Pleroma.Object.normalize(note_activity)
-
- object_data =
- Map.put(object.data, "attachment", [
- %{
- "url" => [
- %{
- "href" => "https://peertube.moe/static/webseed/480.jpg",
- "mediaType" => "image/jpg",
- "type" => "Link"
- }
- ]
- }
- ])
-
- object
- |> Ecto.Changeset.change(data: object_data)
- |> Pleroma.Repo.update()
-
- conn
- |> get("/notice/#{note_activity.id}/embed_player")
- |> response(404)
- end
-
- test "it requires authentication if instance is NOT federating", %{
- conn: conn,
- note_activity: note_activity
- } do
- user = insert(:user)
- conn = put_req_header(conn, "accept", "text/html")
-
- ensure_federating_or_authenticated(conn, "/notice/#{note_activity.id}/embed_player", user)
- end
- end
-end
diff --git a/test/web/pleroma_api/controllers/account_controller_test.exs b/test/web/pleroma_api/controllers/account_controller_test.exs
@@ -1,284 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Web.PleromaAPI.AccountControllerTest do
- use Pleroma.Web.ConnCase
-
- alias Pleroma.Config
- alias Pleroma.Tests.ObanHelpers
- alias Pleroma.User
- alias Pleroma.Web.CommonAPI
-
- import Pleroma.Factory
- import Swoosh.TestAssertions
-
- describe "POST /api/v1/pleroma/accounts/confirmation_resend" do
- setup do
- {:ok, user} =
- insert(:user)
- |> User.confirmation_changeset(need_confirmation: true)
- |> User.update_and_set_cache()
-
- assert user.confirmation_pending
-
- [user: user]
- end
-
- setup do: clear_config([:instance, :account_activation_required], true)
-
- test "resend account confirmation email", %{conn: conn, user: user} do
- conn
- |> put_req_header("content-type", "application/json")
- |> post("/api/v1/pleroma/accounts/confirmation_resend?email=#{user.email}")
- |> json_response_and_validate_schema(:no_content)
-
- ObanHelpers.perform_all()
-
- email = Pleroma.Emails.UserEmail.account_confirmation_email(user)
- notify_email = Config.get([:instance, :notify_email])
- instance_name = Config.get([:instance, :name])
-
- assert_email_sent(
- from: {instance_name, notify_email},
- to: {user.name, user.email},
- html_body: email.html_body
- )
- end
-
- test "resend account confirmation email (with nickname)", %{conn: conn, user: user} do
- conn
- |> put_req_header("content-type", "application/json")
- |> post("/api/v1/pleroma/accounts/confirmation_resend?nickname=#{user.nickname}")
- |> json_response_and_validate_schema(:no_content)
-
- ObanHelpers.perform_all()
-
- email = Pleroma.Emails.UserEmail.account_confirmation_email(user)
- notify_email = Config.get([:instance, :notify_email])
- instance_name = Config.get([:instance, :name])
-
- assert_email_sent(
- from: {instance_name, notify_email},
- to: {user.name, user.email},
- html_body: email.html_body
- )
- end
- end
-
- describe "getting favorites timeline of specified user" do
- setup do
- [current_user, user] = insert_pair(:user, hide_favorites: false)
- %{user: current_user, conn: conn} = oauth_access(["read:favourites"], user: current_user)
- [current_user: current_user, user: user, conn: conn]
- end
-
- test "returns list of statuses favorited by specified user", %{
- conn: conn,
- user: user
- } do
- [activity | _] = insert_pair(:note_activity)
- CommonAPI.favorite(user, activity.id)
-
- response =
- conn
- |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
- |> json_response_and_validate_schema(:ok)
-
- [like] = response
-
- assert length(response) == 1
- assert like["id"] == activity.id
- end
-
- test "returns favorites for specified user_id when requester is not logged in", %{
- user: user
- } do
- activity = insert(:note_activity)
- CommonAPI.favorite(user, activity.id)
-
- response =
- build_conn()
- |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
- |> json_response_and_validate_schema(200)
-
- assert length(response) == 1
- end
-
- test "returns favorited DM only when user is logged in and he is one of recipients", %{
- current_user: current_user,
- user: user
- } do
- {:ok, direct} =
- CommonAPI.post(current_user, %{
- status: "Hi @#{user.nickname}!",
- visibility: "direct"
- })
-
- CommonAPI.favorite(user, direct.id)
-
- for u <- [user, current_user] do
- response =
- build_conn()
- |> assign(:user, u)
- |> assign(:token, insert(:oauth_token, user: u, scopes: ["read:favourites"]))
- |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
- |> json_response_and_validate_schema(:ok)
-
- assert length(response) == 1
- end
-
- response =
- build_conn()
- |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
- |> json_response_and_validate_schema(200)
-
- assert length(response) == 0
- end
-
- test "does not return others' favorited DM when user is not one of recipients", %{
- conn: conn,
- user: user
- } do
- user_two = insert(:user)
-
- {:ok, direct} =
- CommonAPI.post(user_two, %{
- status: "Hi @#{user.nickname}!",
- visibility: "direct"
- })
-
- CommonAPI.favorite(user, direct.id)
-
- response =
- conn
- |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
- |> json_response_and_validate_schema(:ok)
-
- assert Enum.empty?(response)
- end
-
- test "paginates favorites using since_id and max_id", %{
- conn: conn,
- user: user
- } do
- activities = insert_list(10, :note_activity)
-
- Enum.each(activities, fn activity ->
- CommonAPI.favorite(user, activity.id)
- end)
-
- third_activity = Enum.at(activities, 2)
- seventh_activity = Enum.at(activities, 6)
-
- response =
- conn
- |> get(
- "/api/v1/pleroma/accounts/#{user.id}/favourites?since_id=#{third_activity.id}&max_id=#{
- seventh_activity.id
- }"
- )
- |> json_response_and_validate_schema(:ok)
-
- assert length(response) == 3
- refute third_activity in response
- refute seventh_activity in response
- end
-
- test "limits favorites using limit parameter", %{
- conn: conn,
- user: user
- } do
- 7
- |> insert_list(:note_activity)
- |> Enum.each(fn activity ->
- CommonAPI.favorite(user, activity.id)
- end)
-
- response =
- conn
- |> get("/api/v1/pleroma/accounts/#{user.id}/favourites?limit=3")
- |> json_response_and_validate_schema(:ok)
-
- assert length(response) == 3
- end
-
- test "returns empty response when user does not have any favorited statuses", %{
- conn: conn,
- user: user
- } do
- response =
- conn
- |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
- |> json_response_and_validate_schema(:ok)
-
- assert Enum.empty?(response)
- end
-
- test "returns 404 error when specified user is not exist", %{conn: conn} do
- conn = get(conn, "/api/v1/pleroma/accounts/test/favourites")
-
- assert json_response_and_validate_schema(conn, 404) == %{"error" => "Record not found"}
- end
-
- test "returns 403 error when user has hidden own favorites", %{conn: conn} do
- user = insert(:user, hide_favorites: true)
- activity = insert(:note_activity)
- CommonAPI.favorite(user, activity.id)
-
- conn = get(conn, "/api/v1/pleroma/accounts/#{user.id}/favourites")
-
- assert json_response_and_validate_schema(conn, 403) == %{"error" => "Can't get favorites"}
- end
-
- test "hides favorites for new users by default", %{conn: conn} do
- user = insert(:user)
- activity = insert(:note_activity)
- CommonAPI.favorite(user, activity.id)
-
- assert user.hide_favorites
- conn = get(conn, "/api/v1/pleroma/accounts/#{user.id}/favourites")
-
- assert json_response_and_validate_schema(conn, 403) == %{"error" => "Can't get favorites"}
- end
- end
-
- describe "subscribing / unsubscribing" do
- test "subscribing / unsubscribing to a user" do
- %{user: user, conn: conn} = oauth_access(["follow"])
- subscription_target = insert(:user)
-
- ret_conn =
- conn
- |> assign(:user, user)
- |> post("/api/v1/pleroma/accounts/#{subscription_target.id}/subscribe")
-
- assert %{"id" => _id, "subscribing" => true} =
- json_response_and_validate_schema(ret_conn, 200)
-
- conn = post(conn, "/api/v1/pleroma/accounts/#{subscription_target.id}/unsubscribe")
-
- assert %{"id" => _id, "subscribing" => false} = json_response_and_validate_schema(conn, 200)
- end
- end
-
- describe "subscribing" do
- test "returns 404 when subscription_target not found" do
- %{conn: conn} = oauth_access(["write:follows"])
-
- conn = post(conn, "/api/v1/pleroma/accounts/target_id/subscribe")
-
- assert %{"error" => "Record not found"} = json_response_and_validate_schema(conn, 404)
- end
- end
-
- describe "unsubscribing" do
- test "returns 404 when subscription_target not found" do
- %{conn: conn} = oauth_access(["follow"])
-
- conn = post(conn, "/api/v1/pleroma/accounts/target_id/unsubscribe")
-
- assert %{"error" => "Record not found"} = json_response_and_validate_schema(conn, 404)
- end
- end
-end
diff --git a/test/web/pleroma_api/controllers/chat_controller_test.exs b/test/web/pleroma_api/controllers/chat_controller_test.exs
@@ -1,410 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-defmodule Pleroma.Web.PleromaAPI.ChatControllerTest do
- use Pleroma.Web.ConnCase
-
- alias Pleroma.Chat
- alias Pleroma.Chat.MessageReference
- alias Pleroma.Object
- alias Pleroma.User
- alias Pleroma.Web.ActivityPub.ActivityPub
- alias Pleroma.Web.CommonAPI
-
- import Pleroma.Factory
-
- describe "POST /api/v1/pleroma/chats/:id/messages/:message_id/read" do
- setup do: oauth_access(["write:chats"])
-
- test "it marks one message as read", %{conn: conn, user: user} do
- other_user = insert(:user)
-
- {:ok, create} = CommonAPI.post_chat_message(other_user, user, "sup")
- {:ok, _create} = CommonAPI.post_chat_message(other_user, user, "sup part 2")
- {:ok, chat} = Chat.get_or_create(user.id, other_user.ap_id)
- object = Object.normalize(create, false)
- cm_ref = MessageReference.for_chat_and_object(chat, object)
-
- assert cm_ref.unread == true
-
- result =
- conn
- |> post("/api/v1/pleroma/chats/#{chat.id}/messages/#{cm_ref.id}/read")
- |> json_response_and_validate_schema(200)
-
- assert result["unread"] == false
-
- cm_ref = MessageReference.for_chat_and_object(chat, object)
-
- assert cm_ref.unread == false
- end
- end
-
- describe "POST /api/v1/pleroma/chats/:id/read" do
- setup do: oauth_access(["write:chats"])
-
- test "given a `last_read_id`, it marks everything until then as read", %{
- conn: conn,
- user: user
- } do
- other_user = insert(:user)
-
- {:ok, create} = CommonAPI.post_chat_message(other_user, user, "sup")
- {:ok, _create} = CommonAPI.post_chat_message(other_user, user, "sup part 2")
- {:ok, chat} = Chat.get_or_create(user.id, other_user.ap_id)
- object = Object.normalize(create, false)
- cm_ref = MessageReference.for_chat_and_object(chat, object)
-
- assert cm_ref.unread == true
-
- result =
- conn
- |> put_req_header("content-type", "application/json")
- |> post("/api/v1/pleroma/chats/#{chat.id}/read", %{"last_read_id" => cm_ref.id})
- |> json_response_and_validate_schema(200)
-
- assert result["unread"] == 1
-
- cm_ref = MessageReference.for_chat_and_object(chat, object)
-
- assert cm_ref.unread == false
- end
- end
-
- describe "POST /api/v1/pleroma/chats/:id/messages" do
- setup do: oauth_access(["write:chats"])
-
- test "it posts a message to the chat", %{conn: conn, user: user} do
- other_user = insert(:user)
-
- {:ok, chat} = Chat.get_or_create(user.id, other_user.ap_id)
-
- result =
- conn
- |> put_req_header("content-type", "application/json")
- |> post("/api/v1/pleroma/chats/#{chat.id}/messages", %{"content" => "Hallo!!"})
- |> json_response_and_validate_schema(200)
-
- assert result["content"] == "Hallo!!"
- assert result["chat_id"] == chat.id |> to_string()
- end
-
- test "it fails if there is no content", %{conn: conn, user: user} do
- other_user = insert(:user)
-
- {:ok, chat} = Chat.get_or_create(user.id, other_user.ap_id)
-
- result =
- conn
- |> put_req_header("content-type", "application/json")
- |> post("/api/v1/pleroma/chats/#{chat.id}/messages")
- |> json_response_and_validate_schema(400)
-
- assert %{"error" => "no_content"} == result
- end
-
- test "it works with an attachment", %{conn: conn, user: user} do
- file = %Plug.Upload{
- content_type: "image/jpg",
- path: Path.absname("test/fixtures/image.jpg"),
- filename: "an_image.jpg"
- }
-
- {:ok, upload} = ActivityPub.upload(file, actor: user.ap_id)
-
- other_user = insert(:user)
-
- {:ok, chat} = Chat.get_or_create(user.id, other_user.ap_id)
-
- result =
- conn
- |> put_req_header("content-type", "application/json")
- |> post("/api/v1/pleroma/chats/#{chat.id}/messages", %{
- "media_id" => to_string(upload.id)
- })
- |> json_response_and_validate_schema(200)
-
- assert result["attachment"]
- end
-
- test "gets MRF reason when rejected", %{conn: conn, user: user} do
- clear_config([:mrf_keyword, :reject], ["GNO"])
- clear_config([:mrf, :policies], [Pleroma.Web.ActivityPub.MRF.KeywordPolicy])
-
- other_user = insert(:user)
-
- {:ok, chat} = Chat.get_or_create(user.id, other_user.ap_id)
-
- result =
- conn
- |> put_req_header("content-type", "application/json")
- |> post("/api/v1/pleroma/chats/#{chat.id}/messages", %{"content" => "GNO/Linux"})
- |> json_response_and_validate_schema(422)
-
- assert %{"error" => "[KeywordPolicy] Matches with rejected keyword"} == result
- end
- end
-
- describe "DELETE /api/v1/pleroma/chats/:id/messages/:message_id" do
- setup do: oauth_access(["write:chats"])
-
- test "it deletes a message from the chat", %{conn: conn, user: user} do
- recipient = insert(:user)
-
- {:ok, message} =
- CommonAPI.post_chat_message(user, recipient, "Hello darkness my old friend")
-
- {:ok, other_message} = CommonAPI.post_chat_message(recipient, user, "nico nico ni")
-
- object = Object.normalize(message, false)
-
- chat = Chat.get(user.id, recipient.ap_id)
-
- cm_ref = MessageReference.for_chat_and_object(chat, object)
-
- # Deleting your own message removes the message and the reference
- result =
- conn
- |> put_req_header("content-type", "application/json")
- |> delete("/api/v1/pleroma/chats/#{chat.id}/messages/#{cm_ref.id}")
- |> json_response_and_validate_schema(200)
-
- assert result["id"] == cm_ref.id
- refute MessageReference.get_by_id(cm_ref.id)
- assert %{data: %{"type" => "Tombstone"}} = Object.get_by_id(object.id)
-
- # Deleting other people's messages just removes the reference
- object = Object.normalize(other_message, false)
- cm_ref = MessageReference.for_chat_and_object(chat, object)
-
- result =
- conn
- |> put_req_header("content-type", "application/json")
- |> delete("/api/v1/pleroma/chats/#{chat.id}/messages/#{cm_ref.id}")
- |> json_response_and_validate_schema(200)
-
- assert result["id"] == cm_ref.id
- refute MessageReference.get_by_id(cm_ref.id)
- assert Object.get_by_id(object.id)
- end
- end
-
- describe "GET /api/v1/pleroma/chats/:id/messages" do
- setup do: oauth_access(["read:chats"])
-
- test "it paginates", %{conn: conn, user: user} do
- recipient = insert(:user)
-
- Enum.each(1..30, fn _ ->
- {:ok, _} = CommonAPI.post_chat_message(user, recipient, "hey")
- end)
-
- chat = Chat.get(user.id, recipient.ap_id)
-
- response = get(conn, "/api/v1/pleroma/chats/#{chat.id}/messages")
- result = json_response_and_validate_schema(response, 200)
-
- [next, prev] = get_resp_header(response, "link") |> hd() |> String.split(", ")
- api_endpoint = "/api/v1/pleroma/chats/"
-
- assert String.match?(
- next,
- ~r(#{api_endpoint}.*/messages\?id=.*&limit=\d+&max_id=.*; rel=\"next\"$)
- )
-
- assert String.match?(
- prev,
- ~r(#{api_endpoint}.*/messages\?id=.*&limit=\d+&min_id=.*; rel=\"prev\"$)
- )
-
- assert length(result) == 20
-
- response =
- get(conn, "/api/v1/pleroma/chats/#{chat.id}/messages?max_id=#{List.last(result)["id"]}")
-
- result = json_response_and_validate_schema(response, 200)
- [next, prev] = get_resp_header(response, "link") |> hd() |> String.split(", ")
-
- assert String.match?(
- next,
- ~r(#{api_endpoint}.*/messages\?id=.*&limit=\d+&max_id=.*; rel=\"next\"$)
- )
-
- assert String.match?(
- prev,
- ~r(#{api_endpoint}.*/messages\?id=.*&limit=\d+&max_id=.*&min_id=.*; rel=\"prev\"$)
- )
-
- assert length(result) == 10
- end
-
- test "it returns the messages for a given chat", %{conn: conn, user: user} do
- other_user = insert(:user)
- third_user = insert(:user)
-
- {:ok, _} = CommonAPI.post_chat_message(user, other_user, "hey")
- {:ok, _} = CommonAPI.post_chat_message(user, third_user, "hey")
- {:ok, _} = CommonAPI.post_chat_message(user, other_user, "how are you?")
- {:ok, _} = CommonAPI.post_chat_message(other_user, user, "fine, how about you?")
-
- chat = Chat.get(user.id, other_user.ap_id)
-
- result =
- conn
- |> get("/api/v1/pleroma/chats/#{chat.id}/messages")
- |> json_response_and_validate_schema(200)
-
- result
- |> Enum.each(fn message ->
- assert message["chat_id"] == chat.id |> to_string()
- end)
-
- assert length(result) == 3
-
- # Trying to get the chat of a different user
- conn
- |> assign(:user, other_user)
- |> get("/api/v1/pleroma/chats/#{chat.id}/messages")
- |> json_response_and_validate_schema(404)
- end
- end
-
- describe "POST /api/v1/pleroma/chats/by-account-id/:id" do
- setup do: oauth_access(["write:chats"])
-
- test "it creates or returns a chat", %{conn: conn} do
- other_user = insert(:user)
-
- result =
- conn
- |> post("/api/v1/pleroma/chats/by-account-id/#{other_user.id}")
- |> json_response_and_validate_schema(200)
-
- assert result["id"]
- end
- end
-
- describe "GET /api/v1/pleroma/chats/:id" do
- setup do: oauth_access(["read:chats"])
-
- test "it returns a chat", %{conn: conn, user: user} do
- other_user = insert(:user)
-
- {:ok, chat} = Chat.get_or_create(user.id, other_user.ap_id)
-
- result =
- conn
- |> get("/api/v1/pleroma/chats/#{chat.id}")
- |> json_response_and_validate_schema(200)
-
- assert result["id"] == to_string(chat.id)
- end
- end
-
- describe "GET /api/v1/pleroma/chats" do
- setup do: oauth_access(["read:chats"])
-
- test "it does not return chats with deleted users", %{conn: conn, user: user} do
- recipient = insert(:user)
- {:ok, _} = Chat.get_or_create(user.id, recipient.ap_id)
-
- Pleroma.Repo.delete(recipient)
- User.invalidate_cache(recipient)
-
- result =
- conn
- |> get("/api/v1/pleroma/chats")
- |> json_response_and_validate_schema(200)
-
- assert length(result) == 0
- end
-
- test "it does not return chats with users you blocked", %{conn: conn, user: user} do
- recipient = insert(:user)
-
- {:ok, _} = Chat.get_or_create(user.id, recipient.ap_id)
-
- result =
- conn
- |> get("/api/v1/pleroma/chats")
- |> json_response_and_validate_schema(200)
-
- assert length(result) == 1
-
- User.block(user, recipient)
-
- result =
- conn
- |> get("/api/v1/pleroma/chats")
- |> json_response_and_validate_schema(200)
-
- assert length(result) == 0
- end
-
- test "it returns all chats", %{conn: conn, user: user} do
- Enum.each(1..30, fn _ ->
- recipient = insert(:user)
- {:ok, _} = Chat.get_or_create(user.id, recipient.ap_id)
- end)
-
- result =
- conn
- |> get("/api/v1/pleroma/chats")
- |> json_response_and_validate_schema(200)
-
- assert length(result) == 30
- end
-
- test "it return a list of chats the current user is participating in, in descending order of updates",
- %{conn: conn, user: user} do
- har = insert(:user)
- jafnhar = insert(:user)
- tridi = insert(:user)
-
- {:ok, chat_1} = Chat.get_or_create(user.id, har.ap_id)
- :timer.sleep(1000)
- {:ok, _chat_2} = Chat.get_or_create(user.id, jafnhar.ap_id)
- :timer.sleep(1000)
- {:ok, chat_3} = Chat.get_or_create(user.id, tridi.ap_id)
- :timer.sleep(1000)
-
- # bump the second one
- {:ok, chat_2} = Chat.bump_or_create(user.id, jafnhar.ap_id)
-
- result =
- conn
- |> get("/api/v1/pleroma/chats")
- |> json_response_and_validate_schema(200)
-
- ids = Enum.map(result, & &1["id"])
-
- assert ids == [
- chat_2.id |> to_string(),
- chat_3.id |> to_string(),
- chat_1.id |> to_string()
- ]
- end
-
- test "it is not affected by :restrict_unauthenticated setting (issue #1973)", %{
- conn: conn,
- user: user
- } do
- clear_config([:restrict_unauthenticated, :profiles, :local], true)
- clear_config([:restrict_unauthenticated, :profiles, :remote], true)
-
- user2 = insert(:user)
- user3 = insert(:user, local: false)
-
- {:ok, _chat_12} = Chat.get_or_create(user.id, user2.ap_id)
- {:ok, _chat_13} = Chat.get_or_create(user.id, user3.ap_id)
-
- result =
- conn
- |> get("/api/v1/pleroma/chats")
- |> json_response_and_validate_schema(200)
-
- account_ids = Enum.map(result, &get_in(&1, ["account", "id"]))
- assert Enum.sort(account_ids) == Enum.sort([user2.id, user3.id])
- end
- end
-end
diff --git a/test/web/pleroma_api/controllers/conversation_controller_test.exs b/test/web/pleroma_api/controllers/conversation_controller_test.exs
@@ -1,136 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Web.PleromaAPI.ConversationControllerTest do
- use Pleroma.Web.ConnCase
-
- alias Pleroma.Conversation.Participation
- alias Pleroma.Repo
- alias Pleroma.User
- alias Pleroma.Web.CommonAPI
-
- import Pleroma.Factory
-
- test "/api/v1/pleroma/conversations/:id" do
- user = insert(:user)
- %{user: other_user, conn: conn} = oauth_access(["read:statuses"])
-
- {:ok, _activity} =
- CommonAPI.post(user, %{status: "Hi @#{other_user.nickname}!", visibility: "direct"})
-
- [participation] = Participation.for_user(other_user)
-
- result =
- conn
- |> get("/api/v1/pleroma/conversations/#{participation.id}")
- |> json_response_and_validate_schema(200)
-
- assert result["id"] == participation.id |> to_string()
- end
-
- test "/api/v1/pleroma/conversations/:id/statuses" do
- user = insert(:user)
- %{user: other_user, conn: conn} = oauth_access(["read:statuses"])
- third_user = insert(:user)
-
- {:ok, _activity} =
- CommonAPI.post(user, %{status: "Hi @#{third_user.nickname}!", visibility: "direct"})
-
- {:ok, activity} =
- CommonAPI.post(user, %{status: "Hi @#{other_user.nickname}!", visibility: "direct"})
-
- [participation] = Participation.for_user(other_user)
-
- {:ok, activity_two} =
- CommonAPI.post(other_user, %{
- status: "Hi!",
- in_reply_to_status_id: activity.id,
- in_reply_to_conversation_id: participation.id
- })
-
- result =
- conn
- |> get("/api/v1/pleroma/conversations/#{participation.id}/statuses")
- |> json_response_and_validate_schema(200)
-
- assert length(result) == 2
-
- id_one = activity.id
- id_two = activity_two.id
- assert [%{"id" => ^id_one}, %{"id" => ^id_two}] = result
-
- {:ok, %{id: id_three}} =
- CommonAPI.post(other_user, %{
- status: "Bye!",
- in_reply_to_status_id: activity.id,
- in_reply_to_conversation_id: participation.id
- })
-
- assert [%{"id" => ^id_two}, %{"id" => ^id_three}] =
- conn
- |> get("/api/v1/pleroma/conversations/#{participation.id}/statuses?limit=2")
- |> json_response_and_validate_schema(:ok)
-
- assert [%{"id" => ^id_three}] =
- conn
- |> get("/api/v1/pleroma/conversations/#{participation.id}/statuses?min_id=#{id_two}")
- |> json_response_and_validate_schema(:ok)
- end
-
- test "PATCH /api/v1/pleroma/conversations/:id" do
- %{user: user, conn: conn} = oauth_access(["write:conversations"])
- other_user = insert(:user)
-
- {:ok, _activity} = CommonAPI.post(user, %{status: "Hi", visibility: "direct"})
-
- [participation] = Participation.for_user(user)
-
- participation = Repo.preload(participation, :recipients)
-
- user = User.get_cached_by_id(user.id)
- assert [user] == participation.recipients
- assert other_user not in participation.recipients
-
- query = "recipients[]=#{user.id}&recipients[]=#{other_user.id}"
-
- result =
- conn
- |> patch("/api/v1/pleroma/conversations/#{participation.id}?#{query}")
- |> json_response_and_validate_schema(200)
-
- assert result["id"] == participation.id |> to_string
-
- [participation] = Participation.for_user(user)
- participation = Repo.preload(participation, :recipients)
-
- assert user in participation.recipients
- assert other_user in participation.recipients
- end
-
- test "POST /api/v1/pleroma/conversations/read" do
- user = insert(:user)
- %{user: other_user, conn: conn} = oauth_access(["write:conversations"])
-
- {:ok, _activity} =
- CommonAPI.post(user, %{status: "Hi @#{other_user.nickname}", visibility: "direct"})
-
- {:ok, _activity} =
- CommonAPI.post(user, %{status: "Hi @#{other_user.nickname}", visibility: "direct"})
-
- [participation2, participation1] = Participation.for_user(other_user)
- assert Participation.get(participation2.id).read == false
- assert Participation.get(participation1.id).read == false
- assert User.get_cached_by_id(other_user.id).unread_conversation_count == 2
-
- [%{"unread" => false}, %{"unread" => false}] =
- conn
- |> post("/api/v1/pleroma/conversations/read", %{})
- |> json_response_and_validate_schema(200)
-
- [participation2, participation1] = Participation.for_user(other_user)
- assert Participation.get(participation2.id).read == true
- assert Participation.get(participation1.id).read == true
- assert User.get_cached_by_id(other_user.id).unread_conversation_count == 0
- end
-end
diff --git a/test/web/pleroma_api/controllers/emoji_pack_controller_test.exs b/test/web/pleroma_api/controllers/emoji_pack_controller_test.exs
@@ -1,604 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Web.PleromaAPI.EmojiPackControllerTest do
- use Pleroma.Web.ConnCase
-
- import Tesla.Mock
- import Pleroma.Factory
-
- @emoji_path Path.join(
- Pleroma.Config.get!([:instance, :static_dir]),
- "emoji"
- )
- setup do: clear_config([:auth, :enforce_oauth_admin_scope_usage], false)
-
- setup do: clear_config([:instance, :public], true)
-
- setup do
- admin = insert(:user, is_admin: true)
- token = insert(:oauth_admin_token, user: admin)
-
- admin_conn =
- build_conn()
- |> assign(:user, admin)
- |> assign(:token, token)
-
- Pleroma.Emoji.reload()
- {:ok, %{admin_conn: admin_conn}}
- end
-
- test "GET /api/pleroma/emoji/packs when :public: false", %{conn: conn} do
- Config.put([:instance, :public], false)
- conn |> get("/api/pleroma/emoji/packs") |> json_response_and_validate_schema(200)
- end
-
- test "GET /api/pleroma/emoji/packs", %{conn: conn} do
- resp = conn |> get("/api/pleroma/emoji/packs") |> json_response_and_validate_schema(200)
-
- assert resp["count"] == 4
-
- assert resp["packs"]
- |> Map.keys()
- |> length() == 4
-
- shared = resp["packs"]["test_pack"]
- assert shared["files"] == %{"blank" => "blank.png", "blank2" => "blank2.png"}
- assert Map.has_key?(shared["pack"], "download-sha256")
- assert shared["pack"]["can-download"]
- assert shared["pack"]["share-files"]
-
- non_shared = resp["packs"]["test_pack_nonshared"]
- assert non_shared["pack"]["share-files"] == false
- assert non_shared["pack"]["can-download"] == false
-
- resp =
- conn
- |> get("/api/pleroma/emoji/packs?page_size=1")
- |> json_response_and_validate_schema(200)
-
- assert resp["count"] == 4
-
- packs = Map.keys(resp["packs"])
-
- assert length(packs) == 1
-
- [pack1] = packs
-
- resp =
- conn
- |> get("/api/pleroma/emoji/packs?page_size=1&page=2")
- |> json_response_and_validate_schema(200)
-
- assert resp["count"] == 4
- packs = Map.keys(resp["packs"])
- assert length(packs) == 1
- [pack2] = packs
-
- resp =
- conn
- |> get("/api/pleroma/emoji/packs?page_size=1&page=3")
- |> json_response_and_validate_schema(200)
-
- assert resp["count"] == 4
- packs = Map.keys(resp["packs"])
- assert length(packs) == 1
- [pack3] = packs
-
- resp =
- conn
- |> get("/api/pleroma/emoji/packs?page_size=1&page=4")
- |> json_response_and_validate_schema(200)
-
- assert resp["count"] == 4
- packs = Map.keys(resp["packs"])
- assert length(packs) == 1
- [pack4] = packs
- assert [pack1, pack2, pack3, pack4] |> Enum.uniq() |> length() == 4
- end
-
- describe "GET /api/pleroma/emoji/packs/remote" do
- test "shareable instance", %{admin_conn: admin_conn, conn: conn} do
- resp =
- conn
- |> get("/api/pleroma/emoji/packs?page=2&page_size=1")
- |> json_response_and_validate_schema(200)
-
- mock(fn
- %{method: :get, url: "https://example.com/.well-known/nodeinfo"} ->
- json(%{links: [%{href: "https://example.com/nodeinfo/2.1.json"}]})
-
- %{method: :get, url: "https://example.com/nodeinfo/2.1.json"} ->
- json(%{metadata: %{features: ["shareable_emoji_packs"]}})
-
- %{method: :get, url: "https://example.com/api/pleroma/emoji/packs?page=2&page_size=1"} ->
- json(resp)
- end)
-
- assert admin_conn
- |> get("/api/pleroma/emoji/packs/remote?url=https://example.com&page=2&page_size=1")
- |> json_response_and_validate_schema(200) == resp
- end
-
- test "non shareable instance", %{admin_conn: admin_conn} do
- mock(fn
- %{method: :get, url: "https://example.com/.well-known/nodeinfo"} ->
- json(%{links: [%{href: "https://example.com/nodeinfo/2.1.json"}]})
-
- %{method: :get, url: "https://example.com/nodeinfo/2.1.json"} ->
- json(%{metadata: %{features: []}})
- end)
-
- assert admin_conn
- |> get("/api/pleroma/emoji/packs/remote?url=https://example.com")
- |> json_response_and_validate_schema(500) == %{
- "error" => "The requested instance does not support sharing emoji packs"
- }
- end
- end
-
- describe "GET /api/pleroma/emoji/packs/archive?name=:name" do
- test "download shared pack", %{conn: conn} do
- resp =
- conn
- |> get("/api/pleroma/emoji/packs/archive?name=test_pack")
- |> response(200)
-
- {:ok, arch} = :zip.unzip(resp, [:memory])
-
- assert Enum.find(arch, fn {n, _} -> n == 'pack.json' end)
- assert Enum.find(arch, fn {n, _} -> n == 'blank.png' end)
- end
-
- test "non existing pack", %{conn: conn} do
- assert conn
- |> get("/api/pleroma/emoji/packs/archive?name=test_pack_for_import")
- |> json_response_and_validate_schema(:not_found) == %{
- "error" => "Pack test_pack_for_import does not exist"
- }
- end
-
- test "non downloadable pack", %{conn: conn} do
- assert conn
- |> get("/api/pleroma/emoji/packs/archive?name=test_pack_nonshared")
- |> json_response_and_validate_schema(:forbidden) == %{
- "error" =>
- "Pack test_pack_nonshared cannot be downloaded from this instance, either pack sharing was disabled for this pack or some files are missing"
- }
- end
- end
-
- describe "POST /api/pleroma/emoji/packs/download" do
- test "shared pack from remote and non shared from fallback-src", %{
- admin_conn: admin_conn,
- conn: conn
- } do
- mock(fn
- %{method: :get, url: "https://example.com/.well-known/nodeinfo"} ->
- json(%{links: [%{href: "https://example.com/nodeinfo/2.1.json"}]})
-
- %{method: :get, url: "https://example.com/nodeinfo/2.1.json"} ->
- json(%{metadata: %{features: ["shareable_emoji_packs"]}})
-
- %{
- method: :get,
- url: "https://example.com/api/pleroma/emoji/pack?name=test_pack"
- } ->
- conn
- |> get("/api/pleroma/emoji/pack?name=test_pack")
- |> json_response_and_validate_schema(200)
- |> json()
-
- %{
- method: :get,
- url: "https://example.com/api/pleroma/emoji/packs/archive?name=test_pack"
- } ->
- conn
- |> get("/api/pleroma/emoji/packs/archive?name=test_pack")
- |> response(200)
- |> text()
-
- %{
- method: :get,
- url: "https://example.com/api/pleroma/emoji/pack?name=test_pack_nonshared"
- } ->
- conn
- |> get("/api/pleroma/emoji/pack?name=test_pack_nonshared")
- |> json_response_and_validate_schema(200)
- |> json()
-
- %{
- method: :get,
- url: "https://nonshared-pack"
- } ->
- text(File.read!("#{@emoji_path}/test_pack_nonshared/nonshared.zip"))
- end)
-
- assert admin_conn
- |> put_req_header("content-type", "multipart/form-data")
- |> post("/api/pleroma/emoji/packs/download", %{
- url: "https://example.com",
- name: "test_pack",
- as: "test_pack2"
- })
- |> json_response_and_validate_schema(200) == "ok"
-
- assert File.exists?("#{@emoji_path}/test_pack2/pack.json")
- assert File.exists?("#{@emoji_path}/test_pack2/blank.png")
-
- assert admin_conn
- |> delete("/api/pleroma/emoji/pack?name=test_pack2")
- |> json_response_and_validate_schema(200) == "ok"
-
- refute File.exists?("#{@emoji_path}/test_pack2")
-
- assert admin_conn
- |> put_req_header("content-type", "multipart/form-data")
- |> post(
- "/api/pleroma/emoji/packs/download",
- %{
- url: "https://example.com",
- name: "test_pack_nonshared",
- as: "test_pack_nonshared2"
- }
- )
- |> json_response_and_validate_schema(200) == "ok"
-
- assert File.exists?("#{@emoji_path}/test_pack_nonshared2/pack.json")
- assert File.exists?("#{@emoji_path}/test_pack_nonshared2/blank.png")
-
- assert admin_conn
- |> delete("/api/pleroma/emoji/pack?name=test_pack_nonshared2")
- |> json_response_and_validate_schema(200) == "ok"
-
- refute File.exists?("#{@emoji_path}/test_pack_nonshared2")
- end
-
- test "nonshareable instance", %{admin_conn: admin_conn} do
- mock(fn
- %{method: :get, url: "https://old-instance/.well-known/nodeinfo"} ->
- json(%{links: [%{href: "https://old-instance/nodeinfo/2.1.json"}]})
-
- %{method: :get, url: "https://old-instance/nodeinfo/2.1.json"} ->
- json(%{metadata: %{features: []}})
- end)
-
- assert admin_conn
- |> put_req_header("content-type", "multipart/form-data")
- |> post(
- "/api/pleroma/emoji/packs/download",
- %{
- url: "https://old-instance",
- name: "test_pack",
- as: "test_pack2"
- }
- )
- |> json_response_and_validate_schema(500) == %{
- "error" => "The requested instance does not support sharing emoji packs"
- }
- end
-
- test "checksum fail", %{admin_conn: admin_conn} do
- mock(fn
- %{method: :get, url: "https://example.com/.well-known/nodeinfo"} ->
- json(%{links: [%{href: "https://example.com/nodeinfo/2.1.json"}]})
-
- %{method: :get, url: "https://example.com/nodeinfo/2.1.json"} ->
- json(%{metadata: %{features: ["shareable_emoji_packs"]}})
-
- %{
- method: :get,
- url: "https://example.com/api/pleroma/emoji/pack?name=pack_bad_sha"
- } ->
- {:ok, pack} = Pleroma.Emoji.Pack.load_pack("pack_bad_sha")
- %Tesla.Env{status: 200, body: Jason.encode!(pack)}
-
- %{
- method: :get,
- url: "https://example.com/api/pleroma/emoji/packs/archive?name=pack_bad_sha"
- } ->
- %Tesla.Env{
- status: 200,
- body: File.read!("test/instance_static/emoji/pack_bad_sha/pack_bad_sha.zip")
- }
- end)
-
- assert admin_conn
- |> put_req_header("content-type", "multipart/form-data")
- |> post("/api/pleroma/emoji/packs/download", %{
- url: "https://example.com",
- name: "pack_bad_sha",
- as: "pack_bad_sha2"
- })
- |> json_response_and_validate_schema(:internal_server_error) == %{
- "error" => "SHA256 for the pack doesn't match the one sent by the server"
- }
- end
-
- test "other error", %{admin_conn: admin_conn} do
- mock(fn
- %{method: :get, url: "https://example.com/.well-known/nodeinfo"} ->
- json(%{links: [%{href: "https://example.com/nodeinfo/2.1.json"}]})
-
- %{method: :get, url: "https://example.com/nodeinfo/2.1.json"} ->
- json(%{metadata: %{features: ["shareable_emoji_packs"]}})
-
- %{
- method: :get,
- url: "https://example.com/api/pleroma/emoji/pack?name=test_pack"
- } ->
- {:ok, pack} = Pleroma.Emoji.Pack.load_pack("test_pack")
- %Tesla.Env{status: 200, body: Jason.encode!(pack)}
- end)
-
- assert admin_conn
- |> put_req_header("content-type", "multipart/form-data")
- |> post("/api/pleroma/emoji/packs/download", %{
- url: "https://example.com",
- name: "test_pack",
- as: "test_pack2"
- })
- |> json_response_and_validate_schema(:internal_server_error) == %{
- "error" =>
- "The pack was not set as shared and there is no fallback src to download from"
- }
- end
- end
-
- describe "PATCH /api/pleroma/emoji/pack?name=:name" do
- setup do
- pack_file = "#{@emoji_path}/test_pack/pack.json"
- original_content = File.read!(pack_file)
-
- on_exit(fn ->
- File.write!(pack_file, original_content)
- end)
-
- {:ok,
- pack_file: pack_file,
- new_data: %{
- "license" => "Test license changed",
- "homepage" => "https://pleroma.social",
- "description" => "Test description",
- "share-files" => false
- }}
- end
-
- test "for a pack without a fallback source", ctx do
- assert ctx[:admin_conn]
- |> put_req_header("content-type", "multipart/form-data")
- |> patch("/api/pleroma/emoji/pack?name=test_pack", %{
- "metadata" => ctx[:new_data]
- })
- |> json_response_and_validate_schema(200) == ctx[:new_data]
-
- assert Jason.decode!(File.read!(ctx[:pack_file]))["pack"] == ctx[:new_data]
- end
-
- test "for a pack with a fallback source", ctx do
- mock(fn
- %{
- method: :get,
- url: "https://nonshared-pack"
- } ->
- text(File.read!("#{@emoji_path}/test_pack_nonshared/nonshared.zip"))
- end)
-
- new_data = Map.put(ctx[:new_data], "fallback-src", "https://nonshared-pack")
-
- new_data_with_sha =
- Map.put(
- new_data,
- "fallback-src-sha256",
- "1967BB4E42BCC34BCC12D57BE7811D3B7BE52F965BCE45C87BD377B9499CE11D"
- )
-
- assert ctx[:admin_conn]
- |> put_req_header("content-type", "multipart/form-data")
- |> patch("/api/pleroma/emoji/pack?name=test_pack", %{metadata: new_data})
- |> json_response_and_validate_schema(200) == new_data_with_sha
-
- assert Jason.decode!(File.read!(ctx[:pack_file]))["pack"] == new_data_with_sha
- end
-
- test "when the fallback source doesn't have all the files", ctx do
- mock(fn
- %{
- method: :get,
- url: "https://nonshared-pack"
- } ->
- {:ok, {'empty.zip', empty_arch}} = :zip.zip('empty.zip', [], [:memory])
- text(empty_arch)
- end)
-
- new_data = Map.put(ctx[:new_data], "fallback-src", "https://nonshared-pack")
-
- assert ctx[:admin_conn]
- |> put_req_header("content-type", "multipart/form-data")
- |> patch("/api/pleroma/emoji/pack?name=test_pack", %{metadata: new_data})
- |> json_response_and_validate_schema(:bad_request) == %{
- "error" => "The fallback archive does not have all files specified in pack.json"
- }
- end
- end
-
- describe "POST/DELETE /api/pleroma/emoji/pack?name=:name" do
- test "creating and deleting a pack", %{admin_conn: admin_conn} do
- assert admin_conn
- |> post("/api/pleroma/emoji/pack?name=test_created")
- |> json_response_and_validate_schema(200) == "ok"
-
- assert File.exists?("#{@emoji_path}/test_created/pack.json")
-
- assert Jason.decode!(File.read!("#{@emoji_path}/test_created/pack.json")) == %{
- "pack" => %{},
- "files" => %{},
- "files_count" => 0
- }
-
- assert admin_conn
- |> delete("/api/pleroma/emoji/pack?name=test_created")
- |> json_response_and_validate_schema(200) == "ok"
-
- refute File.exists?("#{@emoji_path}/test_created/pack.json")
- end
-
- test "if pack exists", %{admin_conn: admin_conn} do
- path = Path.join(@emoji_path, "test_created")
- File.mkdir(path)
- pack_file = Jason.encode!(%{files: %{}, pack: %{}})
- File.write!(Path.join(path, "pack.json"), pack_file)
-
- assert admin_conn
- |> post("/api/pleroma/emoji/pack?name=test_created")
- |> json_response_and_validate_schema(:conflict) == %{
- "error" => "A pack named \"test_created\" already exists"
- }
-
- on_exit(fn -> File.rm_rf(path) end)
- end
-
- test "with empty name", %{admin_conn: admin_conn} do
- assert admin_conn
- |> post("/api/pleroma/emoji/pack?name= ")
- |> json_response_and_validate_schema(:bad_request) == %{
- "error" => "pack name cannot be empty"
- }
- end
- end
-
- test "deleting nonexisting pack", %{admin_conn: admin_conn} do
- assert admin_conn
- |> delete("/api/pleroma/emoji/pack?name=non_existing")
- |> json_response_and_validate_schema(:not_found) == %{
- "error" => "Pack non_existing does not exist"
- }
- end
-
- test "deleting with empty name", %{admin_conn: admin_conn} do
- assert admin_conn
- |> delete("/api/pleroma/emoji/pack?name= ")
- |> json_response_and_validate_schema(:bad_request) == %{
- "error" => "pack name cannot be empty"
- }
- end
-
- test "filesystem import", %{admin_conn: admin_conn, conn: conn} do
- on_exit(fn ->
- File.rm!("#{@emoji_path}/test_pack_for_import/emoji.txt")
- File.rm!("#{@emoji_path}/test_pack_for_import/pack.json")
- end)
-
- resp = conn |> get("/api/pleroma/emoji/packs") |> json_response_and_validate_schema(200)
-
- refute Map.has_key?(resp["packs"], "test_pack_for_import")
-
- assert admin_conn
- |> get("/api/pleroma/emoji/packs/import")
- |> json_response_and_validate_schema(200) == ["test_pack_for_import"]
-
- resp = conn |> get("/api/pleroma/emoji/packs") |> json_response_and_validate_schema(200)
- assert resp["packs"]["test_pack_for_import"]["files"] == %{"blank" => "blank.png"}
-
- File.rm!("#{@emoji_path}/test_pack_for_import/pack.json")
- refute File.exists?("#{@emoji_path}/test_pack_for_import/pack.json")
-
- emoji_txt_content = """
- blank, blank.png, Fun
- blank2, blank.png
- foo, /emoji/test_pack_for_import/blank.png
- bar
- """
-
- File.write!("#{@emoji_path}/test_pack_for_import/emoji.txt", emoji_txt_content)
-
- assert admin_conn
- |> get("/api/pleroma/emoji/packs/import")
- |> json_response_and_validate_schema(200) == ["test_pack_for_import"]
-
- resp = conn |> get("/api/pleroma/emoji/packs") |> json_response_and_validate_schema(200)
-
- assert resp["packs"]["test_pack_for_import"]["files"] == %{
- "blank" => "blank.png",
- "blank2" => "blank.png",
- "foo" => "blank.png"
- }
- end
-
- describe "GET /api/pleroma/emoji/pack?name=:name" do
- test "shows pack.json", %{conn: conn} do
- assert %{
- "files" => files,
- "files_count" => 2,
- "pack" => %{
- "can-download" => true,
- "description" => "Test description",
- "download-sha256" => _,
- "homepage" => "https://pleroma.social",
- "license" => "Test license",
- "share-files" => true
- }
- } =
- conn
- |> get("/api/pleroma/emoji/pack?name=test_pack")
- |> json_response_and_validate_schema(200)
-
- assert files == %{"blank" => "blank.png", "blank2" => "blank2.png"}
-
- assert %{
- "files" => files,
- "files_count" => 2
- } =
- conn
- |> get("/api/pleroma/emoji/pack?name=test_pack&page_size=1")
- |> json_response_and_validate_schema(200)
-
- assert files |> Map.keys() |> length() == 1
-
- assert %{
- "files" => files,
- "files_count" => 2
- } =
- conn
- |> get("/api/pleroma/emoji/pack?name=test_pack&page_size=1&page=2")
- |> json_response_and_validate_schema(200)
-
- assert files |> Map.keys() |> length() == 1
- end
-
- test "for pack name with special chars", %{conn: conn} do
- assert %{
- "files" => files,
- "files_count" => 1,
- "pack" => %{
- "can-download" => true,
- "description" => "Test description",
- "download-sha256" => _,
- "homepage" => "https://pleroma.social",
- "license" => "Test license",
- "share-files" => true
- }
- } =
- conn
- |> get("/api/pleroma/emoji/pack?name=blobs.gg")
- |> json_response_and_validate_schema(200)
- end
-
- test "non existing pack", %{conn: conn} do
- assert conn
- |> get("/api/pleroma/emoji/pack?name=non_existing")
- |> json_response_and_validate_schema(:not_found) == %{
- "error" => "Pack non_existing does not exist"
- }
- end
-
- test "error name", %{conn: conn} do
- assert conn
- |> get("/api/pleroma/emoji/pack?name= ")
- |> json_response_and_validate_schema(:bad_request) == %{
- "error" => "pack name cannot be empty"
- }
- end
- end
-end
diff --git a/test/web/pleroma_api/controllers/emoji_reaction_controller_test.exs b/test/web/pleroma_api/controllers/emoji_reaction_controller_test.exs
@@ -1,149 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Web.PleromaAPI.EmojiReactionControllerTest do
- use Oban.Testing, repo: Pleroma.Repo
- use Pleroma.Web.ConnCase
-
- alias Pleroma.Object
- alias Pleroma.Tests.ObanHelpers
- alias Pleroma.User
- alias Pleroma.Web.CommonAPI
-
- import Pleroma.Factory
-
- test "PUT /api/v1/pleroma/statuses/:id/reactions/:emoji", %{conn: conn} do
- user = insert(:user)
- other_user = insert(:user)
-
- {:ok, activity} = CommonAPI.post(user, %{status: "#cofe"})
-
- result =
- conn
- |> assign(:user, other_user)
- |> assign(:token, insert(:oauth_token, user: other_user, scopes: ["write:statuses"]))
- |> put("/api/v1/pleroma/statuses/#{activity.id}/reactions/☕")
- |> json_response_and_validate_schema(200)
-
- # We return the status, but this our implementation detail.
- assert %{"id" => id} = result
- assert to_string(activity.id) == id
-
- assert result["pleroma"]["emoji_reactions"] == [
- %{"name" => "☕", "count" => 1, "me" => true}
- ]
-
- # Reacting with a non-emoji
- assert conn
- |> assign(:user, other_user)
- |> assign(:token, insert(:oauth_token, user: other_user, scopes: ["write:statuses"]))
- |> put("/api/v1/pleroma/statuses/#{activity.id}/reactions/x")
- |> json_response_and_validate_schema(400)
- end
-
- test "DELETE /api/v1/pleroma/statuses/:id/reactions/:emoji", %{conn: conn} do
- user = insert(:user)
- other_user = insert(:user)
-
- {:ok, activity} = CommonAPI.post(user, %{status: "#cofe"})
- {:ok, _reaction_activity} = CommonAPI.react_with_emoji(activity.id, other_user, "☕")
-
- ObanHelpers.perform_all()
-
- result =
- conn
- |> assign(:user, other_user)
- |> assign(:token, insert(:oauth_token, user: other_user, scopes: ["write:statuses"]))
- |> delete("/api/v1/pleroma/statuses/#{activity.id}/reactions/☕")
-
- assert %{"id" => id} = json_response_and_validate_schema(result, 200)
- assert to_string(activity.id) == id
-
- ObanHelpers.perform_all()
-
- object = Object.get_by_ap_id(activity.data["object"])
-
- assert object.data["reaction_count"] == 0
- end
-
- test "GET /api/v1/pleroma/statuses/:id/reactions", %{conn: conn} do
- user = insert(:user)
- other_user = insert(:user)
- doomed_user = insert(:user)
-
- {:ok, activity} = CommonAPI.post(user, %{status: "#cofe"})
-
- result =
- conn
- |> get("/api/v1/pleroma/statuses/#{activity.id}/reactions")
- |> json_response_and_validate_schema(200)
-
- assert result == []
-
- {:ok, _} = CommonAPI.react_with_emoji(activity.id, other_user, "🎅")
- {:ok, _} = CommonAPI.react_with_emoji(activity.id, doomed_user, "🎅")
-
- User.perform(:delete, doomed_user)
-
- result =
- conn
- |> get("/api/v1/pleroma/statuses/#{activity.id}/reactions")
- |> json_response_and_validate_schema(200)
-
- [%{"name" => "🎅", "count" => 1, "accounts" => [represented_user], "me" => false}] = result
-
- assert represented_user["id"] == other_user.id
-
- result =
- conn
- |> assign(:user, other_user)
- |> assign(:token, insert(:oauth_token, user: other_user, scopes: ["read:statuses"]))
- |> get("/api/v1/pleroma/statuses/#{activity.id}/reactions")
- |> json_response_and_validate_schema(200)
-
- assert [%{"name" => "🎅", "count" => 1, "accounts" => [_represented_user], "me" => true}] =
- result
- end
-
- test "GET /api/v1/pleroma/statuses/:id/reactions with :show_reactions disabled", %{conn: conn} do
- clear_config([:instance, :show_reactions], false)
-
- user = insert(:user)
- other_user = insert(:user)
-
- {:ok, activity} = CommonAPI.post(user, %{status: "#cofe"})
- {:ok, _} = CommonAPI.react_with_emoji(activity.id, other_user, "🎅")
-
- result =
- conn
- |> get("/api/v1/pleroma/statuses/#{activity.id}/reactions")
- |> json_response_and_validate_schema(200)
-
- assert result == []
- end
-
- test "GET /api/v1/pleroma/statuses/:id/reactions/:emoji", %{conn: conn} do
- user = insert(:user)
- other_user = insert(:user)
-
- {:ok, activity} = CommonAPI.post(user, %{status: "#cofe"})
-
- result =
- conn
- |> get("/api/v1/pleroma/statuses/#{activity.id}/reactions/🎅")
- |> json_response_and_validate_schema(200)
-
- assert result == []
-
- {:ok, _} = CommonAPI.react_with_emoji(activity.id, other_user, "🎅")
- {:ok, _} = CommonAPI.react_with_emoji(activity.id, other_user, "☕")
-
- assert [%{"name" => "🎅", "count" => 1, "accounts" => [represented_user], "me" => false}] =
- conn
- |> get("/api/v1/pleroma/statuses/#{activity.id}/reactions/🎅")
- |> json_response_and_validate_schema(200)
-
- assert represented_user["id"] == other_user.id
- end
-end
diff --git a/test/web/pleroma_api/controllers/mascot_controller_test.exs b/test/web/pleroma_api/controllers/mascot_controller_test.exs
@@ -1,73 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Web.PleromaAPI.MascotControllerTest do
- use Pleroma.Web.ConnCase
-
- alias Pleroma.User
-
- test "mascot upload" do
- %{conn: conn} = oauth_access(["write:accounts"])
-
- non_image_file = %Plug.Upload{
- content_type: "audio/mpeg",
- path: Path.absname("test/fixtures/sound.mp3"),
- filename: "sound.mp3"
- }
-
- ret_conn =
- conn
- |> put_req_header("content-type", "multipart/form-data")
- |> put("/api/v1/pleroma/mascot", %{"file" => non_image_file})
-
- assert json_response_and_validate_schema(ret_conn, 415)
-
- file = %Plug.Upload{
- content_type: "image/jpg",
- path: Path.absname("test/fixtures/image.jpg"),
- filename: "an_image.jpg"
- }
-
- conn =
- conn
- |> put_req_header("content-type", "multipart/form-data")
- |> put("/api/v1/pleroma/mascot", %{"file" => file})
-
- assert %{"id" => _, "type" => image} = json_response_and_validate_schema(conn, 200)
- end
-
- test "mascot retrieving" do
- %{user: user, conn: conn} = oauth_access(["read:accounts", "write:accounts"])
-
- # When user hasn't set a mascot, we should just get pleroma tan back
- ret_conn = get(conn, "/api/v1/pleroma/mascot")
-
- assert %{"url" => url} = json_response_and_validate_schema(ret_conn, 200)
- assert url =~ "pleroma-fox-tan-smol"
-
- # When a user sets their mascot, we should get that back
- file = %Plug.Upload{
- content_type: "image/jpg",
- path: Path.absname("test/fixtures/image.jpg"),
- filename: "an_image.jpg"
- }
-
- ret_conn =
- conn
- |> put_req_header("content-type", "multipart/form-data")
- |> put("/api/v1/pleroma/mascot", %{"file" => file})
-
- assert json_response_and_validate_schema(ret_conn, 200)
-
- user = User.get_cached_by_id(user.id)
-
- conn =
- conn
- |> assign(:user, user)
- |> get("/api/v1/pleroma/mascot")
-
- assert %{"url" => url, "type" => "image"} = json_response_and_validate_schema(conn, 200)
- assert url =~ "an_image"
- end
-end
diff --git a/test/web/pleroma_api/controllers/notification_controller_test.exs b/test/web/pleroma_api/controllers/notification_controller_test.exs
@@ -1,68 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Web.PleromaAPI.NotificationControllerTest do
- use Pleroma.Web.ConnCase
-
- alias Pleroma.Notification
- alias Pleroma.Repo
- alias Pleroma.Web.CommonAPI
-
- import Pleroma.Factory
-
- describe "POST /api/v1/pleroma/notifications/read" do
- setup do: oauth_access(["write:notifications"])
-
- test "it marks a single notification as read", %{user: user1, conn: conn} do
- user2 = insert(:user)
- {:ok, activity1} = CommonAPI.post(user2, %{status: "hi @#{user1.nickname}"})
- {:ok, activity2} = CommonAPI.post(user2, %{status: "hi @#{user1.nickname}"})
- {:ok, [notification1]} = Notification.create_notifications(activity1)
- {:ok, [notification2]} = Notification.create_notifications(activity2)
-
- response =
- conn
- |> put_req_header("content-type", "application/json")
- |> post("/api/v1/pleroma/notifications/read", %{id: notification1.id})
- |> json_response_and_validate_schema(:ok)
-
- assert %{"pleroma" => %{"is_seen" => true}} = response
- assert Repo.get(Notification, notification1.id).seen
- refute Repo.get(Notification, notification2.id).seen
- end
-
- test "it marks multiple notifications as read", %{user: user1, conn: conn} do
- user2 = insert(:user)
- {:ok, _activity1} = CommonAPI.post(user2, %{status: "hi @#{user1.nickname}"})
- {:ok, _activity2} = CommonAPI.post(user2, %{status: "hi @#{user1.nickname}"})
- {:ok, _activity3} = CommonAPI.post(user2, %{status: "HIE @#{user1.nickname}"})
-
- [notification3, notification2, notification1] = Notification.for_user(user1, %{limit: 3})
-
- [response1, response2] =
- conn
- |> put_req_header("content-type", "application/json")
- |> post("/api/v1/pleroma/notifications/read", %{max_id: notification2.id})
- |> json_response_and_validate_schema(:ok)
-
- assert %{"pleroma" => %{"is_seen" => true}} = response1
- assert %{"pleroma" => %{"is_seen" => true}} = response2
- assert Repo.get(Notification, notification1.id).seen
- assert Repo.get(Notification, notification2.id).seen
- refute Repo.get(Notification, notification3.id).seen
- end
-
- test "it returns error when notification not found", %{conn: conn} do
- response =
- conn
- |> put_req_header("content-type", "application/json")
- |> post("/api/v1/pleroma/notifications/read", %{
- id: 22_222_222_222_222
- })
- |> json_response_and_validate_schema(:bad_request)
-
- assert response == %{"error" => "Cannot get notification"}
- end
- end
-end
diff --git a/test/web/pleroma_api/controllers/scrobble_controller_test.exs b/test/web/pleroma_api/controllers/scrobble_controller_test.exs
@@ -1,60 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Web.PleromaAPI.ScrobbleControllerTest do
- use Pleroma.Web.ConnCase
-
- alias Pleroma.Web.CommonAPI
-
- describe "POST /api/v1/pleroma/scrobble" do
- test "works correctly" do
- %{conn: conn} = oauth_access(["write"])
-
- conn =
- conn
- |> put_req_header("content-type", "application/json")
- |> post("/api/v1/pleroma/scrobble", %{
- "title" => "lain radio episode 1",
- "artist" => "lain",
- "album" => "lain radio",
- "length" => "180000"
- })
-
- assert %{"title" => "lain radio episode 1"} = json_response_and_validate_schema(conn, 200)
- end
- end
-
- describe "GET /api/v1/pleroma/accounts/:id/scrobbles" do
- test "works correctly" do
- %{user: user, conn: conn} = oauth_access(["read"])
-
- {:ok, _activity} =
- CommonAPI.listen(user, %{
- title: "lain radio episode 1",
- artist: "lain",
- album: "lain radio"
- })
-
- {:ok, _activity} =
- CommonAPI.listen(user, %{
- title: "lain radio episode 2",
- artist: "lain",
- album: "lain radio"
- })
-
- {:ok, _activity} =
- CommonAPI.listen(user, %{
- title: "lain radio episode 3",
- artist: "lain",
- album: "lain radio"
- })
-
- conn = get(conn, "/api/v1/pleroma/accounts/#{user.id}/scrobbles")
-
- result = json_response_and_validate_schema(conn, 200)
-
- assert length(result) == 3
- end
- end
-end
diff --git a/test/web/pleroma_api/controllers/two_factor_authentication_controller_test.exs b/test/web/pleroma_api/controllers/two_factor_authentication_controller_test.exs
@@ -1,264 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Web.PleromaAPI.TwoFactorAuthenticationControllerTest do
- use Pleroma.Web.ConnCase
-
- import Pleroma.Factory
- alias Pleroma.MFA.Settings
- alias Pleroma.MFA.TOTP
-
- describe "GET /api/pleroma/accounts/mfa/settings" do
- test "returns user mfa settings for new user", %{conn: conn} do
- token = insert(:oauth_token, scopes: ["read", "follow"])
- token2 = insert(:oauth_token, scopes: ["write"])
-
- assert conn
- |> put_req_header("authorization", "Bearer #{token.token}")
- |> get("/api/pleroma/accounts/mfa")
- |> json_response(:ok) == %{
- "settings" => %{"enabled" => false, "totp" => false}
- }
-
- assert conn
- |> put_req_header("authorization", "Bearer #{token2.token}")
- |> get("/api/pleroma/accounts/mfa")
- |> json_response(403) == %{
- "error" => "Insufficient permissions: read:security."
- }
- end
-
- test "returns user mfa settings with enabled totp", %{conn: conn} do
- user =
- insert(:user,
- multi_factor_authentication_settings: %Settings{
- enabled: true,
- totp: %Settings.TOTP{secret: "XXX", delivery_type: "app", confirmed: true}
- }
- )
-
- token = insert(:oauth_token, scopes: ["read", "follow"], user: user)
-
- assert conn
- |> put_req_header("authorization", "Bearer #{token.token}")
- |> get("/api/pleroma/accounts/mfa")
- |> json_response(:ok) == %{
- "settings" => %{"enabled" => true, "totp" => true}
- }
- end
- end
-
- describe "GET /api/pleroma/accounts/mfa/backup_codes" do
- test "returns backup codes", %{conn: conn} do
- user =
- insert(:user,
- multi_factor_authentication_settings: %Settings{
- backup_codes: ["1", "2", "3"],
- totp: %Settings.TOTP{secret: "secret"}
- }
- )
-
- token = insert(:oauth_token, scopes: ["write", "follow"], user: user)
- token2 = insert(:oauth_token, scopes: ["read"])
-
- response =
- conn
- |> put_req_header("authorization", "Bearer #{token.token}")
- |> get("/api/pleroma/accounts/mfa/backup_codes")
- |> json_response(:ok)
-
- assert [<<_::bytes-size(6)>>, <<_::bytes-size(6)>>] = response["codes"]
- user = refresh_record(user)
- mfa_settings = user.multi_factor_authentication_settings
- assert mfa_settings.totp.secret == "secret"
- refute mfa_settings.backup_codes == ["1", "2", "3"]
- refute mfa_settings.backup_codes == []
-
- assert conn
- |> put_req_header("authorization", "Bearer #{token2.token}")
- |> get("/api/pleroma/accounts/mfa/backup_codes")
- |> json_response(403) == %{
- "error" => "Insufficient permissions: write:security."
- }
- end
- end
-
- describe "GET /api/pleroma/accounts/mfa/setup/totp" do
- test "return errors when method is invalid", %{conn: conn} do
- user = insert(:user)
- token = insert(:oauth_token, scopes: ["write", "follow"], user: user)
-
- response =
- conn
- |> put_req_header("authorization", "Bearer #{token.token}")
- |> get("/api/pleroma/accounts/mfa/setup/torf")
- |> json_response(400)
-
- assert response == %{"error" => "undefined method"}
- end
-
- test "returns key and provisioning_uri", %{conn: conn} do
- user =
- insert(:user,
- multi_factor_authentication_settings: %Settings{backup_codes: ["1", "2", "3"]}
- )
-
- token = insert(:oauth_token, scopes: ["write", "follow"], user: user)
- token2 = insert(:oauth_token, scopes: ["read"])
-
- response =
- conn
- |> put_req_header("authorization", "Bearer #{token.token}")
- |> get("/api/pleroma/accounts/mfa/setup/totp")
- |> json_response(:ok)
-
- user = refresh_record(user)
- mfa_settings = user.multi_factor_authentication_settings
- secret = mfa_settings.totp.secret
- refute mfa_settings.enabled
- assert mfa_settings.backup_codes == ["1", "2", "3"]
-
- assert response == %{
- "key" => secret,
- "provisioning_uri" => TOTP.provisioning_uri(secret, "#{user.email}")
- }
-
- assert conn
- |> put_req_header("authorization", "Bearer #{token2.token}")
- |> get("/api/pleroma/accounts/mfa/setup/totp")
- |> json_response(403) == %{
- "error" => "Insufficient permissions: write:security."
- }
- end
- end
-
- describe "GET /api/pleroma/accounts/mfa/confirm/totp" do
- test "returns success result", %{conn: conn} do
- secret = TOTP.generate_secret()
- code = TOTP.generate_token(secret)
-
- user =
- insert(:user,
- multi_factor_authentication_settings: %Settings{
- backup_codes: ["1", "2", "3"],
- totp: %Settings.TOTP{secret: secret}
- }
- )
-
- token = insert(:oauth_token, scopes: ["write", "follow"], user: user)
- token2 = insert(:oauth_token, scopes: ["read"])
-
- assert conn
- |> put_req_header("authorization", "Bearer #{token.token}")
- |> post("/api/pleroma/accounts/mfa/confirm/totp", %{password: "test", code: code})
- |> json_response(:ok)
-
- settings = refresh_record(user).multi_factor_authentication_settings
- assert settings.enabled
- assert settings.totp.secret == secret
- assert settings.totp.confirmed
- assert settings.backup_codes == ["1", "2", "3"]
-
- assert conn
- |> put_req_header("authorization", "Bearer #{token2.token}")
- |> post("/api/pleroma/accounts/mfa/confirm/totp", %{password: "test", code: code})
- |> json_response(403) == %{
- "error" => "Insufficient permissions: write:security."
- }
- end
-
- test "returns error if password incorrect", %{conn: conn} do
- secret = TOTP.generate_secret()
- code = TOTP.generate_token(secret)
-
- user =
- insert(:user,
- multi_factor_authentication_settings: %Settings{
- backup_codes: ["1", "2", "3"],
- totp: %Settings.TOTP{secret: secret}
- }
- )
-
- token = insert(:oauth_token, scopes: ["write", "follow"], user: user)
-
- response =
- conn
- |> put_req_header("authorization", "Bearer #{token.token}")
- |> post("/api/pleroma/accounts/mfa/confirm/totp", %{password: "xxx", code: code})
- |> json_response(422)
-
- settings = refresh_record(user).multi_factor_authentication_settings
- refute settings.enabled
- refute settings.totp.confirmed
- assert settings.backup_codes == ["1", "2", "3"]
- assert response == %{"error" => "Invalid password."}
- end
-
- test "returns error if code incorrect", %{conn: conn} do
- secret = TOTP.generate_secret()
-
- user =
- insert(:user,
- multi_factor_authentication_settings: %Settings{
- backup_codes: ["1", "2", "3"],
- totp: %Settings.TOTP{secret: secret}
- }
- )
-
- token = insert(:oauth_token, scopes: ["write", "follow"], user: user)
- token2 = insert(:oauth_token, scopes: ["read"])
-
- response =
- conn
- |> put_req_header("authorization", "Bearer #{token.token}")
- |> post("/api/pleroma/accounts/mfa/confirm/totp", %{password: "test", code: "code"})
- |> json_response(422)
-
- settings = refresh_record(user).multi_factor_authentication_settings
- refute settings.enabled
- refute settings.totp.confirmed
- assert settings.backup_codes == ["1", "2", "3"]
- assert response == %{"error" => "invalid_token"}
-
- assert conn
- |> put_req_header("authorization", "Bearer #{token2.token}")
- |> post("/api/pleroma/accounts/mfa/confirm/totp", %{password: "test", code: "code"})
- |> json_response(403) == %{
- "error" => "Insufficient permissions: write:security."
- }
- end
- end
-
- describe "DELETE /api/pleroma/accounts/mfa/totp" do
- test "returns success result", %{conn: conn} do
- user =
- insert(:user,
- multi_factor_authentication_settings: %Settings{
- backup_codes: ["1", "2", "3"],
- totp: %Settings.TOTP{secret: "secret"}
- }
- )
-
- token = insert(:oauth_token, scopes: ["write", "follow"], user: user)
- token2 = insert(:oauth_token, scopes: ["read"])
-
- assert conn
- |> put_req_header("authorization", "Bearer #{token.token}")
- |> delete("/api/pleroma/accounts/mfa/totp", %{password: "test"})
- |> json_response(:ok)
-
- settings = refresh_record(user).multi_factor_authentication_settings
- refute settings.enabled
- assert settings.totp.secret == nil
- refute settings.totp.confirmed
-
- assert conn
- |> put_req_header("authorization", "Bearer #{token2.token}")
- |> delete("/api/pleroma/accounts/mfa/totp", %{password: "test"})
- |> json_response(403) == %{
- "error" => "Insufficient permissions: write:security."
- }
- end
- end
-end
diff --git a/test/web/pleroma_api/views/chat/message_reference_view_test.exs b/test/web/pleroma_api/views/chat/message_reference_view_test.exs
@@ -1,72 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Web.PleromaAPI.Chat.MessageReferenceViewTest do
- use Pleroma.DataCase
-
- alias Pleroma.Chat
- alias Pleroma.Chat.MessageReference
- alias Pleroma.Object
- alias Pleroma.Web.ActivityPub.ActivityPub
- alias Pleroma.Web.CommonAPI
- alias Pleroma.Web.PleromaAPI.Chat.MessageReferenceView
-
- import Pleroma.Factory
-
- test "it displays a chat message" do
- user = insert(:user)
- recipient = insert(:user)
-
- file = %Plug.Upload{
- content_type: "image/jpg",
- path: Path.absname("test/fixtures/image.jpg"),
- filename: "an_image.jpg"
- }
-
- {:ok, upload} = ActivityPub.upload(file, actor: user.ap_id)
- {:ok, activity} = CommonAPI.post_chat_message(user, recipient, "kippis :firefox:")
-
- chat = Chat.get(user.id, recipient.ap_id)
-
- object = Object.normalize(activity)
-
- cm_ref = MessageReference.for_chat_and_object(chat, object)
-
- chat_message = MessageReferenceView.render("show.json", chat_message_reference: cm_ref)
-
- assert chat_message[:id] == cm_ref.id
- assert chat_message[:content] == "kippis :firefox:"
- assert chat_message[:account_id] == user.id
- assert chat_message[:chat_id]
- assert chat_message[:created_at]
- assert chat_message[:unread] == false
- assert match?([%{shortcode: "firefox"}], chat_message[:emojis])
-
- clear_config([:rich_media, :enabled], true)
-
- Tesla.Mock.mock(fn
- %{url: "https://example.com/ogp"} ->
- %Tesla.Env{status: 200, body: File.read!("test/fixtures/rich_media/ogp.html")}
- end)
-
- {:ok, activity} =
- CommonAPI.post_chat_message(recipient, user, "gkgkgk https://example.com/ogp",
- media_id: upload.id
- )
-
- object = Object.normalize(activity)
-
- cm_ref = MessageReference.for_chat_and_object(chat, object)
-
- chat_message_two = MessageReferenceView.render("show.json", chat_message_reference: cm_ref)
-
- assert chat_message_two[:id] == cm_ref.id
- assert chat_message_two[:content] == object.data["content"]
- assert chat_message_two[:account_id] == recipient.id
- assert chat_message_two[:chat_id] == chat_message[:chat_id]
- assert chat_message_two[:attachment]
- assert chat_message_two[:unread] == true
- assert chat_message_two[:card]
- end
-end
diff --git a/test/web/pleroma_api/views/chat_view_test.exs b/test/web/pleroma_api/views/chat_view_test.exs
@@ -1,49 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Web.PleromaAPI.ChatViewTest do
- use Pleroma.DataCase
-
- alias Pleroma.Chat
- alias Pleroma.Chat.MessageReference
- alias Pleroma.Object
- alias Pleroma.Web.CommonAPI
- alias Pleroma.Web.CommonAPI.Utils
- alias Pleroma.Web.MastodonAPI.AccountView
- alias Pleroma.Web.PleromaAPI.Chat.MessageReferenceView
- alias Pleroma.Web.PleromaAPI.ChatView
-
- import Pleroma.Factory
-
- test "it represents a chat" do
- user = insert(:user)
- recipient = insert(:user)
-
- {:ok, chat} = Chat.get_or_create(user.id, recipient.ap_id)
-
- represented_chat = ChatView.render("show.json", chat: chat)
-
- assert represented_chat == %{
- id: "#{chat.id}",
- account:
- AccountView.render("show.json", user: recipient, skip_visibility_check: true),
- unread: 0,
- last_message: nil,
- updated_at: Utils.to_masto_date(chat.updated_at)
- }
-
- {:ok, chat_message_creation} = CommonAPI.post_chat_message(user, recipient, "hello")
-
- chat_message = Object.normalize(chat_message_creation, false)
-
- {:ok, chat} = Chat.get_or_create(user.id, recipient.ap_id)
-
- represented_chat = ChatView.render("show.json", chat: chat)
-
- cm_ref = MessageReference.for_chat_and_object(chat, chat_message)
-
- assert represented_chat[:last_message] ==
- MessageReferenceView.render("show.json", chat_message_reference: cm_ref)
- end
-end
diff --git a/test/web/pleroma_api/views/scrobble_view_test.exs b/test/web/pleroma_api/views/scrobble_view_test.exs
@@ -1,20 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Web.PleromaAPI.StatusViewTest do
- use Pleroma.DataCase
-
- alias Pleroma.Web.PleromaAPI.ScrobbleView
-
- import Pleroma.Factory
-
- test "successfully renders a Listen activity (pleroma extension)" do
- listen_activity = insert(:listen)
-
- status = ScrobbleView.render("show.json", activity: listen_activity)
-
- assert status.length == listen_activity.data["object"]["length"]
- assert status.title == listen_activity.data["object"]["title"]
- end
-end
diff --git a/test/web/plugs/federating_plug_test.exs b/test/web/plugs/federating_plug_test.exs
@@ -1,31 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Web.FederatingPlugTest do
- use Pleroma.Web.ConnCase
-
- setup do: clear_config([:instance, :federating])
-
- test "returns and halt the conn when federating is disabled" do
- Pleroma.Config.put([:instance, :federating], false)
-
- conn =
- build_conn()
- |> Pleroma.Web.FederatingPlug.call(%{})
-
- assert conn.status == 404
- assert conn.halted
- end
-
- test "does nothing when federating is enabled" do
- Pleroma.Config.put([:instance, :federating], true)
-
- conn =
- build_conn()
- |> Pleroma.Web.FederatingPlug.call(%{})
-
- refute conn.status
- refute conn.halted
- end
-end
diff --git a/test/web/plugs/plug_test.exs b/test/web/plugs/plug_test.exs
@@ -1,91 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Web.PlugTest do
- @moduledoc "Tests for the functionality added via `use Pleroma.Web, :plug`"
-
- alias Pleroma.Plugs.ExpectAuthenticatedCheckPlug
- alias Pleroma.Plugs.ExpectPublicOrAuthenticatedCheckPlug
- alias Pleroma.Plugs.PlugHelper
-
- import Mock
-
- use Pleroma.Web.ConnCase
-
- describe "when plug is skipped, " do
- setup_with_mocks(
- [
- {ExpectPublicOrAuthenticatedCheckPlug, [:passthrough], []}
- ],
- %{conn: conn}
- ) do
- conn = ExpectPublicOrAuthenticatedCheckPlug.skip_plug(conn)
- %{conn: conn}
- end
-
- test "it neither adds plug to called plugs list nor calls `perform/2`, " <>
- "regardless of :if_func / :unless_func options",
- %{conn: conn} do
- for opts <- [%{}, %{if_func: fn _ -> true end}, %{unless_func: fn _ -> false end}] do
- ret_conn = ExpectPublicOrAuthenticatedCheckPlug.call(conn, opts)
-
- refute called(ExpectPublicOrAuthenticatedCheckPlug.perform(:_, :_))
- refute PlugHelper.plug_called?(ret_conn, ExpectPublicOrAuthenticatedCheckPlug)
- end
- end
- end
-
- describe "when plug is NOT skipped, " do
- setup_with_mocks([{ExpectAuthenticatedCheckPlug, [:passthrough], []}]) do
- :ok
- end
-
- test "with no pre-run checks, adds plug to called plugs list and calls `perform/2`", %{
- conn: conn
- } do
- ret_conn = ExpectAuthenticatedCheckPlug.call(conn, %{})
-
- assert called(ExpectAuthenticatedCheckPlug.perform(ret_conn, :_))
- assert PlugHelper.plug_called?(ret_conn, ExpectAuthenticatedCheckPlug)
- end
-
- test "when :if_func option is given, calls the plug only if provided function evals tru-ish",
- %{conn: conn} do
- ret_conn = ExpectAuthenticatedCheckPlug.call(conn, %{if_func: fn _ -> false end})
-
- refute called(ExpectAuthenticatedCheckPlug.perform(:_, :_))
- refute PlugHelper.plug_called?(ret_conn, ExpectAuthenticatedCheckPlug)
-
- ret_conn = ExpectAuthenticatedCheckPlug.call(conn, %{if_func: fn _ -> true end})
-
- assert called(ExpectAuthenticatedCheckPlug.perform(ret_conn, :_))
- assert PlugHelper.plug_called?(ret_conn, ExpectAuthenticatedCheckPlug)
- end
-
- test "if :unless_func option is given, calls the plug only if provided function evals falsy",
- %{conn: conn} do
- ret_conn = ExpectAuthenticatedCheckPlug.call(conn, %{unless_func: fn _ -> true end})
-
- refute called(ExpectAuthenticatedCheckPlug.perform(:_, :_))
- refute PlugHelper.plug_called?(ret_conn, ExpectAuthenticatedCheckPlug)
-
- ret_conn = ExpectAuthenticatedCheckPlug.call(conn, %{unless_func: fn _ -> false end})
-
- assert called(ExpectAuthenticatedCheckPlug.perform(ret_conn, :_))
- assert PlugHelper.plug_called?(ret_conn, ExpectAuthenticatedCheckPlug)
- end
-
- test "allows a plug to be called multiple times (even if it's in called plugs list)", %{
- conn: conn
- } do
- conn = ExpectAuthenticatedCheckPlug.call(conn, %{an_option: :value1})
- assert called(ExpectAuthenticatedCheckPlug.perform(conn, %{an_option: :value1}))
-
- assert PlugHelper.plug_called?(conn, ExpectAuthenticatedCheckPlug)
-
- conn = ExpectAuthenticatedCheckPlug.call(conn, %{an_option: :value2})
- assert called(ExpectAuthenticatedCheckPlug.perform(conn, %{an_option: :value2}))
- end
- end
-end
diff --git a/test/web/push/impl_test.exs b/test/web/push/impl_test.exs
@@ -1,344 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Web.Push.ImplTest do
- use Pleroma.DataCase
-
- import Pleroma.Factory
-
- alias Pleroma.Notification
- alias Pleroma.Object
- alias Pleroma.User
- alias Pleroma.Web.ActivityPub.ActivityPub
- alias Pleroma.Web.CommonAPI
- alias Pleroma.Web.Push.Impl
- alias Pleroma.Web.Push.Subscription
-
- setup do
- Tesla.Mock.mock(fn
- %{method: :post, url: "https://example.com/example/1234"} ->
- %Tesla.Env{status: 200}
-
- %{method: :post, url: "https://example.com/example/not_found"} ->
- %Tesla.Env{status: 400}
-
- %{method: :post, url: "https://example.com/example/bad"} ->
- %Tesla.Env{status: 100}
- end)
-
- :ok
- end
-
- @sub %{
- endpoint: "https://example.com/example/1234",
- keys: %{
- auth: "8eDyX_uCN0XRhSbY5hs7Hg==",
- p256dh:
- "BCIWgsnyXDv1VkhqL2P7YRBvdeuDnlwAPT2guNhdIoW3IP7GmHh1SMKPLxRf7x8vJy6ZFK3ol2ohgn_-0yP7QQA="
- }
- }
- @api_key "BASgACIHpN1GYgzSRp"
- @message "@Bob: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce sagittis fini..."
-
- test "performs sending notifications" do
- user = insert(:user)
- user2 = insert(:user)
- insert(:push_subscription, user: user, data: %{alerts: %{"mention" => true}})
- insert(:push_subscription, user: user2, data: %{alerts: %{"mention" => true}})
-
- insert(:push_subscription,
- user: user,
- data: %{alerts: %{"follow" => true, "mention" => true}}
- )
-
- insert(:push_subscription,
- user: user,
- data: %{alerts: %{"follow" => true, "mention" => false}}
- )
-
- {:ok, activity} = CommonAPI.post(user, %{status: "<Lorem ipsum dolor sit amet."})
-
- notif =
- insert(:notification,
- user: user,
- activity: activity,
- type: "mention"
- )
-
- assert Impl.perform(notif) == {:ok, [:ok, :ok]}
- end
-
- @tag capture_log: true
- test "returns error if notif does not match " do
- assert Impl.perform(%{}) == {:error, :unknown_type}
- end
-
- test "successful message sending" do
- assert Impl.push_message(@message, @sub, @api_key, %Subscription{}) == :ok
- end
-
- @tag capture_log: true
- test "fail message sending" do
- assert Impl.push_message(
- @message,
- Map.merge(@sub, %{endpoint: "https://example.com/example/bad"}),
- @api_key,
- %Subscription{}
- ) == :error
- end
-
- test "delete subscription if result send message between 400..500" do
- subscription = insert(:push_subscription)
-
- assert Impl.push_message(
- @message,
- Map.merge(@sub, %{endpoint: "https://example.com/example/not_found"}),
- @api_key,
- subscription
- ) == :ok
-
- refute Pleroma.Repo.get(Subscription, subscription.id)
- end
-
- test "deletes subscription when token has been deleted" do
- subscription = insert(:push_subscription)
-
- Pleroma.Repo.delete(subscription.token)
-
- refute Pleroma.Repo.get(Subscription, subscription.id)
- end
-
- test "renders title and body for create activity" do
- user = insert(:user, nickname: "Bob")
-
- {:ok, activity} =
- CommonAPI.post(user, %{
- status:
- "<span>Lorem ipsum dolor sit amet</span>, consectetur :firefox: adipiscing elit. Fusce sagittis finibus turpis."
- })
-
- object = Object.normalize(activity)
-
- assert Impl.format_body(
- %{
- activity: activity
- },
- user,
- object
- ) ==
- "@Bob: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce sagittis fini..."
-
- assert Impl.format_title(%{activity: activity, type: "mention"}) ==
- "New Mention"
- end
-
- test "renders title and body for follow activity" do
- user = insert(:user, nickname: "Bob")
- other_user = insert(:user)
- {:ok, _, _, activity} = CommonAPI.follow(user, other_user)
- object = Object.normalize(activity, false)
-
- assert Impl.format_body(%{activity: activity, type: "follow"}, user, object) ==
- "@Bob has followed you"
-
- assert Impl.format_title(%{activity: activity, type: "follow"}) ==
- "New Follower"
- end
-
- test "renders title and body for announce activity" do
- user = insert(:user)
-
- {:ok, activity} =
- CommonAPI.post(user, %{
- status:
- "<span>Lorem ipsum dolor sit amet</span>, consectetur :firefox: adipiscing elit. Fusce sagittis finibus turpis."
- })
-
- {:ok, announce_activity} = CommonAPI.repeat(activity.id, user)
- object = Object.normalize(activity)
-
- assert Impl.format_body(%{activity: announce_activity}, user, object) ==
- "@#{user.nickname} repeated: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce sagittis fini..."
-
- assert Impl.format_title(%{activity: announce_activity, type: "reblog"}) ==
- "New Repeat"
- end
-
- test "renders title and body for like activity" do
- user = insert(:user, nickname: "Bob")
-
- {:ok, activity} =
- CommonAPI.post(user, %{
- status:
- "<span>Lorem ipsum dolor sit amet</span>, consectetur :firefox: adipiscing elit. Fusce sagittis finibus turpis."
- })
-
- {:ok, activity} = CommonAPI.favorite(user, activity.id)
- object = Object.normalize(activity)
-
- assert Impl.format_body(%{activity: activity, type: "favourite"}, user, object) ==
- "@Bob has favorited your post"
-
- assert Impl.format_title(%{activity: activity, type: "favourite"}) ==
- "New Favorite"
- end
-
- test "renders title for create activity with direct visibility" do
- user = insert(:user, nickname: "Bob")
-
- {:ok, activity} =
- CommonAPI.post(user, %{
- visibility: "direct",
- status: "This is just between you and me, pal"
- })
-
- assert Impl.format_title(%{activity: activity}) ==
- "New Direct Message"
- end
-
- describe "build_content/3" do
- test "builds content for chat messages" do
- user = insert(:user)
- recipient = insert(:user)
-
- {:ok, chat} = CommonAPI.post_chat_message(user, recipient, "hey")
- object = Object.normalize(chat, false)
- [notification] = Notification.for_user(recipient)
-
- res = Impl.build_content(notification, user, object)
-
- assert res == %{
- body: "@#{user.nickname}: hey",
- title: "New Chat Message"
- }
- end
-
- test "builds content for chat messages with no content" do
- user = insert(:user)
- recipient = insert(:user)
-
- file = %Plug.Upload{
- content_type: "image/jpg",
- path: Path.absname("test/fixtures/image.jpg"),
- filename: "an_image.jpg"
- }
-
- {:ok, upload} = ActivityPub.upload(file, actor: user.ap_id)
-
- {:ok, chat} = CommonAPI.post_chat_message(user, recipient, nil, media_id: upload.id)
- object = Object.normalize(chat, false)
- [notification] = Notification.for_user(recipient)
-
- res = Impl.build_content(notification, user, object)
-
- assert res == %{
- body: "@#{user.nickname}: (Attachment)",
- title: "New Chat Message"
- }
- end
-
- test "hides contents of notifications when option enabled" do
- user = insert(:user, nickname: "Bob")
-
- user2 =
- insert(:user, nickname: "Rob", notification_settings: %{hide_notification_contents: true})
-
- {:ok, activity} =
- CommonAPI.post(user, %{
- visibility: "direct",
- status: "<Lorem ipsum dolor sit amet."
- })
-
- notif = insert(:notification, user: user2, activity: activity)
-
- actor = User.get_cached_by_ap_id(notif.activity.data["actor"])
- object = Object.normalize(activity)
-
- assert Impl.build_content(notif, actor, object) == %{
- body: "New Direct Message"
- }
-
- {:ok, activity} =
- CommonAPI.post(user, %{
- visibility: "public",
- status: "<Lorem ipsum dolor sit amet."
- })
-
- notif = insert(:notification, user: user2, activity: activity, type: "mention")
-
- actor = User.get_cached_by_ap_id(notif.activity.data["actor"])
- object = Object.normalize(activity)
-
- assert Impl.build_content(notif, actor, object) == %{
- body: "New Mention"
- }
-
- {:ok, activity} = CommonAPI.favorite(user, activity.id)
-
- notif = insert(:notification, user: user2, activity: activity, type: "favourite")
-
- actor = User.get_cached_by_ap_id(notif.activity.data["actor"])
- object = Object.normalize(activity)
-
- assert Impl.build_content(notif, actor, object) == %{
- body: "New Favorite"
- }
- end
-
- test "returns regular content when hiding contents option disabled" do
- user = insert(:user, nickname: "Bob")
-
- user2 =
- insert(:user, nickname: "Rob", notification_settings: %{hide_notification_contents: false})
-
- {:ok, activity} =
- CommonAPI.post(user, %{
- visibility: "direct",
- status:
- "<span>Lorem ipsum dolor sit amet</span>, consectetur :firefox: adipiscing elit. Fusce sagittis finibus turpis."
- })
-
- notif = insert(:notification, user: user2, activity: activity)
-
- actor = User.get_cached_by_ap_id(notif.activity.data["actor"])
- object = Object.normalize(activity)
-
- assert Impl.build_content(notif, actor, object) == %{
- body:
- "@Bob: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce sagittis fini...",
- title: "New Direct Message"
- }
-
- {:ok, activity} =
- CommonAPI.post(user, %{
- visibility: "public",
- status:
- "<span>Lorem ipsum dolor sit amet</span>, consectetur :firefox: adipiscing elit. Fusce sagittis finibus turpis."
- })
-
- notif = insert(:notification, user: user2, activity: activity, type: "mention")
-
- actor = User.get_cached_by_ap_id(notif.activity.data["actor"])
- object = Object.normalize(activity)
-
- assert Impl.build_content(notif, actor, object) == %{
- body:
- "@Bob: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce sagittis fini...",
- title: "New Mention"
- }
-
- {:ok, activity} = CommonAPI.favorite(user, activity.id)
-
- notif = insert(:notification, user: user2, activity: activity, type: "favourite")
-
- actor = User.get_cached_by_ap_id(notif.activity.data["actor"])
- object = Object.normalize(activity)
-
- assert Impl.build_content(notif, actor, object) == %{
- body: "@Bob has favorited your post",
- title: "New Favorite"
- }
- end
- end
-end
diff --git a/test/web/rel_me_test.exs b/test/web/rel_me_test.exs
@@ -1,48 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Web.RelMeTest do
- use ExUnit.Case
-
- setup_all do
- Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end)
- :ok
- end
-
- test "parse/1" do
- hrefs = ["https://social.example.org/users/lain"]
-
- assert Pleroma.Web.RelMe.parse("http://example.com/rel_me/null") == {:ok, []}
-
- assert {:ok, %Tesla.Env{status: 404}} =
- Pleroma.Web.RelMe.parse("http://example.com/rel_me/error")
-
- assert Pleroma.Web.RelMe.parse("http://example.com/rel_me/link") == {:ok, hrefs}
- assert Pleroma.Web.RelMe.parse("http://example.com/rel_me/anchor") == {:ok, hrefs}
- assert Pleroma.Web.RelMe.parse("http://example.com/rel_me/anchor_nofollow") == {:ok, hrefs}
- end
-
- test "maybe_put_rel_me/2" do
- profile_urls = ["https://social.example.org/users/lain"]
- attr = "me"
- fallback = nil
-
- assert Pleroma.Web.RelMe.maybe_put_rel_me("http://example.com/rel_me/null", profile_urls) ==
- fallback
-
- assert Pleroma.Web.RelMe.maybe_put_rel_me("http://example.com/rel_me/error", profile_urls) ==
- fallback
-
- assert Pleroma.Web.RelMe.maybe_put_rel_me("http://example.com/rel_me/anchor", profile_urls) ==
- attr
-
- assert Pleroma.Web.RelMe.maybe_put_rel_me(
- "http://example.com/rel_me/anchor_nofollow",
- profile_urls
- ) == attr
-
- assert Pleroma.Web.RelMe.maybe_put_rel_me("http://example.com/rel_me/link", profile_urls) ==
- attr
- end
-end
diff --git a/test/web/rich_media/aws_signed_url_test.exs b/test/web/rich_media/aws_signed_url_test.exs
@@ -1,82 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Web.RichMedia.TTL.AwsSignedUrlTest do
- use ExUnit.Case, async: true
-
- test "s3 signed url is parsed correct for expiration time" do
- url = "https://pleroma.social/amz"
-
- {:ok, timestamp} =
- Timex.now()
- |> DateTime.truncate(:second)
- |> Timex.format("{ISO:Basic:Z}")
-
- # in seconds
- valid_till = 30
-
- metadata = construct_metadata(timestamp, valid_till, url)
-
- expire_time =
- Timex.parse!(timestamp, "{ISO:Basic:Z}") |> Timex.to_unix() |> Kernel.+(valid_till)
-
- assert {:ok, expire_time} == Pleroma.Web.RichMedia.Parser.TTL.AwsSignedUrl.ttl(metadata, url)
- end
-
- test "s3 signed url is parsed and correct ttl is set for rich media" do
- url = "https://pleroma.social/amz"
-
- {:ok, timestamp} =
- Timex.now()
- |> DateTime.truncate(:second)
- |> Timex.format("{ISO:Basic:Z}")
-
- # in seconds
- valid_till = 30
-
- metadata = construct_metadata(timestamp, valid_till, url)
-
- body = """
- <meta name="twitter:card" content="Pleroma" />
- <meta name="twitter:site" content="Pleroma" />
- <meta name="twitter:title" content="Pleroma" />
- <meta name="twitter:description" content="Pleroma" />
- <meta name="twitter:image" content="#{Map.get(metadata, :image)}" />
- """
-
- Tesla.Mock.mock(fn
- %{
- method: :get,
- url: "https://pleroma.social/amz"
- } ->
- %Tesla.Env{status: 200, body: body}
- end)
-
- Cachex.put(:rich_media_cache, url, metadata)
-
- Pleroma.Web.RichMedia.Parser.set_ttl_based_on_image(metadata, url)
-
- {:ok, cache_ttl} = Cachex.ttl(:rich_media_cache, url)
-
- # as there is delay in setting and pulling the data from cache we ignore 1 second
- # make it 2 seconds for flakyness
- assert_in_delta(valid_till * 1000, cache_ttl, 2000)
- end
-
- defp construct_s3_url(timestamp, valid_till) do
- "https://pleroma.s3.ap-southeast-1.amazonaws.com/sachin%20%281%29%20_a%20-%25%2Aasdasd%20BNN%20bnnn%20.png?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAIBLWWK6RGDQXDLJQ%2F20190716%2Fap-southeast-1%2Fs3%2Faws4_request&X-Amz-Date=#{
- timestamp
- }&X-Amz-Expires=#{valid_till}&X-Amz-Signature=04ffd6b98634f4b1bbabc62e0fac4879093cd54a6eed24fe8eb38e8369526bbf&X-Amz-SignedHeaders=host"
- end
-
- defp construct_metadata(timestamp, valid_till, url) do
- %{
- image: construct_s3_url(timestamp, valid_till),
- site: "Pleroma",
- title: "Pleroma",
- description: "Pleroma",
- url: url
- }
- end
-end
diff --git a/test/web/rich_media/helpers_test.exs b/test/web/rich_media/helpers_test.exs
@@ -1,86 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Web.RichMedia.HelpersTest do
- use Pleroma.DataCase
-
- alias Pleroma.Config
- alias Pleroma.Object
- alias Pleroma.Web.CommonAPI
- alias Pleroma.Web.RichMedia.Helpers
-
- import Pleroma.Factory
- import Tesla.Mock
-
- setup do
- mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
-
- :ok
- end
-
- setup do: clear_config([:rich_media, :enabled])
-
- test "refuses to crawl incomplete URLs" do
- user = insert(:user)
-
- {:ok, activity} =
- CommonAPI.post(user, %{
- status: "[test](example.com/ogp)",
- content_type: "text/markdown"
- })
-
- Config.put([:rich_media, :enabled], true)
-
- assert %{} == Pleroma.Web.RichMedia.Helpers.fetch_data_for_activity(activity)
- end
-
- test "refuses to crawl malformed URLs" do
- user = insert(:user)
-
- {:ok, activity} =
- CommonAPI.post(user, %{
- status: "[test](example.com[]/ogp)",
- content_type: "text/markdown"
- })
-
- Config.put([:rich_media, :enabled], true)
-
- assert %{} == Pleroma.Web.RichMedia.Helpers.fetch_data_for_activity(activity)
- end
-
- test "crawls valid, complete URLs" do
- user = insert(:user)
-
- {:ok, activity} =
- CommonAPI.post(user, %{
- status: "[test](https://example.com/ogp)",
- content_type: "text/markdown"
- })
-
- Config.put([:rich_media, :enabled], true)
-
- assert %{page_url: "https://example.com/ogp", rich_media: _} =
- Pleroma.Web.RichMedia.Helpers.fetch_data_for_activity(activity)
- end
-
- test "refuses to crawl URLs of private network from posts" do
- user = insert(:user)
-
- {:ok, activity} =
- CommonAPI.post(user, %{status: "http://127.0.0.1:4000/notice/9kCP7VNyPJXFOXDrgO"})
-
- {:ok, activity2} = CommonAPI.post(user, %{status: "https://10.111.10.1/notice/9kCP7V"})
- {:ok, activity3} = CommonAPI.post(user, %{status: "https://172.16.32.40/notice/9kCP7V"})
- {:ok, activity4} = CommonAPI.post(user, %{status: "https://192.168.10.40/notice/9kCP7V"})
- {:ok, activity5} = CommonAPI.post(user, %{status: "https://pleroma.local/notice/9kCP7V"})
-
- Config.put([:rich_media, :enabled], true)
-
- assert %{} = Helpers.fetch_data_for_activity(activity)
- assert %{} = Helpers.fetch_data_for_activity(activity2)
- assert %{} = Helpers.fetch_data_for_activity(activity3)
- assert %{} = Helpers.fetch_data_for_activity(activity4)
- assert %{} = Helpers.fetch_data_for_activity(activity5)
- end
-end
diff --git a/test/web/rich_media/parser_test.exs b/test/web/rich_media/parser_test.exs
@@ -1,176 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Web.RichMedia.ParserTest do
- use ExUnit.Case, async: true
-
- alias Pleroma.Web.RichMedia.Parser
-
- setup do
- Tesla.Mock.mock(fn
- %{
- method: :get,
- url: "http://example.com/ogp"
- } ->
- %Tesla.Env{status: 200, body: File.read!("test/fixtures/rich_media/ogp.html")}
-
- %{
- method: :get,
- url: "http://example.com/non-ogp"
- } ->
- %Tesla.Env{status: 200, body: File.read!("test/fixtures/rich_media/non_ogp_embed.html")}
-
- %{
- method: :get,
- url: "http://example.com/ogp-missing-title"
- } ->
- %Tesla.Env{
- status: 200,
- body: File.read!("test/fixtures/rich_media/ogp-missing-title.html")
- }
-
- %{
- method: :get,
- url: "http://example.com/twitter-card"
- } ->
- %Tesla.Env{status: 200, body: File.read!("test/fixtures/rich_media/twitter_card.html")}
-
- %{
- method: :get,
- url: "http://example.com/oembed"
- } ->
- %Tesla.Env{status: 200, body: File.read!("test/fixtures/rich_media/oembed.html")}
-
- %{
- method: :get,
- url: "http://example.com/oembed.json"
- } ->
- %Tesla.Env{status: 200, body: File.read!("test/fixtures/rich_media/oembed.json")}
-
- %{method: :get, url: "http://example.com/empty"} ->
- %Tesla.Env{status: 200, body: "hello"}
-
- %{method: :get, url: "http://example.com/malformed"} ->
- %Tesla.Env{status: 200, body: File.read!("test/fixtures/rich_media/malformed-data.html")}
-
- %{method: :get, url: "http://example.com/error"} ->
- {:error, :overload}
-
- %{
- method: :head,
- url: "http://example.com/huge-page"
- } ->
- %Tesla.Env{
- status: 200,
- headers: [{"content-length", "2000001"}, {"content-type", "text/html"}]
- }
-
- %{
- method: :head,
- url: "http://example.com/pdf-file"
- } ->
- %Tesla.Env{
- status: 200,
- headers: [{"content-length", "1000000"}, {"content-type", "application/pdf"}]
- }
-
- %{method: :head} ->
- %Tesla.Env{status: 404, body: "", headers: []}
- end)
-
- :ok
- end
-
- test "returns error when no metadata present" do
- assert {:error, _} = Parser.parse("http://example.com/empty")
- end
-
- test "doesn't just add a title" do
- assert {:error, {:invalid_metadata, _}} = Parser.parse("http://example.com/non-ogp")
- end
-
- test "parses ogp" do
- assert Parser.parse("http://example.com/ogp") ==
- {:ok,
- %{
- "image" => "http://ia.media-imdb.com/images/rock.jpg",
- "title" => "The Rock",
- "description" =>
- "Directed by Michael Bay. With Sean Connery, Nicolas Cage, Ed Harris, John Spencer.",
- "type" => "video.movie",
- "url" => "http://example.com/ogp"
- }}
- end
-
- test "falls back to <title> when ogp:title is missing" do
- assert Parser.parse("http://example.com/ogp-missing-title") ==
- {:ok,
- %{
- "image" => "http://ia.media-imdb.com/images/rock.jpg",
- "title" => "The Rock (1996)",
- "description" =>
- "Directed by Michael Bay. With Sean Connery, Nicolas Cage, Ed Harris, John Spencer.",
- "type" => "video.movie",
- "url" => "http://example.com/ogp-missing-title"
- }}
- end
-
- test "parses twitter card" do
- assert Parser.parse("http://example.com/twitter-card") ==
- {:ok,
- %{
- "card" => "summary",
- "site" => "@flickr",
- "image" => "https://farm6.staticflickr.com/5510/14338202952_93595258ff_z.jpg",
- "title" => "Small Island Developing States Photo Submission",
- "description" => "View the album on Flickr.",
- "url" => "http://example.com/twitter-card"
- }}
- end
-
- test "parses OEmbed" do
- assert Parser.parse("http://example.com/oembed") ==
- {:ok,
- %{
- "author_name" => "bees",
- "author_url" => "https://www.flickr.com/photos/bees/",
- "cache_age" => 3600,
- "flickr_type" => "photo",
- "height" => "768",
- "html" =>
- "<a data-flickr-embed=\"true\" href=\"https://www.flickr.com/photos/bees/2362225867/\" title=\"Bacon Lollys by bees, on Flickr\"><img src=\"https://farm4.staticflickr.com/3040/2362225867_4a87ab8baf_b.jpg\" width=\"1024\" height=\"768\" alt=\"Bacon Lollys\"></a><script async src=\"https://embedr.flickr.com/assets/client-code.js\" charset=\"utf-8\"></script>",
- "license" => "All Rights Reserved",
- "license_id" => 0,
- "provider_name" => "Flickr",
- "provider_url" => "https://www.flickr.com/",
- "thumbnail_height" => 150,
- "thumbnail_url" =>
- "https://farm4.staticflickr.com/3040/2362225867_4a87ab8baf_q.jpg",
- "thumbnail_width" => 150,
- "title" => "Bacon Lollys",
- "type" => "photo",
- "url" => "http://example.com/oembed",
- "version" => "1.0",
- "web_page" => "https://www.flickr.com/photos/bees/2362225867/",
- "web_page_short_url" => "https://flic.kr/p/4AK2sc",
- "width" => "1024"
- }}
- end
-
- test "rejects invalid OGP data" do
- assert {:error, _} = Parser.parse("http://example.com/malformed")
- end
-
- test "returns error if getting page was not successful" do
- assert {:error, :overload} = Parser.parse("http://example.com/error")
- end
-
- test "does a HEAD request to check if the body is too large" do
- assert {:error, :body_too_large} = Parser.parse("http://example.com/huge-page")
- end
-
- test "does a HEAD request to check if the body is html" do
- assert {:error, {:content_type, _}} = Parser.parse("http://example.com/pdf-file")
- end
-end
diff --git a/test/web/rich_media/parsers/twitter_card_test.exs b/test/web/rich_media/parsers/twitter_card_test.exs
@@ -1,127 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Web.RichMedia.Parsers.TwitterCardTest do
- use ExUnit.Case, async: true
- alias Pleroma.Web.RichMedia.Parsers.TwitterCard
-
- test "returns error when html not contains twitter card" do
- assert TwitterCard.parse([{"html", [], [{"head", [], []}, {"body", [], []}]}], %{}) == %{}
- end
-
- test "parses twitter card with only name attributes" do
- html =
- File.read!("test/fixtures/nypd-facial-recognition-children-teenagers3.html")
- |> Floki.parse_document!()
-
- assert TwitterCard.parse(html, %{}) ==
- %{
- "app:id:googleplay" => "com.nytimes.android",
- "app:name:googleplay" => "NYTimes",
- "app:url:googleplay" => "nytimes://reader/id/100000006583622",
- "site" => nil,
- "description" =>
- "With little oversight, the N.Y.P.D. has been using powerful surveillance technology on photos of children and teenagers.",
- "image" =>
- "https://static01.nyt.com/images/2019/08/01/nyregion/01nypd-juveniles-promo/01nypd-juveniles-promo-facebookJumbo.jpg",
- "type" => "article",
- "url" =>
- "https://www.nytimes.com/2019/08/01/nyregion/nypd-facial-recognition-children-teenagers.html",
- "title" =>
- "She Was Arrested at 14. Then Her Photo Went to a Facial Recognition Database."
- }
- end
-
- test "parses twitter card with only property attributes" do
- html =
- File.read!("test/fixtures/nypd-facial-recognition-children-teenagers2.html")
- |> Floki.parse_document!()
-
- assert TwitterCard.parse(html, %{}) ==
- %{
- "card" => "summary_large_image",
- "description" =>
- "With little oversight, the N.Y.P.D. has been using powerful surveillance technology on photos of children and teenagers.",
- "image" =>
- "https://static01.nyt.com/images/2019/08/01/nyregion/01nypd-juveniles-promo/01nypd-juveniles-promo-videoSixteenByNineJumbo1600.jpg",
- "image:alt" => "",
- "title" =>
- "She Was Arrested at 14. Then Her Photo Went to a Facial Recognition Database.",
- "url" =>
- "https://www.nytimes.com/2019/08/01/nyregion/nypd-facial-recognition-children-teenagers.html",
- "type" => "article"
- }
- end
-
- test "parses twitter card with name & property attributes" do
- html =
- File.read!("test/fixtures/nypd-facial-recognition-children-teenagers.html")
- |> Floki.parse_document!()
-
- assert TwitterCard.parse(html, %{}) ==
- %{
- "app:id:googleplay" => "com.nytimes.android",
- "app:name:googleplay" => "NYTimes",
- "app:url:googleplay" => "nytimes://reader/id/100000006583622",
- "card" => "summary_large_image",
- "description" =>
- "With little oversight, the N.Y.P.D. has been using powerful surveillance technology on photos of children and teenagers.",
- "image" =>
- "https://static01.nyt.com/images/2019/08/01/nyregion/01nypd-juveniles-promo/01nypd-juveniles-promo-videoSixteenByNineJumbo1600.jpg",
- "image:alt" => "",
- "site" => nil,
- "title" =>
- "She Was Arrested at 14. Then Her Photo Went to a Facial Recognition Database.",
- "url" =>
- "https://www.nytimes.com/2019/08/01/nyregion/nypd-facial-recognition-children-teenagers.html",
- "type" => "article"
- }
- end
-
- test "respect only first title tag on the page" do
- image_path =
- "https://assets.atlasobscura.com/media/W1siZiIsInVwbG9hZHMvYXNzZXRzLzkwYzgyMzI4LThlMDUtNGRiNS05MDg3LTUzMGUxZTM5N2RmMmVkOTM5ZDM4MGM4OTIx" <>
- "YTQ5MF9EQVIgZXhodW1hdGlvbiBvZiBNYXJnYXJldCBDb3JiaW4gZ3JhdmUgMTkyNi5qcGciXSxbInAiLCJjb252ZXJ0IiwiIl0sWyJwIiwiY29udmVydCIsIi1xdWFsaXR5IDgxIC1hdXRvLW9" <>
- "yaWVudCJdLFsicCIsInRodW1iIiwiNjAweD4iXV0/DAR%20exhumation%20of%20Margaret%20Corbin%20grave%201926.jpg"
-
- html =
- File.read!("test/fixtures/margaret-corbin-grave-west-point.html") |> Floki.parse_document!()
-
- assert TwitterCard.parse(html, %{}) ==
- %{
- "site" => "@atlasobscura",
- "title" => "The Missing Grave of Margaret Corbin, Revolutionary War Veteran",
- "card" => "summary_large_image",
- "image" => image_path,
- "description" =>
- "She's the only woman veteran honored with a monument at West Point. But where was she buried?",
- "site_name" => "Atlas Obscura",
- "type" => "article",
- "url" => "http://www.atlasobscura.com/articles/margaret-corbin-grave-west-point"
- }
- end
-
- test "takes first founded title in html head if there is html markup error" do
- html =
- File.read!("test/fixtures/nypd-facial-recognition-children-teenagers4.html")
- |> Floki.parse_document!()
-
- assert TwitterCard.parse(html, %{}) ==
- %{
- "site" => nil,
- "title" =>
- "She Was Arrested at 14. Then Her Photo Went to a Facial Recognition Database.",
- "app:id:googleplay" => "com.nytimes.android",
- "app:name:googleplay" => "NYTimes",
- "app:url:googleplay" => "nytimes://reader/id/100000006583622",
- "description" =>
- "With little oversight, the N.Y.P.D. has been using powerful surveillance technology on photos of children and teenagers.",
- "image" =>
- "https://static01.nyt.com/images/2019/08/01/nyregion/01nypd-juveniles-promo/01nypd-juveniles-promo-facebookJumbo.jpg",
- "type" => "article",
- "url" =>
- "https://www.nytimes.com/2019/08/01/nyregion/nypd-facial-recognition-children-teenagers.html"
- }
- end
-end
diff --git a/test/web/static_fe/static_fe_controller_test.exs b/test/web/static_fe/static_fe_controller_test.exs
@@ -1,196 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Web.StaticFE.StaticFEControllerTest do
- use Pleroma.Web.ConnCase
-
- alias Pleroma.Activity
- alias Pleroma.Config
- alias Pleroma.Web.ActivityPub.Transmogrifier
- alias Pleroma.Web.CommonAPI
-
- import Pleroma.Factory
-
- setup_all do: clear_config([:static_fe, :enabled], true)
- setup do: clear_config([:instance, :federating], true)
-
- setup %{conn: conn} do
- conn = put_req_header(conn, "accept", "text/html")
- user = insert(:user)
-
- %{conn: conn, user: user}
- end
-
- describe "user profile html" do
- test "just the profile as HTML", %{conn: conn, user: user} do
- conn = get(conn, "/users/#{user.nickname}")
-
- assert html_response(conn, 200) =~ user.nickname
- end
-
- test "404 when user not found", %{conn: conn} do
- conn = get(conn, "/users/limpopo")
-
- assert html_response(conn, 404) =~ "not found"
- end
-
- test "profile does not include private messages", %{conn: conn, user: user} do
- CommonAPI.post(user, %{status: "public"})
- CommonAPI.post(user, %{status: "private", visibility: "private"})
-
- conn = get(conn, "/users/#{user.nickname}")
-
- html = html_response(conn, 200)
-
- assert html =~ ">public<"
- refute html =~ ">private<"
- end
-
- test "pagination", %{conn: conn, user: user} do
- Enum.map(1..30, fn i -> CommonAPI.post(user, %{status: "test#{i}"}) end)
-
- conn = get(conn, "/users/#{user.nickname}")
-
- html = html_response(conn, 200)
-
- assert html =~ ">test30<"
- assert html =~ ">test11<"
- refute html =~ ">test10<"
- refute html =~ ">test1<"
- end
-
- test "pagination, page 2", %{conn: conn, user: user} do
- activities = Enum.map(1..30, fn i -> CommonAPI.post(user, %{status: "test#{i}"}) end)
- {:ok, a11} = Enum.at(activities, 11)
-
- conn = get(conn, "/users/#{user.nickname}?max_id=#{a11.id}")
-
- html = html_response(conn, 200)
-
- assert html =~ ">test1<"
- assert html =~ ">test10<"
- refute html =~ ">test20<"
- refute html =~ ">test29<"
- end
-
- test "it requires authentication if instance is NOT federating", %{conn: conn, user: user} do
- ensure_federating_or_authenticated(conn, "/users/#{user.nickname}", user)
- end
- end
-
- describe "notice html" do
- test "single notice page", %{conn: conn, user: user} do
- {:ok, activity} = CommonAPI.post(user, %{status: "testing a thing!"})
-
- conn = get(conn, "/notice/#{activity.id}")
-
- html = html_response(conn, 200)
- assert html =~ "<header>"
- assert html =~ user.nickname
- 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>"})
-
- conn =
- conn
- |> put_req_header("accept", "text/html")
- |> get("/notice/#{activity.id}")
-
- html = html_response(conn, 200)
- assert html =~ ~s[<script>alert('xss')</script>]
- end
-
- test "shows the whole thread", %{conn: conn, user: user} do
- {:ok, activity} = CommonAPI.post(user, %{status: "space: the final frontier"})
-
- CommonAPI.post(user, %{
- status: "these are the voyages or something",
- in_reply_to_status_id: activity.id
- })
-
- conn = get(conn, "/notice/#{activity.id}")
-
- html = html_response(conn, 200)
- assert html =~ "the final frontier"
- assert html =~ "voyages"
- end
-
- test "redirect by AP object ID", %{conn: conn, user: user} do
- {:ok, %Activity{data: %{"object" => object_url}}} =
- CommonAPI.post(user, %{status: "beam me up"})
-
- conn = get(conn, URI.parse(object_url).path)
-
- assert html_response(conn, 302) =~ "redirected"
- end
-
- test "redirect by activity ID", %{conn: conn, user: user} do
- {:ok, %Activity{data: %{"id" => id}}} =
- CommonAPI.post(user, %{status: "I'm a doctor, not a devops!"})
-
- conn = get(conn, URI.parse(id).path)
-
- assert html_response(conn, 302) =~ "redirected"
- end
-
- test "404 when notice not found", %{conn: conn} do
- conn = get(conn, "/notice/88c9c317")
-
- assert html_response(conn, 404) =~ "not found"
- end
-
- test "404 for private status", %{conn: conn, user: user} do
- {:ok, activity} = CommonAPI.post(user, %{status: "don't show me!", visibility: "private"})
-
- conn = get(conn, "/notice/#{activity.id}")
-
- assert html_response(conn, 404) =~ "not found"
- end
-
- test "302 for remote cached status", %{conn: conn, user: user} do
- message = %{
- "@context" => "https://www.w3.org/ns/activitystreams",
- "to" => user.follower_address,
- "cc" => "https://www.w3.org/ns/activitystreams#Public",
- "type" => "Create",
- "object" => %{
- "content" => "blah blah blah",
- "type" => "Note",
- "attributedTo" => user.ap_id,
- "inReplyTo" => nil
- },
- "actor" => user.ap_id
- }
-
- assert {:ok, activity} = Transmogrifier.handle_incoming(message)
-
- conn = get(conn, "/notice/#{activity.id}")
-
- assert html_response(conn, 302) =~ "redirected"
- end
-
- test "it requires authentication if instance is NOT federating", %{conn: conn, user: user} do
- {:ok, activity} = CommonAPI.post(user, %{status: "testing a thing!"})
-
- ensure_federating_or_authenticated(conn, "/notice/#{activity.id}", user)
- end
- end
-end
diff --git a/test/web/streamer/streamer_test.exs b/test/web/streamer/streamer_test.exs
@@ -1,767 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Web.StreamerTest do
- use Pleroma.DataCase
-
- import Pleroma.Factory
-
- alias Pleroma.Chat
- alias Pleroma.Chat.MessageReference
- alias Pleroma.Conversation.Participation
- alias Pleroma.List
- alias Pleroma.Object
- alias Pleroma.User
- alias Pleroma.Web.CommonAPI
- alias Pleroma.Web.Streamer
- alias Pleroma.Web.StreamerView
-
- @moduletag needs_streamer: true, capture_log: true
-
- setup do: clear_config([:instance, :skip_thread_containment])
-
- describe "get_topic/_ (unauthenticated)" do
- test "allows public" do
- assert {:ok, "public"} = Streamer.get_topic("public", nil, nil)
- assert {:ok, "public:local"} = Streamer.get_topic("public:local", nil, nil)
- assert {:ok, "public:media"} = Streamer.get_topic("public:media", nil, nil)
- assert {:ok, "public:local:media"} = Streamer.get_topic("public:local:media", nil, nil)
- end
-
- test "allows hashtag streams" do
- assert {:ok, "hashtag:cofe"} = Streamer.get_topic("hashtag", nil, nil, %{"tag" => "cofe"})
- end
-
- test "disallows user streams" do
- assert {:error, _} = Streamer.get_topic("user", nil, nil)
- assert {:error, _} = Streamer.get_topic("user:notification", nil, nil)
- assert {:error, _} = Streamer.get_topic("direct", nil, nil)
- end
-
- test "disallows list streams" do
- assert {:error, _} = Streamer.get_topic("list", nil, nil, %{"list" => 42})
- end
- end
-
- describe "get_topic/_ (authenticated)" do
- setup do: oauth_access(["read"])
-
- test "allows public streams (regardless of OAuth token scopes)", %{
- user: user,
- token: read_oauth_token
- } do
- with oauth_token <- [nil, read_oauth_token] do
- assert {:ok, "public"} = Streamer.get_topic("public", user, oauth_token)
- assert {:ok, "public:local"} = Streamer.get_topic("public:local", user, oauth_token)
- assert {:ok, "public:media"} = Streamer.get_topic("public:media", user, oauth_token)
-
- assert {:ok, "public:local:media"} =
- Streamer.get_topic("public:local:media", user, oauth_token)
- end
- end
-
- test "allows user streams (with proper OAuth token scopes)", %{
- user: user,
- token: read_oauth_token
- } do
- %{token: read_notifications_token} = oauth_access(["read:notifications"], user: user)
- %{token: read_statuses_token} = oauth_access(["read:statuses"], user: user)
- %{token: badly_scoped_token} = oauth_access(["irrelevant:scope"], user: user)
-
- expected_user_topic = "user:#{user.id}"
- expected_notification_topic = "user:notification:#{user.id}"
- expected_direct_topic = "direct:#{user.id}"
- expected_pleroma_chat_topic = "user:pleroma_chat:#{user.id}"
-
- for valid_user_token <- [read_oauth_token, read_statuses_token] do
- assert {:ok, ^expected_user_topic} = Streamer.get_topic("user", user, valid_user_token)
-
- assert {:ok, ^expected_direct_topic} =
- Streamer.get_topic("direct", user, valid_user_token)
-
- assert {:ok, ^expected_pleroma_chat_topic} =
- Streamer.get_topic("user:pleroma_chat", user, valid_user_token)
- end
-
- for invalid_user_token <- [read_notifications_token, badly_scoped_token],
- user_topic <- ["user", "direct", "user:pleroma_chat"] do
- assert {:error, :unauthorized} = Streamer.get_topic(user_topic, user, invalid_user_token)
- end
-
- for valid_notification_token <- [read_oauth_token, read_notifications_token] do
- assert {:ok, ^expected_notification_topic} =
- Streamer.get_topic("user:notification", user, valid_notification_token)
- end
-
- for invalid_notification_token <- [read_statuses_token, badly_scoped_token] do
- assert {:error, :unauthorized} =
- Streamer.get_topic("user:notification", user, invalid_notification_token)
- end
- end
-
- test "allows hashtag streams (regardless of OAuth token scopes)", %{
- user: user,
- token: read_oauth_token
- } do
- for oauth_token <- [nil, read_oauth_token] do
- assert {:ok, "hashtag:cofe"} =
- Streamer.get_topic("hashtag", user, oauth_token, %{"tag" => "cofe"})
- end
- end
-
- test "disallows registering to another user's stream", %{user: user, token: read_oauth_token} do
- another_user = insert(:user)
- assert {:error, _} = Streamer.get_topic("user:#{another_user.id}", user, read_oauth_token)
-
- assert {:error, _} =
- Streamer.get_topic("user:notification:#{another_user.id}", user, read_oauth_token)
-
- assert {:error, _} = Streamer.get_topic("direct:#{another_user.id}", user, read_oauth_token)
- end
-
- test "allows list stream that are owned by the user (with `read` or `read:lists` scopes)", %{
- user: user,
- token: read_oauth_token
- } do
- %{token: read_lists_token} = oauth_access(["read:lists"], user: user)
- %{token: invalid_token} = oauth_access(["irrelevant:scope"], user: user)
- {:ok, list} = List.create("Test", user)
-
- assert {:error, _} = Streamer.get_topic("list:#{list.id}", user, read_oauth_token)
-
- for valid_token <- [read_oauth_token, read_lists_token] do
- assert {:ok, _} = Streamer.get_topic("list", user, valid_token, %{"list" => list.id})
- end
-
- assert {:error, _} = Streamer.get_topic("list", user, invalid_token, %{"list" => list.id})
- end
-
- test "disallows list stream that are not owned by the user", %{user: user, token: oauth_token} do
- another_user = insert(:user)
- {:ok, list} = List.create("Test", another_user)
-
- assert {:error, _} = Streamer.get_topic("list:#{list.id}", user, oauth_token)
- assert {:error, _} = Streamer.get_topic("list", user, oauth_token, %{"list" => list.id})
- end
- end
-
- describe "user streams" do
- setup do
- %{user: user, token: token} = oauth_access(["read"])
- notify = insert(:notification, user: user, activity: build(:note_activity))
- {:ok, %{user: user, notify: notify, token: token}}
- end
-
- test "it streams the user's post in the 'user' stream", %{user: user, token: oauth_token} do
- Streamer.get_topic_and_add_socket("user", user, oauth_token)
- {:ok, activity} = CommonAPI.post(user, %{status: "hey"})
-
- assert_receive {:render_with_user, _, _, ^activity}
- refute Streamer.filtered_by_user?(user, activity)
- end
-
- test "it streams boosts of the user in the 'user' stream", %{user: user, token: oauth_token} do
- Streamer.get_topic_and_add_socket("user", user, oauth_token)
-
- other_user = insert(:user)
- {:ok, activity} = CommonAPI.post(other_user, %{status: "hey"})
- {:ok, announce} = CommonAPI.repeat(activity.id, user)
-
- assert_receive {:render_with_user, Pleroma.Web.StreamerView, "update.json", ^announce}
- 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,
- token: oauth_token
- } do
- Streamer.get_topic_and_add_socket("user", user, oauth_token)
-
- 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,
- token: oauth_token
- } do
- Streamer.get_topic_and_add_socket("user", user, oauth_token)
-
- 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,
- token: oauth_token
- } do
- Streamer.get_topic_and_add_socket("user", user, oauth_token)
-
- other_user = insert(:user)
- {:ok, activity} = CommonAPI.post(other_user, %{status: "hey"})
-
- data =
- File.read!("test/fixtures/mastodon-announce.json")
- |> Poison.decode!()
- |> Map.put("object", activity.data["object"])
- |> Map.put("actor", user.ap_id)
-
- {:ok, %Pleroma.Activity{data: _data, local: false} = announce} =
- Pleroma.Web.ActivityPub.Transmogrifier.handle_incoming(data)
-
- assert_receive {:render_with_user, Pleroma.Web.StreamerView, "update.json", ^announce}
- refute Streamer.filtered_by_user?(user, announce)
- end
-
- test "it sends notify to in the 'user' stream", %{
- user: user,
- token: oauth_token,
- notify: notify
- } do
- Streamer.get_topic_and_add_socket("user", user, oauth_token)
- Streamer.stream("user", notify)
-
- assert_receive {:render_with_user, _, _, ^notify}
- refute Streamer.filtered_by_user?(user, notify)
- end
-
- test "it sends notify to in the 'user:notification' stream", %{
- user: user,
- token: oauth_token,
- notify: notify
- } do
- Streamer.get_topic_and_add_socket("user:notification", user, oauth_token)
- Streamer.stream("user:notification", notify)
-
- assert_receive {:render_with_user, _, _, ^notify}
- refute Streamer.filtered_by_user?(user, notify)
- end
-
- test "it sends chat messages to the 'user:pleroma_chat' stream", %{
- user: user,
- token: oauth_token
- } do
- other_user = insert(:user)
-
- {:ok, create_activity} = CommonAPI.post_chat_message(other_user, user, "hey cirno")
- object = Object.normalize(create_activity, false)
- chat = Chat.get(user.id, other_user.ap_id)
- cm_ref = MessageReference.for_chat_and_object(chat, object)
- cm_ref = %{cm_ref | chat: chat, object: object}
-
- Streamer.get_topic_and_add_socket("user:pleroma_chat", user, oauth_token)
- Streamer.stream("user:pleroma_chat", {user, cm_ref})
-
- text = StreamerView.render("chat_update.json", %{chat_message_reference: cm_ref})
-
- assert text =~ "hey cirno"
- assert_receive {:text, ^text}
- end
-
- test "it sends chat messages to the 'user' stream", %{user: user, token: oauth_token} do
- other_user = insert(:user)
-
- {:ok, create_activity} = CommonAPI.post_chat_message(other_user, user, "hey cirno")
- object = Object.normalize(create_activity, false)
- chat = Chat.get(user.id, other_user.ap_id)
- cm_ref = MessageReference.for_chat_and_object(chat, object)
- cm_ref = %{cm_ref | chat: chat, object: object}
-
- Streamer.get_topic_and_add_socket("user", user, oauth_token)
- Streamer.stream("user", {user, cm_ref})
-
- text = StreamerView.render("chat_update.json", %{chat_message_reference: cm_ref})
-
- assert text =~ "hey cirno"
- assert_receive {:text, ^text}
- end
-
- test "it sends chat message notifications to the 'user:notification' stream", %{
- user: user,
- token: oauth_token
- } do
- other_user = insert(:user)
-
- {:ok, create_activity} = CommonAPI.post_chat_message(other_user, user, "hey")
-
- notify =
- Repo.get_by(Pleroma.Notification, user_id: user.id, activity_id: create_activity.id)
- |> Repo.preload(:activity)
-
- Streamer.get_topic_and_add_socket("user:notification", user, oauth_token)
- Streamer.stream("user:notification", notify)
-
- assert_receive {:render_with_user, _, _, ^notify}
- refute Streamer.filtered_by_user?(user, notify)
- end
-
- test "it doesn't send notify to the 'user:notification' stream when a user is blocked", %{
- user: user,
- token: oauth_token
- } do
- blocked = insert(:user)
- {:ok, _user_relationship} = User.block(user, blocked)
-
- Streamer.get_topic_and_add_socket("user:notification", user, oauth_token)
-
- {:ok, activity} = CommonAPI.post(user, %{status: ":("})
- {:ok, _} = CommonAPI.favorite(blocked, activity.id)
-
- refute_receive _
- end
-
- test "it doesn't send notify to the 'user:notification' stream when a thread is muted", %{
- user: user,
- token: oauth_token
- } do
- user2 = insert(:user)
-
- {:ok, activity} = CommonAPI.post(user, %{status: "super hot take"})
- {:ok, _} = CommonAPI.add_mute(user, activity)
-
- Streamer.get_topic_and_add_socket("user:notification", user, oauth_token)
-
- {:ok, favorite_activity} = CommonAPI.favorite(user2, activity.id)
-
- refute_receive _
- assert Streamer.filtered_by_user?(user, favorite_activity)
- end
-
- test "it sends favorite to 'user:notification' stream'", %{
- user: user,
- token: oauth_token
- } do
- user2 = insert(:user, %{ap_id: "https://hecking-lewd-place.com/user/meanie"})
-
- {:ok, activity} = CommonAPI.post(user, %{status: "super hot take"})
- Streamer.get_topic_and_add_socket("user:notification", user, oauth_token)
- {:ok, favorite_activity} = CommonAPI.favorite(user2, activity.id)
-
- assert_receive {:render_with_user, _, "notification.json", notif}
- assert notif.activity.id == favorite_activity.id
- refute Streamer.filtered_by_user?(user, notif)
- end
-
- test "it doesn't send the 'user:notification' stream' when a domain is blocked", %{
- user: user,
- token: oauth_token
- } do
- user2 = insert(:user, %{ap_id: "https://hecking-lewd-place.com/user/meanie"})
-
- {:ok, user} = User.block_domain(user, "hecking-lewd-place.com")
- {:ok, activity} = CommonAPI.post(user, %{status: "super hot take"})
- Streamer.get_topic_and_add_socket("user:notification", user, oauth_token)
- {:ok, favorite_activity} = CommonAPI.favorite(user2, activity.id)
-
- refute_receive _
- assert Streamer.filtered_by_user?(user, favorite_activity)
- end
-
- test "it sends follow activities to the 'user:notification' stream", %{
- user: user,
- token: oauth_token
- } do
- user_url = user.ap_id
- user2 = insert(:user)
-
- body =
- File.read!("test/fixtures/users_mock/localhost.json")
- |> String.replace("{{nickname}}", user.nickname)
- |> Jason.encode!()
-
- Tesla.Mock.mock_global(fn
- %{method: :get, url: ^user_url} ->
- %Tesla.Env{status: 200, body: body}
- end)
-
- Streamer.get_topic_and_add_socket("user:notification", user, oauth_token)
- {:ok, _follower, _followed, follow_activity} = CommonAPI.follow(user2, user)
-
- assert_receive {:render_with_user, _, "notification.json", notif}
- assert notif.activity.id == follow_activity.id
- refute Streamer.filtered_by_user?(user, notif)
- end
- end
-
- describe "public streams" do
- test "it sends to public (authenticated)" do
- %{user: user, token: oauth_token} = oauth_access(["read"])
- other_user = insert(:user)
-
- Streamer.get_topic_and_add_socket("public", user, oauth_token)
-
- {:ok, activity} = CommonAPI.post(other_user, %{status: "Test"})
- assert_receive {:render_with_user, _, _, ^activity}
- refute Streamer.filtered_by_user?(other_user, activity)
- end
-
- test "it sends to public (unauthenticated)" do
- user = insert(:user)
-
- Streamer.get_topic_and_add_socket("public", nil, nil)
-
- {:ok, activity} = CommonAPI.post(user, %{status: "Test"})
- activity_id = activity.id
- assert_receive {:text, event}
- assert %{"event" => "update", "payload" => payload} = Jason.decode!(event)
- assert %{"id" => ^activity_id} = Jason.decode!(payload)
-
- {:ok, _} = CommonAPI.delete(activity.id, user)
- assert_receive {:text, event}
- assert %{"event" => "delete", "payload" => ^activity_id} = Jason.decode!(event)
- end
-
- test "handles deletions" do
- %{user: user, token: oauth_token} = oauth_access(["read"])
- other_user = insert(:user)
- {:ok, activity} = CommonAPI.post(other_user, %{status: "Test"})
-
- Streamer.get_topic_and_add_socket("public", user, oauth_token)
-
- {:ok, _} = CommonAPI.delete(activity.id, other_user)
- activity_id = activity.id
- assert_receive {:text, event}
- assert %{"event" => "delete", "payload" => ^activity_id} = Jason.decode!(event)
- end
- end
-
- describe "thread_containment/2" do
- test "it filters to user if recipients invalid and thread containment is enabled" do
- Pleroma.Config.put([:instance, :skip_thread_containment], false)
- author = insert(:user)
- %{user: user, token: oauth_token} = oauth_access(["read"])
- User.follow(user, author, :follow_accept)
-
- activity =
- insert(:note_activity,
- note:
- insert(:note,
- user: author,
- data: %{"to" => ["TEST-FFF"]}
- )
- )
-
- Streamer.get_topic_and_add_socket("public", user, oauth_token)
- Streamer.stream("public", activity)
- assert_receive {:render_with_user, _, _, ^activity}
- assert Streamer.filtered_by_user?(user, activity)
- end
-
- test "it sends message if recipients invalid and thread containment is disabled" do
- Pleroma.Config.put([:instance, :skip_thread_containment], true)
- author = insert(:user)
- %{user: user, token: oauth_token} = oauth_access(["read"])
- User.follow(user, author, :follow_accept)
-
- activity =
- insert(:note_activity,
- note:
- insert(:note,
- user: author,
- data: %{"to" => ["TEST-FFF"]}
- )
- )
-
- Streamer.get_topic_and_add_socket("public", user, oauth_token)
- Streamer.stream("public", activity)
-
- assert_receive {:render_with_user, _, _, ^activity}
- refute Streamer.filtered_by_user?(user, activity)
- end
-
- test "it sends message if recipients invalid and thread containment is enabled but user's thread containment is disabled" do
- Pleroma.Config.put([:instance, :skip_thread_containment], false)
- author = insert(:user)
- user = insert(:user, skip_thread_containment: true)
- %{token: oauth_token} = oauth_access(["read"], user: user)
- User.follow(user, author, :follow_accept)
-
- activity =
- insert(:note_activity,
- note:
- insert(:note,
- user: author,
- data: %{"to" => ["TEST-FFF"]}
- )
- )
-
- Streamer.get_topic_and_add_socket("public", user, oauth_token)
- Streamer.stream("public", activity)
-
- assert_receive {:render_with_user, _, _, ^activity}
- refute Streamer.filtered_by_user?(user, activity)
- end
- end
-
- describe "blocks" do
- setup do: oauth_access(["read"])
-
- test "it filters messages involving blocked users", %{user: user, token: oauth_token} do
- blocked_user = insert(:user)
- {:ok, _user_relationship} = User.block(user, blocked_user)
-
- Streamer.get_topic_and_add_socket("public", user, oauth_token)
- {:ok, activity} = CommonAPI.post(blocked_user, %{status: "Test"})
- assert_receive {:render_with_user, _, _, ^activity}
- assert Streamer.filtered_by_user?(user, activity)
- end
-
- test "it filters messages transitively involving blocked users", %{
- user: blocker,
- token: blocker_token
- } do
- blockee = insert(:user)
- friend = insert(:user)
-
- Streamer.get_topic_and_add_socket("public", blocker, blocker_token)
-
- {:ok, _user_relationship} = User.block(blocker, blockee)
-
- {:ok, activity_one} = CommonAPI.post(friend, %{status: "hey! @#{blockee.nickname}"})
-
- assert_receive {:render_with_user, _, _, ^activity_one}
- assert Streamer.filtered_by_user?(blocker, activity_one)
-
- {:ok, activity_two} = CommonAPI.post(blockee, %{status: "hey! @#{friend.nickname}"})
-
- assert_receive {:render_with_user, _, _, ^activity_two}
- assert Streamer.filtered_by_user?(blocker, activity_two)
-
- {:ok, activity_three} = CommonAPI.post(blockee, %{status: "hey! @#{blocker.nickname}"})
-
- assert_receive {:render_with_user, _, _, ^activity_three}
- assert Streamer.filtered_by_user?(blocker, activity_three)
- end
- end
-
- describe "lists" do
- setup do: oauth_access(["read"])
-
- test "it doesn't send unwanted DMs to list", %{user: user_a, token: user_a_token} do
- user_b = insert(:user)
- user_c = insert(:user)
-
- {:ok, user_a} = User.follow(user_a, user_b)
-
- {:ok, list} = List.create("Test", user_a)
- {:ok, list} = List.follow(list, user_b)
-
- Streamer.get_topic_and_add_socket("list", user_a, user_a_token, %{"list" => list.id})
-
- {:ok, _activity} =
- CommonAPI.post(user_b, %{
- status: "@#{user_c.nickname} Test",
- visibility: "direct"
- })
-
- refute_receive _
- end
-
- test "it doesn't send unwanted private posts to list", %{user: user_a, token: user_a_token} do
- user_b = insert(:user)
-
- {:ok, list} = List.create("Test", user_a)
- {:ok, list} = List.follow(list, user_b)
-
- Streamer.get_topic_and_add_socket("list", user_a, user_a_token, %{"list" => list.id})
-
- {:ok, _activity} =
- CommonAPI.post(user_b, %{
- status: "Test",
- visibility: "private"
- })
-
- refute_receive _
- end
-
- test "it sends wanted private posts to list", %{user: user_a, token: user_a_token} do
- user_b = insert(:user)
-
- {:ok, user_a} = User.follow(user_a, user_b)
-
- {:ok, list} = List.create("Test", user_a)
- {:ok, list} = List.follow(list, user_b)
-
- Streamer.get_topic_and_add_socket("list", user_a, user_a_token, %{"list" => list.id})
-
- {:ok, activity} =
- CommonAPI.post(user_b, %{
- status: "Test",
- visibility: "private"
- })
-
- assert_receive {:render_with_user, _, _, ^activity}
- refute Streamer.filtered_by_user?(user_a, activity)
- end
- end
-
- describe "muted reblogs" do
- setup do: oauth_access(["read"])
-
- test "it filters muted reblogs", %{user: user1, token: user1_token} do
- user2 = insert(:user)
- user3 = insert(:user)
- CommonAPI.follow(user1, user2)
- CommonAPI.hide_reblogs(user1, user2)
-
- {:ok, create_activity} = CommonAPI.post(user3, %{status: "I'm kawen"})
-
- Streamer.get_topic_and_add_socket("user", user1, user1_token)
- {:ok, announce_activity} = CommonAPI.repeat(create_activity.id, user2)
- assert_receive {:render_with_user, _, _, ^announce_activity}
- assert Streamer.filtered_by_user?(user1, announce_activity)
- end
-
- test "it filters reblog notification for reblog-muted actors", %{
- user: user1,
- token: user1_token
- } do
- user2 = insert(:user)
- CommonAPI.follow(user1, user2)
- CommonAPI.hide_reblogs(user1, user2)
-
- {:ok, create_activity} = CommonAPI.post(user1, %{status: "I'm kawen"})
- Streamer.get_topic_and_add_socket("user", user1, user1_token)
- {:ok, _announce_activity} = CommonAPI.repeat(create_activity.id, user2)
-
- assert_receive {:render_with_user, _, "notification.json", notif}
- assert Streamer.filtered_by_user?(user1, notif)
- end
-
- test "it send non-reblog notification for reblog-muted actors", %{
- user: user1,
- token: user1_token
- } do
- user2 = insert(:user)
- CommonAPI.follow(user1, user2)
- CommonAPI.hide_reblogs(user1, user2)
-
- {:ok, create_activity} = CommonAPI.post(user1, %{status: "I'm kawen"})
- Streamer.get_topic_and_add_socket("user", user1, user1_token)
- {:ok, _favorite_activity} = CommonAPI.favorite(user2, create_activity.id)
-
- assert_receive {:render_with_user, _, "notification.json", notif}
- refute Streamer.filtered_by_user?(user1, notif)
- end
- end
-
- describe "muted threads" do
- test "it filters posts from muted threads" do
- user = insert(:user)
- %{user: user2, token: user2_token} = oauth_access(["read"])
- Streamer.get_topic_and_add_socket("user", user2, user2_token)
-
- {:ok, user2, user, _activity} = CommonAPI.follow(user2, user)
- {:ok, activity} = CommonAPI.post(user, %{status: "super hot take"})
- {:ok, _} = CommonAPI.add_mute(user2, activity)
-
- assert_receive {:render_with_user, _, _, ^activity}
- assert Streamer.filtered_by_user?(user2, activity)
- end
- end
-
- describe "direct streams" do
- setup do: oauth_access(["read"])
-
- test "it sends conversation update to the 'direct' stream", %{user: user, token: oauth_token} do
- another_user = insert(:user)
-
- Streamer.get_topic_and_add_socket("direct", user, oauth_token)
-
- {:ok, _create_activity} =
- CommonAPI.post(another_user, %{
- status: "hey @#{user.nickname}",
- visibility: "direct"
- })
-
- assert_receive {:text, received_event}
-
- assert %{"event" => "conversation", "payload" => received_payload} =
- Jason.decode!(received_event)
-
- assert %{"last_status" => last_status} = Jason.decode!(received_payload)
- [participation] = Participation.for_user(user)
- assert last_status["pleroma"]["direct_conversation_id"] == participation.id
- end
-
- test "it doesn't send conversation update to the 'direct' stream when the last message in the conversation is deleted",
- %{user: user, token: oauth_token} do
- another_user = insert(:user)
-
- Streamer.get_topic_and_add_socket("direct", user, oauth_token)
-
- {:ok, create_activity} =
- CommonAPI.post(another_user, %{
- status: "hi @#{user.nickname}",
- visibility: "direct"
- })
-
- create_activity_id = create_activity.id
- assert_receive {:render_with_user, _, _, ^create_activity}
- assert_receive {:text, received_conversation1}
- assert %{"event" => "conversation", "payload" => _} = Jason.decode!(received_conversation1)
-
- {:ok, _} = CommonAPI.delete(create_activity_id, another_user)
-
- assert_receive {:text, received_event}
-
- assert %{"event" => "delete", "payload" => ^create_activity_id} =
- Jason.decode!(received_event)
-
- refute_receive _
- end
-
- test "it sends conversation update to the 'direct' stream when a message is deleted", %{
- user: user,
- token: oauth_token
- } do
- another_user = insert(:user)
- Streamer.get_topic_and_add_socket("direct", user, oauth_token)
-
- {:ok, create_activity} =
- CommonAPI.post(another_user, %{
- status: "hi @#{user.nickname}",
- visibility: "direct"
- })
-
- {:ok, create_activity2} =
- CommonAPI.post(another_user, %{
- status: "hi @#{user.nickname} 2",
- in_reply_to_status_id: create_activity.id,
- visibility: "direct"
- })
-
- assert_receive {:render_with_user, _, _, ^create_activity}
- assert_receive {:render_with_user, _, _, ^create_activity2}
- assert_receive {:text, received_conversation1}
- assert %{"event" => "conversation", "payload" => _} = Jason.decode!(received_conversation1)
- assert_receive {:text, received_conversation1}
- assert %{"event" => "conversation", "payload" => _} = Jason.decode!(received_conversation1)
-
- {:ok, _} = CommonAPI.delete(create_activity2.id, another_user)
-
- assert_receive {:text, received_event}
- assert %{"event" => "delete", "payload" => _} = Jason.decode!(received_event)
-
- assert_receive {:text, received_event}
-
- assert %{"event" => "conversation", "payload" => received_payload} =
- Jason.decode!(received_event)
-
- assert %{"last_status" => last_status} = Jason.decode!(received_payload)
- assert last_status["id"] == to_string(create_activity.id)
- end
- end
-end
diff --git a/test/web/twitter_api/password_controller_test.exs b/test/web/twitter_api/password_controller_test.exs
@@ -1,81 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Web.TwitterAPI.PasswordControllerTest do
- use Pleroma.Web.ConnCase
-
- alias Pleroma.PasswordResetToken
- alias Pleroma.User
- alias Pleroma.Web.OAuth.Token
- import Pleroma.Factory
-
- describe "GET /api/pleroma/password_reset/token" do
- test "it returns error when token invalid", %{conn: conn} do
- response =
- conn
- |> get("/api/pleroma/password_reset/token")
- |> html_response(:ok)
-
- assert response =~ "<h2>Invalid Token</h2>"
- end
-
- test "it shows password reset form", %{conn: conn} do
- user = insert(:user)
- {:ok, token} = PasswordResetToken.create_token(user)
-
- response =
- conn
- |> get("/api/pleroma/password_reset/#{token.token}")
- |> html_response(:ok)
-
- assert response =~ "<h2>Password Reset for #{user.nickname}</h2>"
- end
- end
-
- describe "POST /api/pleroma/password_reset" do
- test "it returns HTTP 200", %{conn: conn} do
- user = insert(:user)
- {:ok, token} = PasswordResetToken.create_token(user)
- {:ok, _access_token} = Token.create(insert(:oauth_app), user, %{})
-
- params = %{
- "password" => "test",
- password_confirmation: "test",
- token: token.token
- }
-
- response =
- conn
- |> assign(:user, user)
- |> post("/api/pleroma/password_reset", %{data: params})
- |> html_response(:ok)
-
- assert response =~ "<h2>Password changed!</h2>"
-
- user = refresh_record(user)
- assert Pbkdf2.verify_pass("test", user.password_hash)
- assert Enum.empty?(Token.get_user_tokens(user))
- end
-
- test "it sets password_reset_pending to false", %{conn: conn} do
- user = insert(:user, password_reset_pending: true)
-
- {:ok, token} = PasswordResetToken.create_token(user)
- {:ok, _access_token} = Token.create(insert(:oauth_app), user, %{})
-
- params = %{
- "password" => "test",
- password_confirmation: "test",
- token: token.token
- }
-
- conn
- |> assign(:user, user)
- |> post("/api/pleroma/password_reset", %{data: params})
- |> html_response(:ok)
-
- assert User.get_by_id(user.id).password_reset_pending == false
- end
- end
-end
diff --git a/test/web/twitter_api/remote_follow_controller_test.exs b/test/web/twitter_api/remote_follow_controller_test.exs
@@ -1,350 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Web.TwitterAPI.RemoteFollowControllerTest do
- use Pleroma.Web.ConnCase
-
- alias Pleroma.Config
- alias Pleroma.MFA
- alias Pleroma.MFA.TOTP
- alias Pleroma.User
- alias Pleroma.Web.CommonAPI
-
- import ExUnit.CaptureLog
- import Pleroma.Factory
- import Ecto.Query
-
- setup do
- Tesla.Mock.mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
- :ok
- end
-
- setup_all do: clear_config([:instance, :federating], true)
- setup do: clear_config([:instance])
- setup do: clear_config([:frontend_configurations, :pleroma_fe])
- setup do: clear_config([:user, :deny_follow_blocked])
-
- describe "GET /ostatus_subscribe - remote_follow/2" do
- test "adds status to pleroma instance if the `acct` is a status", %{conn: conn} do
- assert conn
- |> get(
- remote_follow_path(conn, :follow, %{
- acct: "https://mastodon.social/users/emelie/statuses/101849165031453009"
- })
- )
- |> redirected_to() =~ "/notice/"
- end
-
- test "show follow account page if the `acct` is a account link", %{conn: conn} do
- response =
- conn
- |> get(remote_follow_path(conn, :follow, %{acct: "https://mastodon.social/users/emelie"}))
- |> html_response(200)
-
- assert response =~ "Log in to follow"
- end
-
- test "show follow page if the `acct` is a account link", %{conn: conn} do
- user = insert(:user)
-
- response =
- conn
- |> assign(:user, user)
- |> get(remote_follow_path(conn, :follow, %{acct: "https://mastodon.social/users/emelie"}))
- |> html_response(200)
-
- assert response =~ "Remote follow"
- end
-
- test "show follow page with error when user cannot fecth by `acct` link", %{conn: conn} do
- user = insert(:user)
-
- assert capture_log(fn ->
- response =
- conn
- |> assign(:user, user)
- |> get(
- remote_follow_path(conn, :follow, %{
- acct: "https://mastodon.social/users/not_found"
- })
- )
- |> html_response(200)
-
- assert response =~ "Error fetching user"
- end) =~ "Object has been deleted"
- end
- end
-
- describe "POST /ostatus_subscribe - do_follow/2 with assigned user " do
- test "required `follow | write:follows` scope", %{conn: conn} do
- user = insert(:user)
- user2 = insert(:user)
- read_token = insert(:oauth_token, user: user, scopes: ["read"])
-
- assert capture_log(fn ->
- response =
- conn
- |> assign(:user, user)
- |> assign(:token, read_token)
- |> post(remote_follow_path(conn, :do_follow), %{"user" => %{"id" => user2.id}})
- |> response(200)
-
- assert response =~ "Error following account"
- end) =~ "Insufficient permissions: follow | write:follows."
- end
-
- test "follows user", %{conn: conn} do
- user = insert(:user)
- user2 = insert(:user)
-
- conn =
- conn
- |> assign(:user, user)
- |> assign(:token, insert(:oauth_token, user: user, scopes: ["write:follows"]))
- |> post(remote_follow_path(conn, :do_follow), %{"user" => %{"id" => user2.id}})
-
- assert redirected_to(conn) == "/users/#{user2.id}"
- end
-
- test "returns error when user is deactivated", %{conn: conn} do
- user = insert(:user, deactivated: true)
- user2 = insert(:user)
-
- response =
- conn
- |> assign(:user, user)
- |> post(remote_follow_path(conn, :do_follow), %{"user" => %{"id" => user2.id}})
- |> response(200)
-
- assert response =~ "Error following account"
- end
-
- test "returns error when user is blocked", %{conn: conn} do
- Pleroma.Config.put([:user, :deny_follow_blocked], true)
- user = insert(:user)
- user2 = insert(:user)
-
- {:ok, _user_block} = Pleroma.User.block(user2, user)
-
- response =
- conn
- |> assign(:user, user)
- |> post(remote_follow_path(conn, :do_follow), %{"user" => %{"id" => user2.id}})
- |> response(200)
-
- assert response =~ "Error following account"
- end
-
- test "returns error when followee not found", %{conn: conn} do
- user = insert(:user)
-
- response =
- conn
- |> assign(:user, user)
- |> post(remote_follow_path(conn, :do_follow), %{"user" => %{"id" => "jimm"}})
- |> response(200)
-
- assert response =~ "Error following account"
- end
-
- test "returns success result when user already in followers", %{conn: conn} do
- user = insert(:user)
- user2 = insert(:user)
- {:ok, _, _, _} = CommonAPI.follow(user, user2)
-
- conn =
- conn
- |> assign(:user, refresh_record(user))
- |> assign(:token, insert(:oauth_token, user: user, scopes: ["write:follows"]))
- |> post(remote_follow_path(conn, :do_follow), %{"user" => %{"id" => user2.id}})
-
- assert redirected_to(conn) == "/users/#{user2.id}"
- end
- end
-
- describe "POST /ostatus_subscribe - follow/2 with enabled Two-Factor Auth " do
- test "render the MFA login form", %{conn: conn} do
- otp_secret = TOTP.generate_secret()
-
- user =
- insert(:user,
- multi_factor_authentication_settings: %MFA.Settings{
- enabled: true,
- totp: %MFA.Settings.TOTP{secret: otp_secret, confirmed: true}
- }
- )
-
- user2 = insert(:user)
-
- response =
- conn
- |> post(remote_follow_path(conn, :do_follow), %{
- "authorization" => %{"name" => user.nickname, "password" => "test", "id" => user2.id}
- })
- |> response(200)
-
- mfa_token = Pleroma.Repo.one(from(q in Pleroma.MFA.Token, where: q.user_id == ^user.id))
-
- assert response =~ "Two-factor authentication"
- assert response =~ "Authentication code"
- assert response =~ mfa_token.token
- refute user2.follower_address in User.following(user)
- end
-
- test "returns error when password is incorrect", %{conn: conn} do
- otp_secret = TOTP.generate_secret()
-
- user =
- insert(:user,
- multi_factor_authentication_settings: %MFA.Settings{
- enabled: true,
- totp: %MFA.Settings.TOTP{secret: otp_secret, confirmed: true}
- }
- )
-
- user2 = insert(:user)
-
- response =
- conn
- |> post(remote_follow_path(conn, :do_follow), %{
- "authorization" => %{"name" => user.nickname, "password" => "test1", "id" => user2.id}
- })
- |> response(200)
-
- assert response =~ "Wrong username or password"
- refute user2.follower_address in User.following(user)
- end
-
- test "follows", %{conn: conn} do
- otp_secret = TOTP.generate_secret()
-
- user =
- insert(:user,
- multi_factor_authentication_settings: %MFA.Settings{
- enabled: true,
- totp: %MFA.Settings.TOTP{secret: otp_secret, confirmed: true}
- }
- )
-
- {:ok, %{token: token}} = MFA.Token.create(user)
-
- user2 = insert(:user)
- otp_token = TOTP.generate_token(otp_secret)
-
- conn =
- conn
- |> post(
- remote_follow_path(conn, :do_follow),
- %{
- "mfa" => %{"code" => otp_token, "token" => token, "id" => user2.id}
- }
- )
-
- assert redirected_to(conn) == "/users/#{user2.id}"
- assert user2.follower_address in User.following(user)
- end
-
- test "returns error when auth code is incorrect", %{conn: conn} do
- otp_secret = TOTP.generate_secret()
-
- user =
- insert(:user,
- multi_factor_authentication_settings: %MFA.Settings{
- enabled: true,
- totp: %MFA.Settings.TOTP{secret: otp_secret, confirmed: true}
- }
- )
-
- {:ok, %{token: token}} = MFA.Token.create(user)
-
- user2 = insert(:user)
- otp_token = TOTP.generate_token(TOTP.generate_secret())
-
- response =
- conn
- |> post(
- remote_follow_path(conn, :do_follow),
- %{
- "mfa" => %{"code" => otp_token, "token" => token, "id" => user2.id}
- }
- )
- |> response(200)
-
- assert response =~ "Wrong authentication code"
- refute user2.follower_address in User.following(user)
- end
- end
-
- describe "POST /ostatus_subscribe - follow/2 without assigned user " do
- test "follows", %{conn: conn} do
- user = insert(:user)
- user2 = insert(:user)
-
- conn =
- conn
- |> post(remote_follow_path(conn, :do_follow), %{
- "authorization" => %{"name" => user.nickname, "password" => "test", "id" => user2.id}
- })
-
- assert redirected_to(conn) == "/users/#{user2.id}"
- assert user2.follower_address in User.following(user)
- end
-
- test "returns error when followee not found", %{conn: conn} do
- user = insert(:user)
-
- response =
- conn
- |> post(remote_follow_path(conn, :do_follow), %{
- "authorization" => %{"name" => user.nickname, "password" => "test", "id" => "jimm"}
- })
- |> response(200)
-
- assert response =~ "Error following account"
- end
-
- test "returns error when login invalid", %{conn: conn} do
- user = insert(:user)
-
- response =
- conn
- |> post(remote_follow_path(conn, :do_follow), %{
- "authorization" => %{"name" => "jimm", "password" => "test", "id" => user.id}
- })
- |> response(200)
-
- assert response =~ "Wrong username or password"
- end
-
- test "returns error when password invalid", %{conn: conn} do
- user = insert(:user)
- user2 = insert(:user)
-
- response =
- conn
- |> post(remote_follow_path(conn, :do_follow), %{
- "authorization" => %{"name" => user.nickname, "password" => "42", "id" => user2.id}
- })
- |> response(200)
-
- assert response =~ "Wrong username or password"
- end
-
- test "returns error when user is blocked", %{conn: conn} do
- Pleroma.Config.put([:user, :deny_follow_blocked], true)
- user = insert(:user)
- user2 = insert(:user)
- {:ok, _user_block} = Pleroma.User.block(user2, user)
-
- response =
- conn
- |> post(remote_follow_path(conn, :do_follow), %{
- "authorization" => %{"name" => user.nickname, "password" => "test", "id" => user2.id}
- })
- |> response(200)
-
- assert response =~ "Error following account"
- end
- end
-end
diff --git a/test/web/twitter_api/twitter_api_controller_test.exs b/test/web/twitter_api/twitter_api_controller_test.exs
@@ -1,138 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Web.TwitterAPI.ControllerTest do
- use Pleroma.Web.ConnCase
-
- alias Pleroma.Builders.ActivityBuilder
- alias Pleroma.Repo
- alias Pleroma.User
- alias Pleroma.Web.OAuth.Token
-
- import Pleroma.Factory
-
- describe "POST /api/qvitter/statuses/notifications/read" do
- test "without valid credentials", %{conn: conn} do
- conn = post(conn, "/api/qvitter/statuses/notifications/read", %{"latest_id" => 1_234_567})
- assert json_response(conn, 403) == %{"error" => "Invalid credentials."}
- end
-
- test "with credentials, without any params" do
- %{conn: conn} = oauth_access(["write:notifications"])
-
- conn = post(conn, "/api/qvitter/statuses/notifications/read")
-
- assert json_response(conn, 400) == %{
- "error" => "You need to specify latest_id",
- "request" => "/api/qvitter/statuses/notifications/read"
- }
- end
-
- test "with credentials, with params" do
- %{user: current_user, conn: conn} =
- oauth_access(["read:notifications", "write:notifications"])
-
- other_user = insert(:user)
-
- {:ok, _activity} =
- ActivityBuilder.insert(%{"to" => [current_user.ap_id]}, %{user: other_user})
-
- response_conn =
- conn
- |> assign(:user, current_user)
- |> get("/api/v1/notifications")
-
- [notification] = response = json_response(response_conn, 200)
-
- assert length(response) == 1
-
- assert notification["pleroma"]["is_seen"] == false
-
- response_conn =
- conn
- |> assign(:user, current_user)
- |> post("/api/qvitter/statuses/notifications/read", %{"latest_id" => notification["id"]})
-
- [notification] = response = json_response(response_conn, 200)
-
- assert length(response) == 1
-
- assert notification["pleroma"]["is_seen"] == true
- end
- end
-
- describe "GET /api/account/confirm_email/:id/:token" do
- setup do
- {:ok, user} =
- insert(:user)
- |> User.confirmation_changeset(need_confirmation: true)
- |> Repo.update()
-
- assert user.confirmation_pending
-
- [user: user]
- end
-
- test "it redirects to root url", %{conn: conn, user: user} do
- conn = get(conn, "/api/account/confirm_email/#{user.id}/#{user.confirmation_token}")
-
- assert 302 == conn.status
- end
-
- test "it confirms the user account", %{conn: conn, user: user} do
- get(conn, "/api/account/confirm_email/#{user.id}/#{user.confirmation_token}")
-
- user = User.get_cached_by_id(user.id)
-
- refute user.confirmation_pending
- refute user.confirmation_token
- end
-
- test "it returns 500 if user cannot be found by id", %{conn: conn, user: user} do
- conn = get(conn, "/api/account/confirm_email/0/#{user.confirmation_token}")
-
- assert 500 == conn.status
- end
-
- test "it returns 500 if token is invalid", %{conn: conn, user: user} do
- conn = get(conn, "/api/account/confirm_email/#{user.id}/wrong_token")
-
- assert 500 == conn.status
- end
- end
-
- describe "GET /api/oauth_tokens" do
- setup do
- token = insert(:oauth_token) |> Repo.preload(:user)
-
- %{token: token}
- end
-
- test "renders list", %{token: token} do
- response =
- build_conn()
- |> assign(:user, token.user)
- |> get("/api/oauth_tokens")
-
- keys =
- json_response(response, 200)
- |> hd()
- |> Map.keys()
-
- assert keys -- ["id", "app_name", "valid_until"] == []
- end
-
- test "revoke token", %{token: token} do
- response =
- build_conn()
- |> assign(:user, token.user)
- |> delete("/api/oauth_tokens/#{token.id}")
-
- tokens = Token.get_user_tokens(token.user)
-
- assert tokens == []
- assert response.status == 201
- end
- end
-end
diff --git a/test/web/twitter_api/twitter_api_test.exs b/test/web/twitter_api/twitter_api_test.exs
@@ -1,432 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do
- use Pleroma.DataCase
- import Pleroma.Factory
- alias Pleroma.Repo
- alias Pleroma.Tests.ObanHelpers
- alias Pleroma.User
- alias Pleroma.UserInviteToken
- alias Pleroma.Web.TwitterAPI.TwitterAPI
-
- setup_all do
- Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end)
- :ok
- end
-
- test "it registers a new user and returns the user." do
- data = %{
- :username => "lain",
- :email => "lain@wired.jp",
- :fullname => "lain iwakura",
- :password => "bear",
- :confirm => "bear"
- }
-
- {:ok, user} = TwitterAPI.register_user(data)
-
- assert user == User.get_cached_by_nickname("lain")
- end
-
- test "it registers a new user with empty string in bio and returns the user" do
- data = %{
- :username => "lain",
- :email => "lain@wired.jp",
- :fullname => "lain iwakura",
- :bio => "",
- :password => "bear",
- :confirm => "bear"
- }
-
- {:ok, user} = TwitterAPI.register_user(data)
-
- assert user == User.get_cached_by_nickname("lain")
- end
-
- test "it sends confirmation email if :account_activation_required is specified in instance config" do
- setting = Pleroma.Config.get([:instance, :account_activation_required])
-
- unless setting do
- Pleroma.Config.put([:instance, :account_activation_required], true)
- on_exit(fn -> Pleroma.Config.put([:instance, :account_activation_required], setting) end)
- end
-
- data = %{
- :username => "lain",
- :email => "lain@wired.jp",
- :fullname => "lain iwakura",
- :bio => "",
- :password => "bear",
- :confirm => "bear"
- }
-
- {:ok, user} = TwitterAPI.register_user(data)
- ObanHelpers.perform_all()
-
- assert user.confirmation_pending
-
- email = Pleroma.Emails.UserEmail.account_confirmation_email(user)
-
- notify_email = Pleroma.Config.get([:instance, :notify_email])
- instance_name = Pleroma.Config.get([:instance, :name])
-
- Swoosh.TestAssertions.assert_email_sent(
- from: {instance_name, notify_email},
- to: {user.name, user.email},
- html_body: email.html_body
- )
- end
-
- test "it sends an admin email if :account_approval_required is specified in instance config" do
- admin = insert(:user, is_admin: true)
- setting = Pleroma.Config.get([:instance, :account_approval_required])
-
- unless setting do
- Pleroma.Config.put([:instance, :account_approval_required], true)
- on_exit(fn -> Pleroma.Config.put([:instance, :account_approval_required], setting) end)
- end
-
- data = %{
- :username => "lain",
- :email => "lain@wired.jp",
- :fullname => "lain iwakura",
- :bio => "",
- :password => "bear",
- :confirm => "bear",
- :reason => "I love anime"
- }
-
- {:ok, user} = TwitterAPI.register_user(data)
- ObanHelpers.perform_all()
-
- assert user.approval_pending
-
- email = Pleroma.Emails.AdminEmail.new_unapproved_registration(admin, user)
-
- notify_email = Pleroma.Config.get([:instance, :notify_email])
- instance_name = Pleroma.Config.get([:instance, :name])
-
- Swoosh.TestAssertions.assert_email_sent(
- from: {instance_name, notify_email},
- to: {admin.name, admin.email},
- html_body: email.html_body
- )
- end
-
- test "it registers a new user and parses mentions in the bio" do
- data1 = %{
- :username => "john",
- :email => "john@gmail.com",
- :fullname => "John Doe",
- :bio => "test",
- :password => "bear",
- :confirm => "bear"
- }
-
- {:ok, user1} = TwitterAPI.register_user(data1)
-
- data2 = %{
- :username => "lain",
- :email => "lain@wired.jp",
- :fullname => "lain iwakura",
- :bio => "@john test",
- :password => "bear",
- :confirm => "bear"
- }
-
- {:ok, user2} = TwitterAPI.register_user(data2)
-
- expected_text =
- ~s(<span class="h-card"><a class="u-url mention" data-user="#{user1.id}" href="#{
- user1.ap_id
- }" rel="ugc">@<span>john</span></a></span> test)
-
- assert user2.bio == expected_text
- end
-
- describe "register with one time token" do
- setup do: clear_config([:instance, :registrations_open], false)
-
- test "returns user on success" do
- {:ok, invite} = UserInviteToken.create_invite()
-
- data = %{
- :username => "vinny",
- :email => "pasta@pizza.vs",
- :fullname => "Vinny Vinesauce",
- :bio => "streamer",
- :password => "hiptofbees",
- :confirm => "hiptofbees",
- :token => invite.token
- }
-
- {:ok, user} = TwitterAPI.register_user(data)
-
- assert user == User.get_cached_by_nickname("vinny")
-
- invite = Repo.get_by(UserInviteToken, token: invite.token)
- assert invite.used == true
- end
-
- test "returns error on invalid token" do
- data = %{
- :username => "GrimReaper",
- :email => "death@reapers.afterlife",
- :fullname => "Reaper Grim",
- :bio => "Your time has come",
- :password => "scythe",
- :confirm => "scythe",
- :token => "DudeLetMeInImAFairy"
- }
-
- {:error, msg} = TwitterAPI.register_user(data)
-
- assert msg == "Invalid token"
- refute User.get_cached_by_nickname("GrimReaper")
- end
-
- test "returns error on expired token" do
- {:ok, invite} = UserInviteToken.create_invite()
- UserInviteToken.update_invite!(invite, used: true)
-
- data = %{
- :username => "GrimReaper",
- :email => "death@reapers.afterlife",
- :fullname => "Reaper Grim",
- :bio => "Your time has come",
- :password => "scythe",
- :confirm => "scythe",
- :token => invite.token
- }
-
- {:error, msg} = TwitterAPI.register_user(data)
-
- assert msg == "Expired token"
- refute User.get_cached_by_nickname("GrimReaper")
- end
- end
-
- describe "registers with date limited token" do
- setup do: clear_config([:instance, :registrations_open], false)
-
- setup do
- data = %{
- :username => "vinny",
- :email => "pasta@pizza.vs",
- :fullname => "Vinny Vinesauce",
- :bio => "streamer",
- :password => "hiptofbees",
- :confirm => "hiptofbees"
- }
-
- check_fn = fn invite ->
- data = Map.put(data, :token, invite.token)
- {:ok, user} = TwitterAPI.register_user(data)
-
- assert user == User.get_cached_by_nickname("vinny")
- end
-
- {:ok, data: data, check_fn: check_fn}
- end
-
- test "returns user on success", %{check_fn: check_fn} do
- {:ok, invite} = UserInviteToken.create_invite(%{expires_at: Date.utc_today()})
-
- check_fn.(invite)
-
- invite = Repo.get_by(UserInviteToken, token: invite.token)
-
- refute invite.used
- end
-
- test "returns user on token which expired tomorrow", %{check_fn: check_fn} do
- {:ok, invite} = UserInviteToken.create_invite(%{expires_at: Date.add(Date.utc_today(), 1)})
-
- check_fn.(invite)
-
- invite = Repo.get_by(UserInviteToken, token: invite.token)
-
- refute invite.used
- end
-
- test "returns an error on overdue date", %{data: data} do
- {:ok, invite} = UserInviteToken.create_invite(%{expires_at: Date.add(Date.utc_today(), -1)})
-
- data = Map.put(data, "token", invite.token)
-
- {:error, msg} = TwitterAPI.register_user(data)
-
- assert msg == "Expired token"
- refute User.get_cached_by_nickname("vinny")
- invite = Repo.get_by(UserInviteToken, token: invite.token)
-
- refute invite.used
- end
- end
-
- describe "registers with reusable token" do
- setup do: clear_config([:instance, :registrations_open], false)
-
- test "returns user on success, after him registration fails" do
- {:ok, invite} = UserInviteToken.create_invite(%{max_use: 100})
-
- UserInviteToken.update_invite!(invite, uses: 99)
-
- data = %{
- :username => "vinny",
- :email => "pasta@pizza.vs",
- :fullname => "Vinny Vinesauce",
- :bio => "streamer",
- :password => "hiptofbees",
- :confirm => "hiptofbees",
- :token => invite.token
- }
-
- {:ok, user} = TwitterAPI.register_user(data)
- assert user == User.get_cached_by_nickname("vinny")
-
- invite = Repo.get_by(UserInviteToken, token: invite.token)
- assert invite.used == true
-
- data = %{
- :username => "GrimReaper",
- :email => "death@reapers.afterlife",
- :fullname => "Reaper Grim",
- :bio => "Your time has come",
- :password => "scythe",
- :confirm => "scythe",
- :token => invite.token
- }
-
- {:error, msg} = TwitterAPI.register_user(data)
-
- assert msg == "Expired token"
- refute User.get_cached_by_nickname("GrimReaper")
- end
- end
-
- describe "registers with reusable date limited token" do
- setup do: clear_config([:instance, :registrations_open], false)
-
- test "returns user on success" do
- {:ok, invite} = UserInviteToken.create_invite(%{expires_at: Date.utc_today(), max_use: 100})
-
- data = %{
- :username => "vinny",
- :email => "pasta@pizza.vs",
- :fullname => "Vinny Vinesauce",
- :bio => "streamer",
- :password => "hiptofbees",
- :confirm => "hiptofbees",
- :token => invite.token
- }
-
- {:ok, user} = TwitterAPI.register_user(data)
- assert user == User.get_cached_by_nickname("vinny")
-
- invite = Repo.get_by(UserInviteToken, token: invite.token)
- refute invite.used
- end
-
- test "error after max uses" do
- {:ok, invite} = UserInviteToken.create_invite(%{expires_at: Date.utc_today(), max_use: 100})
-
- UserInviteToken.update_invite!(invite, uses: 99)
-
- data = %{
- :username => "vinny",
- :email => "pasta@pizza.vs",
- :fullname => "Vinny Vinesauce",
- :bio => "streamer",
- :password => "hiptofbees",
- :confirm => "hiptofbees",
- :token => invite.token
- }
-
- {:ok, user} = TwitterAPI.register_user(data)
- assert user == User.get_cached_by_nickname("vinny")
-
- invite = Repo.get_by(UserInviteToken, token: invite.token)
- assert invite.used == true
-
- data = %{
- :username => "GrimReaper",
- :email => "death@reapers.afterlife",
- :fullname => "Reaper Grim",
- :bio => "Your time has come",
- :password => "scythe",
- :confirm => "scythe",
- :token => invite.token
- }
-
- {:error, msg} = TwitterAPI.register_user(data)
-
- assert msg == "Expired token"
- refute User.get_cached_by_nickname("GrimReaper")
- end
-
- test "returns error on overdue date" do
- {:ok, invite} =
- UserInviteToken.create_invite(%{expires_at: Date.add(Date.utc_today(), -1), max_use: 100})
-
- data = %{
- :username => "GrimReaper",
- :email => "death@reapers.afterlife",
- :fullname => "Reaper Grim",
- :bio => "Your time has come",
- :password => "scythe",
- :confirm => "scythe",
- :token => invite.token
- }
-
- {:error, msg} = TwitterAPI.register_user(data)
-
- assert msg == "Expired token"
- refute User.get_cached_by_nickname("GrimReaper")
- end
-
- test "returns error on with overdue date and after max" do
- {:ok, invite} =
- UserInviteToken.create_invite(%{expires_at: Date.add(Date.utc_today(), -1), max_use: 100})
-
- UserInviteToken.update_invite!(invite, uses: 100)
-
- data = %{
- :username => "GrimReaper",
- :email => "death@reapers.afterlife",
- :fullname => "Reaper Grim",
- :bio => "Your time has come",
- :password => "scythe",
- :confirm => "scythe",
- :token => invite.token
- }
-
- {:error, msg} = TwitterAPI.register_user(data)
-
- assert msg == "Expired token"
- refute User.get_cached_by_nickname("GrimReaper")
- end
- end
-
- test "it returns the error on registration problems" do
- data = %{
- :username => "lain",
- :email => "lain@wired.jp",
- :fullname => "lain iwakura",
- :bio => "close the world."
- }
-
- {:error, error} = TwitterAPI.register_user(data)
-
- assert is_binary(error)
- refute User.get_cached_by_nickname("lain")
- end
-
- setup do
- Supervisor.terminate_child(Pleroma.Supervisor, Cachex)
- Supervisor.restart_child(Pleroma.Supervisor, Cachex)
- :ok
- end
-end
diff --git a/test/web/twitter_api/util_controller_test.exs b/test/web/twitter_api/util_controller_test.exs
@@ -1,437 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Web.TwitterAPI.UtilControllerTest do
- use Pleroma.Web.ConnCase
- use Oban.Testing, repo: Pleroma.Repo
-
- alias Pleroma.Config
- alias Pleroma.Tests.ObanHelpers
- alias Pleroma.User
-
- import Pleroma.Factory
- import Mock
-
- setup do
- Tesla.Mock.mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
- :ok
- end
-
- setup do: clear_config([:instance])
- setup do: clear_config([:frontend_configurations, :pleroma_fe])
-
- describe "PUT /api/pleroma/notification_settings" do
- setup do: oauth_access(["write:accounts"])
-
- test "it updates notification settings", %{user: user, conn: conn} do
- conn
- |> put("/api/pleroma/notification_settings", %{
- "block_from_strangers" => true,
- "bar" => 1
- })
- |> json_response(:ok)
-
- user = refresh_record(user)
-
- assert %Pleroma.User.NotificationSetting{
- block_from_strangers: true,
- hide_notification_contents: false
- } == user.notification_settings
- end
-
- test "it updates notification settings to enable hiding contents", %{user: user, conn: conn} do
- conn
- |> put("/api/pleroma/notification_settings", %{"hide_notification_contents" => "1"})
- |> json_response(:ok)
-
- user = refresh_record(user)
-
- assert %Pleroma.User.NotificationSetting{
- block_from_strangers: false,
- hide_notification_contents: true
- } == user.notification_settings
- end
- end
-
- describe "GET /api/pleroma/frontend_configurations" do
- test "returns everything in :pleroma, :frontend_configurations", %{conn: conn} do
- config = [
- frontend_a: %{
- x: 1,
- y: 2
- },
- frontend_b: %{
- z: 3
- }
- ]
-
- Config.put(:frontend_configurations, config)
-
- response =
- conn
- |> get("/api/pleroma/frontend_configurations")
- |> json_response(:ok)
-
- assert response == Jason.encode!(config |> Enum.into(%{})) |> Jason.decode!()
- end
- end
-
- describe "/api/pleroma/emoji" do
- test "returns json with custom emoji with tags", %{conn: conn} do
- emoji =
- conn
- |> get("/api/pleroma/emoji")
- |> json_response(200)
-
- assert Enum.all?(emoji, fn
- {_key,
- %{
- "image_url" => url,
- "tags" => tags
- }} ->
- is_binary(url) and is_list(tags)
- end)
- end
- end
-
- describe "GET /api/pleroma/healthcheck" do
- setup do: clear_config([:instance, :healthcheck])
-
- test "returns 503 when healthcheck disabled", %{conn: conn} do
- Config.put([:instance, :healthcheck], false)
-
- response =
- conn
- |> get("/api/pleroma/healthcheck")
- |> json_response(503)
-
- assert response == %{}
- end
-
- test "returns 200 when healthcheck enabled and all ok", %{conn: conn} do
- Config.put([:instance, :healthcheck], true)
-
- with_mock Pleroma.Healthcheck,
- system_info: fn -> %Pleroma.Healthcheck{healthy: true} end do
- response =
- conn
- |> get("/api/pleroma/healthcheck")
- |> json_response(200)
-
- assert %{
- "active" => _,
- "healthy" => true,
- "idle" => _,
- "memory_used" => _,
- "pool_size" => _
- } = response
- end
- end
-
- test "returns 503 when healthcheck enabled and health is false", %{conn: conn} do
- Config.put([:instance, :healthcheck], true)
-
- with_mock Pleroma.Healthcheck,
- system_info: fn -> %Pleroma.Healthcheck{healthy: false} end do
- response =
- conn
- |> get("/api/pleroma/healthcheck")
- |> json_response(503)
-
- assert %{
- "active" => _,
- "healthy" => false,
- "idle" => _,
- "memory_used" => _,
- "pool_size" => _
- } = response
- end
- end
- end
-
- describe "POST /api/pleroma/disable_account" do
- setup do: oauth_access(["write:accounts"])
-
- test "with valid permissions and password, it disables the account", %{conn: conn, user: user} do
- response =
- conn
- |> post("/api/pleroma/disable_account", %{"password" => "test"})
- |> json_response(:ok)
-
- assert response == %{"status" => "success"}
- ObanHelpers.perform_all()
-
- user = User.get_cached_by_id(user.id)
-
- assert user.deactivated == true
- end
-
- test "with valid permissions and invalid password, it returns an error", %{conn: conn} do
- user = insert(:user)
-
- response =
- conn
- |> post("/api/pleroma/disable_account", %{"password" => "test1"})
- |> json_response(:ok)
-
- assert response == %{"error" => "Invalid password."}
- user = User.get_cached_by_id(user.id)
-
- refute user.deactivated
- end
- end
-
- describe "POST /main/ostatus - remote_subscribe/2" do
- setup do: clear_config([:instance, :federating], true)
-
- test "renders subscribe form", %{conn: conn} do
- user = insert(:user)
-
- response =
- conn
- |> post("/main/ostatus", %{"nickname" => user.nickname, "profile" => ""})
- |> response(:ok)
-
- refute response =~ "Could not find user"
- assert response =~ "Remotely follow #{user.nickname}"
- end
-
- test "renders subscribe form with error when user not found", %{conn: conn} do
- response =
- conn
- |> post("/main/ostatus", %{"nickname" => "nickname", "profile" => ""})
- |> response(:ok)
-
- assert response =~ "Could not find user"
- refute response =~ "Remotely follow"
- end
-
- test "it redirect to webfinger url", %{conn: conn} do
- user = insert(:user)
- user2 = insert(:user, ap_id: "shp@social.heldscal.la")
-
- conn =
- conn
- |> post("/main/ostatus", %{
- "user" => %{"nickname" => user.nickname, "profile" => user2.ap_id}
- })
-
- assert redirected_to(conn) ==
- "https://social.heldscal.la/main/ostatussub?profile=#{user.ap_id}"
- end
-
- test "it renders form with error when user not found", %{conn: conn} do
- user2 = insert(:user, ap_id: "shp@social.heldscal.la")
-
- response =
- conn
- |> post("/main/ostatus", %{"user" => %{"nickname" => "jimm", "profile" => user2.ap_id}})
- |> response(:ok)
-
- assert response =~ "Something went wrong."
- end
- end
-
- test "it returns new captcha", %{conn: conn} do
- with_mock Pleroma.Captcha,
- new: fn -> "test_captcha" end do
- resp =
- conn
- |> get("/api/pleroma/captcha")
- |> response(200)
-
- assert resp == "\"test_captcha\""
- assert called(Pleroma.Captcha.new())
- end
- end
-
- describe "POST /api/pleroma/change_email" do
- setup do: oauth_access(["write:accounts"])
-
- test "without permissions", %{conn: conn} do
- conn =
- conn
- |> assign(:token, nil)
- |> post("/api/pleroma/change_email")
-
- assert json_response(conn, 403) == %{"error" => "Insufficient permissions: write:accounts."}
- end
-
- test "with proper permissions and invalid password", %{conn: conn} do
- conn =
- post(conn, "/api/pleroma/change_email", %{
- "password" => "hi",
- "email" => "test@test.com"
- })
-
- assert json_response(conn, 200) == %{"error" => "Invalid password."}
- end
-
- test "with proper permissions, valid password and invalid email", %{
- conn: conn
- } do
- conn =
- post(conn, "/api/pleroma/change_email", %{
- "password" => "test",
- "email" => "foobar"
- })
-
- assert json_response(conn, 200) == %{"error" => "Email has invalid format."}
- end
-
- test "with proper permissions, valid password and no email", %{
- conn: conn
- } do
- conn =
- post(conn, "/api/pleroma/change_email", %{
- "password" => "test"
- })
-
- assert json_response(conn, 200) == %{"error" => "Email can't be blank."}
- end
-
- test "with proper permissions, valid password and blank email", %{
- conn: conn
- } do
- conn =
- post(conn, "/api/pleroma/change_email", %{
- "password" => "test",
- "email" => ""
- })
-
- assert json_response(conn, 200) == %{"error" => "Email can't be blank."}
- end
-
- test "with proper permissions, valid password and non unique email", %{
- conn: conn
- } do
- user = insert(:user)
-
- conn =
- post(conn, "/api/pleroma/change_email", %{
- "password" => "test",
- "email" => user.email
- })
-
- assert json_response(conn, 200) == %{"error" => "Email has already been taken."}
- end
-
- test "with proper permissions, valid password and valid email", %{
- conn: conn
- } do
- conn =
- post(conn, "/api/pleroma/change_email", %{
- "password" => "test",
- "email" => "cofe@foobar.com"
- })
-
- assert json_response(conn, 200) == %{"status" => "success"}
- end
- end
-
- describe "POST /api/pleroma/change_password" do
- setup do: oauth_access(["write:accounts"])
-
- test "without permissions", %{conn: conn} do
- conn =
- conn
- |> assign(:token, nil)
- |> post("/api/pleroma/change_password")
-
- assert json_response(conn, 403) == %{"error" => "Insufficient permissions: write:accounts."}
- end
-
- test "with proper permissions and invalid password", %{conn: conn} do
- conn =
- post(conn, "/api/pleroma/change_password", %{
- "password" => "hi",
- "new_password" => "newpass",
- "new_password_confirmation" => "newpass"
- })
-
- assert json_response(conn, 200) == %{"error" => "Invalid password."}
- end
-
- test "with proper permissions, valid password and new password and confirmation not matching",
- %{
- conn: conn
- } do
- conn =
- post(conn, "/api/pleroma/change_password", %{
- "password" => "test",
- "new_password" => "newpass",
- "new_password_confirmation" => "notnewpass"
- })
-
- assert json_response(conn, 200) == %{
- "error" => "New password does not match confirmation."
- }
- end
-
- test "with proper permissions, valid password and invalid new password", %{
- conn: conn
- } do
- conn =
- post(conn, "/api/pleroma/change_password", %{
- "password" => "test",
- "new_password" => "",
- "new_password_confirmation" => ""
- })
-
- assert json_response(conn, 200) == %{
- "error" => "New password can't be blank."
- }
- end
-
- test "with proper permissions, valid password and matching new password and confirmation", %{
- conn: conn,
- user: user
- } do
- conn =
- post(conn, "/api/pleroma/change_password", %{
- "password" => "test",
- "new_password" => "newpass",
- "new_password_confirmation" => "newpass"
- })
-
- assert json_response(conn, 200) == %{"status" => "success"}
- fetched_user = User.get_cached_by_id(user.id)
- assert Pbkdf2.verify_pass("newpass", fetched_user.password_hash) == true
- end
- end
-
- describe "POST /api/pleroma/delete_account" do
- setup do: oauth_access(["write:accounts"])
-
- test "without permissions", %{conn: conn} do
- conn =
- conn
- |> assign(:token, nil)
- |> post("/api/pleroma/delete_account")
-
- assert json_response(conn, 403) ==
- %{"error" => "Insufficient permissions: write:accounts."}
- end
-
- test "with proper permissions and wrong or missing password", %{conn: conn} do
- for params <- [%{"password" => "hi"}, %{}] do
- ret_conn = post(conn, "/api/pleroma/delete_account", params)
-
- assert json_response(ret_conn, 200) == %{"error" => "Invalid password."}
- end
- end
-
- test "with proper permissions and valid password", %{conn: conn, user: user} do
- conn = post(conn, "/api/pleroma/delete_account", %{"password" => "test"})
- ObanHelpers.perform_all()
- assert json_response(conn, 200) == %{"status" => "success"}
-
- user = User.get_by_id(user.id)
- assert user.deactivated == true
- assert user.name == nil
- assert user.bio == ""
- assert user.password_hash == nil
- end
- end
-end
diff --git a/test/web/uploader_controller_test.exs b/test/web/uploader_controller_test.exs
@@ -1,43 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Web.UploaderControllerTest do
- use Pleroma.Web.ConnCase
- alias Pleroma.Uploaders.Uploader
-
- describe "callback/2" do
- test "it returns 400 response when process callback isn't alive", %{conn: conn} do
- res =
- conn
- |> post(uploader_path(conn, :callback, "test-path"))
-
- assert res.status == 400
- assert res.resp_body == "{\"error\":\"bad request\"}"
- end
-
- test "it returns success result", %{conn: conn} do
- task =
- Task.async(fn ->
- receive do
- {Uploader, pid, conn, _params} ->
- conn =
- conn
- |> put_status(:ok)
- |> Phoenix.Controller.json(%{upload_path: "test-path"})
-
- send(pid, {Uploader, conn})
- end
- end)
-
- :global.register_name({Uploader, "test-path"}, task.pid)
-
- res =
- conn
- |> post(uploader_path(conn, :callback, "test-path"))
- |> json_response(200)
-
- assert res == %{"upload_path" => "test-path"}
- end
- end
-end
diff --git a/test/web/views/error_view_test.exs b/test/web/views/error_view_test.exs
@@ -1,36 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Web.ErrorViewTest do
- use Pleroma.Web.ConnCase, async: true
- import ExUnit.CaptureLog
-
- # Bring render/3 and render_to_string/3 for testing custom views
- import Phoenix.View
-
- test "renders 404.json" do
- assert render(Pleroma.Web.ErrorView, "404.json", []) == %{errors: %{detail: "Page not found"}}
- end
-
- test "render 500.json" do
- assert capture_log(fn ->
- assert render(Pleroma.Web.ErrorView, "500.json", []) ==
- %{errors: %{detail: "Internal server error", reason: "nil"}}
- end) =~ "[error] Internal server error: nil"
- end
-
- test "render any other" do
- assert capture_log(fn ->
- assert render(Pleroma.Web.ErrorView, "505.json", []) ==
- %{errors: %{detail: "Internal server error", reason: "nil"}}
- end) =~ "[error] Internal server error: nil"
- end
-
- test "render 500.json with reason" do
- assert capture_log(fn ->
- assert render(Pleroma.Web.ErrorView, "500.json", reason: "test reason") ==
- %{errors: %{detail: "Internal server error", reason: "\"test reason\""}}
- end) =~ "[error] Internal server error: \"test reason\""
- end
-end
diff --git a/test/web/web_finger/web_finger_controller_test.exs b/test/web/web_finger/web_finger_controller_test.exs
@@ -1,94 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Web.WebFinger.WebFingerControllerTest do
- use Pleroma.Web.ConnCase
-
- import ExUnit.CaptureLog
- import Pleroma.Factory
- import Tesla.Mock
-
- setup do
- mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
- :ok
- end
-
- setup_all do: clear_config([:instance, :federating], true)
-
- test "GET host-meta" do
- response =
- build_conn()
- |> get("/.well-known/host-meta")
-
- assert response.status == 200
-
- assert response.resp_body ==
- ~s(<?xml version="1.0" encoding="UTF-8"?><XRD xmlns="http://docs.oasis-open.org/ns/xri/xrd-1.0"><Link rel="lrdd" template="#{
- Pleroma.Web.base_url()
- }/.well-known/webfinger?resource={uri}" type="application/xrd+xml" /></XRD>)
- end
-
- test "Webfinger JRD" do
- user = insert(:user)
-
- response =
- build_conn()
- |> put_req_header("accept", "application/jrd+json")
- |> get("/.well-known/webfinger?resource=acct:#{user.nickname}@localhost")
-
- assert json_response(response, 200)["subject"] == "acct:#{user.nickname}@localhost"
- end
-
- test "it returns 404 when user isn't found (JSON)" do
- result =
- build_conn()
- |> put_req_header("accept", "application/jrd+json")
- |> get("/.well-known/webfinger?resource=acct:jimm@localhost")
- |> json_response(404)
-
- assert result == "Couldn't find user"
- end
-
- test "Webfinger XML" do
- user = insert(:user)
-
- response =
- build_conn()
- |> put_req_header("accept", "application/xrd+xml")
- |> get("/.well-known/webfinger?resource=acct:#{user.nickname}@localhost")
-
- assert response(response, 200)
- end
-
- test "it returns 404 when user isn't found (XML)" do
- result =
- build_conn()
- |> put_req_header("accept", "application/xrd+xml")
- |> get("/.well-known/webfinger?resource=acct:jimm@localhost")
- |> response(404)
-
- assert result == "Couldn't find user"
- end
-
- test "Sends a 404 when invalid format" do
- user = insert(:user)
-
- assert capture_log(fn ->
- assert_raise Phoenix.NotAcceptableError, fn ->
- build_conn()
- |> put_req_header("accept", "text/html")
- |> get("/.well-known/webfinger?resource=acct:#{user.nickname}@localhost")
- end
- end) =~ "no supported media type in accept header"
- end
-
- test "Sends a 400 when resource param is missing" do
- response =
- build_conn()
- |> put_req_header("accept", "application/xrd+xml,application/jrd+json")
- |> get("/.well-known/webfinger")
-
- assert response(response, 400)
- end
-end
diff --git a/test/web/web_finger/web_finger_test.exs b/test/web/web_finger/web_finger_test.exs
@@ -1,116 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Web.WebFingerTest do
- use Pleroma.DataCase
- alias Pleroma.Web.WebFinger
- import Pleroma.Factory
- import Tesla.Mock
-
- setup do
- mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
- :ok
- end
-
- describe "host meta" do
- test "returns a link to the xml lrdd" do
- host_info = WebFinger.host_meta()
-
- assert String.contains?(host_info, Pleroma.Web.base_url())
- end
- end
-
- describe "incoming webfinger request" do
- test "works for fqns" do
- user = insert(:user)
-
- {:ok, result} =
- WebFinger.webfinger("#{user.nickname}@#{Pleroma.Web.Endpoint.host()}", "XML")
-
- assert is_binary(result)
- end
-
- test "works for ap_ids" do
- user = insert(:user)
-
- {:ok, result} = WebFinger.webfinger(user.ap_id, "XML")
- assert is_binary(result)
- end
- end
-
- describe "fingering" do
- test "returns error for nonsensical input" do
- assert {:error, _} = WebFinger.finger("bliblablu")
- assert {:error, _} = WebFinger.finger("pleroma.social")
- end
-
- test "returns error when fails parse xml or json" do
- user = "invalid_content@social.heldscal.la"
- assert {:error, %Jason.DecodeError{}} = WebFinger.finger(user)
- end
-
- test "returns the ActivityPub actor URI for an ActivityPub user" do
- user = "framasoft@framatube.org"
-
- {:ok, _data} = WebFinger.finger(user)
- end
-
- test "returns the ActivityPub actor URI for an ActivityPub user with the ld+json mimetype" do
- user = "kaniini@gerzilla.de"
-
- {:ok, data} = WebFinger.finger(user)
-
- assert data["ap_id"] == "https://gerzilla.de/channel/kaniini"
- end
-
- test "it work for AP-only user" do
- user = "kpherox@mstdn.jp"
-
- {:ok, data} = WebFinger.finger(user)
-
- assert data["magic_key"] == nil
- assert data["salmon"] == nil
-
- assert data["topic"] == nil
- assert data["subject"] == "acct:kPherox@mstdn.jp"
- assert data["ap_id"] == "https://mstdn.jp/users/kPherox"
- assert data["subscribe_address"] == "https://mstdn.jp/authorize_interaction?acct={uri}"
- end
-
- test "it works for friendica" do
- user = "lain@squeet.me"
-
- {:ok, _data} = WebFinger.finger(user)
- end
-
- test "it gets the xrd endpoint" do
- {:ok, template} = WebFinger.find_lrdd_template("social.heldscal.la")
-
- assert template == "https://social.heldscal.la/.well-known/webfinger?resource={uri}"
- end
-
- test "it gets the xrd endpoint for hubzilla" do
- {:ok, template} = WebFinger.find_lrdd_template("macgirvin.com")
-
- assert template == "https://macgirvin.com/xrd/?uri={uri}"
- end
-
- test "it gets the xrd endpoint for statusnet" do
- {:ok, template} = WebFinger.find_lrdd_template("status.alpicola.com")
-
- assert template == "http://status.alpicola.com/main/xrd?uri={uri}"
- end
-
- test "it works with idna domains as nickname" do
- nickname = "lain@" <> to_string(:idna.encode("zetsubou.みんな"))
-
- {:ok, _data} = WebFinger.finger(nickname)
- end
-
- test "it works with idna domains as link" do
- ap_id = "https://" <> to_string(:idna.encode("zetsubou.みんな")) <> "/users/lain"
- {:ok, _data} = WebFinger.finger(ap_id)
- end
- end
-end
diff --git a/test/workers/cron/digest_emails_worker_test.exs b/test/workers/cron/digest_emails_worker_test.exs
@@ -1,54 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Workers.Cron.DigestEmailsWorkerTest do
- use Pleroma.DataCase
-
- import Pleroma.Factory
-
- alias Pleroma.Tests.ObanHelpers
- alias Pleroma.User
- alias Pleroma.Web.CommonAPI
-
- setup do: clear_config([:email_notifications, :digest])
-
- setup do
- Pleroma.Config.put([:email_notifications, :digest], %{
- active: true,
- inactivity_threshold: 7,
- interval: 7
- })
-
- user = insert(:user)
-
- date =
- Timex.now()
- |> Timex.shift(days: -10)
- |> Timex.to_naive_datetime()
-
- user2 = insert(:user, last_digest_emailed_at: date)
- {:ok, _} = User.switch_email_notifications(user2, "digest", true)
- CommonAPI.post(user, %{status: "hey @#{user2.nickname}!"})
-
- {:ok, user2: user2}
- end
-
- test "it sends digest emails", %{user2: user2} do
- Pleroma.Workers.Cron.DigestEmailsWorker.perform(%Oban.Job{})
- # Performing job(s) enqueued at previous step
- ObanHelpers.perform_all()
-
- assert_received {:email, email}
- assert email.to == [{user2.name, user2.email}]
- assert email.subject == "Your digest from #{Pleroma.Config.get(:instance)[:name]}"
- end
-
- test "it doesn't fail when a user has no email", %{user2: user2} do
- {:ok, _} = user2 |> Ecto.Changeset.change(%{email: nil}) |> Pleroma.Repo.update()
-
- Pleroma.Workers.Cron.DigestEmailsWorker.perform(%Oban.Job{})
- # Performing job(s) enqueued at previous step
- ObanHelpers.perform_all()
- end
-end
diff --git a/test/workers/cron/new_users_digest_worker_test.exs b/test/workers/cron/new_users_digest_worker_test.exs
@@ -1,45 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Workers.Cron.NewUsersDigestWorkerTest do
- use Pleroma.DataCase
- import Pleroma.Factory
-
- alias Pleroma.Tests.ObanHelpers
- alias Pleroma.Web.CommonAPI
- alias Pleroma.Workers.Cron.NewUsersDigestWorker
-
- test "it sends new users digest emails" do
- yesterday = NaiveDateTime.utc_now() |> Timex.shift(days: -1)
- admin = insert(:user, %{is_admin: true})
- user = insert(:user, %{inserted_at: yesterday})
- user2 = insert(:user, %{inserted_at: yesterday})
- CommonAPI.post(user, %{status: "cofe"})
-
- NewUsersDigestWorker.perform(%Oban.Job{})
- ObanHelpers.perform_all()
-
- assert_received {:email, email}
- assert email.to == [{admin.name, admin.email}]
- assert email.subject == "#{Pleroma.Config.get([:instance, :name])} New Users"
-
- refute email.html_body =~ admin.nickname
- assert email.html_body =~ user.nickname
- assert email.html_body =~ user2.nickname
- assert email.html_body =~ "cofe"
- assert email.html_body =~ "#{Pleroma.Web.Endpoint.url()}/static/logo.png"
- end
-
- test "it doesn't fail when admin has no email" do
- yesterday = NaiveDateTime.utc_now() |> Timex.shift(days: -1)
- insert(:user, %{is_admin: true, email: nil})
- insert(:user, %{inserted_at: yesterday})
- user = insert(:user, %{inserted_at: yesterday})
-
- CommonAPI.post(user, %{status: "cofe"})
-
- NewUsersDigestWorker.perform(%Oban.Job{})
- ObanHelpers.perform_all()
- end
-end
diff --git a/test/workers/scheduled_activity_worker_test.exs b/test/workers/scheduled_activity_worker_test.exs
@@ -1,49 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Workers.ScheduledActivityWorkerTest do
- use Pleroma.DataCase
-
- alias Pleroma.ScheduledActivity
- alias Pleroma.Workers.ScheduledActivityWorker
-
- import Pleroma.Factory
- import ExUnit.CaptureLog
-
- setup do: clear_config([ScheduledActivity, :enabled])
-
- test "creates a status from the scheduled activity" do
- Pleroma.Config.put([ScheduledActivity, :enabled], true)
- user = insert(:user)
-
- naive_datetime =
- NaiveDateTime.add(
- NaiveDateTime.utc_now(),
- -:timer.minutes(2),
- :millisecond
- )
-
- scheduled_activity =
- insert(
- :scheduled_activity,
- scheduled_at: naive_datetime,
- user: user,
- params: %{status: "hi"}
- )
-
- ScheduledActivityWorker.perform(%Oban.Job{args: %{"activity_id" => scheduled_activity.id}})
-
- refute Repo.get(ScheduledActivity, scheduled_activity.id)
- activity = Repo.all(Pleroma.Activity) |> Enum.find(&(&1.actor == user.ap_id))
- assert Pleroma.Object.normalize(activity).data["content"] == "hi"
- end
-
- test "adds log message if ScheduledActivity isn't find" do
- Pleroma.Config.put([ScheduledActivity, :enabled], true)
-
- assert capture_log([level: :error], fn ->
- ScheduledActivityWorker.perform(%Oban.Job{args: %{"activity_id" => 42}})
- end) =~ "Couldn't find scheduled activity"
- end
-end
diff --git a/test/xml_builder_test.exs b/test/xml_builder_test.exs
@@ -1,65 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.XmlBuilderTest do
- use Pleroma.DataCase
- alias Pleroma.XmlBuilder
-
- test "Build a basic xml string from a tuple" do
- data = {:feed, %{xmlns: "http://www.w3.org/2005/Atom"}, "Some content"}
-
- expected_xml = "<feed xmlns=\"http://www.w3.org/2005/Atom\">Some content</feed>"
-
- assert XmlBuilder.to_xml(data) == expected_xml
- end
-
- test "returns a complete document" do
- data = {:feed, %{xmlns: "http://www.w3.org/2005/Atom"}, "Some content"}
-
- expected_xml =
- "<?xml version=\"1.0\" encoding=\"UTF-8\"?><feed xmlns=\"http://www.w3.org/2005/Atom\">Some content</feed>"
-
- assert XmlBuilder.to_doc(data) == expected_xml
- end
-
- test "Works without attributes" do
- data = {
- :feed,
- "Some content"
- }
-
- expected_xml = "<feed>Some content</feed>"
-
- assert XmlBuilder.to_xml(data) == expected_xml
- end
-
- test "It works with nested tuples" do
- data = {
- :feed,
- [
- {:guy, "brush"},
- {:lament, %{configuration: "puzzle"}, "pinhead"}
- ]
- }
-
- expected_xml =
- ~s[<feed><guy>brush</guy><lament configuration="puzzle">pinhead</lament></feed>]
-
- assert XmlBuilder.to_xml(data) == expected_xml
- end
-
- test "Represents NaiveDateTime as iso8601" do
- assert XmlBuilder.to_xml(~N[2000-01-01 13:13:33]) == "2000-01-01T13:13:33"
- end
-
- test "Uses self-closing tags when no content is giving" do
- data = {
- :link,
- %{rel: "self"}
- }
-
- expected_xml = ~s[<link rel="self" />]
- assert XmlBuilder.to_xml(data) == expected_xml
- end
-end