Merge branch 'develop' into issue/1276-2
This commit is contained in:
commit
b078e0567d
108 changed files with 4417 additions and 2063 deletions
|
|
@ -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
|
||||
|
|
|
|||
11
test/mfa/backup_codes_test.exs
Normal file
11
test/mfa/backup_codes_test.exs
Normal file
|
|
@ -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
|
||||
17
test/mfa/totp_test.exs
Normal file
17
test/mfa/totp_test.exs
Normal file
|
|
@ -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
|
||||
53
test/mfa_test.exs
Normal file
53
test/mfa_test.exs
Normal file
|
|
@ -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
|
||||
|
|
@ -165,14 +165,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)
|
||||
|
||||
|
|
@ -737,7 +741,7 @@ defmodule Pleroma.NotificationTest do
|
|||
|
||||
assert length(Notification.for_user(user)) == 1
|
||||
|
||||
{:ok, _, _, _} = CommonAPI.unfavorite(activity.id, other_user)
|
||||
{:ok, _} = CommonAPI.unfavorite(activity.id, other_user)
|
||||
|
||||
assert Enum.empty?(Notification.for_user(user))
|
||||
end
|
||||
|
|
@ -771,7 +775,7 @@ defmodule Pleroma.NotificationTest do
|
|||
|
||||
assert length(Notification.for_user(user)) == 1
|
||||
|
||||
{:ok, _, _} = CommonAPI.unrepeat(activity.id, other_user)
|
||||
{:ok, _} = CommonAPI.unrepeat(activity.id, other_user)
|
||||
|
||||
assert Enum.empty?(Notification.for_user(user))
|
||||
end
|
||||
|
|
|
|||
|
|
@ -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
|
||||
%{
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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{}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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()}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -4,14 +4,17 @@
|
|||
|
||||
defmodule Mix.Tasks.Pleroma.UserTest do
|
||||
alias Pleroma.Repo
|
||||
alias Pleroma.Tests.ObanHelpers
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.OAuth.Authorization
|
||||
alias Pleroma.Web.OAuth.Token
|
||||
|
||||
use Pleroma.DataCase
|
||||
use Oban.Testing, repo: Pleroma.Repo
|
||||
|
||||
import Pleroma.Factory
|
||||
import ExUnit.CaptureIO
|
||||
import Mock
|
||||
import Pleroma.Factory
|
||||
|
||||
setup_all do
|
||||
Mix.shell(Mix.Shell.Process)
|
||||
|
|
@ -87,12 +90,17 @@ defmodule Mix.Tasks.Pleroma.UserTest do
|
|||
test "user is deleted" do
|
||||
user = insert(:user)
|
||||
|
||||
Mix.Tasks.Pleroma.User.run(["rm", user.nickname])
|
||||
with_mock Pleroma.Web.Federator,
|
||||
publish: fn _ -> nil end do
|
||||
Mix.Tasks.Pleroma.User.run(["rm", user.nickname])
|
||||
ObanHelpers.perform_all()
|
||||
|
||||
assert_received {:mix_shell, :info, [message]}
|
||||
assert message =~ " deleted"
|
||||
assert_received {:mix_shell, :info, [message]}
|
||||
assert message =~ " deleted"
|
||||
assert %{deactivated: true} = User.get_by_nickname(user.nickname)
|
||||
|
||||
assert %{deactivated: true} = User.get_by_nickname(user.nickname)
|
||||
assert called(Pleroma.Web.Federator.publish(:_))
|
||||
end
|
||||
end
|
||||
|
||||
test "no user to delete" do
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@ defmodule Pleroma.UserTest do
|
|||
use Pleroma.DataCase
|
||||
use Oban.Testing, repo: Pleroma.Repo
|
||||
|
||||
import Mock
|
||||
import Pleroma.Factory
|
||||
import ExUnit.CaptureLog
|
||||
|
||||
|
|
@ -1131,7 +1130,7 @@ defmodule Pleroma.UserTest do
|
|||
|
||||
User.delete_user_activities(user)
|
||||
|
||||
# TODO: Remove favorites, repeats, delete activities.
|
||||
# TODO: Test removal favorites, repeats, delete activities.
|
||||
refute Activity.get_by_id(activity.id)
|
||||
end
|
||||
|
||||
|
|
@ -1170,31 +1169,6 @@ defmodule Pleroma.UserTest do
|
|||
refute Activity.get_by_id(like_two.id)
|
||||
refute Activity.get_by_id(repeat.id)
|
||||
end
|
||||
|
||||
test_with_mock "it sends out User Delete activity",
|
||||
%{user: user},
|
||||
Pleroma.Web.ActivityPub.Publisher,
|
||||
[:passthrough],
|
||||
[] do
|
||||
Pleroma.Config.put([:instance, :federating], true)
|
||||
|
||||
{:ok, follower} = User.get_or_fetch_by_ap_id("http://mastodon.example.org/users/admin")
|
||||
{:ok, _} = User.follow(follower, user)
|
||||
|
||||
{:ok, job} = User.delete(user)
|
||||
{:ok, _user} = ObanHelpers.perform(job)
|
||||
|
||||
assert ObanHelpers.member?(
|
||||
%{
|
||||
"op" => "publish_one",
|
||||
"params" => %{
|
||||
"inbox" => "http://mastodon.example.org/inbox",
|
||||
"id" => "pleroma:fakeid"
|
||||
}
|
||||
},
|
||||
all_enqueued(worker: Pleroma.Workers.PublisherWorker)
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
test "get_public_key_for_ap_id fetches a user that's not in the db" do
|
||||
|
|
|
|||
|
|
@ -815,6 +815,21 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
|
|||
assert object["content"] == activity["object"]["content"]
|
||||
end
|
||||
|
||||
test "it rejects anything beyond 'Note' creations", %{conn: conn, activity: activity} do
|
||||
user = insert(:user)
|
||||
|
||||
activity =
|
||||
activity
|
||||
|> put_in(["object", "type"], "Benis")
|
||||
|
||||
_result =
|
||||
conn
|
||||
|> assign(:user, user)
|
||||
|> put_req_header("content-type", "application/activity+json")
|
||||
|> post("/users/#{user.nickname}/outbox", activity)
|
||||
|> json_response(400)
|
||||
end
|
||||
|
||||
test "it inserts an incoming sensitive activity into the database", %{
|
||||
conn: conn,
|
||||
activity: activity
|
||||
|
|
|
|||
|
|
@ -939,122 +939,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
|
|||
end
|
||||
end
|
||||
|
||||
describe "unreacting to an object" do
|
||||
test_with_mock "sends an activity to federation", Federator, [:passthrough], [] do
|
||||
Config.put([:instance, :federating], true)
|
||||
user = insert(:user)
|
||||
reactor = insert(:user)
|
||||
{:ok, activity} = CommonAPI.post(user, %{"status" => "YASSSS queen slay"})
|
||||
assert object = Object.normalize(activity)
|
||||
|
||||
{:ok, reaction_activity, _object} = ActivityPub.react_with_emoji(reactor, object, "🔥")
|
||||
|
||||
assert called(Federator.publish(reaction_activity))
|
||||
|
||||
{:ok, unreaction_activity, _object} =
|
||||
ActivityPub.unreact_with_emoji(reactor, reaction_activity.data["id"])
|
||||
|
||||
assert called(Federator.publish(unreaction_activity))
|
||||
end
|
||||
|
||||
test "adds an undo activity to the db" do
|
||||
user = insert(:user)
|
||||
reactor = insert(:user)
|
||||
{:ok, activity} = CommonAPI.post(user, %{"status" => "YASSSS queen slay"})
|
||||
assert object = Object.normalize(activity)
|
||||
|
||||
{:ok, reaction_activity, _object} = ActivityPub.react_with_emoji(reactor, object, "🔥")
|
||||
|
||||
{:ok, unreaction_activity, _object} =
|
||||
ActivityPub.unreact_with_emoji(reactor, reaction_activity.data["id"])
|
||||
|
||||
assert unreaction_activity.actor == reactor.ap_id
|
||||
assert unreaction_activity.data["object"] == reaction_activity.data["id"]
|
||||
|
||||
object = Object.get_by_ap_id(object.data["id"])
|
||||
assert object.data["reaction_count"] == 0
|
||||
assert object.data["reactions"] == []
|
||||
end
|
||||
|
||||
test "reverts emoji unreact on error" do
|
||||
[user, reactor] = insert_list(2, :user)
|
||||
{:ok, activity} = CommonAPI.post(user, %{"status" => "Status"})
|
||||
object = Object.normalize(activity)
|
||||
|
||||
{:ok, reaction_activity, _object} = ActivityPub.react_with_emoji(reactor, object, "😀")
|
||||
|
||||
with_mock(Utils, [:passthrough], maybe_federate: fn _ -> {:error, :reverted} end) do
|
||||
assert {:error, :reverted} =
|
||||
ActivityPub.unreact_with_emoji(reactor, reaction_activity.data["id"])
|
||||
end
|
||||
|
||||
object = Object.get_by_ap_id(object.data["id"])
|
||||
|
||||
assert object.data["reaction_count"] == 1
|
||||
assert object.data["reactions"] == [["😀", [reactor.ap_id]]]
|
||||
end
|
||||
end
|
||||
|
||||
describe "unliking" do
|
||||
test_with_mock "sends an activity to federation", Federator, [:passthrough], [] do
|
||||
Config.put([:instance, :federating], true)
|
||||
|
||||
note_activity = insert(:note_activity)
|
||||
object = Object.normalize(note_activity)
|
||||
user = insert(:user)
|
||||
|
||||
{:ok, object} = ActivityPub.unlike(user, object)
|
||||
refute called(Federator.publish())
|
||||
|
||||
{:ok, _like_activity} = CommonAPI.favorite(user, note_activity.id)
|
||||
object = Object.get_by_id(object.id)
|
||||
assert object.data["like_count"] == 1
|
||||
|
||||
{:ok, unlike_activity, _, object} = ActivityPub.unlike(user, object)
|
||||
assert object.data["like_count"] == 0
|
||||
|
||||
assert called(Federator.publish(unlike_activity))
|
||||
end
|
||||
|
||||
test "reverts unliking on error" do
|
||||
note_activity = insert(:note_activity)
|
||||
user = insert(:user)
|
||||
|
||||
{:ok, like_activity} = CommonAPI.favorite(user, note_activity.id)
|
||||
object = Object.normalize(note_activity)
|
||||
assert object.data["like_count"] == 1
|
||||
|
||||
with_mock(Utils, [:passthrough], maybe_federate: fn _ -> {:error, :reverted} end) do
|
||||
assert {:error, :reverted} = ActivityPub.unlike(user, object)
|
||||
end
|
||||
|
||||
assert Object.get_by_ap_id(object.data["id"]) == object
|
||||
assert object.data["like_count"] == 1
|
||||
assert Activity.get_by_id(like_activity.id)
|
||||
end
|
||||
|
||||
test "unliking a previously liked object" do
|
||||
note_activity = insert(:note_activity)
|
||||
object = Object.normalize(note_activity)
|
||||
user = insert(:user)
|
||||
|
||||
# Unliking something that hasn't been liked does nothing
|
||||
{:ok, object} = ActivityPub.unlike(user, object)
|
||||
assert object.data["like_count"] == 0
|
||||
|
||||
{:ok, like_activity} = CommonAPI.favorite(user, note_activity.id)
|
||||
|
||||
object = Object.get_by_id(object.id)
|
||||
assert object.data["like_count"] == 1
|
||||
|
||||
{:ok, unlike_activity, _, object} = ActivityPub.unlike(user, object)
|
||||
assert object.data["like_count"] == 0
|
||||
|
||||
assert Activity.get_by_id(like_activity.id) == nil
|
||||
assert note_activity.actor in unlike_activity.recipients
|
||||
end
|
||||
end
|
||||
|
||||
describe "announcing an object" do
|
||||
test "adds an announce activity to the db" do
|
||||
note_activity = insert(:note_activity)
|
||||
|
|
@ -1124,52 +1008,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
|
|||
end
|
||||
end
|
||||
|
||||
describe "unannouncing an object" do
|
||||
test "unannouncing a previously announced object" do
|
||||
note_activity = insert(:note_activity)
|
||||
object = Object.normalize(note_activity)
|
||||
user = insert(:user)
|
||||
|
||||
# Unannouncing an object that is not announced does nothing
|
||||
{:ok, object} = ActivityPub.unannounce(user, object)
|
||||
refute object.data["announcement_count"]
|
||||
|
||||
{:ok, announce_activity, object} = ActivityPub.announce(user, object)
|
||||
assert object.data["announcement_count"] == 1
|
||||
|
||||
{:ok, unannounce_activity, object} = ActivityPub.unannounce(user, object)
|
||||
assert object.data["announcement_count"] == 0
|
||||
|
||||
assert unannounce_activity.data["to"] == [
|
||||
User.ap_followers(user),
|
||||
object.data["actor"]
|
||||
]
|
||||
|
||||
assert unannounce_activity.data["type"] == "Undo"
|
||||
assert unannounce_activity.data["object"] == announce_activity.data
|
||||
assert unannounce_activity.data["actor"] == user.ap_id
|
||||
assert unannounce_activity.data["context"] == announce_activity.data["context"]
|
||||
|
||||
assert Activity.get_by_id(announce_activity.id) == nil
|
||||
end
|
||||
|
||||
test "reverts unannouncing on error" do
|
||||
note_activity = insert(:note_activity)
|
||||
object = Object.normalize(note_activity)
|
||||
user = insert(:user)
|
||||
|
||||
{:ok, _announce_activity, object} = ActivityPub.announce(user, object)
|
||||
assert object.data["announcement_count"] == 1
|
||||
|
||||
with_mock(Utils, [:passthrough], maybe_federate: fn _ -> {:error, :reverted} end) do
|
||||
assert {:error, :reverted} = ActivityPub.unannounce(user, object)
|
||||
end
|
||||
|
||||
object = Object.get_by_ap_id(object.data["id"])
|
||||
assert object.data["announcement_count"] == 1
|
||||
end
|
||||
end
|
||||
|
||||
describe "uploading files" do
|
||||
test "copies the file to the configured folder" do
|
||||
file = %Plug.Upload{
|
||||
|
|
@ -1276,7 +1114,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
|
|||
end
|
||||
end
|
||||
|
||||
describe "blocking / unblocking" do
|
||||
describe "blocking" do
|
||||
test "reverts block activity on error" do
|
||||
[blocker, blocked] = insert_list(2, :user)
|
||||
|
||||
|
|
@ -1298,175 +1136,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
|
|||
assert activity.data["actor"] == blocker.ap_id
|
||||
assert activity.data["object"] == blocked.ap_id
|
||||
end
|
||||
|
||||
test "reverts unblock activity on error" do
|
||||
[blocker, blocked] = insert_list(2, :user)
|
||||
{:ok, block_activity} = ActivityPub.block(blocker, blocked)
|
||||
|
||||
with_mock(Utils, [:passthrough], maybe_federate: fn _ -> {:error, :reverted} end) do
|
||||
assert {:error, :reverted} = ActivityPub.unblock(blocker, blocked)
|
||||
end
|
||||
|
||||
assert block_activity.data["type"] == "Block"
|
||||
assert block_activity.data["actor"] == blocker.ap_id
|
||||
|
||||
assert Repo.aggregate(Activity, :count, :id) == 1
|
||||
assert Repo.aggregate(Object, :count, :id) == 1
|
||||
end
|
||||
|
||||
test "creates an undo activity for the last block" do
|
||||
blocker = insert(:user)
|
||||
blocked = insert(:user)
|
||||
|
||||
{:ok, block_activity} = ActivityPub.block(blocker, blocked)
|
||||
{:ok, activity} = ActivityPub.unblock(blocker, blocked)
|
||||
|
||||
assert activity.data["type"] == "Undo"
|
||||
assert activity.data["actor"] == blocker.ap_id
|
||||
|
||||
embedded_object = activity.data["object"]
|
||||
assert is_map(embedded_object)
|
||||
assert embedded_object["type"] == "Block"
|
||||
assert embedded_object["object"] == blocked.ap_id
|
||||
assert embedded_object["id"] == block_activity.data["id"]
|
||||
end
|
||||
end
|
||||
|
||||
describe "deletion" do
|
||||
setup do: clear_config([:instance, :rewrite_policy])
|
||||
|
||||
test "it reverts deletion on error" do
|
||||
note = insert(:note_activity)
|
||||
object = Object.normalize(note)
|
||||
|
||||
with_mock(Utils, [:passthrough], maybe_federate: fn _ -> {:error, :reverted} end) do
|
||||
assert {:error, :reverted} = ActivityPub.delete(object)
|
||||
end
|
||||
|
||||
assert Repo.aggregate(Activity, :count, :id) == 1
|
||||
assert Repo.get(Object, object.id) == object
|
||||
assert Activity.get_by_id(note.id) == note
|
||||
end
|
||||
|
||||
test "it creates a delete activity and deletes the original object" do
|
||||
note = insert(:note_activity)
|
||||
object = Object.normalize(note)
|
||||
{:ok, delete} = ActivityPub.delete(object)
|
||||
|
||||
assert delete.data["type"] == "Delete"
|
||||
assert delete.data["actor"] == note.data["actor"]
|
||||
assert delete.data["object"] == object.data["id"]
|
||||
|
||||
assert Activity.get_by_id(delete.id) != nil
|
||||
|
||||
assert Repo.get(Object, object.id).data["type"] == "Tombstone"
|
||||
end
|
||||
|
||||
test "it doesn't fail when an activity was already deleted" do
|
||||
{:ok, delete} = insert(:note_activity) |> Object.normalize() |> ActivityPub.delete()
|
||||
|
||||
assert {:ok, ^delete} = delete |> Object.normalize() |> ActivityPub.delete()
|
||||
end
|
||||
|
||||
test "decrements user note count only for public activities" do
|
||||
user = insert(:user, note_count: 10)
|
||||
|
||||
{:ok, a1} =
|
||||
CommonAPI.post(User.get_cached_by_id(user.id), %{
|
||||
"status" => "yeah",
|
||||
"visibility" => "public"
|
||||
})
|
||||
|
||||
{:ok, a2} =
|
||||
CommonAPI.post(User.get_cached_by_id(user.id), %{
|
||||
"status" => "yeah",
|
||||
"visibility" => "unlisted"
|
||||
})
|
||||
|
||||
{:ok, a3} =
|
||||
CommonAPI.post(User.get_cached_by_id(user.id), %{
|
||||
"status" => "yeah",
|
||||
"visibility" => "private"
|
||||
})
|
||||
|
||||
{:ok, a4} =
|
||||
CommonAPI.post(User.get_cached_by_id(user.id), %{
|
||||
"status" => "yeah",
|
||||
"visibility" => "direct"
|
||||
})
|
||||
|
||||
{:ok, _} = Object.normalize(a1) |> ActivityPub.delete()
|
||||
{:ok, _} = Object.normalize(a2) |> ActivityPub.delete()
|
||||
{:ok, _} = Object.normalize(a3) |> ActivityPub.delete()
|
||||
{:ok, _} = Object.normalize(a4) |> ActivityPub.delete()
|
||||
|
||||
user = User.get_cached_by_id(user.id)
|
||||
assert user.note_count == 10
|
||||
end
|
||||
|
||||
test "it creates a delete activity and checks that it is also sent to users mentioned by the deleted object" do
|
||||
user = insert(:user)
|
||||
note = insert(:note_activity)
|
||||
object = Object.normalize(note)
|
||||
|
||||
{:ok, object} =
|
||||
object
|
||||
|> Object.change(%{
|
||||
data: %{
|
||||
"actor" => object.data["actor"],
|
||||
"id" => object.data["id"],
|
||||
"to" => [user.ap_id],
|
||||
"type" => "Note"
|
||||
}
|
||||
})
|
||||
|> Object.update_and_set_cache()
|
||||
|
||||
{:ok, delete} = ActivityPub.delete(object)
|
||||
|
||||
assert user.ap_id in delete.data["to"]
|
||||
end
|
||||
|
||||
test "decreases reply count" do
|
||||
user = insert(:user)
|
||||
user2 = insert(:user)
|
||||
|
||||
{:ok, activity} = CommonAPI.post(user, %{"status" => "1", "visibility" => "public"})
|
||||
reply_data = %{"status" => "1", "in_reply_to_status_id" => activity.id}
|
||||
ap_id = activity.data["id"]
|
||||
|
||||
{:ok, public_reply} = CommonAPI.post(user2, Map.put(reply_data, "visibility", "public"))
|
||||
{:ok, unlisted_reply} = CommonAPI.post(user2, Map.put(reply_data, "visibility", "unlisted"))
|
||||
{:ok, private_reply} = CommonAPI.post(user2, Map.put(reply_data, "visibility", "private"))
|
||||
{:ok, direct_reply} = CommonAPI.post(user2, Map.put(reply_data, "visibility", "direct"))
|
||||
|
||||
_ = CommonAPI.delete(direct_reply.id, user2)
|
||||
assert %{data: data, object: object} = Activity.get_by_ap_id_with_object(ap_id)
|
||||
assert object.data["repliesCount"] == 2
|
||||
|
||||
_ = CommonAPI.delete(private_reply.id, user2)
|
||||
assert %{data: data, object: object} = Activity.get_by_ap_id_with_object(ap_id)
|
||||
assert object.data["repliesCount"] == 2
|
||||
|
||||
_ = CommonAPI.delete(public_reply.id, user2)
|
||||
assert %{data: data, object: object} = Activity.get_by_ap_id_with_object(ap_id)
|
||||
assert object.data["repliesCount"] == 1
|
||||
|
||||
_ = CommonAPI.delete(unlisted_reply.id, user2)
|
||||
assert %{data: data, object: object} = Activity.get_by_ap_id_with_object(ap_id)
|
||||
assert object.data["repliesCount"] == 0
|
||||
end
|
||||
|
||||
test "it passes delete activity through MRF before deleting the object" do
|
||||
Pleroma.Config.put([:instance, :rewrite_policy], Pleroma.Web.ActivityPub.MRF.DropPolicy)
|
||||
|
||||
note = insert(:note_activity)
|
||||
object = Object.normalize(note)
|
||||
|
||||
{:error, {:reject, _}} = ActivityPub.delete(object)
|
||||
|
||||
assert Activity.get_by_id(note.id)
|
||||
assert Repo.get(Object, object.id).data["type"] == object.data["type"]
|
||||
end
|
||||
end
|
||||
|
||||
describe "timeline post-processing" do
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
defmodule Pleroma.Web.ActivityPub.ObjectValidatorTest do
|
||||
use Pleroma.DataCase
|
||||
|
||||
alias Pleroma.Object
|
||||
alias Pleroma.Web.ActivityPub.Builder
|
||||
alias Pleroma.Web.ActivityPub.ObjectValidator
|
||||
alias Pleroma.Web.ActivityPub.ObjectValidators.LikeValidator
|
||||
alias Pleroma.Web.ActivityPub.Utils
|
||||
|
|
@ -8,6 +10,138 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidatorTest do
|
|||
|
||||
import Pleroma.Factory
|
||||
|
||||
describe "Undos" do
|
||||
setup do
|
||||
user = insert(:user)
|
||||
{:ok, post_activity} = CommonAPI.post(user, %{"status" => "uguu"})
|
||||
{:ok, like} = CommonAPI.favorite(user, post_activity.id)
|
||||
{:ok, valid_like_undo, []} = Builder.undo(user, like)
|
||||
|
||||
%{user: user, like: like, valid_like_undo: valid_like_undo}
|
||||
end
|
||||
|
||||
test "it validates a basic like undo", %{valid_like_undo: valid_like_undo} do
|
||||
assert {:ok, _, _} = ObjectValidator.validate(valid_like_undo, [])
|
||||
end
|
||||
|
||||
test "it does not validate if the actor of the undo is not the actor of the object", %{
|
||||
valid_like_undo: valid_like_undo
|
||||
} do
|
||||
other_user = insert(:user, ap_id: "https://gensokyo.2hu/users/raymoo")
|
||||
|
||||
bad_actor =
|
||||
valid_like_undo
|
||||
|> Map.put("actor", other_user.ap_id)
|
||||
|
||||
{:error, cng} = ObjectValidator.validate(bad_actor, [])
|
||||
|
||||
assert {:actor, {"not the same as object actor", []}} in cng.errors
|
||||
end
|
||||
|
||||
test "it does not validate if the object is missing", %{valid_like_undo: valid_like_undo} do
|
||||
missing_object =
|
||||
valid_like_undo
|
||||
|> Map.put("object", "https://gensokyo.2hu/objects/1")
|
||||
|
||||
{:error, cng} = ObjectValidator.validate(missing_object, [])
|
||||
|
||||
assert {:object, {"can't find object", []}} in cng.errors
|
||||
assert length(cng.errors) == 1
|
||||
end
|
||||
end
|
||||
|
||||
describe "deletes" do
|
||||
setup do
|
||||
user = insert(:user)
|
||||
{:ok, post_activity} = CommonAPI.post(user, %{"status" => "cancel me daddy"})
|
||||
|
||||
{:ok, valid_post_delete, _} = Builder.delete(user, post_activity.data["object"])
|
||||
{:ok, valid_user_delete, _} = Builder.delete(user, user.ap_id)
|
||||
|
||||
%{user: user, valid_post_delete: valid_post_delete, valid_user_delete: valid_user_delete}
|
||||
end
|
||||
|
||||
test "it is valid for a post deletion", %{valid_post_delete: valid_post_delete} do
|
||||
{:ok, valid_post_delete, _} = ObjectValidator.validate(valid_post_delete, [])
|
||||
|
||||
assert valid_post_delete["deleted_activity_id"]
|
||||
end
|
||||
|
||||
test "it is invalid if the object isn't in a list of certain types", %{
|
||||
valid_post_delete: valid_post_delete
|
||||
} do
|
||||
object = Object.get_by_ap_id(valid_post_delete["object"])
|
||||
|
||||
data =
|
||||
object.data
|
||||
|> Map.put("type", "Like")
|
||||
|
||||
{:ok, _object} =
|
||||
object
|
||||
|> Ecto.Changeset.change(%{data: data})
|
||||
|> Object.update_and_set_cache()
|
||||
|
||||
{:error, cng} = ObjectValidator.validate(valid_post_delete, [])
|
||||
assert {:object, {"object not in allowed types", []}} in cng.errors
|
||||
end
|
||||
|
||||
test "it is valid for a user deletion", %{valid_user_delete: valid_user_delete} do
|
||||
assert match?({:ok, _, _}, ObjectValidator.validate(valid_user_delete, []))
|
||||
end
|
||||
|
||||
test "it's invalid if the id is missing", %{valid_post_delete: valid_post_delete} do
|
||||
no_id =
|
||||
valid_post_delete
|
||||
|> Map.delete("id")
|
||||
|
||||
{:error, cng} = ObjectValidator.validate(no_id, [])
|
||||
|
||||
assert {:id, {"can't be blank", [validation: :required]}} in cng.errors
|
||||
end
|
||||
|
||||
test "it's invalid if the object doesn't exist", %{valid_post_delete: valid_post_delete} do
|
||||
missing_object =
|
||||
valid_post_delete
|
||||
|> Map.put("object", "http://does.not/exist")
|
||||
|
||||
{:error, cng} = ObjectValidator.validate(missing_object, [])
|
||||
|
||||
assert {:object, {"can't find object", []}} in cng.errors
|
||||
end
|
||||
|
||||
test "it's invalid if the actor of the object and the actor of delete are from different domains",
|
||||
%{valid_post_delete: valid_post_delete} do
|
||||
valid_user = insert(:user)
|
||||
|
||||
valid_other_actor =
|
||||
valid_post_delete
|
||||
|> Map.put("actor", valid_user.ap_id)
|
||||
|
||||
assert match?({:ok, _, _}, ObjectValidator.validate(valid_other_actor, []))
|
||||
|
||||
invalid_other_actor =
|
||||
valid_post_delete
|
||||
|> Map.put("actor", "https://gensokyo.2hu/users/raymoo")
|
||||
|
||||
{:error, cng} = ObjectValidator.validate(invalid_other_actor, [])
|
||||
|
||||
assert {:actor, {"is not allowed to delete object", []}} in cng.errors
|
||||
end
|
||||
|
||||
test "it's valid if the actor of the object is a local superuser",
|
||||
%{valid_post_delete: valid_post_delete} do
|
||||
user =
|
||||
insert(:user, local: true, is_moderator: true, ap_id: "https://gensokyo.2hu/users/raymoo")
|
||||
|
||||
valid_other_actor =
|
||||
valid_post_delete
|
||||
|> Map.put("actor", user.ap_id)
|
||||
|
||||
{:ok, _, meta} = ObjectValidator.validate(valid_other_actor, [])
|
||||
assert meta[:do_not_federate]
|
||||
end
|
||||
end
|
||||
|
||||
describe "likes" do
|
||||
setup do
|
||||
user = insert(:user)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,27 @@
|
|||
defmodule Pleroma.Web.ObjectValidators.Types.RecipientsTest do
|
||||
alias Pleroma.Web.ActivityPub.ObjectValidators.Types.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
|
||||
|
|
@ -3,17 +3,174 @@
|
|||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.ActivityPub.SideEffectsTest do
|
||||
use Oban.Testing, repo: Pleroma.Repo
|
||||
use Pleroma.DataCase
|
||||
|
||||
alias Pleroma.Activity
|
||||
alias Pleroma.Notification
|
||||
alias Pleroma.Object
|
||||
alias Pleroma.Repo
|
||||
alias Pleroma.Tests.ObanHelpers
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||
alias Pleroma.Web.ActivityPub.Builder
|
||||
alias Pleroma.Web.ActivityPub.SideEffects
|
||||
alias Pleroma.Web.CommonAPI
|
||||
|
||||
import Pleroma.Factory
|
||||
import Mock
|
||||
|
||||
describe "delete objects" do
|
||||
setup do
|
||||
user = insert(:user)
|
||||
other_user = insert(:user)
|
||||
|
||||
{:ok, op} = CommonAPI.post(other_user, %{"status" => "big oof"})
|
||||
{:ok, post} = CommonAPI.post(user, %{"status" => "hey", "in_reply_to_id" => op})
|
||||
object = Object.normalize(post)
|
||||
{:ok, delete_data, _meta} = Builder.delete(user, object.data["id"])
|
||||
{:ok, delete_user_data, _meta} = Builder.delete(user, user.ap_id)
|
||||
{:ok, delete, _meta} = ActivityPub.persist(delete_data, local: true)
|
||||
{:ok, delete_user, _meta} = ActivityPub.persist(delete_user_data, local: true)
|
||||
%{user: user, delete: delete, post: post, object: object, delete_user: delete_user, op: op}
|
||||
end
|
||||
|
||||
test "it handles object deletions", %{
|
||||
delete: delete,
|
||||
post: post,
|
||||
object: object,
|
||||
user: user,
|
||||
op: op
|
||||
} do
|
||||
with_mock Pleroma.Web.ActivityPub.ActivityPub, [:passthrough],
|
||||
stream_out: fn _ -> nil end,
|
||||
stream_out_participations: fn _, _ -> nil end do
|
||||
{:ok, delete, _} = SideEffects.handle(delete)
|
||||
user = User.get_cached_by_ap_id(object.data["actor"])
|
||||
|
||||
assert called(Pleroma.Web.ActivityPub.ActivityPub.stream_out(delete))
|
||||
assert called(Pleroma.Web.ActivityPub.ActivityPub.stream_out_participations(object, user))
|
||||
end
|
||||
|
||||
object = Object.get_by_id(object.id)
|
||||
assert object.data["type"] == "Tombstone"
|
||||
refute Activity.get_by_id(post.id)
|
||||
|
||||
user = User.get_by_id(user.id)
|
||||
assert user.note_count == 0
|
||||
|
||||
object = Object.normalize(op.data["object"], false)
|
||||
|
||||
assert object.data["repliesCount"] == 0
|
||||
end
|
||||
|
||||
test "it handles user deletions", %{delete_user: delete, user: user} do
|
||||
{:ok, _delete, _} = SideEffects.handle(delete)
|
||||
ObanHelpers.perform_all()
|
||||
|
||||
assert User.get_cached_by_ap_id(user.ap_id).deactivated
|
||||
end
|
||||
end
|
||||
|
||||
describe "Undo objects" do
|
||||
setup do
|
||||
poster = insert(:user)
|
||||
user = insert(:user)
|
||||
{:ok, post} = CommonAPI.post(poster, %{"status" => "hey"})
|
||||
{:ok, like} = CommonAPI.favorite(user, post.id)
|
||||
{:ok, reaction, _} = CommonAPI.react_with_emoji(post.id, user, "👍")
|
||||
{:ok, announce, _} = CommonAPI.repeat(post.id, user)
|
||||
{:ok, block} = ActivityPub.block(user, poster)
|
||||
User.block(user, poster)
|
||||
|
||||
{:ok, undo_data, _meta} = Builder.undo(user, like)
|
||||
{:ok, like_undo, _meta} = ActivityPub.persist(undo_data, local: true)
|
||||
|
||||
{:ok, undo_data, _meta} = Builder.undo(user, reaction)
|
||||
{:ok, reaction_undo, _meta} = ActivityPub.persist(undo_data, local: true)
|
||||
|
||||
{:ok, undo_data, _meta} = Builder.undo(user, announce)
|
||||
{:ok, announce_undo, _meta} = ActivityPub.persist(undo_data, local: true)
|
||||
|
||||
{:ok, undo_data, _meta} = Builder.undo(user, block)
|
||||
{:ok, block_undo, _meta} = ActivityPub.persist(undo_data, local: true)
|
||||
|
||||
%{
|
||||
like_undo: like_undo,
|
||||
post: post,
|
||||
like: like,
|
||||
reaction_undo: reaction_undo,
|
||||
reaction: reaction,
|
||||
announce_undo: announce_undo,
|
||||
announce: announce,
|
||||
block_undo: block_undo,
|
||||
block: block,
|
||||
poster: poster,
|
||||
user: user
|
||||
}
|
||||
end
|
||||
|
||||
test "deletes the original block", %{block_undo: block_undo, block: block} do
|
||||
{:ok, _block_undo, _} = SideEffects.handle(block_undo)
|
||||
refute Activity.get_by_id(block.id)
|
||||
end
|
||||
|
||||
test "unblocks the blocked user", %{block_undo: block_undo, block: block} do
|
||||
blocker = User.get_by_ap_id(block.data["actor"])
|
||||
blocked = User.get_by_ap_id(block.data["object"])
|
||||
|
||||
{:ok, _block_undo, _} = SideEffects.handle(block_undo)
|
||||
refute User.blocks?(blocker, blocked)
|
||||
end
|
||||
|
||||
test "an announce undo removes the announce from the object", %{
|
||||
announce_undo: announce_undo,
|
||||
post: post
|
||||
} do
|
||||
{:ok, _announce_undo, _} = SideEffects.handle(announce_undo)
|
||||
|
||||
object = Object.get_by_ap_id(post.data["object"])
|
||||
|
||||
assert object.data["announcement_count"] == 0
|
||||
assert object.data["announcements"] == []
|
||||
end
|
||||
|
||||
test "deletes the original announce", %{announce_undo: announce_undo, announce: announce} do
|
||||
{:ok, _announce_undo, _} = SideEffects.handle(announce_undo)
|
||||
refute Activity.get_by_id(announce.id)
|
||||
end
|
||||
|
||||
test "a reaction undo removes the reaction from the object", %{
|
||||
reaction_undo: reaction_undo,
|
||||
post: post
|
||||
} do
|
||||
{:ok, _reaction_undo, _} = SideEffects.handle(reaction_undo)
|
||||
|
||||
object = Object.get_by_ap_id(post.data["object"])
|
||||
|
||||
assert object.data["reaction_count"] == 0
|
||||
assert object.data["reactions"] == []
|
||||
end
|
||||
|
||||
test "deletes the original reaction", %{reaction_undo: reaction_undo, reaction: reaction} do
|
||||
{:ok, _reaction_undo, _} = SideEffects.handle(reaction_undo)
|
||||
refute Activity.get_by_id(reaction.id)
|
||||
end
|
||||
|
||||
test "a like undo removes the like from the object", %{like_undo: like_undo, post: post} do
|
||||
{:ok, _like_undo, _} = SideEffects.handle(like_undo)
|
||||
|
||||
object = Object.get_by_ap_id(post.data["object"])
|
||||
|
||||
assert object.data["like_count"] == 0
|
||||
assert object.data["likes"] == []
|
||||
end
|
||||
|
||||
test "deletes the original like", %{like_undo: like_undo, like: like} do
|
||||
{:ok, _like_undo, _} = SideEffects.handle(like_undo)
|
||||
refute Activity.get_by_id(like.id)
|
||||
end
|
||||
end
|
||||
|
||||
describe "like objects" do
|
||||
setup do
|
||||
|
|
|
|||
|
|
@ -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.ActivityPub.Transmogrifier.DeleteHandlingTest do
|
||||
use Oban.Testing, repo: Pleroma.Repo
|
||||
use Pleroma.DataCase
|
||||
|
||||
alias Pleroma.Activity
|
||||
alias Pleroma.Object
|
||||
alias Pleroma.Tests.ObanHelpers
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.ActivityPub.Transmogrifier
|
||||
|
||||
import Pleroma.Factory
|
||||
|
||||
setup_all do
|
||||
Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end)
|
||||
:ok
|
||||
end
|
||||
|
||||
test "it works for incoming deletes" do
|
||||
activity = insert(:note_activity)
|
||||
deleting_user = insert(:user)
|
||||
|
||||
data =
|
||||
File.read!("test/fixtures/mastodon-delete.json")
|
||||
|> Poison.decode!()
|
||||
|> Map.put("actor", deleting_user.ap_id)
|
||||
|> put_in(["object", "id"], activity.data["object"])
|
||||
|
||||
{:ok, %Activity{actor: actor, local: false, data: %{"id" => id}}} =
|
||||
Transmogrifier.handle_incoming(data)
|
||||
|
||||
assert id == data["id"]
|
||||
|
||||
# We delete the Create activity because we base our timelines on it.
|
||||
# This should be changed after we unify objects and activities
|
||||
refute Activity.get_by_id(activity.id)
|
||||
assert actor == deleting_user.ap_id
|
||||
|
||||
# Objects are replaced by a tombstone object.
|
||||
object = Object.normalize(activity.data["object"])
|
||||
assert object.data["type"] == "Tombstone"
|
||||
end
|
||||
|
||||
test "it fails for incoming deletes with spoofed origin" do
|
||||
activity = insert(:note_activity)
|
||||
%{ap_id: ap_id} = insert(:user, ap_id: "https://gensokyo.2hu/users/raymoo")
|
||||
|
||||
data =
|
||||
File.read!("test/fixtures/mastodon-delete.json")
|
||||
|> Poison.decode!()
|
||||
|> Map.put("actor", ap_id)
|
||||
|> put_in(["object", "id"], activity.data["object"])
|
||||
|
||||
assert match?({:error, _}, Transmogrifier.handle_incoming(data))
|
||||
end
|
||||
|
||||
@tag capture_log: true
|
||||
test "it works for incoming user deletes" do
|
||||
%{ap_id: ap_id} = insert(:user, ap_id: "http://mastodon.example.org/users/admin")
|
||||
|
||||
data =
|
||||
File.read!("test/fixtures/mastodon-delete-user.json")
|
||||
|> Poison.decode!()
|
||||
|
||||
{:ok, _} = Transmogrifier.handle_incoming(data)
|
||||
ObanHelpers.perform_all()
|
||||
|
||||
assert User.get_cached_by_ap_id(ap_id).deactivated
|
||||
end
|
||||
|
||||
test "it fails for incoming user deletes with spoofed origin" do
|
||||
%{ap_id: ap_id} = insert(:user)
|
||||
|
||||
data =
|
||||
File.read!("test/fixtures/mastodon-delete-user.json")
|
||||
|> Poison.decode!()
|
||||
|> Map.put("actor", ap_id)
|
||||
|
||||
assert match?({:error, _}, Transmogrifier.handle_incoming(data))
|
||||
|
||||
assert User.get_cached_by_ap_id(ap_id)
|
||||
end
|
||||
end
|
||||
185
test/web/activity_pub/transmogrifier/undo_handling_test.exs
Normal file
185
test/web/activity_pub/transmogrifier/undo_handling_test.exs
Normal file
|
|
@ -0,0 +1,185 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.ActivityPub.Transmogrifier.UndoHandlingTest do
|
||||
use Pleroma.DataCase
|
||||
|
||||
alias Pleroma.Activity
|
||||
alias Pleroma.Object
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.ActivityPub.Transmogrifier
|
||||
alias Pleroma.Web.CommonAPI
|
||||
|
||||
import Pleroma.Factory
|
||||
|
||||
test "it works for incoming emoji reaction undos" do
|
||||
user = insert(:user)
|
||||
|
||||
{:ok, activity} = CommonAPI.post(user, %{"status" => "hello"})
|
||||
{:ok, reaction_activity, _object} = CommonAPI.react_with_emoji(activity.id, user, "👌")
|
||||
|
||||
data =
|
||||
File.read!("test/fixtures/mastodon-undo-like.json")
|
||||
|> Poison.decode!()
|
||||
|> Map.put("object", reaction_activity.data["id"])
|
||||
|> Map.put("actor", user.ap_id)
|
||||
|
||||
{:ok, activity} = Transmogrifier.handle_incoming(data)
|
||||
|
||||
assert activity.actor == user.ap_id
|
||||
assert activity.data["id"] == data["id"]
|
||||
assert activity.data["type"] == "Undo"
|
||||
end
|
||||
|
||||
test "it returns an error for incoming unlikes wihout a like activity" do
|
||||
user = insert(:user)
|
||||
{:ok, activity} = CommonAPI.post(user, %{"status" => "leave a like pls"})
|
||||
|
||||
data =
|
||||
File.read!("test/fixtures/mastodon-undo-like.json")
|
||||
|> Poison.decode!()
|
||||
|> Map.put("object", activity.data["object"])
|
||||
|
||||
assert Transmogrifier.handle_incoming(data) == :error
|
||||
end
|
||||
|
||||
test "it works for incoming unlikes with an existing like activity" do
|
||||
user = insert(:user)
|
||||
{:ok, activity} = CommonAPI.post(user, %{"status" => "leave a like pls"})
|
||||
|
||||
like_data =
|
||||
File.read!("test/fixtures/mastodon-like.json")
|
||||
|> Poison.decode!()
|
||||
|> Map.put("object", activity.data["object"])
|
||||
|
||||
_liker = insert(:user, ap_id: like_data["actor"], local: false)
|
||||
|
||||
{:ok, %Activity{data: like_data, local: false}} = Transmogrifier.handle_incoming(like_data)
|
||||
|
||||
data =
|
||||
File.read!("test/fixtures/mastodon-undo-like.json")
|
||||
|> Poison.decode!()
|
||||
|> Map.put("object", like_data)
|
||||
|> Map.put("actor", like_data["actor"])
|
||||
|
||||
{:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
|
||||
|
||||
assert data["actor"] == "http://mastodon.example.org/users/admin"
|
||||
assert data["type"] == "Undo"
|
||||
assert data["id"] == "http://mastodon.example.org/users/admin#likes/2/undo"
|
||||
assert data["object"] == "http://mastodon.example.org/users/admin#likes/2"
|
||||
|
||||
note = Object.get_by_ap_id(like_data["object"])
|
||||
assert note.data["like_count"] == 0
|
||||
assert note.data["likes"] == []
|
||||
end
|
||||
|
||||
test "it works for incoming unlikes with an existing like activity and a compact object" do
|
||||
user = insert(:user)
|
||||
{:ok, activity} = CommonAPI.post(user, %{"status" => "leave a like pls"})
|
||||
|
||||
like_data =
|
||||
File.read!("test/fixtures/mastodon-like.json")
|
||||
|> Poison.decode!()
|
||||
|> Map.put("object", activity.data["object"])
|
||||
|
||||
_liker = insert(:user, ap_id: like_data["actor"], local: false)
|
||||
|
||||
{:ok, %Activity{data: like_data, local: false}} = Transmogrifier.handle_incoming(like_data)
|
||||
|
||||
data =
|
||||
File.read!("test/fixtures/mastodon-undo-like.json")
|
||||
|> Poison.decode!()
|
||||
|> Map.put("object", like_data["id"])
|
||||
|> Map.put("actor", like_data["actor"])
|
||||
|
||||
{:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
|
||||
|
||||
assert data["actor"] == "http://mastodon.example.org/users/admin"
|
||||
assert data["type"] == "Undo"
|
||||
assert data["id"] == "http://mastodon.example.org/users/admin#likes/2/undo"
|
||||
assert data["object"] == "http://mastodon.example.org/users/admin#likes/2"
|
||||
end
|
||||
|
||||
test "it works for incoming unannounces with an existing notice" do
|
||||
user = insert(:user)
|
||||
{:ok, activity} = CommonAPI.post(user, %{"status" => "hey"})
|
||||
|
||||
announce_data =
|
||||
File.read!("test/fixtures/mastodon-announce.json")
|
||||
|> Poison.decode!()
|
||||
|> Map.put("object", activity.data["object"])
|
||||
|
||||
_announcer = insert(:user, ap_id: announce_data["actor"], local: false)
|
||||
|
||||
{:ok, %Activity{data: announce_data, local: false}} =
|
||||
Transmogrifier.handle_incoming(announce_data)
|
||||
|
||||
data =
|
||||
File.read!("test/fixtures/mastodon-undo-announce.json")
|
||||
|> Poison.decode!()
|
||||
|> Map.put("object", announce_data)
|
||||
|> Map.put("actor", announce_data["actor"])
|
||||
|
||||
{:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
|
||||
|
||||
assert data["type"] == "Undo"
|
||||
|
||||
assert data["object"] ==
|
||||
"http://mastodon.example.org/users/admin/statuses/99542391527669785/activity"
|
||||
end
|
||||
|
||||
test "it works for incomming unfollows with an existing follow" do
|
||||
user = insert(:user)
|
||||
|
||||
follow_data =
|
||||
File.read!("test/fixtures/mastodon-follow-activity.json")
|
||||
|> Poison.decode!()
|
||||
|> Map.put("object", user.ap_id)
|
||||
|
||||
_follower = insert(:user, ap_id: follow_data["actor"], local: false)
|
||||
|
||||
{:ok, %Activity{data: _, local: false}} = Transmogrifier.handle_incoming(follow_data)
|
||||
|
||||
data =
|
||||
File.read!("test/fixtures/mastodon-unfollow-activity.json")
|
||||
|> Poison.decode!()
|
||||
|> Map.put("object", follow_data)
|
||||
|
||||
{:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
|
||||
|
||||
assert data["type"] == "Undo"
|
||||
assert data["object"]["type"] == "Follow"
|
||||
assert data["object"]["object"] == user.ap_id
|
||||
assert data["actor"] == "http://mastodon.example.org/users/admin"
|
||||
|
||||
refute User.following?(User.get_cached_by_ap_id(data["actor"]), user)
|
||||
end
|
||||
|
||||
test "it works for incoming unblocks with an existing block" do
|
||||
user = insert(:user)
|
||||
|
||||
block_data =
|
||||
File.read!("test/fixtures/mastodon-block-activity.json")
|
||||
|> Poison.decode!()
|
||||
|> Map.put("object", user.ap_id)
|
||||
|
||||
_blocker = insert(:user, ap_id: block_data["actor"], local: false)
|
||||
|
||||
{:ok, %Activity{data: _, local: false}} = Transmogrifier.handle_incoming(block_data)
|
||||
|
||||
data =
|
||||
File.read!("test/fixtures/mastodon-unblock-activity.json")
|
||||
|> Poison.decode!()
|
||||
|> Map.put("object", block_data)
|
||||
|
||||
{:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
|
||||
assert data["type"] == "Undo"
|
||||
assert data["object"] == block_data["id"]
|
||||
|
||||
blocker = User.get_cached_by_ap_id(data["actor"])
|
||||
|
||||
refute User.blocks?(blocker, user)
|
||||
end
|
||||
end
|
||||
|
|
@ -362,87 +362,6 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
|
|||
assert :error = Transmogrifier.handle_incoming(data)
|
||||
end
|
||||
|
||||
test "it works for incoming emoji reaction undos" do
|
||||
user = insert(:user)
|
||||
|
||||
{:ok, activity} = CommonAPI.post(user, %{"status" => "hello"})
|
||||
{:ok, reaction_activity, _object} = CommonAPI.react_with_emoji(activity.id, user, "👌")
|
||||
|
||||
data =
|
||||
File.read!("test/fixtures/mastodon-undo-like.json")
|
||||
|> Poison.decode!()
|
||||
|> Map.put("object", reaction_activity.data["id"])
|
||||
|> Map.put("actor", user.ap_id)
|
||||
|
||||
{:ok, activity} = Transmogrifier.handle_incoming(data)
|
||||
|
||||
assert activity.actor == user.ap_id
|
||||
assert activity.data["id"] == data["id"]
|
||||
assert activity.data["type"] == "Undo"
|
||||
end
|
||||
|
||||
test "it returns an error for incoming unlikes wihout a like activity" do
|
||||
user = insert(:user)
|
||||
{:ok, activity} = CommonAPI.post(user, %{"status" => "leave a like pls"})
|
||||
|
||||
data =
|
||||
File.read!("test/fixtures/mastodon-undo-like.json")
|
||||
|> Poison.decode!()
|
||||
|> Map.put("object", activity.data["object"])
|
||||
|
||||
assert Transmogrifier.handle_incoming(data) == :error
|
||||
end
|
||||
|
||||
test "it works for incoming unlikes with an existing like activity" do
|
||||
user = insert(:user)
|
||||
{:ok, activity} = CommonAPI.post(user, %{"status" => "leave a like pls"})
|
||||
|
||||
like_data =
|
||||
File.read!("test/fixtures/mastodon-like.json")
|
||||
|> Poison.decode!()
|
||||
|> Map.put("object", activity.data["object"])
|
||||
|
||||
{:ok, %Activity{data: like_data, local: false}} = Transmogrifier.handle_incoming(like_data)
|
||||
|
||||
data =
|
||||
File.read!("test/fixtures/mastodon-undo-like.json")
|
||||
|> Poison.decode!()
|
||||
|> Map.put("object", like_data)
|
||||
|> Map.put("actor", like_data["actor"])
|
||||
|
||||
{:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
|
||||
|
||||
assert data["actor"] == "http://mastodon.example.org/users/admin"
|
||||
assert data["type"] == "Undo"
|
||||
assert data["id"] == "http://mastodon.example.org/users/admin#likes/2/undo"
|
||||
assert data["object"]["id"] == "http://mastodon.example.org/users/admin#likes/2"
|
||||
end
|
||||
|
||||
test "it works for incoming unlikes with an existing like activity and a compact object" do
|
||||
user = insert(:user)
|
||||
{:ok, activity} = CommonAPI.post(user, %{"status" => "leave a like pls"})
|
||||
|
||||
like_data =
|
||||
File.read!("test/fixtures/mastodon-like.json")
|
||||
|> Poison.decode!()
|
||||
|> Map.put("object", activity.data["object"])
|
||||
|
||||
{:ok, %Activity{data: like_data, local: false}} = Transmogrifier.handle_incoming(like_data)
|
||||
|
||||
data =
|
||||
File.read!("test/fixtures/mastodon-undo-like.json")
|
||||
|> Poison.decode!()
|
||||
|> Map.put("object", like_data["id"])
|
||||
|> Map.put("actor", like_data["actor"])
|
||||
|
||||
{:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
|
||||
|
||||
assert data["actor"] == "http://mastodon.example.org/users/admin"
|
||||
assert data["type"] == "Undo"
|
||||
assert data["id"] == "http://mastodon.example.org/users/admin#likes/2/undo"
|
||||
assert data["object"]["id"] == "http://mastodon.example.org/users/admin#likes/2"
|
||||
end
|
||||
|
||||
test "it works for incoming announces" do
|
||||
data = File.read!("test/fixtures/mastodon-announce.json") |> Poison.decode!()
|
||||
|
||||
|
|
@ -766,113 +685,6 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
|
|||
assert user.locked == true
|
||||
end
|
||||
|
||||
test "it works for incoming deletes" do
|
||||
activity = insert(:note_activity)
|
||||
deleting_user = insert(:user)
|
||||
|
||||
data =
|
||||
File.read!("test/fixtures/mastodon-delete.json")
|
||||
|> Poison.decode!()
|
||||
|
||||
object =
|
||||
data["object"]
|
||||
|> Map.put("id", activity.data["object"])
|
||||
|
||||
data =
|
||||
data
|
||||
|> Map.put("object", object)
|
||||
|> Map.put("actor", deleting_user.ap_id)
|
||||
|
||||
{:ok, %Activity{actor: actor, local: false, data: %{"id" => id}}} =
|
||||
Transmogrifier.handle_incoming(data)
|
||||
|
||||
assert id == data["id"]
|
||||
refute Activity.get_by_id(activity.id)
|
||||
assert actor == deleting_user.ap_id
|
||||
end
|
||||
|
||||
test "it fails for incoming deletes with spoofed origin" do
|
||||
activity = insert(:note_activity)
|
||||
|
||||
data =
|
||||
File.read!("test/fixtures/mastodon-delete.json")
|
||||
|> Poison.decode!()
|
||||
|
||||
object =
|
||||
data["object"]
|
||||
|> Map.put("id", activity.data["object"])
|
||||
|
||||
data =
|
||||
data
|
||||
|> Map.put("object", object)
|
||||
|
||||
assert capture_log(fn ->
|
||||
:error = Transmogrifier.handle_incoming(data)
|
||||
end) =~
|
||||
"[error] Could not decode user at fetch http://mastodon.example.org/users/gargron, {:error, :nxdomain}"
|
||||
|
||||
assert Activity.get_by_id(activity.id)
|
||||
end
|
||||
|
||||
@tag capture_log: true
|
||||
test "it works for incoming user deletes" do
|
||||
%{ap_id: ap_id} =
|
||||
insert(:user, ap_id: "http://mastodon.example.org/users/admin", local: false)
|
||||
|
||||
data =
|
||||
File.read!("test/fixtures/mastodon-delete-user.json")
|
||||
|> Poison.decode!()
|
||||
|
||||
{:ok, _} = Transmogrifier.handle_incoming(data)
|
||||
ObanHelpers.perform_all()
|
||||
|
||||
refute User.get_cached_by_ap_id(ap_id)
|
||||
end
|
||||
|
||||
test "it fails for incoming user deletes with spoofed origin" do
|
||||
%{ap_id: ap_id} = insert(:user)
|
||||
|
||||
data =
|
||||
File.read!("test/fixtures/mastodon-delete-user.json")
|
||||
|> Poison.decode!()
|
||||
|> Map.put("actor", ap_id)
|
||||
|
||||
assert capture_log(fn ->
|
||||
assert :error == Transmogrifier.handle_incoming(data)
|
||||
end) =~ "Object containment failed"
|
||||
|
||||
assert User.get_cached_by_ap_id(ap_id)
|
||||
end
|
||||
|
||||
test "it works for incoming unannounces with an existing notice" do
|
||||
user = insert(:user)
|
||||
{:ok, activity} = CommonAPI.post(user, %{"status" => "hey"})
|
||||
|
||||
announce_data =
|
||||
File.read!("test/fixtures/mastodon-announce.json")
|
||||
|> Poison.decode!()
|
||||
|> Map.put("object", activity.data["object"])
|
||||
|
||||
{:ok, %Activity{data: announce_data, local: false}} =
|
||||
Transmogrifier.handle_incoming(announce_data)
|
||||
|
||||
data =
|
||||
File.read!("test/fixtures/mastodon-undo-announce.json")
|
||||
|> Poison.decode!()
|
||||
|> Map.put("object", announce_data)
|
||||
|> Map.put("actor", announce_data["actor"])
|
||||
|
||||
{:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
|
||||
|
||||
assert data["type"] == "Undo"
|
||||
assert object_data = data["object"]
|
||||
assert object_data["type"] == "Announce"
|
||||
assert object_data["object"] == activity.data["object"]
|
||||
|
||||
assert object_data["id"] ==
|
||||
"http://mastodon.example.org/users/admin/statuses/99542391527669785/activity"
|
||||
end
|
||||
|
||||
test "it works for incomming unfollows with an existing follow" do
|
||||
user = insert(:user)
|
||||
|
||||
|
|
@ -967,32 +779,6 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
|
|||
refute User.following?(blocked, blocker)
|
||||
end
|
||||
|
||||
test "it works for incoming unblocks with an existing block" do
|
||||
user = insert(:user)
|
||||
|
||||
block_data =
|
||||
File.read!("test/fixtures/mastodon-block-activity.json")
|
||||
|> Poison.decode!()
|
||||
|> Map.put("object", user.ap_id)
|
||||
|
||||
{:ok, %Activity{data: _, local: false}} = Transmogrifier.handle_incoming(block_data)
|
||||
|
||||
data =
|
||||
File.read!("test/fixtures/mastodon-unblock-activity.json")
|
||||
|> Poison.decode!()
|
||||
|> Map.put("object", block_data)
|
||||
|
||||
{:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
|
||||
assert data["type"] == "Undo"
|
||||
assert data["object"]["type"] == "Block"
|
||||
assert data["object"]["object"] == user.ap_id
|
||||
assert data["actor"] == "http://mastodon.example.org/users/admin"
|
||||
|
||||
blocker = User.get_cached_by_ap_id(data["actor"])
|
||||
|
||||
refute User.blocks?(blocker, user)
|
||||
end
|
||||
|
||||
test "it works for incoming accepts which were pre-accepted" do
|
||||
follower = insert(:user)
|
||||
followed = insert(:user)
|
||||
|
|
|
|||
|
|
@ -102,34 +102,6 @@ defmodule Pleroma.Web.ActivityPub.UtilsTest do
|
|||
end
|
||||
end
|
||||
|
||||
describe "make_unlike_data/3" do
|
||||
test "returns data for unlike activity" do
|
||||
user = insert(:user)
|
||||
like_activity = insert(:like_activity, data_attrs: %{"context" => "test context"})
|
||||
|
||||
object = Object.normalize(like_activity.data["object"])
|
||||
|
||||
assert Utils.make_unlike_data(user, like_activity, nil) == %{
|
||||
"type" => "Undo",
|
||||
"actor" => user.ap_id,
|
||||
"object" => like_activity.data,
|
||||
"to" => [user.follower_address, object.data["actor"]],
|
||||
"cc" => [Pleroma.Constants.as_public()],
|
||||
"context" => like_activity.data["context"]
|
||||
}
|
||||
|
||||
assert Utils.make_unlike_data(user, like_activity, "9mJEZK0tky1w2xD2vY") == %{
|
||||
"type" => "Undo",
|
||||
"actor" => user.ap_id,
|
||||
"object" => like_activity.data,
|
||||
"to" => [user.follower_address, object.data["actor"]],
|
||||
"cc" => [Pleroma.Constants.as_public()],
|
||||
"context" => like_activity.data["context"],
|
||||
"id" => "9mJEZK0tky1w2xD2vY"
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
describe "make_like_data" do
|
||||
setup do
|
||||
user = insert(:user)
|
||||
|
|
|
|||
|
|
@ -6,13 +6,15 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
|
|||
use Pleroma.Web.ConnCase
|
||||
use Oban.Testing, repo: Pleroma.Repo
|
||||
|
||||
import Pleroma.Factory
|
||||
import ExUnit.CaptureLog
|
||||
import Mock
|
||||
import Pleroma.Factory
|
||||
|
||||
alias Pleroma.Activity
|
||||
alias Pleroma.Config
|
||||
alias Pleroma.ConfigDB
|
||||
alias Pleroma.HTML
|
||||
alias Pleroma.MFA
|
||||
alias Pleroma.ModerationLog
|
||||
alias Pleroma.Repo
|
||||
alias Pleroma.ReportNote
|
||||
|
|
@ -147,17 +149,26 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
|
|||
test "single user", %{admin: admin, conn: conn} do
|
||||
user = insert(:user)
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> put_req_header("accept", "application/json")
|
||||
|> delete("/api/pleroma/admin/users?nickname=#{user.nickname}")
|
||||
with_mock Pleroma.Web.Federator,
|
||||
publish: fn _ -> nil end do
|
||||
conn =
|
||||
conn
|
||||
|> put_req_header("accept", "application/json")
|
||||
|> delete("/api/pleroma/admin/users?nickname=#{user.nickname}")
|
||||
|
||||
log_entry = Repo.one(ModerationLog)
|
||||
ObanHelpers.perform_all()
|
||||
|
||||
assert ModerationLog.get_log_entry_message(log_entry) ==
|
||||
"@#{admin.nickname} deleted users: @#{user.nickname}"
|
||||
assert User.get_by_nickname(user.nickname).deactivated
|
||||
|
||||
assert json_response(conn, 200) == user.nickname
|
||||
log_entry = Repo.one(ModerationLog)
|
||||
|
||||
assert ModerationLog.get_log_entry_message(log_entry) ==
|
||||
"@#{admin.nickname} deleted users: @#{user.nickname}"
|
||||
|
||||
assert json_response(conn, 200) == [user.nickname]
|
||||
|
||||
assert called(Pleroma.Web.Federator.publish(:_))
|
||||
end
|
||||
end
|
||||
|
||||
test "multiple users", %{admin: admin, conn: conn} do
|
||||
|
|
@ -1268,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")
|
||||
|
|
|
|||
43
test/web/auth/pleroma_authenticator_test.exs
Normal file
43
test/web/auth/pleroma_authenticator_test.exs
Normal file
|
|
@ -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
|
||||
51
test/web/auth/totp_authenticator_test.exs
Normal file
51
test/web/auth/totp_authenticator_test.exs
Normal file
|
|
@ -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
|
||||
|
|
@ -9,11 +9,13 @@ defmodule Pleroma.Web.CommonAPITest do
|
|||
alias Pleroma.Object
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||
alias Pleroma.Web.ActivityPub.Transmogrifier
|
||||
alias Pleroma.Web.ActivityPub.Visibility
|
||||
alias Pleroma.Web.AdminAPI.AccountView
|
||||
alias Pleroma.Web.CommonAPI
|
||||
|
||||
import Pleroma.Factory
|
||||
import Mock
|
||||
|
||||
require Pleroma.Constants
|
||||
|
||||
|
|
@ -21,6 +23,84 @@ defmodule Pleroma.Web.CommonAPITest do
|
|||
setup do: clear_config([:instance, :limit])
|
||||
setup do: clear_config([:instance, :max_pinned_statuses])
|
||||
|
||||
describe "deletion" do
|
||||
test "it allows users to delete their posts" do
|
||||
user = insert(:user)
|
||||
|
||||
{:ok, post} = CommonAPI.post(user, %{"status" => "namu amida butsu"})
|
||||
|
||||
with_mock Pleroma.Web.Federator,
|
||||
publish: fn _ -> nil end do
|
||||
assert {:ok, delete} = CommonAPI.delete(post.id, user)
|
||||
assert delete.local
|
||||
assert called(Pleroma.Web.Federator.publish(delete))
|
||||
end
|
||||
|
||||
refute Activity.get_by_id(post.id)
|
||||
end
|
||||
|
||||
test "it does not allow a user to delete their posts" do
|
||||
user = insert(:user)
|
||||
other_user = insert(:user)
|
||||
|
||||
{:ok, post} = CommonAPI.post(user, %{"status" => "namu amida butsu"})
|
||||
|
||||
assert {:error, "Could not delete"} = CommonAPI.delete(post.id, other_user)
|
||||
assert Activity.get_by_id(post.id)
|
||||
end
|
||||
|
||||
test "it allows moderators to delete other user's posts" do
|
||||
user = insert(:user)
|
||||
moderator = insert(:user, is_moderator: true)
|
||||
|
||||
{:ok, post} = CommonAPI.post(user, %{"status" => "namu amida butsu"})
|
||||
|
||||
assert {:ok, delete} = CommonAPI.delete(post.id, moderator)
|
||||
assert delete.local
|
||||
|
||||
refute Activity.get_by_id(post.id)
|
||||
end
|
||||
|
||||
test "it allows admins to delete other user's posts" do
|
||||
user = insert(:user)
|
||||
moderator = insert(:user, is_admin: true)
|
||||
|
||||
{:ok, post} = CommonAPI.post(user, %{"status" => "namu amida butsu"})
|
||||
|
||||
assert {:ok, delete} = CommonAPI.delete(post.id, moderator)
|
||||
assert delete.local
|
||||
|
||||
refute Activity.get_by_id(post.id)
|
||||
end
|
||||
|
||||
test "superusers deleting non-local posts won't federate the delete" do
|
||||
# This is the user of the ingested activity
|
||||
_user =
|
||||
insert(:user,
|
||||
local: false,
|
||||
ap_id: "http://mastodon.example.org/users/admin",
|
||||
last_refreshed_at: NaiveDateTime.utc_now()
|
||||
)
|
||||
|
||||
moderator = insert(:user, is_admin: true)
|
||||
|
||||
data =
|
||||
File.read!("test/fixtures/mastodon-post-activity.json")
|
||||
|> Jason.decode!()
|
||||
|
||||
{:ok, post} = Transmogrifier.handle_incoming(data)
|
||||
|
||||
with_mock Pleroma.Web.Federator,
|
||||
publish: fn _ -> nil end do
|
||||
assert {:ok, delete} = CommonAPI.delete(post.id, moderator)
|
||||
assert delete.local
|
||||
refute called(Pleroma.Web.Federator.publish(:_))
|
||||
end
|
||||
|
||||
refute Activity.get_by_id(post.id)
|
||||
end
|
||||
end
|
||||
|
||||
test "favoriting race condition" do
|
||||
user = insert(:user)
|
||||
users_serial = insert_list(10, :user)
|
||||
|
|
@ -295,10 +375,11 @@ defmodule Pleroma.Web.CommonAPITest do
|
|||
{:ok, activity} = CommonAPI.post(other_user, %{"status" => "cofe"})
|
||||
{:ok, reaction, _} = CommonAPI.react_with_emoji(activity.id, user, "👍")
|
||||
|
||||
{:ok, unreaction, _} = CommonAPI.unreact_with_emoji(activity.id, user, "👍")
|
||||
{:ok, unreaction} = CommonAPI.unreact_with_emoji(activity.id, user, "👍")
|
||||
|
||||
assert unreaction.data["type"] == "Undo"
|
||||
assert unreaction.data["object"] == reaction.data["id"]
|
||||
assert unreaction.local
|
||||
end
|
||||
|
||||
test "repeating a status" do
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -27,8 +27,8 @@ defmodule Pleroma.Web.MastodonAPI.SearchControllerTest do
|
|||
capture_log(fn ->
|
||||
results =
|
||||
conn
|
||||
|> get("/api/v2/search", %{"q" => "2hu"})
|
||||
|> json_response(200)
|
||||
|> get("/api/v2/search?q=2hu")
|
||||
|> json_response_and_validate_schema(200)
|
||||
|
||||
assert results["accounts"] == []
|
||||
assert results["statuses"] == []
|
||||
|
|
@ -54,8 +54,8 @@ defmodule Pleroma.Web.MastodonAPI.SearchControllerTest do
|
|||
|
||||
results =
|
||||
conn
|
||||
|> get("/api/v2/search", %{"q" => "2hu #private"})
|
||||
|> json_response(200)
|
||||
|> get("/api/v2/search?#{URI.encode_query(%{q: "2hu #private"})}")
|
||||
|> json_response_and_validate_schema(200)
|
||||
|
||||
[account | _] = results["accounts"]
|
||||
assert account["id"] == to_string(user_three.id)
|
||||
|
|
@ -68,8 +68,8 @@ defmodule Pleroma.Web.MastodonAPI.SearchControllerTest do
|
|||
assert status["id"] == to_string(activity.id)
|
||||
|
||||
results =
|
||||
get(conn, "/api/v2/search", %{"q" => "天子"})
|
||||
|> json_response(200)
|
||||
get(conn, "/api/v2/search?q=天子")
|
||||
|> json_response_and_validate_schema(200)
|
||||
|
||||
[status] = results["statuses"]
|
||||
assert status["id"] == to_string(activity.id)
|
||||
|
|
@ -89,8 +89,8 @@ defmodule Pleroma.Web.MastodonAPI.SearchControllerTest do
|
|||
conn
|
||||
|> assign(:user, user)
|
||||
|> assign(:token, insert(:oauth_token, user: user, scopes: ["read"]))
|
||||
|> get("/api/v2/search", %{"q" => "Agent"})
|
||||
|> json_response(200)
|
||||
|> get("/api/v2/search?q=Agent")
|
||||
|> json_response_and_validate_schema(200)
|
||||
|
||||
status_ids = Enum.map(results["statuses"], fn g -> g["id"] end)
|
||||
|
||||
|
|
@ -107,8 +107,8 @@ defmodule Pleroma.Web.MastodonAPI.SearchControllerTest do
|
|||
|
||||
results =
|
||||
conn
|
||||
|> get("/api/v1/accounts/search", %{"q" => "shp"})
|
||||
|> json_response(200)
|
||||
|> get("/api/v1/accounts/search?q=shp")
|
||||
|> json_response_and_validate_schema(200)
|
||||
|
||||
result_ids = for result <- results, do: result["acct"]
|
||||
|
||||
|
|
@ -117,8 +117,8 @@ defmodule Pleroma.Web.MastodonAPI.SearchControllerTest do
|
|||
|
||||
results =
|
||||
conn
|
||||
|> get("/api/v1/accounts/search", %{"q" => "2hu"})
|
||||
|> json_response(200)
|
||||
|> get("/api/v1/accounts/search?q=2hu")
|
||||
|> json_response_and_validate_schema(200)
|
||||
|
||||
result_ids = for result <- results, do: result["acct"]
|
||||
|
||||
|
|
@ -130,8 +130,8 @@ defmodule Pleroma.Web.MastodonAPI.SearchControllerTest do
|
|||
|
||||
results =
|
||||
conn
|
||||
|> get("/api/v1/accounts/search", %{"q" => "shp@shitposter.club xxx "})
|
||||
|> json_response(200)
|
||||
|> get("/api/v1/accounts/search?q=shp@shitposter.club xxx")
|
||||
|> json_response_and_validate_schema(200)
|
||||
|
||||
assert length(results) == 1
|
||||
end
|
||||
|
|
@ -146,8 +146,8 @@ defmodule Pleroma.Web.MastodonAPI.SearchControllerTest do
|
|||
capture_log(fn ->
|
||||
results =
|
||||
conn
|
||||
|> get("/api/v1/search", %{"q" => "2hu"})
|
||||
|> json_response(200)
|
||||
|> get("/api/v1/search?q=2hu")
|
||||
|> json_response_and_validate_schema(200)
|
||||
|
||||
assert results["accounts"] == []
|
||||
assert results["statuses"] == []
|
||||
|
|
@ -173,8 +173,8 @@ defmodule Pleroma.Web.MastodonAPI.SearchControllerTest do
|
|||
|
||||
results =
|
||||
conn
|
||||
|> get("/api/v1/search", %{"q" => "2hu"})
|
||||
|> json_response(200)
|
||||
|> get("/api/v1/search?q=2hu")
|
||||
|> json_response_and_validate_schema(200)
|
||||
|
||||
[account | _] = results["accounts"]
|
||||
assert account["id"] == to_string(user_three.id)
|
||||
|
|
@ -194,8 +194,8 @@ defmodule Pleroma.Web.MastodonAPI.SearchControllerTest do
|
|||
|
||||
results =
|
||||
conn
|
||||
|> get("/api/v1/search", %{"q" => "https://shitposter.club/notice/2827873"})
|
||||
|> json_response(200)
|
||||
|> get("/api/v1/search?q=https://shitposter.club/notice/2827873")
|
||||
|> json_response_and_validate_schema(200)
|
||||
|
||||
[status, %{"id" => ^activity_id}] = results["statuses"]
|
||||
|
||||
|
|
@ -212,10 +212,12 @@ defmodule Pleroma.Web.MastodonAPI.SearchControllerTest do
|
|||
})
|
||||
|
||||
capture_log(fn ->
|
||||
q = Object.normalize(activity).data["id"]
|
||||
|
||||
results =
|
||||
conn
|
||||
|> get("/api/v1/search", %{"q" => Object.normalize(activity).data["id"]})
|
||||
|> json_response(200)
|
||||
|> get("/api/v1/search?q=#{q}")
|
||||
|> json_response_and_validate_schema(200)
|
||||
|
||||
[] = results["statuses"]
|
||||
end)
|
||||
|
|
@ -228,8 +230,8 @@ defmodule Pleroma.Web.MastodonAPI.SearchControllerTest do
|
|||
conn
|
||||
|> assign(:user, user)
|
||||
|> assign(:token, insert(:oauth_token, user: user, scopes: ["read"]))
|
||||
|> get("/api/v1/search", %{"q" => "mike@osada.macgirvin.com", "resolve" => "true"})
|
||||
|> json_response(200)
|
||||
|> get("/api/v1/search?q=mike@osada.macgirvin.com&resolve=true")
|
||||
|> json_response_and_validate_schema(200)
|
||||
|
||||
[account] = results["accounts"]
|
||||
assert account["acct"] == "mike@osada.macgirvin.com"
|
||||
|
|
@ -238,8 +240,8 @@ defmodule Pleroma.Web.MastodonAPI.SearchControllerTest do
|
|||
test "search doesn't fetch remote accounts if resolve is false", %{conn: conn} do
|
||||
results =
|
||||
conn
|
||||
|> get("/api/v1/search", %{"q" => "mike@osada.macgirvin.com", "resolve" => "false"})
|
||||
|> json_response(200)
|
||||
|> get("/api/v1/search?q=mike@osada.macgirvin.com&resolve=false")
|
||||
|> json_response_and_validate_schema(200)
|
||||
|
||||
assert [] == results["accounts"]
|
||||
end
|
||||
|
|
@ -254,16 +256,16 @@ defmodule Pleroma.Web.MastodonAPI.SearchControllerTest do
|
|||
|
||||
result =
|
||||
conn
|
||||
|> get("/api/v1/search", %{"q" => "2hu", "limit" => 1})
|
||||
|> get("/api/v1/search?q=2hu&limit=1")
|
||||
|
||||
assert results = json_response(result, 200)
|
||||
assert results = json_response_and_validate_schema(result, 200)
|
||||
assert [%{"id" => activity_id1}] = results["statuses"]
|
||||
assert [_] = results["accounts"]
|
||||
|
||||
results =
|
||||
conn
|
||||
|> get("/api/v1/search", %{"q" => "2hu", "limit" => 1, "offset" => 1})
|
||||
|> json_response(200)
|
||||
|> get("/api/v1/search?q=2hu&limit=1&offset=1")
|
||||
|> json_response_and_validate_schema(200)
|
||||
|
||||
assert [%{"id" => activity_id2}] = results["statuses"]
|
||||
assert [] = results["accounts"]
|
||||
|
|
@ -279,13 +281,13 @@ defmodule Pleroma.Web.MastodonAPI.SearchControllerTest do
|
|||
|
||||
assert %{"statuses" => [_activity], "accounts" => [], "hashtags" => []} =
|
||||
conn
|
||||
|> get("/api/v1/search", %{"q" => "2hu", "type" => "statuses"})
|
||||
|> json_response(200)
|
||||
|> get("/api/v1/search?q=2hu&type=statuses")
|
||||
|> json_response_and_validate_schema(200)
|
||||
|
||||
assert %{"statuses" => [], "accounts" => [_user_two], "hashtags" => []} =
|
||||
conn
|
||||
|> get("/api/v1/search", %{"q" => "2hu", "type" => "accounts"})
|
||||
|> json_response(200)
|
||||
|> get("/api/v1/search?q=2hu&type=accounts")
|
||||
|> json_response_and_validate_schema(200)
|
||||
end
|
||||
|
||||
test "search uses account_id to filter statuses by the author", %{conn: conn} do
|
||||
|
|
@ -297,8 +299,8 @@ defmodule Pleroma.Web.MastodonAPI.SearchControllerTest do
|
|||
|
||||
results =
|
||||
conn
|
||||
|> get("/api/v1/search", %{"q" => "2hu", "account_id" => user.id})
|
||||
|> json_response(200)
|
||||
|> get("/api/v1/search?q=2hu&account_id=#{user.id}")
|
||||
|> json_response_and_validate_schema(200)
|
||||
|
||||
assert [%{"id" => activity_id1}] = results["statuses"]
|
||||
assert activity_id1 == activity1.id
|
||||
|
|
@ -306,8 +308,8 @@ defmodule Pleroma.Web.MastodonAPI.SearchControllerTest do
|
|||
|
||||
results =
|
||||
conn
|
||||
|> get("/api/v1/search", %{"q" => "2hu", "account_id" => user_two.id})
|
||||
|> json_response(200)
|
||||
|> get("/api/v1/search?q=2hu&account_id=#{user_two.id}")
|
||||
|> json_response_and_validate_schema(200)
|
||||
|
||||
assert [%{"id" => activity_id2}] = results["statuses"]
|
||||
assert activity_id2 == activity2.id
|
||||
|
|
|
|||
306
test/web/oauth/mfa_controller_test.exs
Normal file
306
test/web/oauth/mfa_controller_test.exs
Normal file
|
|
@ -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
|
||||
|
|
@ -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"])
|
||||
|
|
|
|||
|
|
@ -3,12 +3,14 @@
|
|||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.PleromaAPI.PleromaAPIControllerTest do
|
||||
use Oban.Testing, repo: Pleroma.Repo
|
||||
use Pleroma.Web.ConnCase
|
||||
|
||||
alias Pleroma.Conversation.Participation
|
||||
alias Pleroma.Notification
|
||||
alias Pleroma.Object
|
||||
alias Pleroma.Repo
|
||||
alias Pleroma.Tests.ObanHelpers
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.CommonAPI
|
||||
|
||||
|
|
@ -41,7 +43,9 @@ defmodule Pleroma.Web.PleromaAPI.PleromaAPIControllerTest do
|
|||
other_user = insert(:user)
|
||||
|
||||
{:ok, activity} = CommonAPI.post(user, %{"status" => "#cofe"})
|
||||
{:ok, activity, _object} = CommonAPI.react_with_emoji(activity.id, other_user, "☕")
|
||||
{:ok, _reaction, _object} = CommonAPI.react_with_emoji(activity.id, other_user, "☕")
|
||||
|
||||
ObanHelpers.perform_all()
|
||||
|
||||
result =
|
||||
conn
|
||||
|
|
@ -52,7 +56,9 @@ defmodule Pleroma.Web.PleromaAPI.PleromaAPIControllerTest do
|
|||
assert %{"id" => id} = json_response(result, 200)
|
||||
assert to_string(activity.id) == id
|
||||
|
||||
object = Object.normalize(activity)
|
||||
ObanHelpers.perform_all()
|
||||
|
||||
object = Object.get_by_ap_id(activity.data["object"])
|
||||
|
||||
assert object.data["reaction_count"] == 0
|
||||
end
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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)
|
||||
|
||||
Streamer.add_socket(
|
||||
"user:notification",
|
||||
%{transport_pid: task.pid, assigns: %{user: 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, _} = CommonAPI.add_mute(user, activity)
|
||||
|
||||
Streamer.stream("user:notification", notif)
|
||||
Task.await(task)
|
||||
Streamer.add_socket("user:notification", user)
|
||||
|
||||
{:ok, favorite_activity} = CommonAPI.favorite(user2, activity.id)
|
||||
|
||||
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)
|
||||
|
||||
Streamer.add_socket(
|
||||
"user:notification",
|
||||
%{transport_pid: task.pid, assigns: %{user: user}}
|
||||
)
|
||||
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
|
||||
} 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,79 +130,57 @@ 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)
|
||||
Streamer.add_socket("user:notification", user)
|
||||
{:ok, _follower, _followed, follow_activity} = CommonAPI.follow(user2, user)
|
||||
|
||||
Process.sleep(@streamer_start_wait)
|
||||
|
||||
Streamer.add_socket(
|
||||
"user:notification",
|
||||
%{transport_pid: task.pid, assigns: %{user: 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)
|
||||
Streamer.add_socket("public", other_user)
|
||||
|
||||
fake_socket = %StreamerSocket{
|
||||
transport_pid: task.pid,
|
||||
user: user
|
||||
}
|
||||
{: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
|
||||
user = insert(:user)
|
||||
other_user = insert(:user)
|
||||
{:ok, activity} = CommonAPI.post(other_user, %{"status" => "Test"})
|
||||
|
||||
topics = %{
|
||||
"public" => [fake_socket]
|
||||
}
|
||||
Streamer.add_socket("public", user)
|
||||
|
||||
Worker.push_to_socket(topics, "public", activity)
|
||||
{: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
|
||||
|
||||
Task.await(task)
|
||||
test "it sends to public unauthenticated" do
|
||||
user = insert(:user)
|
||||
|
||||
task =
|
||||
Task.async(fn ->
|
||||
expected_event =
|
||||
%{
|
||||
"event" => "delete",
|
||||
"payload" => activity.id
|
||||
}
|
||||
|> Jason.encode!()
|
||||
Streamer.add_socket("public", nil)
|
||||
|
||||
assert_receive {:text, received_event}, @streamer_timeout
|
||||
assert received_event == expected_event
|
||||
end)
|
||||
{: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)
|
||||
|
||||
fake_socket = %StreamerSocket{
|
||||
transport_pid: task.pid,
|
||||
user: user
|
||||
}
|
||||
|
||||
{:ok, activity} = CommonAPI.delete(activity.id, other_user)
|
||||
|
||||
topics = %{
|
||||
"public" => [fake_socket]
|
||||
}
|
||||
|
||||
Worker.push_to_socket(topics, "public", activity)
|
||||
|
||||
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)
|
||||
|
|
@ -256,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
|
||||
|
|
@ -279,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
|
||||
|
|
@ -302,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)
|
||||
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, user_a} = User.follow(user_a, user_b)
|
||||
{:ok, user_a} = User.follow(user_a, user_b)
|
||||
|
||||
{:ok, list} = List.create("Test", user_a)
|
||||
{:ok, list} = List.follow(list, 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"
|
||||
})
|
||||
Streamer.add_socket("list:#{list.id}", user_a)
|
||||
|
||||
task =
|
||||
Task.async(fn ->
|
||||
refute_receive {:text, _}, 1_000
|
||||
end)
|
||||
{:ok, _activity} =
|
||||
CommonAPI.post(user_b, %{
|
||||
"status" => "@#{user_c.nickname} Test",
|
||||
"visibility" => "direct"
|
||||
})
|
||||
|
||||
fake_socket = %StreamerSocket{
|
||||
transport_pid: task.pid,
|
||||
user: user_a
|
||||
}
|
||||
refute_receive _
|
||||
end
|
||||
|
||||
topics = %{
|
||||
"list:#{list.id}" => [fake_socket]
|
||||
}
|
||||
test "it doesn't send unwanted private posts to list" do
|
||||
user_a = insert(:user)
|
||||
user_b = insert(:user)
|
||||
|
||||
Worker.handle_call({:stream, "list", activity}, self(), topics)
|
||||
{:ok, list} = List.create("Test", user_a)
|
||||
{:ok, list} = List.follow(list, user_b)
|
||||
|
||||
Task.await(task)
|
||||
Streamer.add_socket("list:#{list.id}", user_a)
|
||||
|
||||
{:ok, _activity} =
|
||||
CommonAPI.post(user_b, %{
|
||||
"status" => "Test",
|
||||
"visibility" => "private"
|
||||
})
|
||||
|
||||
refute_receive _
|
||||
end
|
||||
|
||||
test "it sends wanted private posts to list" do
|
||||
user_a = insert(:user)
|
||||
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.add_socket("list:#{list.id}", user_a)
|
||||
|
||||
{: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
|
||||
|
||||
test "it doesn't send unwanted private posts to list" do
|
||||
user_a = insert(:user)
|
||||
user_b = insert(:user)
|
||||
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)
|
||||
|
||||
{:ok, list} = List.create("Test", user_a)
|
||||
{:ok, list} = List.follow(list, user_b)
|
||||
{:ok, create_activity} = CommonAPI.post(user3, %{"status" => "I'm kawen"})
|
||||
|
||||
{:ok, activity} =
|
||||
CommonAPI.post(user_b, %{
|
||||
"status" => "Test",
|
||||
"visibility" => "private"
|
||||
})
|
||||
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
|
||||
|
||||
task =
|
||||
Task.async(fn ->
|
||||
refute_receive {:text, _}, 1_000
|
||||
end)
|
||||
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)
|
||||
|
||||
fake_socket = %StreamerSocket{
|
||||
transport_pid: task.pid,
|
||||
user: user_a
|
||||
}
|
||||
{:ok, create_activity} = CommonAPI.post(user1, %{"status" => "I'm kawen"})
|
||||
Streamer.add_socket("user", user1)
|
||||
{:ok, _favorite_activity, _} = CommonAPI.repeat(create_activity.id, user2)
|
||||
|
||||
topics = %{
|
||||
"list:#{list.id}" => [fake_socket]
|
||||
}
|
||||
assert_receive {:render_with_user, _, "notification.json", notif}
|
||||
assert Streamer.filtered_by_user?(user1, notif)
|
||||
end
|
||||
|
||||
Worker.handle_call({:stream, "list", activity}, self(), topics)
|
||||
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)
|
||||
|
||||
Task.await(task)
|
||||
{:ok, create_activity} = CommonAPI.post(user1, %{"status" => "I'm kawen"})
|
||||
Streamer.add_socket("user", user1)
|
||||
{: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
|
||||
|
||||
test "it sends wanted private posts to list" do
|
||||
user_a = insert(:user)
|
||||
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)
|
||||
|
||||
{:ok, activity} =
|
||||
CommonAPI.post(user_b, %{
|
||||
"status" => "Test",
|
||||
"visibility" => "private"
|
||||
})
|
||||
|
||||
task =
|
||||
Task.async(fn ->
|
||||
assert_receive {:text, _}, 1_000
|
||||
end)
|
||||
|
||||
fake_socket = %StreamerSocket{
|
||||
transport_pid: task.pid,
|
||||
user: user_a
|
||||
}
|
||||
|
||||
Streamer.add_socket(
|
||||
"list:#{list.id}",
|
||||
fake_socket
|
||||
)
|
||||
|
||||
Worker.handle_call({:stream, "list", activity}, self(), %{})
|
||||
|
||||
Task.await(task)
|
||||
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)
|
||||
|
||||
Task.await(task)
|
||||
end
|
||||
|
||||
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)
|
||||
|
||||
{:ok, create_activity} = CommonAPI.post(user3, %{"status" => "I'm kawen"})
|
||||
{:ok, favorite_activity} = CommonAPI.favorite(user2, create_activity.id)
|
||||
|
||||
task =
|
||||
Task.async(fn ->
|
||||
assert_receive {:text, _}, 1_000
|
||||
end)
|
||||
|
||||
fake_socket = %StreamerSocket{
|
||||
transport_pid: task.pid,
|
||||
user: user1
|
||||
}
|
||||
|
||||
topics = %{
|
||||
"public" => [fake_socket]
|
||||
}
|
||||
|
||||
Worker.push_to_socket(topics, "public", favorite_activity)
|
||||
|
||||
Task.await(task)
|
||||
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
|
||||
|
|
@ -562,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, %{
|
||||
|
|
@ -585,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, %{
|
||||
|
|
@ -630,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 %{"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)
|
||||
|
||||
Process.sleep(@streamer_start_wait)
|
||||
|
||||
Streamer.add_socket(
|
||||
"direct",
|
||||
%{transport_pid: task.pid, assigns: %{user: user}}
|
||||
)
|
||||
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)
|
||||
|
||||
Task.await(task)
|
||||
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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue