aboutsummaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
Diffstat (limited to 'test')
-rw-r--r--test/integration/mastodon_websocket_test.exs7
-rw-r--r--test/mfa/backup_codes_test.exs11
-rw-r--r--test/mfa/totp_test.exs17
-rw-r--r--test/mfa_test.exs53
-rw-r--r--test/notification_test.exs18
-rw-r--r--test/plugs/ensure_authenticated_plug_test.exs25
-rw-r--r--test/support/builders/activity_builder.ex10
-rw-r--r--test/support/builders/user_builder.ex1
-rw-r--r--test/support/conn_case.ex6
-rw-r--r--test/support/data_case.ex6
-rw-r--r--test/support/factory.ex12
-rw-r--r--test/user_search_test.exs1
-rw-r--r--test/web/admin_api/admin_api_controller_test.exs33
-rw-r--r--test/web/auth/pleroma_authenticator_test.exs43
-rw-r--r--test/web/auth/totp_authenticator_test.exs51
-rw-r--r--test/web/mastodon_api/controllers/poll_controller_test.exs38
-rw-r--r--test/web/oauth/mfa_controller_test.exs306
-rw-r--r--test/web/oauth/oauth_controller_test.exs77
-rw-r--r--test/web/pleroma_api/controllers/two_factor_authentication_controller_test.exs260
-rw-r--r--test/web/streamer/ping_test.exs36
-rw-r--r--test/web/streamer/state_test.exs54
-rw-r--r--test/web/streamer/streamer_test.exs594
-rw-r--r--test/web/twitter_api/remote_follow_controller_test.exs116
23 files changed, 1274 insertions, 501 deletions
diff --git a/test/integration/mastodon_websocket_test.exs b/test/integration/mastodon_websocket_test.exs
index bd229c55f..109c7b4cb 100644
--- a/test/integration/mastodon_websocket_test.exs
+++ b/test/integration/mastodon_websocket_test.exs
@@ -12,17 +12,14 @@ defmodule Pleroma.Integration.MastodonWebsocketTest do
alias Pleroma.Web.CommonAPI
alias Pleroma.Web.OAuth
+ @moduletag needs_streamer: true, capture_log: true
+
@path Pleroma.Web.Endpoint.url()
|> URI.parse()
|> Map.put(:scheme, "ws")
|> Map.put(:path, "/api/v1/streaming")
|> URI.to_string()
- setup_all do
- start_supervised(Pleroma.Web.Streamer.supervisor())
- :ok
- end
-
def start_socket(qs \\ nil, headers \\ []) do
path =
case qs do
diff --git a/test/mfa/backup_codes_test.exs b/test/mfa/backup_codes_test.exs
new file mode 100644
index 000000000..7bc01b36b
--- /dev/null
+++ b/test/mfa/backup_codes_test.exs
@@ -0,0 +1,11 @@
+defmodule Pleroma.MFA.BackupCodesTest do
+ use Pleroma.DataCase
+
+ alias Pleroma.MFA.BackupCodes
+
+ test "generate backup codes" do
+ codes = BackupCodes.generate(number_of_codes: 2, length: 4)
+
+ assert [<<_::bytes-size(4)>>, <<_::bytes-size(4)>>] = codes
+ end
+end
diff --git a/test/mfa/totp_test.exs b/test/mfa/totp_test.exs
new file mode 100644
index 000000000..50153d208
--- /dev/null
+++ b/test/mfa/totp_test.exs
@@ -0,0 +1,17 @@
+defmodule Pleroma.MFA.TOTPTest do
+ use Pleroma.DataCase
+
+ alias Pleroma.MFA.TOTP
+
+ test "create provisioning_uri to generate qrcode" do
+ uri =
+ TOTP.provisioning_uri("test-secrcet", "test@example.com",
+ issuer: "Plerome-42",
+ digits: 8,
+ period: 60
+ )
+
+ assert uri ==
+ "otpauth://totp/test@example.com?digits=8&issuer=Plerome-42&period=60&secret=test-secrcet"
+ end
+end
diff --git a/test/mfa_test.exs b/test/mfa_test.exs
new file mode 100644
index 000000000..94bc48c26
--- /dev/null
+++ b/test/mfa_test.exs
@@ -0,0 +1,53 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2018 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.MFATest do
+ use Pleroma.DataCase
+
+ import Pleroma.Factory
+ alias Comeonin.Pbkdf2
+ alias Pleroma.MFA
+
+ describe "mfa_settings" do
+ test "returns settings user's" do
+ user =
+ insert(:user,
+ multi_factor_authentication_settings: %MFA.Settings{
+ enabled: true,
+ totp: %MFA.Settings.TOTP{secret: "xx", confirmed: true}
+ }
+ )
+
+ settings = MFA.mfa_settings(user)
+ assert match?(^settings, %{enabled: true, totp: true})
+ end
+ end
+
+ describe "generate backup codes" do
+ test "returns backup codes" do
+ user = insert(:user)
+
+ {:ok, [code1, code2]} = MFA.generate_backup_codes(user)
+ updated_user = refresh_record(user)
+ [hash1, hash2] = updated_user.multi_factor_authentication_settings.backup_codes
+ assert Pbkdf2.checkpw(code1, hash1)
+ assert Pbkdf2.checkpw(code2, hash2)
+ end
+ end
+
+ describe "invalidate_backup_code" do
+ test "invalid used code" do
+ user = insert(:user)
+
+ {:ok, _} = MFA.generate_backup_codes(user)
+ user = refresh_record(user)
+ assert length(user.multi_factor_authentication_settings.backup_codes) == 2
+ [hash_code | _] = user.multi_factor_authentication_settings.backup_codes
+
+ {:ok, user} = MFA.invalidate_backup_code(user, hash_code)
+
+ assert length(user.multi_factor_authentication_settings.backup_codes) == 1
+ end
+ end
+end
diff --git a/test/notification_test.exs b/test/notification_test.exs
index 601a6c0ca..5c85f3368 100644
--- a/test/notification_test.exs
+++ b/test/notification_test.exs
@@ -162,14 +162,18 @@ defmodule Pleroma.NotificationTest do
@tag needs_streamer: true
test "it creates a notification for user and send to the 'user' and the 'user:notification' stream" do
user = insert(:user)
- task = Task.async(fn -> assert_receive {:text, _}, 4_000 end)
- task_user_notification = Task.async(fn -> assert_receive {:text, _}, 4_000 end)
- Streamer.add_socket("user", %{transport_pid: task.pid, assigns: %{user: user}})
- Streamer.add_socket(
- "user:notification",
- %{transport_pid: task_user_notification.pid, assigns: %{user: user}}
- )
+ task =
+ Task.async(fn ->
+ Streamer.add_socket("user", user)
+ assert_receive {:render_with_user, _, _, _}, 4_000
+ end)
+
+ task_user_notification =
+ Task.async(fn ->
+ Streamer.add_socket("user:notification", user)
+ assert_receive {:render_with_user, _, _, _}, 4_000
+ end)
activity = insert(:note_activity)
diff --git a/test/plugs/ensure_authenticated_plug_test.exs b/test/plugs/ensure_authenticated_plug_test.exs
index 4e6142aab..a0667c5e0 100644
--- a/test/plugs/ensure_authenticated_plug_test.exs
+++ b/test/plugs/ensure_authenticated_plug_test.exs
@@ -24,6 +24,31 @@ defmodule Pleroma.Plugs.EnsureAuthenticatedPlugTest do
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
%{
diff --git a/test/support/builders/activity_builder.ex b/test/support/builders/activity_builder.ex
index 6e5a8e059..7c4950bfa 100644
--- a/test/support/builders/activity_builder.ex
+++ b/test/support/builders/activity_builder.ex
@@ -21,7 +21,15 @@ defmodule Pleroma.Builders.ActivityBuilder do
def insert(data \\ %{}, opts \\ %{}) do
activity = build(data, opts)
- ActivityPub.insert(activity)
+
+ case ActivityPub.insert(activity) do
+ ok = {:ok, activity} ->
+ ActivityPub.notify_and_stream(activity)
+ ok
+
+ error ->
+ error
+ end
end
def insert_list(times, data \\ %{}, opts \\ %{}) do
diff --git a/test/support/builders/user_builder.ex b/test/support/builders/user_builder.ex
index fcfea666f..0d0490714 100644
--- a/test/support/builders/user_builder.ex
+++ b/test/support/builders/user_builder.ex
@@ -11,6 +11,7 @@ defmodule Pleroma.Builders.UserBuilder do
bio: "A tester.",
ap_id: "some id",
last_digest_emailed_at: NaiveDateTime.truncate(NaiveDateTime.utc_now(), :second),
+ multi_factor_authentication_settings: %Pleroma.MFA.Settings{},
notification_settings: %Pleroma.User.NotificationSetting{}
}
diff --git a/test/support/conn_case.ex b/test/support/conn_case.ex
index 91c03b1a8..b23918dd1 100644
--- a/test/support/conn_case.ex
+++ b/test/support/conn_case.ex
@@ -139,7 +139,11 @@ defmodule Pleroma.Web.ConnCase do
end
if tags[:needs_streamer] do
- start_supervised(Pleroma.Web.Streamer.supervisor())
+ start_supervised(%{
+ id: Pleroma.Web.Streamer.registry(),
+ start:
+ {Registry, :start_link, [[keys: :duplicate, name: Pleroma.Web.Streamer.registry()]]}
+ })
end
{:ok, conn: Phoenix.ConnTest.build_conn()}
diff --git a/test/support/data_case.ex b/test/support/data_case.ex
index 1669f2520..ba8848952 100644
--- a/test/support/data_case.ex
+++ b/test/support/data_case.ex
@@ -40,7 +40,11 @@ defmodule Pleroma.DataCase do
end
if tags[:needs_streamer] do
- start_supervised(Pleroma.Web.Streamer.supervisor())
+ start_supervised(%{
+ id: Pleroma.Web.Streamer.registry(),
+ start:
+ {Registry, :start_link, [[keys: :duplicate, name: Pleroma.Web.Streamer.registry()]]}
+ })
end
:ok
diff --git a/test/support/factory.ex b/test/support/factory.ex
index 495764782..c8c45e2a7 100644
--- a/test/support/factory.ex
+++ b/test/support/factory.ex
@@ -33,7 +33,8 @@ defmodule Pleroma.Factory do
bio: sequence(:bio, &"Tester Number #{&1}"),
last_digest_emailed_at: NaiveDateTime.utc_now(),
last_refreshed_at: NaiveDateTime.utc_now(),
- notification_settings: %Pleroma.User.NotificationSetting{}
+ notification_settings: %Pleroma.User.NotificationSetting{},
+ multi_factor_authentication_settings: %Pleroma.MFA.Settings{}
}
%{
@@ -422,4 +423,13 @@ defmodule Pleroma.Factory do
last_read_id: "1"
}
end
+
+ def mfa_token_factory do
+ %Pleroma.MFA.Token{
+ token: :crypto.strong_rand_bytes(32) |> Base.url_encode64(padding: false),
+ authorization: build(:oauth_authorization),
+ valid_until: NaiveDateTime.add(NaiveDateTime.utc_now(), 60 * 10),
+ user: build(:user)
+ }
+ end
end
diff --git a/test/user_search_test.exs b/test/user_search_test.exs
index cb847b516..17c63322a 100644
--- a/test/user_search_test.exs
+++ b/test/user_search_test.exs
@@ -172,6 +172,7 @@ defmodule Pleroma.UserSearchTest do
|> Map.put(:search_rank, nil)
|> Map.put(:search_type, nil)
|> Map.put(:last_digest_emailed_at, nil)
+ |> Map.put(:multi_factor_authentication_settings, nil)
|> Map.put(:notification_settings, nil)
assert user == expected
diff --git a/test/web/admin_api/admin_api_controller_test.exs b/test/web/admin_api/admin_api_controller_test.exs
index 7ab7cc15c..4697af50e 100644
--- a/test/web/admin_api/admin_api_controller_test.exs
+++ b/test/web/admin_api/admin_api_controller_test.exs
@@ -14,6 +14,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
alias Pleroma.Config
alias Pleroma.ConfigDB
alias Pleroma.HTML
+ alias Pleroma.MFA
alias Pleroma.ModerationLog
alias Pleroma.Repo
alias Pleroma.ReportNote
@@ -1278,6 +1279,38 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
"@#{admin.nickname} deactivated users: @#{user.nickname}"
end
+ describe "PUT disable_mfa" do
+ test "returns 200 and disable 2fa", %{conn: conn} do
+ user =
+ insert(:user,
+ multi_factor_authentication_settings: %MFA.Settings{
+ enabled: true,
+ totp: %MFA.Settings.TOTP{secret: "otp_secret", confirmed: true}
+ }
+ )
+
+ response =
+ conn
+ |> put("/api/pleroma/admin/users/disable_mfa", %{nickname: user.nickname})
+ |> json_response(200)
+
+ assert response == user.nickname
+ mfa_settings = refresh_record(user).multi_factor_authentication_settings
+
+ refute mfa_settings.enabled
+ refute mfa_settings.totp.confirmed
+ end
+
+ test "returns 404 if user not found", %{conn: conn} do
+ response =
+ conn
+ |> put("/api/pleroma/admin/users/disable_mfa", %{nickname: "nickname"})
+ |> json_response(404)
+
+ assert response == "Not found"
+ end
+ end
+
describe "POST /api/pleroma/admin/users/invite_token" do
test "without options", %{conn: conn} do
conn = post(conn, "/api/pleroma/admin/users/invite_token")
diff --git a/test/web/auth/pleroma_authenticator_test.exs b/test/web/auth/pleroma_authenticator_test.exs
new file mode 100644
index 000000000..7125c5081
--- /dev/null
+++ b/test/web/auth/pleroma_authenticator_test.exs
@@ -0,0 +1,43 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.Auth.PleromaAuthenticatorTest do
+ use Pleroma.Web.ConnCase
+
+ alias Pleroma.Web.Auth.PleromaAuthenticator
+ import Pleroma.Factory
+
+ setup do
+ password = "testpassword"
+ name = "AgentSmith"
+ user = insert(:user, nickname: name, password_hash: Comeonin.Pbkdf2.hashpwsalt(password))
+ {:ok, [user: user, name: name, password: password]}
+ end
+
+ test "get_user/authorization", %{user: user, name: name, password: password} do
+ params = %{"authorization" => %{"name" => name, "password" => password}}
+ res = PleromaAuthenticator.get_user(%Plug.Conn{params: params})
+
+ assert {:ok, user} == res
+ end
+
+ test "get_user/authorization with invalid password", %{name: name} do
+ params = %{"authorization" => %{"name" => name, "password" => "password"}}
+ res = PleromaAuthenticator.get_user(%Plug.Conn{params: params})
+
+ assert {:error, {:checkpw, false}} == res
+ end
+
+ test "get_user/grant_type_password", %{user: user, name: name, password: password} do
+ params = %{"grant_type" => "password", "username" => name, "password" => password}
+ res = PleromaAuthenticator.get_user(%Plug.Conn{params: params})
+
+ assert {:ok, user} == res
+ end
+
+ test "error credintails" do
+ res = PleromaAuthenticator.get_user(%Plug.Conn{params: %{}})
+ assert {:error, :invalid_credentials} == res
+ end
+end
diff --git a/test/web/auth/totp_authenticator_test.exs b/test/web/auth/totp_authenticator_test.exs
new file mode 100644
index 000000000..e08069490
--- /dev/null
+++ b/test/web/auth/totp_authenticator_test.exs
@@ -0,0 +1,51 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.Auth.TOTPAuthenticatorTest do
+ use Pleroma.Web.ConnCase
+
+ alias Pleroma.MFA
+ alias Pleroma.MFA.BackupCodes
+ alias Pleroma.MFA.TOTP
+ alias Pleroma.Web.Auth.TOTPAuthenticator
+
+ import Pleroma.Factory
+
+ test "verify token" do
+ otp_secret = TOTP.generate_secret()
+ otp_token = TOTP.generate_token(otp_secret)
+
+ user =
+ insert(:user,
+ multi_factor_authentication_settings: %MFA.Settings{
+ enabled: true,
+ totp: %MFA.Settings.TOTP{secret: otp_secret, confirmed: true}
+ }
+ )
+
+ assert TOTPAuthenticator.verify(otp_token, user) == {:ok, :pass}
+ assert TOTPAuthenticator.verify(nil, user) == {:error, :invalid_token}
+ assert TOTPAuthenticator.verify("", user) == {:error, :invalid_token}
+ end
+
+ test "checks backup codes" do
+ [code | _] = backup_codes = BackupCodes.generate()
+
+ hashed_codes =
+ backup_codes
+ |> Enum.map(&Comeonin.Pbkdf2.hashpwsalt(&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}
+ }
+ )
+
+ assert TOTPAuthenticator.verify_recovery_code(user, code) == {:ok, :pass}
+ refute TOTPAuthenticator.verify_recovery_code(code, refresh_record(user)) == {:ok, :pass}
+ end
+end
diff --git a/test/web/mastodon_api/controllers/poll_controller_test.exs b/test/web/mastodon_api/controllers/poll_controller_test.exs
index 88b13a25a..d8f34aa86 100644
--- a/test/web/mastodon_api/controllers/poll_controller_test.exs
+++ b/test/web/mastodon_api/controllers/poll_controller_test.exs
@@ -24,7 +24,7 @@ defmodule Pleroma.Web.MastodonAPI.PollControllerTest do
conn = get(conn, "/api/v1/polls/#{object.id}")
- response = json_response(conn, 200)
+ response = json_response_and_validate_schema(conn, 200)
id = to_string(object.id)
assert %{"id" => ^id, "expired" => false, "multiple" => false} = response
end
@@ -43,7 +43,7 @@ defmodule Pleroma.Web.MastodonAPI.PollControllerTest do
conn = get(conn, "/api/v1/polls/#{object.id}")
- assert json_response(conn, 404)
+ assert json_response_and_validate_schema(conn, 404)
end
end
@@ -65,9 +65,12 @@ defmodule Pleroma.Web.MastodonAPI.PollControllerTest do
object = Object.normalize(activity)
- conn = post(conn, "/api/v1/polls/#{object.id}/votes", %{"choices" => [0, 1, 2]})
+ conn =
+ conn
+ |> put_req_header("content-type", "application/json")
+ |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0, 1, 2]})
- assert json_response(conn, 200)
+ assert json_response_and_validate_schema(conn, 200)
object = Object.get_by_id(object.id)
assert Enum.all?(object.data["anyOf"], fn %{"replies" => %{"totalItems" => total_items}} ->
@@ -85,8 +88,9 @@ defmodule Pleroma.Web.MastodonAPI.PollControllerTest do
object = Object.normalize(activity)
assert conn
+ |> put_req_header("content-type", "application/json")
|> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [1]})
- |> json_response(422) == %{"error" => "Poll's author can't vote"}
+ |> json_response_and_validate_schema(422) == %{"error" => "Poll's author can't vote"}
object = Object.get_by_id(object.id)
@@ -105,8 +109,9 @@ defmodule Pleroma.Web.MastodonAPI.PollControllerTest do
object = Object.normalize(activity)
assert conn
+ |> put_req_header("content-type", "application/json")
|> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0, 1]})
- |> json_response(422) == %{"error" => "Too many choices"}
+ |> json_response_and_validate_schema(422) == %{"error" => "Too many choices"}
object = Object.get_by_id(object.id)
@@ -126,15 +131,21 @@ defmodule Pleroma.Web.MastodonAPI.PollControllerTest do
object = Object.normalize(activity)
- conn = post(conn, "/api/v1/polls/#{object.id}/votes", %{"choices" => [2]})
+ conn =
+ conn
+ |> put_req_header("content-type", "application/json")
+ |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [2]})
- assert json_response(conn, 422) == %{"error" => "Invalid indices"}
+ assert json_response_and_validate_schema(conn, 422) == %{"error" => "Invalid indices"}
end
test "returns 404 error when object is not exist", %{conn: conn} do
- conn = post(conn, "/api/v1/polls/1/votes", %{"choices" => [0]})
+ conn =
+ conn
+ |> put_req_header("content-type", "application/json")
+ |> post("/api/v1/polls/1/votes", %{"choices" => [0]})
- assert json_response(conn, 404) == %{"error" => "Record not found"}
+ assert json_response_and_validate_schema(conn, 404) == %{"error" => "Record not found"}
end
test "returns 404 when poll is private and not available for user", %{conn: conn} do
@@ -149,9 +160,12 @@ defmodule Pleroma.Web.MastodonAPI.PollControllerTest do
object = Object.normalize(activity)
- conn = post(conn, "/api/v1/polls/#{object.id}/votes", %{"choices" => [0]})
+ conn =
+ conn
+ |> put_req_header("content-type", "application/json")
+ |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0]})
- assert json_response(conn, 404) == %{"error" => "Record not found"}
+ assert json_response_and_validate_schema(conn, 404) == %{"error" => "Record not found"}
end
end
end
diff --git a/test/web/oauth/mfa_controller_test.exs b/test/web/oauth/mfa_controller_test.exs
new file mode 100644
index 000000000..ce4a07320
--- /dev/null
+++ b/test/web/oauth/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: [Comeonin.Pbkdf2.hashpwsalt("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(&Comeonin.Pbkdf2.hashpwsalt(&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
index f2f98d768..7a107584d 100644
--- a/test/web/oauth/oauth_controller_test.exs
+++ b/test/web/oauth/oauth_controller_test.exs
@@ -6,6 +6,8 @@ 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
@@ -604,6 +606,41 @@ defmodule Pleroma.Web.OAuth.OAuthControllerTest do
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)
@@ -735,6 +772,46 @@ defmodule Pleroma.Web.OAuth.OAuthControllerTest do
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: Comeonin.Pbkdf2.hashpwsalt(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"])
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
new file mode 100644
index 000000000..d23d08a00
--- /dev/null
+++ b/test/web/pleroma_api/controllers/two_factor_authentication_controller_test.exs
@@ -0,0 +1,260 @@
+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/streamer/ping_test.exs b/test/web/streamer/ping_test.exs
deleted file mode 100644
index 5df6c1cc3..000000000
--- a/test/web/streamer/ping_test.exs
+++ /dev/null
@@ -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.PingTest do
- use Pleroma.DataCase
-
- import Pleroma.Factory
- alias Pleroma.Web.Streamer
-
- setup do
- start_supervised({Streamer.supervisor(), [ping_interval: 30]})
-
- :ok
- end
-
- describe "sockets" do
- setup do
- user = insert(:user)
- {:ok, %{user: user}}
- end
-
- test "it sends pings", %{user: user} do
- task =
- Task.async(fn ->
- assert_receive {:text, received_event}, 40
- assert_receive {:text, received_event}, 40
- assert_receive {:text, received_event}, 40
- end)
-
- Streamer.add_socket("public", %{transport_pid: task.pid, assigns: %{user: user}})
-
- Task.await(task)
- end
- end
-end
diff --git a/test/web/streamer/state_test.exs b/test/web/streamer/state_test.exs
deleted file mode 100644
index a755e75c0..000000000
--- a/test/web/streamer/state_test.exs
+++ /dev/null
@@ -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.Web.StateTest do
- use Pleroma.DataCase
-
- import Pleroma.Factory
- alias Pleroma.Web.Streamer
- alias Pleroma.Web.Streamer.StreamerSocket
-
- @moduletag needs_streamer: true
-
- describe "sockets" do
- setup do
- user = insert(:user)
- user2 = insert(:user)
- {:ok, %{user: user, user2: user2}}
- end
-
- test "it can add a socket", %{user: user} do
- Streamer.add_socket("public", %{transport_pid: 1, assigns: %{user: user}})
-
- assert(%{"public" => [%StreamerSocket{transport_pid: 1}]} = Streamer.get_sockets())
- end
-
- test "it can add multiple sockets per user", %{user: user} do
- Streamer.add_socket("public", %{transport_pid: 1, assigns: %{user: user}})
- Streamer.add_socket("public", %{transport_pid: 2, assigns: %{user: user}})
-
- assert(
- %{
- "public" => [
- %StreamerSocket{transport_pid: 2},
- %StreamerSocket{transport_pid: 1}
- ]
- } = Streamer.get_sockets()
- )
- end
-
- test "it will not add a duplicate socket", %{user: user} do
- Streamer.add_socket("activity", %{transport_pid: 1, assigns: %{user: user}})
- Streamer.add_socket("activity", %{transport_pid: 1, assigns: %{user: user}})
-
- assert(
- %{
- "activity" => [
- %StreamerSocket{transport_pid: 1}
- ]
- } = Streamer.get_sockets()
- )
- end
- end
-end
diff --git a/test/web/streamer/streamer_test.exs b/test/web/streamer/streamer_test.exs
index 3c0f240f5..ee530f4e9 100644
--- a/test/web/streamer/streamer_test.exs
+++ b/test/web/streamer/streamer_test.exs
@@ -12,13 +12,9 @@ defmodule Pleroma.Web.StreamerTest do
alias Pleroma.User
alias Pleroma.Web.CommonAPI
alias Pleroma.Web.Streamer
- alias Pleroma.Web.Streamer.StreamerSocket
- alias Pleroma.Web.Streamer.Worker
@moduletag needs_streamer: true, capture_log: true
- @streamer_timeout 150
- @streamer_start_wait 10
setup do: clear_config([:instance, :skip_thread_containment])
describe "user streams" do
@@ -29,69 +25,35 @@ defmodule Pleroma.Web.StreamerTest do
end
test "it streams the user's post in the 'user' stream", %{user: user} do
- task =
- Task.async(fn ->
- assert_receive {:text, _}, @streamer_timeout
- end)
-
- Streamer.add_socket(
- "user",
- %{transport_pid: task.pid, assigns: %{user: user}}
- )
-
+ Streamer.add_socket("user", user)
{:ok, activity} = CommonAPI.post(user, %{"status" => "hey"})
-
- Streamer.stream("user", activity)
- Task.await(task)
+ 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} do
- task =
- Task.async(fn ->
- assert_receive {:text, _}, @streamer_timeout
- end)
-
- Streamer.add_socket(
- "user",
- %{transport_pid: task.pid, assigns: %{user: user}}
- )
+ Streamer.add_socket("user", user)
other_user = insert(:user)
{:ok, activity} = CommonAPI.post(other_user, %{"status" => "hey"})
{:ok, announce, _} = CommonAPI.repeat(activity.id, user)
- Streamer.stream("user", announce)
- Task.await(task)
+ 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, notify: notify} do
- task =
- Task.async(fn ->
- assert_receive {:text, _}, @streamer_timeout
- end)
-
- Streamer.add_socket(
- "user",
- %{transport_pid: task.pid, assigns: %{user: user}}
- )
-
+ Streamer.add_socket("user", user)
Streamer.stream("user", notify)
- Task.await(task)
+ 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, notify: notify} do
- task =
- Task.async(fn ->
- assert_receive {:text, _}, @streamer_timeout
- end)
-
- Streamer.add_socket(
- "user:notification",
- %{transport_pid: task.pid, assigns: %{user: user}}
- )
-
+ Streamer.add_socket("user:notification", user)
Streamer.stream("user:notification", notify)
- Task.await(task)
+ 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", %{
@@ -100,18 +62,12 @@ defmodule Pleroma.Web.StreamerTest do
blocked = insert(:user)
{:ok, _user_relationship} = User.block(user, blocked)
- task = Task.async(fn -> refute_receive {:text, _}, @streamer_timeout end)
-
- Streamer.add_socket(
- "user:notification",
- %{transport_pid: task.pid, assigns: %{user: user}}
- )
+ Streamer.add_socket("user:notification", user)
{:ok, activity} = CommonAPI.post(user, %{"status" => ":("})
- {:ok, notif} = CommonAPI.favorite(blocked, activity.id)
+ {:ok, _} = CommonAPI.favorite(blocked, activity.id)
- Streamer.stream("user:notification", notif)
- Task.await(task)
+ refute_receive _
end
test "it doesn't send notify to the 'user:notification' stream when a thread is muted", %{
@@ -119,45 +75,50 @@ defmodule Pleroma.Web.StreamerTest do
} do
user2 = insert(:user)
- task = Task.async(fn -> refute_receive {:text, _}, @streamer_timeout end)
+ {:ok, activity} = CommonAPI.post(user, %{"status" => "super hot take"})
+ {:ok, _} = CommonAPI.add_mute(user, activity)
- Streamer.add_socket(
- "user:notification",
- %{transport_pid: task.pid, assigns: %{user: user}}
- )
+ Streamer.add_socket("user:notification", user)
- {:ok, activity} = CommonAPI.post(user, %{"status" => "super hot take"})
- {:ok, activity} = CommonAPI.add_mute(user, activity)
- {:ok, notif} = CommonAPI.favorite(user2, activity.id)
+ {:ok, favorite_activity} = CommonAPI.favorite(user2, activity.id)
- Streamer.stream("user:notification", notif)
- Task.await(task)
+ refute_receive _
+ assert Streamer.filtered_by_user?(user, favorite_activity)
end
- test "it doesn't send notify to the 'user:notification' stream' when a domain is blocked", %{
+ test "it sends favorite to 'user:notification' stream'", %{
user: user
} do
user2 = insert(:user, %{ap_id: "https://hecking-lewd-place.com/user/meanie"})
- task = Task.async(fn -> refute_receive {:text, _}, @streamer_timeout end)
+ {:ok, activity} = CommonAPI.post(user, %{"status" => "super hot take"})
+ Streamer.add_socket("user:notification", user)
+ {: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
- Streamer.add_socket(
- "user:notification",
- %{transport_pid: task.pid, assigns: %{user: user}}
- )
+ test "it doesn't send the 'user:notification' stream' when a domain is blocked", %{
+ user: user
+ } 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"})
- {:ok, notif} = CommonAPI.favorite(user2, activity.id)
+ Streamer.add_socket("user:notification", user)
+ {:ok, favorite_activity} = CommonAPI.favorite(user2, activity.id)
- Streamer.stream("user:notification", notif)
- Task.await(task)
+ refute_receive _
+ assert Streamer.filtered_by_user?(user, favorite_activity)
end
test "it sends follow activities to the 'user:notification' stream", %{
user: user
} do
user_url = user.ap_id
+ user2 = insert(:user)
body =
File.read!("test/fixtures/users_mock/localhost.json")
@@ -169,47 +130,24 @@ defmodule Pleroma.Web.StreamerTest do
%Tesla.Env{status: 200, body: body}
end)
- user2 = insert(:user)
- task = Task.async(fn -> assert_receive {:text, _}, @streamer_timeout end)
-
- Process.sleep(@streamer_start_wait)
-
- Streamer.add_socket(
- "user:notification",
- %{transport_pid: task.pid, assigns: %{user: user}}
- )
+ Streamer.add_socket("user:notification", user)
+ {:ok, _follower, _followed, follow_activity} = CommonAPI.follow(user2, user)
- {:ok, _follower, _followed, _activity} = CommonAPI.follow(user2, user)
-
- # We don't directly pipe the notification to the streamer as it's already
- # generated as a side effect of CommonAPI.follow().
- Task.await(task)
+ assert_receive {:render_with_user, _, "notification.json", notif}
+ assert notif.activity.id == follow_activity.id
+ refute Streamer.filtered_by_user?(user, notif)
end
end
- test "it sends to public" do
+ test "it sends to public authenticated" do
user = insert(:user)
other_user = insert(:user)
- task =
- Task.async(fn ->
- assert_receive {:text, _}, @streamer_timeout
- end)
-
- fake_socket = %StreamerSocket{
- transport_pid: task.pid,
- user: user
- }
-
- {:ok, activity} = CommonAPI.post(other_user, %{"status" => "Test"})
+ Streamer.add_socket("public", other_user)
- topics = %{
- "public" => [fake_socket]
- }
-
- Worker.push_to_socket(topics, "public", activity)
-
- Task.await(task)
+ {:ok, activity} = CommonAPI.post(user, %{"status" => "Test"})
+ assert_receive {:render_with_user, _, _, ^activity}
+ refute Streamer.filtered_by_user?(user, activity)
end
test "works for deletions" do
@@ -217,37 +155,32 @@ defmodule Pleroma.Web.StreamerTest do
other_user = insert(:user)
{:ok, activity} = CommonAPI.post(other_user, %{"status" => "Test"})
- task =
- Task.async(fn ->
- expected_event =
- %{
- "event" => "delete",
- "payload" => activity.id
- }
- |> Jason.encode!()
-
- assert_receive {:text, received_event}, @streamer_timeout
- assert received_event == expected_event
- end)
+ Streamer.add_socket("public", user)
- fake_socket = %StreamerSocket{
- transport_pid: task.pid,
- user: user
- }
+ {: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
- {:ok, activity} = CommonAPI.delete(activity.id, other_user)
+ test "it sends to public unauthenticated" do
+ user = insert(:user)
- topics = %{
- "public" => [fake_socket]
- }
+ Streamer.add_socket("public", nil)
- Worker.push_to_socket(topics, "public", activity)
+ {: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)
- Task.await(task)
+ {:ok, _} = CommonAPI.delete(activity.id, user)
+ assert_receive {:text, event}
+ assert %{"event" => "delete", "payload" => ^activity_id} = Jason.decode!(event)
end
describe "thread_containment" do
- test "it doesn't send to user if recipients invalid and thread containment is enabled" 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 = insert(:user)
@@ -262,12 +195,10 @@ defmodule Pleroma.Web.StreamerTest do
)
)
- task = Task.async(fn -> refute_receive {:text, _}, 1_000 end)
- fake_socket = %StreamerSocket{transport_pid: task.pid, user: user}
- topics = %{"public" => [fake_socket]}
- Worker.push_to_socket(topics, "public", activity)
-
- Task.await(task)
+ Streamer.add_socket("public", user)
+ 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
@@ -285,12 +216,11 @@ defmodule Pleroma.Web.StreamerTest do
)
)
- task = Task.async(fn -> assert_receive {:text, _}, 1_000 end)
- fake_socket = %StreamerSocket{transport_pid: task.pid, user: user}
- topics = %{"public" => [fake_socket]}
- Worker.push_to_socket(topics, "public", activity)
+ Streamer.add_socket("public", user)
+ Streamer.stream("public", activity)
- Task.await(task)
+ 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
@@ -308,255 +238,168 @@ defmodule Pleroma.Web.StreamerTest do
)
)
- task = Task.async(fn -> assert_receive {:text, _}, 1_000 end)
- fake_socket = %StreamerSocket{transport_pid: task.pid, user: user}
- topics = %{"public" => [fake_socket]}
- Worker.push_to_socket(topics, "public", activity)
+ Streamer.add_socket("public", user)
+ Streamer.stream("public", activity)
- Task.await(task)
+ assert_receive {:render_with_user, _, _, ^activity}
+ refute Streamer.filtered_by_user?(user, activity)
end
end
describe "blocks" do
- test "it doesn't send messages involving blocked users" do
+ test "it filters messages involving blocked users" do
user = insert(:user)
blocked_user = insert(:user)
{:ok, _user_relationship} = User.block(user, blocked_user)
+ Streamer.add_socket("public", user)
{:ok, activity} = CommonAPI.post(blocked_user, %{"status" => "Test"})
-
- task =
- Task.async(fn ->
- refute_receive {:text, _}, 1_000
- end)
-
- fake_socket = %StreamerSocket{
- transport_pid: task.pid,
- user: user
- }
-
- topics = %{
- "public" => [fake_socket]
- }
-
- Worker.push_to_socket(topics, "public", activity)
-
- Task.await(task)
+ assert_receive {:render_with_user, _, _, ^activity}
+ assert Streamer.filtered_by_user?(user, activity)
end
- test "it doesn't send messages transitively involving blocked users" do
+ test "it filters messages transitively involving blocked users" do
blocker = insert(:user)
blockee = insert(:user)
friend = insert(:user)
- task =
- Task.async(fn ->
- refute_receive {:text, _}, 1_000
- end)
-
- fake_socket = %StreamerSocket{
- transport_pid: task.pid,
- user: blocker
- }
-
- topics = %{
- "public" => [fake_socket]
- }
+ Streamer.add_socket("public", blocker)
{:ok, _user_relationship} = User.block(blocker, blockee)
{:ok, activity_one} = CommonAPI.post(friend, %{"status" => "hey! @#{blockee.nickname}"})
- Worker.push_to_socket(topics, "public", activity_one)
+ 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}"})
- Worker.push_to_socket(topics, "public", activity_two)
+ 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}"})
- Worker.push_to_socket(topics, "public", activity_three)
-
- Task.await(task)
+ assert_receive {:render_with_user, _, _, ^activity_three}
+ assert Streamer.filtered_by_user?(blocker, activity_three)
end
end
- test "it doesn't send unwanted DMs to list" do
- user_a = insert(:user)
- 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)
-
- {:ok, activity} =
- CommonAPI.post(user_b, %{
- "status" => "@#{user_c.nickname} Test",
- "visibility" => "direct"
- })
-
- task =
- Task.async(fn ->
- refute_receive {:text, _}, 1_000
- end)
-
- fake_socket = %StreamerSocket{
- transport_pid: task.pid,
- user: user_a
- }
-
- topics = %{
- "list:#{list.id}" => [fake_socket]
- }
-
- Worker.handle_call({:stream, "list", activity}, self(), topics)
-
- Task.await(task)
- end
-
- test "it doesn't send unwanted private posts to list" do
- user_a = insert(:user)
- user_b = insert(:user)
+ describe "lists" do
+ test "it doesn't send unwanted DMs to list" do
+ user_a = insert(:user)
+ user_b = insert(:user)
+ user_c = insert(:user)
- {:ok, list} = List.create("Test", user_a)
- {:ok, list} = List.follow(list, user_b)
+ {:ok, user_a} = User.follow(user_a, user_b)
- {:ok, activity} =
- CommonAPI.post(user_b, %{
- "status" => "Test",
- "visibility" => "private"
- })
+ {:ok, list} = List.create("Test", user_a)
+ {:ok, list} = List.follow(list, user_b)
- task =
- Task.async(fn ->
- refute_receive {:text, _}, 1_000
- end)
+ Streamer.add_socket("list:#{list.id}", user_a)
- fake_socket = %StreamerSocket{
- transport_pid: task.pid,
- user: user_a
- }
+ {:ok, _activity} =
+ CommonAPI.post(user_b, %{
+ "status" => "@#{user_c.nickname} Test",
+ "visibility" => "direct"
+ })
- topics = %{
- "list:#{list.id}" => [fake_socket]
- }
+ refute_receive _
+ end
- Worker.handle_call({:stream, "list", activity}, self(), topics)
+ test "it doesn't send unwanted private posts to list" do
+ user_a = insert(:user)
+ user_b = insert(:user)
- Task.await(task)
- end
+ {:ok, list} = List.create("Test", user_a)
+ {:ok, list} = List.follow(list, user_b)
- test "it sends wanted private posts to list" do
- user_a = insert(:user)
- user_b = insert(:user)
+ Streamer.add_socket("list:#{list.id}", user_a)
- {:ok, user_a} = User.follow(user_a, user_b)
+ {:ok, _activity} =
+ CommonAPI.post(user_b, %{
+ "status" => "Test",
+ "visibility" => "private"
+ })
- {:ok, list} = List.create("Test", user_a)
- {:ok, list} = List.follow(list, user_b)
+ refute_receive _
+ end
- {:ok, activity} =
- CommonAPI.post(user_b, %{
- "status" => "Test",
- "visibility" => "private"
- })
+ test "it sends wanted private posts to list" do
+ user_a = insert(:user)
+ user_b = insert(:user)
- task =
- Task.async(fn ->
- assert_receive {:text, _}, 1_000
- end)
+ {:ok, user_a} = User.follow(user_a, user_b)
- fake_socket = %StreamerSocket{
- transport_pid: task.pid,
- user: user_a
- }
+ {:ok, list} = List.create("Test", user_a)
+ {:ok, list} = List.follow(list, user_b)
- Streamer.add_socket(
- "list:#{list.id}",
- fake_socket
- )
+ Streamer.add_socket("list:#{list.id}", user_a)
- Worker.handle_call({:stream, "list", activity}, self(), %{})
+ {:ok, activity} =
+ CommonAPI.post(user_b, %{
+ "status" => "Test",
+ "visibility" => "private"
+ })
- Task.await(task)
+ assert_receive {:render_with_user, _, _, ^activity}
+ refute Streamer.filtered_by_user?(user_a, activity)
+ end
end
- test "it doesn't send muted reblogs" do
- user1 = insert(:user)
- user2 = insert(:user)
- user3 = insert(:user)
- CommonAPI.hide_reblogs(user1, user2)
-
- {:ok, create_activity} = CommonAPI.post(user3, %{"status" => "I'm kawen"})
- {:ok, announce_activity, _} = CommonAPI.repeat(create_activity.id, user2)
-
- task =
- Task.async(fn ->
- refute_receive {:text, _}, 1_000
- end)
-
- fake_socket = %StreamerSocket{
- transport_pid: task.pid,
- user: user1
- }
-
- topics = %{
- "public" => [fake_socket]
- }
-
- Worker.push_to_socket(topics, "public", announce_activity)
+ describe "muted reblogs" do
+ test "it filters muted reblogs" do
+ user1 = insert(:user)
+ user2 = insert(:user)
+ user3 = insert(:user)
+ CommonAPI.follow(user1, user2)
+ CommonAPI.hide_reblogs(user1, user2)
- Task.await(task)
- end
+ {:ok, create_activity} = CommonAPI.post(user3, %{"status" => "I'm kawen"})
- test "it does send non-reblog notification for reblog-muted actors" do
- user1 = insert(:user)
- user2 = insert(:user)
- user3 = insert(:user)
- CommonAPI.hide_reblogs(user1, user2)
+ Streamer.add_socket("user", user1)
+ {: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
- {:ok, create_activity} = CommonAPI.post(user3, %{"status" => "I'm kawen"})
- {:ok, favorite_activity} = CommonAPI.favorite(user2, create_activity.id)
+ test "it filters reblog notification for reblog-muted actors" do
+ user1 = insert(:user)
+ user2 = insert(:user)
+ CommonAPI.follow(user1, user2)
+ CommonAPI.hide_reblogs(user1, user2)
- task =
- Task.async(fn ->
- assert_receive {:text, _}, 1_000
- end)
+ {:ok, create_activity} = CommonAPI.post(user1, %{"status" => "I'm kawen"})
+ Streamer.add_socket("user", user1)
+ {:ok, _favorite_activity, _} = CommonAPI.repeat(create_activity.id, user2)
- fake_socket = %StreamerSocket{
- transport_pid: task.pid,
- user: user1
- }
+ assert_receive {:render_with_user, _, "notification.json", notif}
+ assert Streamer.filtered_by_user?(user1, notif)
+ end
- topics = %{
- "public" => [fake_socket]
- }
+ test "it send non-reblog notification for reblog-muted actors" do
+ user1 = insert(:user)
+ user2 = insert(:user)
+ CommonAPI.follow(user1, user2)
+ CommonAPI.hide_reblogs(user1, user2)
- Worker.push_to_socket(topics, "public", favorite_activity)
+ {:ok, create_activity} = CommonAPI.post(user1, %{"status" => "I'm kawen"})
+ Streamer.add_socket("user", user1)
+ {:ok, _favorite_activity} = CommonAPI.favorite(user2, create_activity.id)
- Task.await(task)
+ assert_receive {:render_with_user, _, "notification.json", notif}
+ refute Streamer.filtered_by_user?(user1, notif)
+ end
end
- test "it doesn't send posts from muted threads" do
+ test "it filters posts from muted threads" do
user = insert(:user)
user2 = insert(:user)
+ Streamer.add_socket("user", user2)
{:ok, user2, user, _activity} = CommonAPI.follow(user2, user)
-
{:ok, activity} = CommonAPI.post(user, %{"status" => "super hot take"})
-
- {:ok, activity} = CommonAPI.add_mute(user2, activity)
-
- task = Task.async(fn -> refute_receive {:text, _}, @streamer_timeout end)
-
- Streamer.add_socket(
- "user",
- %{transport_pid: task.pid, assigns: %{user: user2}}
- )
-
- Streamer.stream("user", activity)
- Task.await(task)
+ {:ok, _} = CommonAPI.add_mute(user2, activity)
+ assert_receive {:render_with_user, _, _, ^activity}
+ assert Streamer.filtered_by_user?(user2, activity)
end
describe "direct streams" do
@@ -568,22 +411,7 @@ defmodule Pleroma.Web.StreamerTest do
user = insert(:user)
another_user = insert(:user)
- task =
- Task.async(fn ->
- assert_receive {:text, received_event}, @streamer_timeout
-
- 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)
-
- Streamer.add_socket(
- "direct",
- %{transport_pid: task.pid, assigns: %{user: user}}
- )
+ Streamer.add_socket("direct", user)
{:ok, _create_activity} =
CommonAPI.post(another_user, %{
@@ -591,42 +419,47 @@ defmodule Pleroma.Web.StreamerTest do
"visibility" => "direct"
})
- Task.await(task)
+ 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" do
user = insert(:user)
another_user = insert(:user)
+ Streamer.add_socket("direct", user)
+
{:ok, create_activity} =
CommonAPI.post(another_user, %{
"status" => "hi @#{user.nickname}",
"visibility" => "direct"
})
- task =
- Task.async(fn ->
- assert_receive {:text, received_event}, @streamer_timeout
- assert %{"event" => "delete", "payload" => _} = Jason.decode!(received_event)
+ 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)
- refute_receive {:text, _}, @streamer_timeout
- end)
+ {:ok, _} = CommonAPI.delete(create_activity_id, another_user)
- Process.sleep(@streamer_start_wait)
+ assert_receive {:text, received_event}
- Streamer.add_socket(
- "direct",
- %{transport_pid: task.pid, assigns: %{user: user}}
- )
+ assert %{"event" => "delete", "payload" => ^create_activity_id} =
+ Jason.decode!(received_event)
- {:ok, _} = CommonAPI.delete(create_activity.id, another_user)
-
- Task.await(task)
+ refute_receive _
end
test "it sends conversation update to the 'direct' stream when a message is deleted" do
user = insert(:user)
another_user = insert(:user)
+ Streamer.add_socket("direct", user)
{:ok, create_activity} =
CommonAPI.post(another_user, %{
@@ -636,35 +469,30 @@ defmodule Pleroma.Web.StreamerTest do
{:ok, create_activity2} =
CommonAPI.post(another_user, %{
- "status" => "hi @#{user.nickname}",
+ "status" => "hi @#{user.nickname} 2",
"in_reply_to_status_id" => create_activity.id,
"visibility" => "direct"
})
- task =
- Task.async(fn ->
- assert_receive {:text, received_event}, @streamer_timeout
- assert %{"event" => "delete", "payload" => _} = Jason.decode!(received_event)
-
- assert_receive {:text, received_event}, @streamer_timeout
+ 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)
- 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)
+ {:ok, _} = CommonAPI.delete(create_activity2.id, another_user)
- Process.sleep(@streamer_start_wait)
+ assert_receive {:text, received_event}
+ assert %{"event" => "delete", "payload" => _} = Jason.decode!(received_event)
- Streamer.add_socket(
- "direct",
- %{transport_pid: task.pid, assigns: %{user: user}}
- )
+ assert_receive {:text, received_event}
- {:ok, _} = CommonAPI.delete(create_activity2.id, another_user)
+ assert %{"event" => "conversation", "payload" => received_payload} =
+ Jason.decode!(received_event)
- Task.await(task)
+ 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/remote_follow_controller_test.exs b/test/web/twitter_api/remote_follow_controller_test.exs
index 5ff8694a8..f7e54c26a 100644
--- a/test/web/twitter_api/remote_follow_controller_test.exs
+++ b/test/web/twitter_api/remote_follow_controller_test.exs
@@ -6,11 +6,14 @@ 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)
@@ -160,6 +163,119 @@ defmodule Pleroma.Web.TwitterAPI.RemoteFollowControllerTest do
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_token(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_token(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)