Merge branch 'develop' into mastodon-admin-api
This commit is contained in:
commit
bab24878c7
188 changed files with 5350 additions and 1212 deletions
1
test/fixtures/users_mock/hubzilla-actor-alsoknownas-string.json
vendored
Normal file
1
test/fixtures/users_mock/hubzilla-actor-alsoknownas-string.json
vendored
Normal file
File diff suppressed because one or more lines are too long
|
|
@ -144,7 +144,13 @@ defmodule Mix.Tasks.Pleroma.ConfigTest do
|
|||
quarantined_instances: [],
|
||||
managed_config: true,
|
||||
static_dir: "instance/static/",
|
||||
allowed_post_formats: ["text/plain", "text/html", "text/markdown", "text/bbcode"],
|
||||
allowed_post_formats: [
|
||||
"text/plain",
|
||||
"text/html",
|
||||
"text/markdown",
|
||||
"text/bbcode",
|
||||
"text/x.misskeymarkdown"
|
||||
],
|
||||
autofollowed_nicknames: [],
|
||||
max_pinned_statuses: 1,
|
||||
attachment_links: false,
|
||||
|
|
|
|||
|
|
@ -174,6 +174,22 @@ defmodule Pleroma.ConfigDBTest do
|
|||
assert updated1.value == [groups: [c: 3, d: 4], key: [a: 1, b: 2]]
|
||||
assert updated2.value == [mascots: [c: 3, d: 4], key: [a: 1, b: 2]]
|
||||
end
|
||||
|
||||
test "rejects invalid :rate_limit values (e.g. empty-string scale from AdminFE)" do
|
||||
assert {:error, _changeset} =
|
||||
ConfigDB.update_or_create(%{
|
||||
group: ":pleroma",
|
||||
key: ":rate_limit",
|
||||
value: [
|
||||
%{
|
||||
"tuple" => [
|
||||
":statuses_actions",
|
||||
[%{"tuple" => ["", 0]}, %{"tuple" => ["", ""]}]
|
||||
]
|
||||
}
|
||||
]
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
describe "delete/1" do
|
||||
|
|
@ -273,24 +289,28 @@ defmodule Pleroma.ConfigDBTest do
|
|||
end
|
||||
|
||||
test "sigil" do
|
||||
assert ConfigDB.to_elixir_types("~r[comp[lL][aA][iI][nN]er]") == ~r/comp[lL][aA][iI][nN]er/
|
||||
assert ConfigDB.to_elixir_types("~r[comp[lL][aA][iI][nN]er]").source ==
|
||||
~r/comp[lL][aA][iI][nN]er/.source
|
||||
end
|
||||
|
||||
test "link sigil" do
|
||||
assert ConfigDB.to_elixir_types("~r/https:\/\/example.com/") == ~r/https:\/\/example.com/
|
||||
assert ConfigDB.to_elixir_types("~r/https:\/\/example.com/").source ==
|
||||
~r/https:\/\/example.com/.source
|
||||
end
|
||||
|
||||
test "link sigil with um modifiers" do
|
||||
assert ConfigDB.to_elixir_types("~r/https:\/\/example.com/um") ==
|
||||
~r/https:\/\/example.com/um
|
||||
assert ConfigDB.to_elixir_types("~r/https:\/\/example.com/um").source ==
|
||||
~r/https:\/\/example.com/um.source
|
||||
end
|
||||
|
||||
test "link sigil with i modifier" do
|
||||
assert ConfigDB.to_elixir_types("~r/https:\/\/example.com/i") == ~r/https:\/\/example.com/i
|
||||
assert ConfigDB.to_elixir_types("~r/https:\/\/example.com/i").source ==
|
||||
~r/https:\/\/example.com/i.source
|
||||
end
|
||||
|
||||
test "link sigil with s modifier" do
|
||||
assert ConfigDB.to_elixir_types("~r/https:\/\/example.com/s") == ~r/https:\/\/example.com/s
|
||||
assert ConfigDB.to_elixir_types("~r/https:\/\/example.com/s").source ==
|
||||
~r/https:\/\/example.com/s.source
|
||||
end
|
||||
|
||||
test "raise if valid delimiter not found" do
|
||||
|
|
@ -460,11 +480,11 @@ defmodule Pleroma.ConfigDBTest do
|
|||
test "complex keyword with sigil" do
|
||||
assert ConfigDB.to_elixir_types([
|
||||
%{"tuple" => [":federated_timeline_removal", []]},
|
||||
%{"tuple" => [":reject", ["~r/comp[lL][aA][iI][nN]er/"]]},
|
||||
%{"tuple" => [":reject", [~r/comp[lL][aA][iI][nN]er/.source]]},
|
||||
%{"tuple" => [":replace", []]}
|
||||
]) == [
|
||||
federated_timeline_removal: [],
|
||||
reject: [~r/comp[lL][aA][iI][nN]er/],
|
||||
reject: [~r/comp[lL][aA][iI][nN]er/.source],
|
||||
replace: []
|
||||
]
|
||||
end
|
||||
|
|
|
|||
|
|
@ -88,6 +88,49 @@ defmodule Pleroma.HTTP.HackneyFollowRedirectRegressionTest do
|
|||
Pleroma.ReverseProxy.Client.Hackney.close(ref)
|
||||
end
|
||||
|
||||
test "hackney reverse proxy follows nested redirects via proxy", %{
|
||||
tls_server: tls_server,
|
||||
proxy: proxy
|
||||
} do
|
||||
url = "#{tls_server.base_url}/nested_redirect"
|
||||
|
||||
opts = [
|
||||
pool: :media,
|
||||
proxy: proxy.proxy_url,
|
||||
insecure: true,
|
||||
connect_timeout: 1_000,
|
||||
recv_timeout: 1_000,
|
||||
follow_redirect: true
|
||||
]
|
||||
|
||||
assert {:ok, 200, _headers, ref} =
|
||||
Pleroma.ReverseProxy.Client.Hackney.request(:get, url, [], "", opts)
|
||||
|
||||
assert collect_body(ref) == "ok"
|
||||
Pleroma.ReverseProxy.Client.Hackney.close(ref)
|
||||
end
|
||||
|
||||
test "hackney reverse proxy stops following redirects after limit is reached", %{
|
||||
tls_server: tls_server,
|
||||
proxy: proxy
|
||||
} do
|
||||
url = "#{tls_server.base_url}/infinite_redirect"
|
||||
|
||||
opts = [
|
||||
pool: :media,
|
||||
proxy: proxy.proxy_url,
|
||||
insecure: true,
|
||||
connect_timeout: 1_000,
|
||||
recv_timeout: 1_000,
|
||||
follow_redirect: true
|
||||
]
|
||||
|
||||
assert {:ok, 302, _headers, ref} =
|
||||
Pleroma.ReverseProxy.Client.Hackney.request(:get, url, [], "", opts)
|
||||
|
||||
Pleroma.ReverseProxy.Client.Hackney.close(ref)
|
||||
end
|
||||
|
||||
defp collect_body(ref, acc \\ "") do
|
||||
case Pleroma.ReverseProxy.Client.Hackney.stream_body(ref) do
|
||||
:done -> acc
|
||||
|
|
@ -148,6 +191,12 @@ defmodule Pleroma.HTTP.HackneyFollowRedirectRegressionTest do
|
|||
{:ok, data} <- recv_ssl_headers(ssl_socket),
|
||||
{:ok, path} <- parse_path(data) do
|
||||
case path do
|
||||
"/infinite_redirect" ->
|
||||
send_ssl_response(ssl_socket, 302, "Found", [{"Location", "/infinite_redirect"}], "")
|
||||
|
||||
"/nested_redirect" ->
|
||||
send_ssl_response(ssl_socket, 302, "Found", [{"Location", "/redirect"}], "")
|
||||
|
||||
"/redirect" ->
|
||||
send_ssl_response(ssl_socket, 302, "Found", [{"Location", "/final"}], "")
|
||||
|
||||
|
|
|
|||
|
|
@ -363,7 +363,7 @@ defmodule Pleroma.Integration.MastodonWebsocketTest do
|
|||
test "accepts the 'list' stream", %{token: token, user: user} do
|
||||
posting_user = insert(:user)
|
||||
|
||||
{:ok, list} = Pleroma.List.create("test", user)
|
||||
{:ok, list} = Pleroma.List.create(%{title: "test"}, user)
|
||||
Pleroma.List.follow(list, posting_user)
|
||||
|
||||
assert {:ok, _} = start_socket("?stream=list&access_token=#{token.token}&list=#{list.id}")
|
||||
|
|
|
|||
|
|
@ -10,22 +10,23 @@ defmodule Pleroma.ListTest do
|
|||
|
||||
test "creating a list" do
|
||||
user = insert(:user)
|
||||
{:ok, %Pleroma.List{} = list} = Pleroma.List.create("title", user)
|
||||
%Pleroma.List{title: title} = Pleroma.List.get(list.id, user)
|
||||
{:ok, %Pleroma.List{} = list} = Pleroma.List.create(%{title: "title"}, user)
|
||||
%Pleroma.List{title: title, exclusive: exclusive} = Pleroma.List.get(list.id, user)
|
||||
assert title == "title"
|
||||
assert exclusive == false
|
||||
end
|
||||
|
||||
test "validates title" do
|
||||
user = insert(:user)
|
||||
|
||||
assert {:error, changeset} = Pleroma.List.create("", user)
|
||||
assert {:error, changeset} = Pleroma.List.create(%{title: ""}, user)
|
||||
assert changeset.errors == [title: {"can't be blank", [validation: :required]}]
|
||||
end
|
||||
|
||||
test "getting a list not belonging to the user" do
|
||||
user = insert(:user)
|
||||
other_user = insert(:user)
|
||||
{:ok, %Pleroma.List{} = list} = Pleroma.List.create("title", user)
|
||||
{:ok, %Pleroma.List{} = list} = Pleroma.List.create(%{title: "title"}, user)
|
||||
ret = Pleroma.List.get(list.id, other_user)
|
||||
assert is_nil(ret)
|
||||
end
|
||||
|
|
@ -33,7 +34,7 @@ defmodule Pleroma.ListTest do
|
|||
test "adding an user to a list" do
|
||||
user = insert(:user)
|
||||
other_user = insert(:user)
|
||||
{:ok, list} = Pleroma.List.create("title", user)
|
||||
{:ok, list} = Pleroma.List.create(%{title: "title"}, user)
|
||||
{:ok, %{following: following}} = Pleroma.List.follow(list, other_user)
|
||||
assert [other_user.follower_address] == following
|
||||
end
|
||||
|
|
@ -41,7 +42,7 @@ defmodule Pleroma.ListTest do
|
|||
test "removing an user from a list" do
|
||||
user = insert(:user)
|
||||
other_user = insert(:user)
|
||||
{:ok, list} = Pleroma.List.create("title", user)
|
||||
{:ok, list} = Pleroma.List.create(%{title: "title"}, user)
|
||||
{:ok, %{following: _following}} = Pleroma.List.follow(list, other_user)
|
||||
{:ok, %{following: following}} = Pleroma.List.unfollow(list, other_user)
|
||||
assert [] == following
|
||||
|
|
@ -49,14 +50,27 @@ defmodule Pleroma.ListTest do
|
|||
|
||||
test "renaming a list" do
|
||||
user = insert(:user)
|
||||
{:ok, list} = Pleroma.List.create("title", user)
|
||||
{:ok, %{title: title}} = Pleroma.List.rename(list, "new")
|
||||
{:ok, list} = Pleroma.List.create(%{title: "title"}, user)
|
||||
{:ok, %{title: title}} = Pleroma.List.update(list, %{title: "new"})
|
||||
assert "new" == title
|
||||
end
|
||||
|
||||
test "updating a list exclusivity" do
|
||||
user = insert(:user)
|
||||
|
||||
{:ok, %{exclusive: exclusive} = list} =
|
||||
Pleroma.List.create(%{title: "title", exclusive: true}, user)
|
||||
|
||||
assert exclusive == true
|
||||
{:ok, %{exclusive: exclusive} = list} = Pleroma.List.update(list, %{exclusive: false})
|
||||
assert exclusive == false
|
||||
{:ok, %{exclusive: exclusive}} = Pleroma.List.update(list, %{exclusive: true})
|
||||
assert exclusive == true
|
||||
end
|
||||
|
||||
test "deleting a list" do
|
||||
user = insert(:user)
|
||||
{:ok, list} = Pleroma.List.create("title", user)
|
||||
{:ok, list} = Pleroma.List.create(%{title: "title"}, user)
|
||||
{:ok, list} = Pleroma.List.delete(list)
|
||||
assert is_nil(Repo.get(Pleroma.List, list.id))
|
||||
end
|
||||
|
|
@ -65,7 +79,7 @@ defmodule Pleroma.ListTest do
|
|||
user = insert(:user)
|
||||
other_user = insert(:user)
|
||||
third_user = insert(:user)
|
||||
{:ok, list} = Pleroma.List.create("title", user)
|
||||
{:ok, list} = Pleroma.List.create(%{title: "title"}, user)
|
||||
{:ok, list} = Pleroma.List.follow(list, other_user)
|
||||
{:ok, list} = Pleroma.List.follow(list, third_user)
|
||||
{:ok, following} = Pleroma.List.get_following(list)
|
||||
|
|
@ -76,9 +90,9 @@ defmodule Pleroma.ListTest do
|
|||
test "getting all lists by an user" do
|
||||
user = insert(:user)
|
||||
other_user = insert(:user)
|
||||
{:ok, list_one} = Pleroma.List.create("title", user)
|
||||
{:ok, list_two} = Pleroma.List.create("other title", user)
|
||||
{:ok, list_three} = Pleroma.List.create("third title", other_user)
|
||||
{:ok, list_one} = Pleroma.List.create(%{title: "title"}, user)
|
||||
{:ok, list_two} = Pleroma.List.create(%{title: "other title"}, user)
|
||||
{:ok, list_three} = Pleroma.List.create(%{title: "third title"}, other_user)
|
||||
lists = Pleroma.List.for_user(user, %{})
|
||||
assert list_one in lists
|
||||
assert list_two in lists
|
||||
|
|
@ -88,9 +102,9 @@ defmodule Pleroma.ListTest do
|
|||
test "getting all lists the user is a member of" do
|
||||
user = insert(:user)
|
||||
other_user = insert(:user)
|
||||
{:ok, list_one} = Pleroma.List.create("title", user)
|
||||
{:ok, list_two} = Pleroma.List.create("other title", user)
|
||||
{:ok, list_three} = Pleroma.List.create("third title", other_user)
|
||||
{:ok, list_one} = Pleroma.List.create(%{title: "title"}, user)
|
||||
{:ok, list_two} = Pleroma.List.create(%{title: "other title"}, user)
|
||||
{:ok, list_three} = Pleroma.List.create(%{title: "third title"}, other_user)
|
||||
{:ok, list_one} = Pleroma.List.follow(list_one, other_user)
|
||||
{:ok, list_two} = Pleroma.List.follow(list_two, other_user)
|
||||
{:ok, list_three} = Pleroma.List.follow(list_three, user)
|
||||
|
|
@ -106,8 +120,8 @@ defmodule Pleroma.ListTest do
|
|||
not_owner = insert(:user)
|
||||
member_1 = insert(:user)
|
||||
member_2 = insert(:user)
|
||||
{:ok, owned_list} = Pleroma.List.create("owned", owner)
|
||||
{:ok, not_owned_list} = Pleroma.List.create("not owned", not_owner)
|
||||
{:ok, owned_list} = Pleroma.List.create(%{title: "owned"}, owner)
|
||||
{:ok, not_owned_list} = Pleroma.List.create(%{title: "not owned"}, not_owner)
|
||||
{:ok, owned_list} = Pleroma.List.follow(owned_list, member_1)
|
||||
{:ok, owned_list} = Pleroma.List.follow(owned_list, member_2)
|
||||
{:ok, not_owned_list} = Pleroma.List.follow(not_owned_list, member_1)
|
||||
|
|
@ -123,14 +137,14 @@ defmodule Pleroma.ListTest do
|
|||
|
||||
test "get by ap_id" do
|
||||
user = insert(:user)
|
||||
{:ok, list} = Pleroma.List.create("foo", user)
|
||||
{:ok, list} = Pleroma.List.create(%{title: "foo"}, user)
|
||||
assert Pleroma.List.get_by_ap_id(list.ap_id) == list
|
||||
end
|
||||
|
||||
test "memberships" do
|
||||
user = insert(:user)
|
||||
member = insert(:user)
|
||||
{:ok, list} = Pleroma.List.create("foo", user)
|
||||
{:ok, list} = Pleroma.List.create(%{title: "foo"}, user)
|
||||
{:ok, list} = Pleroma.List.follow(list, member)
|
||||
|
||||
assert Pleroma.List.memberships(member) == [list.ap_id]
|
||||
|
|
@ -140,7 +154,7 @@ defmodule Pleroma.ListTest do
|
|||
user = insert(:user)
|
||||
member = insert(:user)
|
||||
|
||||
{:ok, list} = Pleroma.List.create("foo", user)
|
||||
{:ok, list} = Pleroma.List.create(%{title: "foo"}, user)
|
||||
{:ok, list} = Pleroma.List.follow(list, member)
|
||||
|
||||
assert Pleroma.List.member?(list, member)
|
||||
|
|
|
|||
|
|
@ -36,11 +36,12 @@ defmodule Pleroma.MarkerTest do
|
|||
insert(:notification, user: user, activity: insert(:note_activity))
|
||||
insert(:notification, user: user, activity: insert(:note_activity))
|
||||
insert(:marker, timeline: "home", user: user)
|
||||
%Marker{} = refreshed_marker = refresh_record(marker)
|
||||
|
||||
assert Marker.get_markers(
|
||||
user,
|
||||
["notifications"]
|
||||
) == [%Marker{refresh_record(marker) | unread_count: 2}]
|
||||
) == [%{refreshed_marker | unread_count: 2}]
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
293
test/pleroma/pleroma_ctl_test.exs
Normal file
293
test/pleroma/pleroma_ctl_test.exs
Normal file
|
|
@ -0,0 +1,293 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.PleromaCtlTest do
|
||||
use ExUnit.Case, async: false
|
||||
|
||||
@pleroma_ctl Path.expand("../../rel/files/bin/pleroma_ctl", __DIR__)
|
||||
|
||||
setup do
|
||||
tmp_dir =
|
||||
Path.join(System.tmp_dir!(), "pleroma_ctl_test_#{System.unique_integer([:positive])}")
|
||||
|
||||
release_root = Path.join(tmp_dir, "release")
|
||||
bin_dir = Path.join(release_root, "bin")
|
||||
|
||||
File.mkdir_p!(bin_dir)
|
||||
File.cp!(@pleroma_ctl, Path.join(bin_dir, "pleroma_ctl"))
|
||||
File.chmod!(Path.join(bin_dir, "pleroma_ctl"), 0o755)
|
||||
|
||||
on_exit(fn -> File.rm_rf!(tmp_dir) end)
|
||||
|
||||
{:ok, tmp_dir: tmp_dir, release_root: release_root, bin_dir: bin_dir}
|
||||
end
|
||||
|
||||
test "update downloads branch-scoped latest OTP package", %{
|
||||
tmp_dir: tmp_dir,
|
||||
bin_dir: bin_dir,
|
||||
release_root: release_root
|
||||
} do
|
||||
stubs_dir = Path.join(tmp_dir, "stubs")
|
||||
curl_args_path = Path.join(tmp_dir, "curl.args")
|
||||
File.mkdir_p!(stubs_dir)
|
||||
write_update_stubs(stubs_dir, curl_args_path, "unused", "glibc")
|
||||
|
||||
update_tmp_dir = Path.join(tmp_dir, "update_tmp")
|
||||
File.mkdir_p!(update_tmp_dir)
|
||||
|
||||
{output, status} =
|
||||
System.cmd(
|
||||
Path.join(bin_dir, "pleroma_ctl"),
|
||||
[
|
||||
"update",
|
||||
"--branch",
|
||||
"develop",
|
||||
"--flavour",
|
||||
"amd64",
|
||||
"--tmp-dir",
|
||||
update_tmp_dir,
|
||||
"--no-rm"
|
||||
],
|
||||
env: [{"PATH", stubs_dir <> ":" <> System.get_env("PATH", "")}],
|
||||
stderr_to_stdout: true
|
||||
)
|
||||
|
||||
assert status == 0, output
|
||||
assert File.exists?(Path.join(release_root, "bin/marker"))
|
||||
|
||||
assert ["-fL", url, "-o", artifact_path] =
|
||||
curl_args_path
|
||||
|> File.read!()
|
||||
|> String.split("\n", trim: true)
|
||||
|
||||
assert url ==
|
||||
"https://git.pleroma.social/api/packages/pleroma/generic/pleroma-otp-develop-amd64/latest/pleroma.zip"
|
||||
|
||||
assert artifact_path == Path.join(update_tmp_dir, "pleroma.zip")
|
||||
end
|
||||
|
||||
test "update detects stable branch and local flavour", %{
|
||||
tmp_dir: tmp_dir,
|
||||
bin_dir: bin_dir,
|
||||
release_root: release_root
|
||||
} do
|
||||
stubs_dir = Path.join(tmp_dir, "stubs")
|
||||
curl_args_path = Path.join(tmp_dir, "curl.args")
|
||||
File.mkdir_p!(stubs_dir)
|
||||
write_update_stubs(stubs_dir, curl_args_path, "x86_64", "glibc")
|
||||
write_start_erl_data(release_root, "2.10.0")
|
||||
|
||||
{output, status} =
|
||||
System.cmd(
|
||||
Path.join(bin_dir, "pleroma_ctl"),
|
||||
["update", "--tmp-dir", create_update_tmp_dir(tmp_dir), "--no-rm"],
|
||||
env: [{"PATH", stubs_dir <> ":" <> System.get_env("PATH", "")}],
|
||||
stderr_to_stdout: true
|
||||
)
|
||||
|
||||
assert status == 0, output
|
||||
|
||||
assert curl_url(curl_args_path) ==
|
||||
"https://git.pleroma.social/api/packages/pleroma/generic/pleroma-otp-stable-amd64/latest/pleroma.zip"
|
||||
end
|
||||
|
||||
test "update detects develop branch and musl arm flavour", %{
|
||||
tmp_dir: tmp_dir,
|
||||
bin_dir: bin_dir,
|
||||
release_root: release_root
|
||||
} do
|
||||
stubs_dir = Path.join(tmp_dir, "stubs")
|
||||
curl_args_path = Path.join(tmp_dir, "curl.args")
|
||||
File.mkdir_p!(stubs_dir)
|
||||
write_update_stubs(stubs_dir, curl_args_path, "armv7l", "musl")
|
||||
write_start_erl_data(release_root, "2.10.0.develop")
|
||||
|
||||
{output, status} =
|
||||
System.cmd(
|
||||
Path.join(bin_dir, "pleroma_ctl"),
|
||||
["update", "--tmp-dir", create_update_tmp_dir(tmp_dir), "--no-rm"],
|
||||
env: [{"PATH", stubs_dir <> ":" <> System.get_env("PATH", "")}],
|
||||
stderr_to_stdout: true
|
||||
)
|
||||
|
||||
assert status == 0, output
|
||||
|
||||
assert curl_url(curl_args_path) ==
|
||||
"https://git.pleroma.social/api/packages/pleroma/generic/pleroma-otp-develop-arm-musl/latest/pleroma.zip"
|
||||
end
|
||||
|
||||
test "update with zip URL bypasses branch and flavour detection", %{
|
||||
tmp_dir: tmp_dir,
|
||||
bin_dir: bin_dir,
|
||||
release_root: release_root
|
||||
} do
|
||||
stubs_dir = Path.join(tmp_dir, "stubs")
|
||||
curl_args_path = Path.join(tmp_dir, "curl.args")
|
||||
File.mkdir_p!(stubs_dir)
|
||||
write_update_stubs(stubs_dir, curl_args_path, "unsupported-arch", "unsupported-libc")
|
||||
write_start_erl_data(release_root, "2.10.0.custombranch")
|
||||
|
||||
custom_url = "https://example.test/custom.zip"
|
||||
|
||||
{output, status} =
|
||||
System.cmd(
|
||||
Path.join(bin_dir, "pleroma_ctl"),
|
||||
[
|
||||
"update",
|
||||
"--zip-url",
|
||||
custom_url,
|
||||
"--tmp-dir",
|
||||
create_update_tmp_dir(tmp_dir),
|
||||
"--no-rm"
|
||||
],
|
||||
env: [{"PATH", stubs_dir <> ":" <> System.get_env("PATH", "")}],
|
||||
stderr_to_stdout: true
|
||||
)
|
||||
|
||||
assert status == 0, output
|
||||
assert curl_url(curl_args_path) == custom_url
|
||||
end
|
||||
|
||||
test "passes arguments with spaces and Elixir string metacharacters", %{
|
||||
tmp_dir: tmp_dir,
|
||||
bin_dir: bin_dir
|
||||
} do
|
||||
capture_path = Path.join(tmp_dir, "captured_args")
|
||||
eval_path = Path.join(tmp_dir, "pleroma_ctl_eval.exs")
|
||||
|
||||
write_executable(Path.join(bin_dir, "pleroma"), """
|
||||
#!/bin/sh
|
||||
{
|
||||
printf '%s\n' 'defmodule Pleroma.ReleaseTasks do'
|
||||
printf '%s\n' ' def run(args), do: File.write!(System.fetch_env!("PLEROMA_CTL_CAPTURE"), :erlang.term_to_binary(args))'
|
||||
printf '%s\n' 'end'
|
||||
printf '%s\n' "$2"
|
||||
} > "$PLEROMA_CTL_EVAL_FILE"
|
||||
|
||||
exec elixir "$PLEROMA_CTL_EVAL_FILE"
|
||||
""")
|
||||
|
||||
{output, status} =
|
||||
System.cmd(
|
||||
Path.join(bin_dir, "pleroma_ctl"),
|
||||
[
|
||||
"user",
|
||||
"",
|
||||
"has space",
|
||||
~s(has "quote"),
|
||||
~s(has \\ backslash),
|
||||
~S(#{:not_interpolated})
|
||||
],
|
||||
env: [
|
||||
{"PLEROMA_CTL_CAPTURE", capture_path},
|
||||
{"PLEROMA_CTL_EVAL_FILE", eval_path}
|
||||
],
|
||||
stderr_to_stdout: true
|
||||
)
|
||||
|
||||
assert status == 0, output
|
||||
|
||||
assert capture_path
|
||||
|> File.read!()
|
||||
|> :erlang.binary_to_term() == [
|
||||
"user",
|
||||
"",
|
||||
"has space",
|
||||
~s(has "quote"),
|
||||
~s(has \\ backslash),
|
||||
~S(#{:not_interpolated})
|
||||
]
|
||||
end
|
||||
|
||||
defp write_executable(path, contents) do
|
||||
File.write!(path, contents)
|
||||
File.chmod!(path, 0o755)
|
||||
end
|
||||
|
||||
defp write_start_erl_data(release_root, version) do
|
||||
releases_dir = Path.join(release_root, "releases")
|
||||
File.mkdir_p!(releases_dir)
|
||||
File.write!(Path.join(releases_dir, "start_erl.data"), "erts-15.0 #{version}\n")
|
||||
end
|
||||
|
||||
defp create_update_tmp_dir(tmp_dir) do
|
||||
update_tmp_dir = Path.join(tmp_dir, "update_tmp")
|
||||
File.mkdir_p!(update_tmp_dir)
|
||||
update_tmp_dir
|
||||
end
|
||||
|
||||
defp write_update_stubs(stubs_dir, curl_args_path, arch, libc) do
|
||||
write_executable(Path.join(stubs_dir, "curl"), """
|
||||
#!/bin/sh
|
||||
printf '%s\n' "$@" > "#{curl_args_path}"
|
||||
|
||||
while [ $# -gt 0 ]; do
|
||||
case "$1" in
|
||||
-o)
|
||||
artifact="$2"
|
||||
shift 2
|
||||
;;
|
||||
*)
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
: > "$artifact"
|
||||
""")
|
||||
|
||||
write_executable(Path.join(stubs_dir, "unzip"), """
|
||||
#!/bin/sh
|
||||
while [ $# -gt 0 ]; do
|
||||
case "$1" in
|
||||
-d)
|
||||
dest="$2"
|
||||
shift 2
|
||||
;;
|
||||
*)
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
mkdir -p "$dest/release/bin"
|
||||
printf 'marker' > "$dest/release/bin/marker"
|
||||
""")
|
||||
|
||||
write_executable(Path.join(stubs_dir, "uname"), """
|
||||
#!/bin/sh
|
||||
printf '%s\n' '#{arch}'
|
||||
""")
|
||||
|
||||
write_executable(Path.join(stubs_dir, "getconf"), getconf_stub(libc))
|
||||
|
||||
write_executable(Path.join(stubs_dir, "ldd"), """
|
||||
#!/bin/sh
|
||||
printf '%s\n' 'musl libc (mock)'
|
||||
""")
|
||||
end
|
||||
|
||||
defp getconf_stub("glibc") do
|
||||
"""
|
||||
#!/bin/sh
|
||||
printf '%s\n' 'glibc 2.40'
|
||||
"""
|
||||
end
|
||||
|
||||
defp getconf_stub(_libc) do
|
||||
"""
|
||||
#!/bin/sh
|
||||
exit 1
|
||||
"""
|
||||
end
|
||||
|
||||
defp curl_url(curl_args_path) do
|
||||
["-fL", url, "-o", _artifact_path] =
|
||||
curl_args_path
|
||||
|> File.read!()
|
||||
|> String.split("\n", trim: true)
|
||||
|
||||
url
|
||||
end
|
||||
end
|
||||
|
|
@ -24,7 +24,8 @@ defmodule Pleroma.RepoTest do
|
|||
describe "get_assoc/2" do
|
||||
test "get assoc from preloaded data" do
|
||||
user = %User{name: "Agent Smith"}
|
||||
token = %Pleroma.Web.OAuth.Token{insert(:oauth_token) | user: user}
|
||||
%Pleroma.Web.OAuth.Token{} = token = insert(:oauth_token)
|
||||
token = %{token | user: user}
|
||||
assert Repo.get_assoc(token, :user) == {:ok, user}
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -294,7 +294,7 @@ defmodule Pleroma.ReverseProxyTest do
|
|||
|> expect(:stream_body, fn _ -> :done end)
|
||||
|
||||
conn = ReverseProxy.call(conn, "/cache")
|
||||
assert {"cache-control", "public, max-age=1209600"} in conn.resp_headers
|
||||
assert {"cache-control", "public, max-age=1209600, immutable"} in conn.resp_headers
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -74,29 +74,6 @@ defmodule Pleroma.Search.MeilisearchTest do
|
|||
assert_received("posted_to_meilisearch")
|
||||
end
|
||||
|
||||
test "doesn't index posts that are not public" do
|
||||
user = insert(:user)
|
||||
|
||||
Enum.each(["private", "direct"], fn visibility ->
|
||||
{:ok, activity} =
|
||||
CommonAPI.post(user, %{
|
||||
status: "guys i just don't wanna leave the swamp",
|
||||
visibility: visibility
|
||||
})
|
||||
|
||||
args = %{"op" => "add_to_index", "activity" => activity.id}
|
||||
|
||||
Config
|
||||
|> expect(:get, fn
|
||||
[Pleroma.Search, :module], nil ->
|
||||
Meilisearch
|
||||
end)
|
||||
|
||||
assert_enqueued(worker: SearchIndexingWorker, args: args)
|
||||
assert :ok = perform_job(SearchIndexingWorker, args)
|
||||
end)
|
||||
end
|
||||
|
||||
test "deletes posts from index when deleted locally" do
|
||||
user = insert(:user)
|
||||
|
||||
|
|
|
|||
69
test/pleroma/search_test.exs
Normal file
69
test/pleroma/search_test.exs
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.SearchTest do
|
||||
use Pleroma.DataCase, async: true
|
||||
use Oban.Testing, repo: Pleroma.Repo
|
||||
|
||||
import Pleroma.Factory
|
||||
|
||||
alias Pleroma.Web.CommonAPI
|
||||
alias Pleroma.Workers.SearchIndexingWorker
|
||||
|
||||
test "indexes posts that are public" do
|
||||
user = insert(:user)
|
||||
|
||||
{:ok, activity} =
|
||||
CommonAPI.post(user, %{
|
||||
status: "Well this is a story all about how my life got flipped turned upside down",
|
||||
visibility: "public"
|
||||
})
|
||||
|
||||
args = %{"op" => "add_to_index", "activity" => activity.id}
|
||||
|
||||
assert_enqueued(worker: SearchIndexingWorker, args: args)
|
||||
end
|
||||
|
||||
test "doesn't index posts that are not public" do
|
||||
user = insert(:user)
|
||||
|
||||
Enum.each(["private", "direct"], fn visibility ->
|
||||
{:ok, activity} =
|
||||
CommonAPI.post(user, %{
|
||||
status: "guys i just don't wanna leave the swamp",
|
||||
visibility: visibility
|
||||
})
|
||||
|
||||
args = %{"op" => "add_to_index", "activity" => activity.id}
|
||||
|
||||
refute_enqueued(worker: SearchIndexingWorker, args: args)
|
||||
end)
|
||||
end
|
||||
|
||||
test "Indexes appropriate activity types" do
|
||||
user = insert(:user)
|
||||
|
||||
{:ok, activity} =
|
||||
CommonAPI.post(user, %{
|
||||
status: "I'm my own hype man",
|
||||
visibility: "public"
|
||||
})
|
||||
|
||||
args = %{"op" => "add_to_index", "activity" => activity.id}
|
||||
|
||||
assert_enqueued(worker: SearchIndexingWorker, args: args)
|
||||
|
||||
{:ok, fav_activity} = CommonAPI.favorite(activity.id, user)
|
||||
|
||||
args = %{"op" => "add_to_index", "activity" => fav_activity.id}
|
||||
|
||||
refute_enqueued(worker: SearchIndexingWorker, args: args)
|
||||
|
||||
{:ok, repeat_activity} = CommonAPI.repeat(activity.id, user)
|
||||
|
||||
args = %{"op" => "add_to_index", "activity" => repeat_activity.id}
|
||||
|
||||
refute_enqueued(worker: SearchIndexingWorker, args: args)
|
||||
end
|
||||
end
|
||||
|
|
@ -876,17 +876,17 @@ defmodule Pleroma.UserTest do
|
|||
describe "get_or_fetch/1 remote users with tld, while BE is running on a subdomain" do
|
||||
setup do: clear_config([Pleroma.Web.WebFinger, :update_nickname_on_user_fetch], true)
|
||||
|
||||
test "for mastodon" do
|
||||
ap_id = "a@mastodon.example"
|
||||
{:ok, fetched_user} = User.get_or_fetch(ap_id)
|
||||
test "fetches a mastodon split-domain nickname" do
|
||||
nickname = "a@mastodon.example"
|
||||
{:ok, fetched_user} = User.get_or_fetch(nickname)
|
||||
|
||||
assert fetched_user.ap_id == "https://sub.mastodon.example/users/a"
|
||||
assert fetched_user.nickname == "a@mastodon.example"
|
||||
end
|
||||
|
||||
test "for pleroma" do
|
||||
ap_id = "a@pleroma.example"
|
||||
{:ok, fetched_user} = User.get_or_fetch(ap_id)
|
||||
test "fetches a pleroma split-domain nickname" do
|
||||
nickname = "a@pleroma.example"
|
||||
{:ok, fetched_user} = User.get_or_fetch(nickname)
|
||||
|
||||
assert fetched_user.ap_id == "https://sub.pleroma.example/users/a"
|
||||
assert fetched_user.nickname == "a@pleroma.example"
|
||||
|
|
@ -936,6 +936,89 @@ defmodule Pleroma.UserTest do
|
|||
assert fetched_user == "not found nonexistent"
|
||||
end
|
||||
|
||||
test "does not rename an existing remote actor from rogue WebFinger data" do
|
||||
clear_config([Pleroma.Web.WebFinger, :update_nickname_on_user_fetch], true)
|
||||
|
||||
actor_id = "https://legit-actor.example/users/alice"
|
||||
|
||||
Tesla.Mock.mock(fn
|
||||
%{url: "https://evil-webfinger.example/.well-known/host-meta"} ->
|
||||
{:ok, %Tesla.Env{status: 404}}
|
||||
|
||||
%{
|
||||
url:
|
||||
"https://evil-webfinger.example/.well-known/webfinger?resource=acct:claimed@evil-webfinger.example"
|
||||
} ->
|
||||
Tesla.Mock.json(%{
|
||||
"subject" => "acct:claimed@evil-webfinger.example",
|
||||
"links" => [
|
||||
%{
|
||||
"rel" => "self",
|
||||
"type" => "application/activity+json",
|
||||
"href" => actor_id
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
%{url: ^actor_id} ->
|
||||
{:ok,
|
||||
%Tesla.Env{
|
||||
status: 200,
|
||||
headers: [{"content-type", "application/activity+json"}],
|
||||
body:
|
||||
Jason.encode!(%{
|
||||
"id" => actor_id,
|
||||
"type" => "Person",
|
||||
"preferredUsername" => "alice",
|
||||
"name" => "Alice",
|
||||
"summary" => "",
|
||||
"inbox" => "https://legit-actor.example/users/alice/inbox",
|
||||
"outbox" => "https://legit-actor.example/users/alice/outbox",
|
||||
"followers" => "https://legit-actor.example/users/alice/followers",
|
||||
"following" => "https://legit-actor.example/users/alice/following"
|
||||
})
|
||||
}}
|
||||
|
||||
%{url: "https://legit-actor.example/.well-known/host-meta"} ->
|
||||
{:ok, %Tesla.Env{status: 404}}
|
||||
|
||||
%{
|
||||
url:
|
||||
"https://legit-actor.example/.well-known/webfinger?resource=acct:alice@legit-actor.example"
|
||||
} ->
|
||||
Tesla.Mock.json(%{
|
||||
"subject" => "acct:alice@legit-actor.example",
|
||||
"links" => [
|
||||
%{
|
||||
"rel" => "self",
|
||||
"type" => "application/activity+json",
|
||||
"href" => actor_id
|
||||
}
|
||||
]
|
||||
})
|
||||
end)
|
||||
|
||||
assert {:error, {:webfinger_actor_mismatch, "claimed@evil-webfinger.example", ^actor_id}} =
|
||||
ActivityPub.make_user_from_nickname("claimed@evil-webfinger.example")
|
||||
|
||||
refute User.get_by_ap_id(actor_id)
|
||||
refute User.get_by_nickname("claimed@evil-webfinger.example")
|
||||
|
||||
orig_user =
|
||||
insert(:user,
|
||||
local: false,
|
||||
nickname: "alice@legit-actor.example",
|
||||
ap_id: actor_id
|
||||
)
|
||||
|
||||
assert {:error, {:webfinger_actor_mismatch, "claimed@evil-webfinger.example", ^actor_id}} =
|
||||
ActivityPub.make_user_from_nickname("claimed@evil-webfinger.example")
|
||||
|
||||
assert {:error, _} = User.get_or_fetch_by_nickname("claimed@evil-webfinger.example")
|
||||
assert User.get_by_id(orig_user.id).nickname == "alice@legit-actor.example"
|
||||
refute User.get_by_nickname("claimed@evil-webfinger.example")
|
||||
end
|
||||
|
||||
test "updates an existing user, if stale" do
|
||||
a_week_ago = NaiveDateTime.add(NaiveDateTime.utc_now(), -604_800)
|
||||
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
|
|||
alias Pleroma.Web.CommonAPI
|
||||
alias Pleroma.Web.Endpoint
|
||||
alias Pleroma.Workers.ReceiverWorker
|
||||
alias Pleroma.Workers.SignatureRetryWorker
|
||||
|
||||
import Pleroma.Factory
|
||||
|
||||
|
|
@ -36,6 +37,36 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
|
|||
|
||||
setup do: clear_config([:instance, :federating], true)
|
||||
|
||||
defp assign_valid_signature_for_actor(conn, %User{ap_id: actor_id}) do
|
||||
assign_valid_signature_for_actor(conn, actor_id)
|
||||
end
|
||||
|
||||
defp assign_valid_signature_for_actor(conn, actor) do
|
||||
actor_id = Utils.get_ap_id(actor)
|
||||
|
||||
conn
|
||||
|> assign(:valid_signature, true)
|
||||
|> put_req_header("signature", "keyId=\"#{actor_id}#main-key\"")
|
||||
end
|
||||
|
||||
defp expect_signature_retry_from(%User{} = signer) do
|
||||
signer_json = UserView.render("user.json", %{user: signer}) |> Map.delete("featured")
|
||||
|
||||
Tesla.Mock.mock(fn
|
||||
%{url: url} when url == signer.ap_id ->
|
||||
%Tesla.Env{
|
||||
status: 200,
|
||||
body: Jason.encode!(signer_json),
|
||||
headers: HttpRequestMock.activitypub_object_headers()
|
||||
}
|
||||
|
||||
env ->
|
||||
apply(HttpRequestMock, :request, [env])
|
||||
end)
|
||||
|
||||
Mox.expect(Pleroma.StubbedHTTPSignaturesMock, :validate_conn, fn _conn -> true end)
|
||||
end
|
||||
|
||||
describe "/relay" do
|
||||
setup do: clear_config([:instance, :allow_relay])
|
||||
|
||||
|
|
@ -688,7 +719,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
|
|||
|
||||
conn =
|
||||
conn
|
||||
|> assign(:valid_signature, true)
|
||||
|> assign_valid_signature_for_actor(data["actor"])
|
||||
|> put_req_header("content-type", "application/activity+json")
|
||||
|> post("/inbox", data)
|
||||
|
||||
|
|
@ -716,7 +747,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
|
|||
|
||||
conn =
|
||||
conn
|
||||
|> assign(:valid_signature, true)
|
||||
|> assign_valid_signature_for_actor(data["actor"])
|
||||
|> put_req_header("content-type", "application/activity+json")
|
||||
|> post("/inbox", data)
|
||||
|
||||
|
|
@ -726,6 +757,199 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
|
|||
assert Activity.get_by_ap_id(data["id"])
|
||||
end
|
||||
|
||||
test "does not create a forged post after failed signature retry", %{conn: conn} do
|
||||
alice = insert(:user, local: false, ap_id: "https://one.com/users/alice")
|
||||
bob = insert(:user, local: false, ap_id: "https://two.com/users/bob")
|
||||
object_id = "https://two.com/objects/inbox-forged-note"
|
||||
|
||||
data = %{
|
||||
"type" => "Create",
|
||||
"actor" => bob.ap_id,
|
||||
"id" => "https://two.com/activities/inbox-forged-create",
|
||||
"context" => "https://two.com/contexts/inbox-forged-create",
|
||||
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||
"cc" => [],
|
||||
"object" => %{
|
||||
"type" => "Note",
|
||||
"id" => object_id,
|
||||
"actor" => bob.ap_id,
|
||||
"attributedTo" => bob.ap_id,
|
||||
"context" => "https://two.com/contexts/inbox-forged-create",
|
||||
"content" => "forged post",
|
||||
"published" => "2024-07-25T13:33:31Z",
|
||||
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||
"cc" => []
|
||||
}
|
||||
}
|
||||
|
||||
expect_signature_retry_from(alice)
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> assign(:valid_signature, false)
|
||||
|> put_req_header("content-type", "application/activity+json")
|
||||
|> put_req_header("signature", "keyId=\"https://one.com/users/alice#main-key\"")
|
||||
|> post("/inbox", data)
|
||||
|
||||
assert "ok" == json_response(conn, 200)
|
||||
|
||||
assert [{:cancel, :actor_signature_mismatch}] =
|
||||
ObanHelpers.perform(all_enqueued(worker: SignatureRetryWorker))
|
||||
|
||||
refute Activity.get_by_ap_id(data["id"])
|
||||
refute Object.get_by_ap_id(object_id)
|
||||
end
|
||||
|
||||
test "does not create a forged like after failed signature retry", %{conn: conn} do
|
||||
alice = insert(:user, local: false, ap_id: "https://one.com/users/alice")
|
||||
bob = insert(:user, local: false, ap_id: "https://two.com/users/bob")
|
||||
note = insert(:note)
|
||||
|
||||
data = %{
|
||||
"type" => "Like",
|
||||
"actor" => bob.ap_id,
|
||||
"id" => "https://two.com/activities/inbox-forged-like",
|
||||
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||
"cc" => [],
|
||||
"object" => note.data["id"]
|
||||
}
|
||||
|
||||
expect_signature_retry_from(alice)
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> assign(:valid_signature, false)
|
||||
|> put_req_header("content-type", "application/activity+json")
|
||||
|> put_req_header("signature", "keyId=\"https://one.com/users/alice#main-key\"")
|
||||
|> post("/inbox", data)
|
||||
|
||||
assert "ok" == json_response(conn, 200)
|
||||
|
||||
assert [{:cancel, :actor_signature_mismatch}] =
|
||||
ObanHelpers.perform(all_enqueued(worker: SignatureRetryWorker))
|
||||
|
||||
refute Activity.get_by_ap_id(data["id"])
|
||||
end
|
||||
|
||||
test "does not delete an object after failed signature retry", %{conn: conn} do
|
||||
alice = insert(:user, local: false, ap_id: "https://one.com/users/alice")
|
||||
bob = insert(:user, local: false, ap_id: "https://two.com/users/bob")
|
||||
note = insert(:note)
|
||||
object_id = note.data["id"]
|
||||
|
||||
data = %{
|
||||
"type" => "Delete",
|
||||
"actor" => bob.ap_id,
|
||||
"id" => "https://two.com/activities/inbox-forged-delete",
|
||||
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||
"cc" => [],
|
||||
"object" => object_id
|
||||
}
|
||||
|
||||
expect_signature_retry_from(alice)
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> assign(:valid_signature, false)
|
||||
|> put_req_header("content-type", "application/activity+json")
|
||||
|> put_req_header("signature", "keyId=\"https://one.com/users/alice#main-key\"")
|
||||
|> post("/inbox", data)
|
||||
|
||||
assert "ok" == json_response(conn, 200)
|
||||
|
||||
assert [{:cancel, :actor_signature_mismatch}] =
|
||||
ObanHelpers.perform(all_enqueued(worker: SignatureRetryWorker))
|
||||
|
||||
refute Activity.get_by_ap_id(data["id"])
|
||||
assert %Object{data: %{"type" => "Note"}} = Object.get_by_ap_id(object_id)
|
||||
end
|
||||
|
||||
test "does not create a forged post signed by a different actor", %{conn: conn} do
|
||||
alice = insert(:user, local: false, ap_id: "https://one.com/users/alice")
|
||||
bob = insert(:user, local: false, ap_id: "https://two.com/users/bob")
|
||||
object_id = "https://two.com/objects/inbox-signed-forged-note"
|
||||
|
||||
data = %{
|
||||
"@context" => "https://www.w3.org/ns/activitystreams",
|
||||
"type" => "Create",
|
||||
"actor" => bob.ap_id,
|
||||
"id" => "https://two.com/activities/inbox-signed-forged-create",
|
||||
"context" => "https://two.com/contexts/inbox-signed-forged-create",
|
||||
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||
"cc" => [],
|
||||
"object" => %{
|
||||
"type" => "Note",
|
||||
"id" => object_id,
|
||||
"actor" => bob.ap_id,
|
||||
"attributedTo" => bob.ap_id,
|
||||
"context" => "https://two.com/contexts/inbox-signed-forged-create",
|
||||
"content" => "forged post",
|
||||
"published" => "2024-07-25T13:33:31Z",
|
||||
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||
"cc" => []
|
||||
}
|
||||
}
|
||||
|
||||
expect_signature_retry_from(alice)
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> put_req_header("content-type", "application/activity+json")
|
||||
|> put_req_header("date", "Thu, 25 Jul 2024 13:33:31 GMT")
|
||||
|> put_req_header("digest", "SHA-256=fake-digest")
|
||||
|> put_req_header(
|
||||
"signature",
|
||||
"keyId=\"#{alice.ap_id}#main-key\",algorithm=\"rsa-sha256\",headers=\"(request-target) host date digest content-type\",signature=\"fake-signature\""
|
||||
)
|
||||
|> post("/inbox", data)
|
||||
|
||||
assert conn.assigns.valid_signature == false
|
||||
assert "ok" == json_response(conn, 200)
|
||||
|
||||
assert [{:cancel, :actor_signature_mismatch}] =
|
||||
ObanHelpers.perform(all_enqueued(worker: SignatureRetryWorker))
|
||||
|
||||
refute Activity.get_by_ap_id(data["id"])
|
||||
refute Object.get_by_ap_id(object_id)
|
||||
end
|
||||
|
||||
test "does not create a forged like signed by a different actor", %{conn: conn} do
|
||||
alice = insert(:user, local: false, ap_id: "https://one.com/users/alice")
|
||||
bob = insert(:user, local: false, ap_id: "https://two.com/users/bob")
|
||||
note = insert(:note)
|
||||
|
||||
data = %{
|
||||
"@context" => "https://www.w3.org/ns/activitystreams",
|
||||
"type" => "Like",
|
||||
"actor" => bob.ap_id,
|
||||
"id" => "https://two.com/activities/inbox-signed-forged-like",
|
||||
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||
"cc" => [],
|
||||
"object" => note.data["id"]
|
||||
}
|
||||
|
||||
expect_signature_retry_from(alice)
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> put_req_header("content-type", "application/activity+json")
|
||||
|> put_req_header("date", "Thu, 25 Jul 2024 13:33:31 GMT")
|
||||
|> put_req_header("digest", "SHA-256=fake-digest")
|
||||
|> put_req_header(
|
||||
"signature",
|
||||
"keyId=\"#{alice.ap_id}#main-key\",algorithm=\"rsa-sha256\",headers=\"(request-target) host date digest content-type\",signature=\"fake-signature\""
|
||||
)
|
||||
|> post("/inbox", data)
|
||||
|
||||
assert conn.assigns.valid_signature == false
|
||||
assert "ok" == json_response(conn, 200)
|
||||
|
||||
assert [{:cancel, :actor_signature_mismatch}] =
|
||||
ObanHelpers.perform(all_enqueued(worker: SignatureRetryWorker))
|
||||
|
||||
refute Activity.get_by_ap_id(data["id"])
|
||||
end
|
||||
|
||||
test "accept follow activity", %{conn: conn} do
|
||||
clear_config([:instance, :federating], true)
|
||||
relay = Relay.get_actor()
|
||||
|
|
@ -742,7 +966,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
|
|||
|
||||
assert "ok" ==
|
||||
conn
|
||||
|> assign(:valid_signature, true)
|
||||
|> assign_valid_signature_for_actor(followed_relay)
|
||||
|> put_req_header("content-type", "application/activity+json")
|
||||
|> post("/inbox", accept)
|
||||
|> json_response(200)
|
||||
|
|
@ -822,16 +1046,19 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
|
|||
test "Unknown activity types are discarded", %{conn: conn} do
|
||||
unknown_types = ["Poke", "Read", "Dazzle"]
|
||||
|
||||
actor =
|
||||
insert(:user, local: false, ap_id: "https://unknown.mastodon.instance/users/somebody")
|
||||
|
||||
Enum.each(unknown_types, fn bad_type ->
|
||||
params =
|
||||
%{
|
||||
"type" => bad_type,
|
||||
"actor" => "https://unknown.mastodon.instance/users/somebody"
|
||||
"actor" => actor.ap_id
|
||||
}
|
||||
|> Jason.encode!()
|
||||
|
||||
conn
|
||||
|> assign(:valid_signature, true)
|
||||
|> assign_valid_signature_for_actor(actor)
|
||||
|> put_req_header("content-type", "application/activity+json")
|
||||
|> post("/inbox", params)
|
||||
|> json_response(400)
|
||||
|
|
@ -900,7 +1127,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
|
|||
|
||||
assert "ok" ==
|
||||
conn
|
||||
|> assign(:valid_signature, true)
|
||||
|> assign_valid_signature_for_actor(data["actor"])
|
||||
|> put_req_header("content-type", "application/activity+json")
|
||||
|> post("/inbox", data)
|
||||
|> json_response(200)
|
||||
|
|
@ -921,7 +1148,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
|
|||
|
||||
assert "ok" ==
|
||||
conn
|
||||
|> assign(:valid_signature, true)
|
||||
|> assign_valid_signature_for_actor(data["actor"])
|
||||
|> put_req_header("content-type", "application/activity+json")
|
||||
|> post("/inbox", data)
|
||||
|> json_response(200)
|
||||
|
|
@ -990,7 +1217,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
|
|||
|
||||
assert "ok" ==
|
||||
conn
|
||||
|> assign(:valid_signature, true)
|
||||
|> assign_valid_signature_for_actor(data["actor"])
|
||||
|> put_req_header("content-type", "application/activity+json")
|
||||
|> post("/inbox", data)
|
||||
|> json_response(200)
|
||||
|
|
@ -1009,7 +1236,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
|
|||
|
||||
assert "ok" ==
|
||||
conn
|
||||
|> assign(:valid_signature, true)
|
||||
|> assign_valid_signature_for_actor(data["actor"])
|
||||
|> put_req_header("content-type", "application/activity+json")
|
||||
|> post("/inbox", data)
|
||||
|> json_response(200)
|
||||
|
|
@ -1040,7 +1267,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
|
|||
|
||||
conn =
|
||||
conn
|
||||
|> assign(:valid_signature, true)
|
||||
|> assign_valid_signature_for_actor(data["actor"])
|
||||
|> put_req_header("content-type", "application/activity+json")
|
||||
|> post("/users/#{user.nickname}/inbox", data)
|
||||
|
||||
|
|
@ -1061,7 +1288,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
|
|||
|
||||
conn =
|
||||
conn
|
||||
|> assign(:valid_signature, true)
|
||||
|> assign_valid_signature_for_actor(data["actor"])
|
||||
|> put_req_header("content-type", "application/activity+json")
|
||||
|> post("/users/#{user.nickname}/inbox", data)
|
||||
|
||||
|
|
@ -1082,7 +1309,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
|
|||
|
||||
conn =
|
||||
conn
|
||||
|> assign(:valid_signature, true)
|
||||
|> assign_valid_signature_for_actor(data["actor"])
|
||||
|> put_req_header("content-type", "application/activity+json")
|
||||
|> post("/users/#{user.nickname}/inbox", data)
|
||||
|
||||
|
|
@ -1106,7 +1333,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
|
|||
|
||||
conn =
|
||||
conn
|
||||
|> assign(:valid_signature, true)
|
||||
|> assign_valid_signature_for_actor(data["actor"])
|
||||
|> put_req_header("content-type", "application/activity+json")
|
||||
|> post("/users/#{user.nickname}/inbox", data)
|
||||
|
||||
|
|
@ -1133,7 +1360,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
|
|||
|
||||
conn =
|
||||
conn
|
||||
|> assign(:valid_signature, true)
|
||||
|> assign_valid_signature_for_actor(data["actor"])
|
||||
|> put_req_header("content-type", "application/activity+json")
|
||||
|> post("/users/#{user.nickname}/inbox", data)
|
||||
|
||||
|
|
@ -1163,7 +1390,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
|
|||
|
||||
conn =
|
||||
conn
|
||||
|> assign(:valid_signature, true)
|
||||
|> assign_valid_signature_for_actor(data["actor"])
|
||||
|> put_req_header("content-type", "application/activity+json")
|
||||
|> post("/users/#{recipient.nickname}/inbox", data)
|
||||
|
||||
|
|
@ -1228,7 +1455,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
|
|||
}
|
||||
|
||||
conn
|
||||
|> assign(:valid_signature, true)
|
||||
|> assign_valid_signature_for_actor(data["actor"])
|
||||
|> put_req_header("content-type", "application/activity+json")
|
||||
|> post("/users/#{recipient.nickname}/inbox", data)
|
||||
|> json_response(200)
|
||||
|
|
@ -1318,7 +1545,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
|
|||
}
|
||||
|
||||
conn
|
||||
|> assign(:valid_signature, true)
|
||||
|> assign_valid_signature_for_actor(data["actor"])
|
||||
|> put_req_header("content-type", "application/activity+json")
|
||||
|> post("/users/#{reported_user.nickname}/inbox", data)
|
||||
|> json_response(200)
|
||||
|
|
@ -1372,7 +1599,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
|
|||
}
|
||||
|
||||
conn
|
||||
|> assign(:valid_signature, true)
|
||||
|> assign_valid_signature_for_actor(data["actor"])
|
||||
|> put_req_header("content-type", "application/activity+json")
|
||||
|> post("/users/#{reported_user.nickname}/inbox", data)
|
||||
|> json_response(200)
|
||||
|
|
@ -1405,7 +1632,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
|
|||
|
||||
conn =
|
||||
conn
|
||||
|> assign(:valid_signature, true)
|
||||
|> assign_valid_signature_for_actor(data["actor"])
|
||||
|> put_req_header("content-type", "application/activity+json")
|
||||
|> post("/users/#{user.nickname}/inbox", data)
|
||||
|
||||
|
|
@ -1428,7 +1655,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
|
|||
|
||||
conn =
|
||||
conn
|
||||
|> assign(:valid_signature, true)
|
||||
|> assign_valid_signature_for_actor(data["actor"])
|
||||
|> put_req_header("content-type", "application/activity+json")
|
||||
|> post("/users/#{user.nickname}/inbox", data)
|
||||
|
||||
|
|
@ -1451,7 +1678,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
|
|||
|
||||
conn =
|
||||
conn
|
||||
|> assign(:valid_signature, true)
|
||||
|> assign_valid_signature_for_actor(data["actor"])
|
||||
|> put_req_header("content-type", "application/activity+json")
|
||||
|> post("/users/#{user.nickname}/inbox", data)
|
||||
|
||||
|
|
@ -2571,6 +2798,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
|
|||
setup do: clear_config([:media_proxy])
|
||||
setup do: clear_config([Pleroma.Upload])
|
||||
|
||||
# majic's libmagic port is unavailable on local Darwin runs; Linux CI still runs this test.
|
||||
@tag :skip_darwin
|
||||
test "POST /api/ap/upload_media", %{conn: conn} do
|
||||
user = insert(:user)
|
||||
|
||||
|
|
|
|||
|
|
@ -499,6 +499,35 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
|
|||
"https://queef.in/storage/banner.gif"
|
||||
end
|
||||
|
||||
test "works with alsoKnownAs as string" do
|
||||
user_id = "https://hub.netzgemeinde.eu/channel/jupiter_rowland"
|
||||
|
||||
user_data =
|
||||
"test/fixtures/users_mock/hubzilla-actor-alsoknownas-string.json"
|
||||
|> File.read!()
|
||||
|
||||
user_data_decoded =
|
||||
user_data
|
||||
|> Jason.decode!()
|
||||
|
||||
Tesla.Mock.mock(fn
|
||||
%{
|
||||
method: :get,
|
||||
url: ^user_id
|
||||
} ->
|
||||
%Tesla.Env{
|
||||
status: 200,
|
||||
body: user_data,
|
||||
headers: [{"content-type", "application/activity+json"}]
|
||||
}
|
||||
end)
|
||||
|
||||
{:ok, user} = ActivityPub.make_user_from_ap_id(user_id)
|
||||
|
||||
assert is_list(user.also_known_as)
|
||||
assert user.also_known_as == [user_data_decoded["alsoKnownAs"]]
|
||||
end
|
||||
|
||||
test "it fetches the appropriate tag-restricted posts" do
|
||||
user = insert(:user)
|
||||
|
||||
|
|
@ -1495,8 +1524,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
|
|||
%{test_file: test_file}
|
||||
end
|
||||
|
||||
test "strips / from filename", %{test_file: file} do
|
||||
file = %Plug.Upload{file | filename: "../../../../../nested/bad.jpg"}
|
||||
test "strips / from filename", %{test_file: %Plug.Upload{} = file} do
|
||||
file = %{file | filename: "../../../../../nested/bad.jpg"}
|
||||
{:ok, %Object{} = object} = ActivityPub.upload(file)
|
||||
[%{"href" => href}] = object.data["url"]
|
||||
assert Regex.match?(~r"/bad.jpg$", href)
|
||||
|
|
@ -1754,13 +1783,14 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
|
|||
test "fetch_activities/2 returns activities addressed to a list " do
|
||||
user = insert(:user)
|
||||
member = insert(:user)
|
||||
{:ok, list} = Pleroma.List.create("foo", user)
|
||||
{:ok, list} = Pleroma.List.create(%{title: "foo"}, user)
|
||||
{:ok, list} = Pleroma.List.follow(list, member)
|
||||
|
||||
{:ok, activity} = CommonAPI.post(user, %{status: "foobar", visibility: "list:#{list.id}"})
|
||||
{:ok, %Activity{} = activity} =
|
||||
CommonAPI.post(user, %{status: "foobar", visibility: "list:#{list.id}"})
|
||||
|
||||
activity = Repo.preload(activity, :bookmark)
|
||||
activity = %Activity{activity | thread_muted?: !!activity.thread_muted?}
|
||||
activity = %{activity | thread_muted?: !!activity.thread_muted?}
|
||||
|
||||
assert ActivityPub.fetch_activities([], %{user: user}) == [activity]
|
||||
end
|
||||
|
|
@ -1960,7 +1990,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
|
|||
assert User.following?(follower, old_user)
|
||||
assert User.following?(follower_move_opted_out, old_user)
|
||||
|
||||
assert {:ok, activity} = ActivityPub.move(old_user, new_user)
|
||||
assert {:ok, %Activity{} = activity} = ActivityPub.move(old_user, new_user)
|
||||
|
||||
assert %Activity{
|
||||
actor: ^old_ap_id,
|
||||
|
|
@ -1992,7 +2022,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
|
|||
assert User.following?(follower_move_opted_out, old_user)
|
||||
refute User.following?(follower_move_opted_out, new_user)
|
||||
|
||||
activity = %Activity{activity | object: nil}
|
||||
activity = %{activity | object: nil}
|
||||
|
||||
assert [%Notification{activity: ^activity}] = Notification.for_user(follower)
|
||||
|
||||
|
|
|
|||
|
|
@ -10,18 +10,25 @@ defmodule Pleroma.Web.ActivityPub.MRFTest do
|
|||
|
||||
alias Pleroma.Web.ActivityPub.MRF
|
||||
|
||||
defp regexes_match!([], []), do: true
|
||||
|
||||
defp regexes_match!([authority | authority_rest], [checked | checked_rest]) do
|
||||
authority.source == checked.source and regexes_match!(authority_rest, checked_rest)
|
||||
end
|
||||
|
||||
defp regexes_match!(_, _), do: false
|
||||
|
||||
test "subdomains_regex/1" do
|
||||
assert MRF.subdomains_regex(["unsafe.tld", "*.unsafe.tld"]) == [
|
||||
~r/^unsafe.tld$/i,
|
||||
~r/^(.*\.)*unsafe.tld$/i
|
||||
]
|
||||
regexes = MRF.subdomains_regex(["unsafe.tld", "*.unsafe.tld"])
|
||||
|
||||
assert regexes_match!(regexes, [~r/^unsafe.tld$/i, ~r/^(.*\.)*unsafe.tld$/i])
|
||||
end
|
||||
|
||||
describe "subdomain_match/2" do
|
||||
test "common domains" do
|
||||
regexes = MRF.subdomains_regex(["unsafe.tld", "unsafe2.tld"])
|
||||
|
||||
assert regexes == [~r/^unsafe.tld$/i, ~r/^unsafe2.tld$/i]
|
||||
assert regexes_match!(regexes, [~r/^unsafe.tld$/i, ~r/^unsafe2.tld$/i])
|
||||
|
||||
assert MRF.subdomain_match?(regexes, "unsafe.tld")
|
||||
assert MRF.subdomain_match?(regexes, "unsafe2.tld")
|
||||
|
|
@ -32,7 +39,7 @@ defmodule Pleroma.Web.ActivityPub.MRFTest do
|
|||
test "wildcard domains with one subdomain" do
|
||||
regexes = MRF.subdomains_regex(["*.unsafe.tld"])
|
||||
|
||||
assert regexes == [~r/^(.*\.)*unsafe.tld$/i]
|
||||
assert regexes_match!(regexes, [~r/^(.*\.)*unsafe.tld$/i])
|
||||
|
||||
assert MRF.subdomain_match?(regexes, "unsafe.tld")
|
||||
assert MRF.subdomain_match?(regexes, "sub.unsafe.tld")
|
||||
|
|
@ -43,7 +50,7 @@ defmodule Pleroma.Web.ActivityPub.MRFTest do
|
|||
test "wildcard domains with two subdomains" do
|
||||
regexes = MRF.subdomains_regex(["*.unsafe.tld"])
|
||||
|
||||
assert regexes == [~r/^(.*\.)*unsafe.tld$/i]
|
||||
assert regexes_match!(regexes, [~r/^(.*\.)*unsafe.tld$/i])
|
||||
|
||||
assert MRF.subdomain_match?(regexes, "unsafe.tld")
|
||||
assert MRF.subdomain_match?(regexes, "sub.sub.unsafe.tld")
|
||||
|
|
@ -54,7 +61,7 @@ defmodule Pleroma.Web.ActivityPub.MRFTest do
|
|||
test "matches are case-insensitive" do
|
||||
regexes = MRF.subdomains_regex(["UnSafe.TLD", "UnSAFE2.Tld"])
|
||||
|
||||
assert regexes == [~r/^UnSafe.TLD$/i, ~r/^UnSAFE2.Tld$/i]
|
||||
assert regexes_match!(regexes, [~r/^UnSafe.TLD$/i, ~r/^UnSAFE2.Tld$/i])
|
||||
|
||||
assert MRF.subdomain_match?(regexes, "UNSAFE.TLD")
|
||||
assert MRF.subdomain_match?(regexes, "UNSAFE2.TLD")
|
||||
|
|
|
|||
|
|
@ -86,23 +86,32 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.AnnounceValidationTest do
|
|||
object = Object.normalize(post_activity, fetch: false)
|
||||
|
||||
# Another user can't announce it
|
||||
{:ok, announce, []} = Builder.announce(announcer, object, public: false)
|
||||
{:ok, announce, []} = Builder.announce(announcer, object, visibility: "private")
|
||||
|
||||
{:error, cng} = ObjectValidator.validate(announce, [])
|
||||
|
||||
assert {:actor, {"can not announce this object", []}} in cng.errors
|
||||
|
||||
# The actor of the object can announce it
|
||||
{:ok, announce, []} = Builder.announce(user, object, public: false)
|
||||
# The actor of the object can announce it with a restrictive scope
|
||||
{:ok, announce, []} = Builder.announce(user, object, visibility: "private")
|
||||
assert {:ok, _, _} = ObjectValidator.validate(announce, [])
|
||||
|
||||
{:ok, announce, []} = Builder.announce(user, object, visibility: "direct")
|
||||
assert {:ok, _, _} = ObjectValidator.validate(announce, [])
|
||||
|
||||
# The actor of the object can not announce it publicly
|
||||
{:ok, announce, []} = Builder.announce(user, object, public: true)
|
||||
{:ok, announce, []} = Builder.announce(user, object, visibility: "public")
|
||||
{:error, cng1} = ObjectValidator.validate(announce, [])
|
||||
|
||||
{:error, cng} = ObjectValidator.validate(announce, [])
|
||||
{:ok, announce, []} = Builder.announce(user, object, visibility: "unlisted")
|
||||
{:error, cng2} = ObjectValidator.validate(announce, [])
|
||||
|
||||
assert {:actor, {"can not announce this object publicly", []}} in cng.errors
|
||||
{:ok, announce, []} = Builder.announce(user, object, visibility: "local")
|
||||
{:error, cng3} = ObjectValidator.validate(announce, [])
|
||||
|
||||
for cng <- [cng1, cng2, cng3] do
|
||||
assert {:actor, {"can not announce this object publicly", []}} in cng.errors
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -149,6 +149,171 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.ArticleNotePageValidatorTest
|
|||
%{valid?: true} = ArticleNotePageValidator.cast_and_validate(note)
|
||||
end
|
||||
|
||||
test "a Misskey MFM note is rendered from source content" do
|
||||
user = insert(:user, ap_id: "https://misskey.example/users/alice")
|
||||
|
||||
note = %{
|
||||
"id" => "https://misskey.example/notes/1",
|
||||
"type" => "Note",
|
||||
"actor" => user.ap_id,
|
||||
"attributedTo" => user.ap_id,
|
||||
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||
"cc" => [],
|
||||
"content" => "original content",
|
||||
"context" => Utils.generate_context_id(),
|
||||
"source" => %{
|
||||
"content" => "$[spin.speed=1s mfm goes here] <script>alert('xss')</script>",
|
||||
"mediaType" => "text/x.misskeymarkdown"
|
||||
}
|
||||
}
|
||||
|
||||
%{valid?: true, changes: %{content: content, source: source}} =
|
||||
ArticleNotePageValidator.cast_and_validate(note)
|
||||
|
||||
assert source["mediaType"] == "text/x.misskeymarkdown"
|
||||
assert content =~ ~s(class="mfm-spin")
|
||||
assert content =~ ~s(data-mfm-speed="1s")
|
||||
assert content =~ "mfm goes here"
|
||||
refute content =~ "original content"
|
||||
refute content =~ "<script"
|
||||
end
|
||||
|
||||
test "a Misskey MFM note resolves only cached AP mention tags" do
|
||||
remote_user = insert(:user, ap_id: "https://misskey.example/users/carol")
|
||||
local_user = insert(:user, nickname: "local_user")
|
||||
|
||||
note = %{
|
||||
"id" => "https://misskey.example/notes/3",
|
||||
"type" => "Note",
|
||||
"actor" => remote_user.ap_id,
|
||||
"attributedTo" => remote_user.ap_id,
|
||||
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||
"cc" => [],
|
||||
"content" => "original content",
|
||||
"context" => Utils.generate_context_id(),
|
||||
"tag" => [
|
||||
%{
|
||||
"type" => "Mention",
|
||||
"name" => "@local_user",
|
||||
"href" => local_user.ap_id
|
||||
},
|
||||
%{
|
||||
"type" => "Mention",
|
||||
"name" => "@uncached",
|
||||
"href" => "https://misskey.example/users/uncached"
|
||||
}
|
||||
],
|
||||
"source" => %{
|
||||
"content" => "@local_user @uncached $[spin hello]",
|
||||
"mediaType" => "text/x.misskeymarkdown"
|
||||
}
|
||||
}
|
||||
|
||||
%{valid?: true, changes: %{content: content}} =
|
||||
ArticleNotePageValidator.cast_and_validate(note)
|
||||
|
||||
assert content =~ local_user.ap_id
|
||||
assert content =~ "@uncached"
|
||||
end
|
||||
|
||||
test "a Misskey MFM note drops oversized source content instead of parsing it" do
|
||||
user = insert(:user, ap_id: "https://misskey.example/users/oversized")
|
||||
|
||||
note = %{
|
||||
"id" => "https://misskey.example/notes/4",
|
||||
"type" => "Note",
|
||||
"actor" => user.ap_id,
|
||||
"attributedTo" => user.ap_id,
|
||||
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||
"cc" => [],
|
||||
"content" => "<span class=\"mfm-spin\">safe fallback</span>",
|
||||
"context" => Utils.generate_context_id(),
|
||||
"source" => %{
|
||||
"content" => String.duplicate("x", 5_001),
|
||||
"mediaType" => "text/x.misskeymarkdown"
|
||||
}
|
||||
}
|
||||
|
||||
%{valid?: true, changes: %{content: content, source: source}} =
|
||||
ArticleNotePageValidator.cast_and_validate(note)
|
||||
|
||||
assert content == "<span class=\"mfm-spin\">safe fallback</span>"
|
||||
refute Map.has_key?(source, "content")
|
||||
end
|
||||
|
||||
test "a note drops oversized non-MFM source content" do
|
||||
user = insert(:user, ap_id: "https://example.com/users/source")
|
||||
|
||||
note = %{
|
||||
"id" => "https://example.com/notes/1",
|
||||
"type" => "Note",
|
||||
"actor" => user.ap_id,
|
||||
"attributedTo" => user.ap_id,
|
||||
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||
"cc" => [],
|
||||
"content" => "regular content",
|
||||
"context" => Utils.generate_context_id(),
|
||||
"source" => %{
|
||||
"content" => String.duplicate("x", 5_001),
|
||||
"mediaType" => "text/markdown"
|
||||
}
|
||||
}
|
||||
|
||||
%{valid?: true, changes: %{source: source}} = ArticleNotePageValidator.cast_and_validate(note)
|
||||
|
||||
assert source == %{"mediaType" => "text/markdown"}
|
||||
end
|
||||
|
||||
test "a Misskey MFM note with legacy _misskey_content is rendered" do
|
||||
user = insert(:user, ap_id: "https://misskey.example/users/legacy")
|
||||
|
||||
note = %{
|
||||
"id" => "https://misskey.example/notes/5",
|
||||
"type" => "Note",
|
||||
"actor" => user.ap_id,
|
||||
"attributedTo" => user.ap_id,
|
||||
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||
"cc" => [],
|
||||
"content" => "original content",
|
||||
"context" => Utils.generate_context_id(),
|
||||
"_misskey_content" => "$[spin legacy]"
|
||||
}
|
||||
|
||||
%{valid?: true, changes: %{content: content, source: source}} =
|
||||
ArticleNotePageValidator.cast_and_validate(note)
|
||||
|
||||
assert source == %{"content" => "$[spin legacy]", "mediaType" => "text/x.misskeymarkdown"}
|
||||
assert content =~ ~s(class="mfm-spin")
|
||||
assert content =~ "legacy"
|
||||
end
|
||||
|
||||
test "a Misskey MFM note with htmlMfm is scrubbed but not rendered from source content" do
|
||||
user = insert(:user, ap_id: "https://misskey.example/users/bob")
|
||||
|
||||
note = %{
|
||||
"id" => "https://misskey.example/notes/2",
|
||||
"type" => "Note",
|
||||
"actor" => user.ap_id,
|
||||
"attributedTo" => user.ap_id,
|
||||
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||
"cc" => [],
|
||||
"content" =>
|
||||
"<span class=\"mfm-spin\">already rendered</span><script>alert('xss')</script>",
|
||||
"htmlMfm" => true,
|
||||
"context" => Utils.generate_context_id(),
|
||||
"source" => %{
|
||||
"content" => String.duplicate("x", 5_001),
|
||||
"mediaType" => "text/x.misskeymarkdown"
|
||||
}
|
||||
}
|
||||
|
||||
%{valid?: true, changes: %{content: content, htmlMfm: true, source: source}} =
|
||||
ArticleNotePageValidator.cast_and_validate(note)
|
||||
|
||||
assert content == "<span class=\"mfm-spin\">already rendered</span>alert('xss')"
|
||||
refute Map.has_key?(source, "content")
|
||||
end
|
||||
|
||||
test "a Note with validated likes collection validates" do
|
||||
insert(:user, ap_id: "https://pol.social/users/mkljczk")
|
||||
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.UpdateHandlingTest do
|
|||
assert {:ok, _update, []} = ObjectValidator.validate(valid_update, [])
|
||||
end
|
||||
|
||||
test "returns an error if the object can't be updated by the actor", %{
|
||||
test "returns an error if the object can't be updated by the actor (different domain)", %{
|
||||
valid_update: valid_update
|
||||
} do
|
||||
other_user = insert(:user, local: false)
|
||||
|
|
@ -41,27 +41,72 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.UpdateHandlingTest do
|
|||
assert {:error, _cng} = ObjectValidator.validate(update, [])
|
||||
end
|
||||
|
||||
test "validates as long as the object is same-origin with the actor", %{
|
||||
test "returns an error if the object can't be updated by the actor (same domain)", %{
|
||||
user: user,
|
||||
valid_update: valid_update
|
||||
} do
|
||||
other_user = insert(:user)
|
||||
user_ap_id = user.ap_id
|
||||
user_domain = URI.parse(user_ap_id).host
|
||||
other_user = insert(:user, local: false, domain: user_domain)
|
||||
|
||||
update =
|
||||
valid_update
|
||||
|> Map.put("actor", other_user.ap_id)
|
||||
|
||||
assert {:ok, _update, []} = ObjectValidator.validate(update, [])
|
||||
assert {:error, _cng} = ObjectValidator.validate(update, [])
|
||||
end
|
||||
|
||||
test "validates if the object is not of an Actor type" do
|
||||
note = insert(:note)
|
||||
test "validates if the object is not of an Actor type", %{user: user} do
|
||||
note = insert(:note, user: user)
|
||||
updated_note = note.data |> Map.put("content", "edited content")
|
||||
other_user = insert(:user)
|
||||
|
||||
{:ok, update, _} = Builder.update(other_user, updated_note)
|
||||
{:ok, update, _} = Builder.update(user, updated_note)
|
||||
|
||||
assert {:ok, _update, _} = ObjectValidator.validate(update, [])
|
||||
end
|
||||
|
||||
test "returns an error if the remote update target is unknown" do
|
||||
remote_user = insert(:user, local: false, ap_id: "https://example.com/users/alice")
|
||||
|
||||
update = %{
|
||||
"type" => "Update",
|
||||
"actor" => remote_user.ap_id,
|
||||
"id" => "https://example.com/activities/update-unknown-object",
|
||||
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||
"cc" => [],
|
||||
"object" => %{
|
||||
"type" => "Note",
|
||||
"id" => "https://example.com/objects/unknown",
|
||||
"actor" => remote_user.ap_id,
|
||||
"content" => "edited content",
|
||||
"published" => "2024-07-25T13:33:31Z",
|
||||
"updated" => "2024-07-25T13:34:31Z",
|
||||
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||
"cc" => []
|
||||
}
|
||||
}
|
||||
|
||||
assert {:error, %Ecto.Changeset{} = cng} = ObjectValidator.validate(update, local: false)
|
||||
refute cng.valid?
|
||||
assert Keyword.has_key?(cng.errors, :object)
|
||||
end
|
||||
|
||||
test "returns an error if the remote update target IRI is unknown" do
|
||||
remote_user = insert(:user, local: false, ap_id: "https://example.com/users/alice")
|
||||
|
||||
update = %{
|
||||
"type" => "Update",
|
||||
"actor" => remote_user.ap_id,
|
||||
"id" => "https://example.com/activities/update-unknown-object-iri",
|
||||
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||
"cc" => [],
|
||||
"object" => "https://example.com/objects/unknown-iri"
|
||||
}
|
||||
|
||||
assert {:error, %Ecto.Changeset{} = cng} = ObjectValidator.validate(update, local: false)
|
||||
refute cng.valid?
|
||||
assert Keyword.has_key?(cng.errors, :object)
|
||||
end
|
||||
end
|
||||
|
||||
describe "update note" do
|
||||
|
|
|
|||
|
|
@ -334,7 +334,7 @@ defmodule Pleroma.Web.ActivityPub.PublisherTest do
|
|||
|
||||
test "activity with BCC is published to a list member." do
|
||||
actor = insert(:user)
|
||||
{:ok, list} = Pleroma.List.create("list", actor)
|
||||
{:ok, list} = Pleroma.List.create(%{title: "list"}, actor)
|
||||
list_member = insert(:user, %{local: false})
|
||||
|
||||
Pleroma.List.follow(list, list_member)
|
||||
|
|
|
|||
|
|
@ -784,13 +784,15 @@ defmodule Pleroma.Web.ActivityPub.SideEffectsTest do
|
|||
{:ok, post} = CommonAPI.post(poster, %{status: "hey"})
|
||||
{:ok, private_post} = CommonAPI.post(poster, %{status: "hey", visibility: "private"})
|
||||
|
||||
{:ok, announce_data, _meta} = Builder.announce(user, post.object, public: true)
|
||||
{:ok, announce_data, _meta} = Builder.announce(user, post.object, visibility: "public")
|
||||
|
||||
{:ok, private_announce_data, _meta} =
|
||||
Builder.announce(user, private_post.object, public: false)
|
||||
Builder.announce(user, private_post.object, visibility: "private")
|
||||
|
||||
{:ok, relay_announce_data, _meta} =
|
||||
Builder.announce(Pleroma.Web.ActivityPub.Relay.get_actor(), post.object, public: true)
|
||||
Builder.announce(Pleroma.Web.ActivityPub.Relay.get_actor(), post.object,
|
||||
visibility: "public"
|
||||
)
|
||||
|
||||
{:ok, announce, _meta} = ActivityPub.persist(announce_data, local: true)
|
||||
{:ok, private_announce, _meta} = ActivityPub.persist(private_announce_data, local: true)
|
||||
|
|
|
|||
|
|
@ -170,4 +170,23 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier.QuestionHandlingTest do
|
|||
|
||||
assert {:ok, %Activity{local: false}} = Transmogrifier.handle_incoming(data)
|
||||
end
|
||||
|
||||
test "it displays voters count for a poll" do
|
||||
user = insert(:user)
|
||||
other_user = insert(:user)
|
||||
|
||||
{:ok, activity} =
|
||||
CommonAPI.post(user, %{
|
||||
status: "???",
|
||||
poll: %{expires_in: 10, options: ["yes", "no"]}
|
||||
})
|
||||
|
||||
object = Object.normalize(activity, fetch: false)
|
||||
{:ok, _, _} = CommonAPI.vote(object, other_user, [1])
|
||||
|
||||
{:ok, modified} = Transmogrifier.prepare_activity(activity.data)
|
||||
|
||||
refute Map.has_key?(modified["object"], "voters")
|
||||
assert modified["object"]["votersCount"] == 1
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -581,7 +581,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
|
|||
|
||||
test "it strips BCC field" do
|
||||
user = insert(:user)
|
||||
{:ok, list} = Pleroma.List.create("foo", user)
|
||||
{:ok, list} = Pleroma.List.create(%{title: "foo"}, user)
|
||||
|
||||
{:ok, activity} = CommonAPI.post(user, %{status: "foobar", visibility: "list:#{list.id}"})
|
||||
|
||||
|
|
|
|||
|
|
@ -180,7 +180,8 @@ defmodule Pleroma.Web.ActivityPub.UtilsTest do
|
|||
"https://www.w3.org/ns/activitystreams",
|
||||
"http://localhost:4001/schemas/litepub-0.1.jsonld",
|
||||
%{
|
||||
"@language" => "und"
|
||||
"@language" => "und",
|
||||
"htmlMfm" => "https://w3id.org/fep/c16b#htmlMfm"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -192,7 +193,8 @@ defmodule Pleroma.Web.ActivityPub.UtilsTest do
|
|||
"https://www.w3.org/ns/activitystreams",
|
||||
"http://localhost:4001/schemas/litepub-0.1.jsonld",
|
||||
%{
|
||||
"@language" => "pl"
|
||||
"@language" => "pl",
|
||||
"htmlMfm" => "https://w3id.org/fep/c16b#htmlMfm"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ defmodule Pleroma.Web.ActivityPub.VisibilityTest do
|
|||
following = insert(:user)
|
||||
unrelated = insert(:user)
|
||||
{:ok, following, user} = Pleroma.User.follow(following, user)
|
||||
{:ok, list} = Pleroma.List.create("foo", user)
|
||||
{:ok, list} = Pleroma.List.create(%{title: "foo"}, user)
|
||||
|
||||
Pleroma.List.follow(list, unrelated)
|
||||
|
||||
|
|
|
|||
|
|
@ -647,7 +647,7 @@ defmodule Pleroma.Web.CommonAPI.UtilsTest do
|
|||
describe "maybe_add_list_data/3" do
|
||||
test "adds list params when found user list" do
|
||||
user = insert(:user)
|
||||
{:ok, %Pleroma.List{} = list} = Pleroma.List.create("title", user)
|
||||
{:ok, %Pleroma.List{} = list} = Pleroma.List.create(%{title: "title"}, user)
|
||||
|
||||
assert Utils.maybe_add_list_data(%{additional: %{}, object: %{}}, user, {:list, list.id}) ==
|
||||
%{
|
||||
|
|
@ -658,7 +658,7 @@ defmodule Pleroma.Web.CommonAPI.UtilsTest do
|
|||
|
||||
test "returns original params when list not found" do
|
||||
user = insert(:user)
|
||||
{:ok, %Pleroma.List{} = list} = Pleroma.List.create("title", insert(:user))
|
||||
{:ok, %Pleroma.List{} = list} = Pleroma.List.create(%{title: "title"}, insert(:user))
|
||||
|
||||
assert Utils.maybe_add_list_data(%{additional: %{}, object: %{}}, user, {:list, list.id}) ==
|
||||
%{additional: %{}, object: %{}}
|
||||
|
|
|
|||
|
|
@ -709,6 +709,47 @@ defmodule Pleroma.Web.CommonAPITest do
|
|||
assert object.data["source"]["content"] == post
|
||||
end
|
||||
|
||||
test "it renders MFM posts and marks their ActivityPub representation" do
|
||||
user = insert(:user)
|
||||
|
||||
post = "<p class='scrub-this'>$[spin.speed=1s 13:37]</p>"
|
||||
|
||||
{:ok, activity} =
|
||||
CommonAPI.post(user, %{
|
||||
status: post,
|
||||
content_type: "text/x.misskeymarkdown"
|
||||
})
|
||||
|
||||
object = Object.normalize(activity, fetch: false)
|
||||
|
||||
assert object.data["htmlMfm"] == true
|
||||
|
||||
assert object.data["source"] == %{
|
||||
"content" => post,
|
||||
"mediaType" => "text/x.misskeymarkdown"
|
||||
}
|
||||
|
||||
assert object.data["content"] =~ ~s(class="mfm-spin")
|
||||
assert object.data["content"] =~ ~s(data-mfm-speed="1s")
|
||||
assert object.data["content"] =~ "13:37"
|
||||
refute object.data["content"] =~ "scrub-this"
|
||||
end
|
||||
|
||||
test "it falls back safely for malformed MFM" do
|
||||
user = insert(:user)
|
||||
|
||||
{:ok, activity} =
|
||||
CommonAPI.post(user, %{
|
||||
status: "$[spin malformed",
|
||||
content_type: "text/x.misskeymarkdown"
|
||||
})
|
||||
|
||||
object = Object.normalize(activity, fetch: false)
|
||||
|
||||
refute object.data["content"] =~ ~s(class="mfm-spin")
|
||||
assert object.data["content"] =~ "malformed"
|
||||
end
|
||||
|
||||
test "it does not allow replies to direct messages that are not direct messages themselves" do
|
||||
user = insert(:user)
|
||||
|
||||
|
|
@ -759,7 +800,7 @@ defmodule Pleroma.Web.CommonAPITest do
|
|||
|
||||
test "it allows to address a list" do
|
||||
user = insert(:user)
|
||||
{:ok, list} = Pleroma.List.create("foo", user)
|
||||
{:ok, list} = Pleroma.List.create(%{title: "foo"}, user)
|
||||
|
||||
{:ok, activity} = CommonAPI.post(user, %{status: "foobar", visibility: "list:#{list.id}"})
|
||||
|
||||
|
|
|
|||
|
|
@ -79,6 +79,12 @@ defmodule Pleroma.Web.FallbackTest do
|
|||
|
||||
test "GET /phoenix/live_dashboard -> /pleroma/live_dashboard", %{conn: conn} do
|
||||
assert redirected_to(get(conn, "/phoenix/live_dashboard")) =~ "/pleroma/live_dashboard"
|
||||
assert redirected_to(get(conn, "/phoenix/live_dashboard/")) =~ "/pleroma/live_dashboard/"
|
||||
end
|
||||
|
||||
test "GET /phoenix/live_dashboard/* -> /pleroma/live_dashboard/*", %{conn: conn} do
|
||||
assert redirected_to(get(conn, "/phoenix/live_dashboard/ecto_stats?nav=diagnose")) =~
|
||||
"/pleroma/live_dashboard/ecto_stats?nav=diagnose"
|
||||
end
|
||||
|
||||
test "OPTIONS /*path", %{conn: conn} do
|
||||
|
|
|
|||
|
|
@ -1815,7 +1815,10 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do
|
|||
test "returns lists to which the account belongs" do
|
||||
%{user: user, conn: conn} = oauth_access(["read:lists"])
|
||||
other_user = insert(:user)
|
||||
assert {:ok, %Pleroma.List{id: _list_id} = list} = Pleroma.List.create("Test List", user)
|
||||
|
||||
assert {:ok, %Pleroma.List{id: _list_id} = list} =
|
||||
Pleroma.List.create(%{title: "Test List"}, user)
|
||||
|
||||
{:ok, %{following: _following}} = Pleroma.List.follow(list, other_user)
|
||||
|
||||
assert [%{"id" => _list_id, "title" => "Test List"}] =
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ defmodule Pleroma.Web.MastodonAPI.ListControllerTest do
|
|||
%{user: user, conn: conn} = oauth_access(["write:lists"])
|
||||
other_user = insert(:user)
|
||||
third_user = insert(:user)
|
||||
{:ok, list} = Pleroma.List.create("name", user)
|
||||
{:ok, list} = Pleroma.List.create(%{title: "name"}, user)
|
||||
|
||||
assert %{} ==
|
||||
conn
|
||||
|
|
@ -77,7 +77,7 @@ defmodule Pleroma.Web.MastodonAPI.ListControllerTest do
|
|||
other_user = insert(:user)
|
||||
third_user = insert(:user)
|
||||
fourth_user = insert(:user)
|
||||
{:ok, list} = Pleroma.List.create("name", user)
|
||||
{:ok, list} = Pleroma.List.create(%{title: "name"}, user)
|
||||
{:ok, list} = Pleroma.List.follow(list, other_user)
|
||||
{:ok, list} = Pleroma.List.follow(list, third_user)
|
||||
{:ok, list} = Pleroma.List.follow(list, fourth_user)
|
||||
|
|
@ -98,7 +98,7 @@ defmodule Pleroma.Web.MastodonAPI.ListControllerTest do
|
|||
%{user: user, conn: conn} = oauth_access(["write:lists"])
|
||||
other_user = insert(:user)
|
||||
third_user = insert(:user)
|
||||
{:ok, list} = Pleroma.List.create("name", user)
|
||||
{:ok, list} = Pleroma.List.create(%{title: "name"}, user)
|
||||
{:ok, list} = Pleroma.List.follow(list, other_user)
|
||||
{:ok, list} = Pleroma.List.follow(list, third_user)
|
||||
|
||||
|
|
@ -115,7 +115,7 @@ defmodule Pleroma.Web.MastodonAPI.ListControllerTest do
|
|||
test "listing users in a list" do
|
||||
%{user: user, conn: conn} = oauth_access(["read:lists"])
|
||||
other_user = insert(:user)
|
||||
{:ok, list} = Pleroma.List.create("name", user)
|
||||
{:ok, list} = Pleroma.List.create(%{title: "name"}, user)
|
||||
{:ok, list} = Pleroma.List.follow(list, other_user)
|
||||
|
||||
conn =
|
||||
|
|
@ -129,7 +129,7 @@ defmodule Pleroma.Web.MastodonAPI.ListControllerTest do
|
|||
|
||||
test "retrieving a list" do
|
||||
%{user: user, conn: conn} = oauth_access(["read:lists"])
|
||||
{:ok, list} = Pleroma.List.create("name", user)
|
||||
{:ok, list} = Pleroma.List.create(%{title: "name"}, user)
|
||||
|
||||
conn =
|
||||
conn
|
||||
|
|
@ -150,7 +150,7 @@ defmodule Pleroma.Web.MastodonAPI.ListControllerTest do
|
|||
|
||||
test "renaming a list" do
|
||||
%{user: user, conn: conn} = oauth_access(["write:lists"])
|
||||
{:ok, list} = Pleroma.List.create("name", user)
|
||||
{:ok, list} = Pleroma.List.create(%{title: "name"}, user)
|
||||
|
||||
assert %{"title" => "newname"} =
|
||||
conn
|
||||
|
|
@ -161,7 +161,7 @@ defmodule Pleroma.Web.MastodonAPI.ListControllerTest do
|
|||
|
||||
test "validates title when renaming a list" do
|
||||
%{user: user, conn: conn} = oauth_access(["write:lists"])
|
||||
{:ok, list} = Pleroma.List.create("name", user)
|
||||
{:ok, list} = Pleroma.List.create(%{title: "name"}, user)
|
||||
|
||||
conn =
|
||||
conn
|
||||
|
|
@ -175,7 +175,7 @@ defmodule Pleroma.Web.MastodonAPI.ListControllerTest do
|
|||
|
||||
test "deleting a list" do
|
||||
%{user: user, conn: conn} = oauth_access(["write:lists"])
|
||||
{:ok, list} = Pleroma.List.create("name", user)
|
||||
{:ok, list} = Pleroma.List.create(%{title: "name"}, user)
|
||||
|
||||
conn = delete(conn, "/api/v1/lists/#{list.id}")
|
||||
|
||||
|
|
|
|||
|
|
@ -129,8 +129,8 @@ defmodule Pleroma.Web.MastodonAPI.MediaControllerTest do
|
|||
assert :ok == File.rm(Path.absname("test/tmp/large_binary.data"))
|
||||
end
|
||||
|
||||
test "Do not allow nested filename", %{conn: conn, image: image} do
|
||||
image = %Plug.Upload{
|
||||
test "Do not allow nested filename", %{conn: conn, image: %Plug.Upload{} = image} do
|
||||
image = %{
|
||||
image
|
||||
| filename: "../../../../../nested/file.jpg"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3068,7 +3068,11 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do
|
|||
|> json_response_and_validate_schema(:ok)
|
||||
|
||||
{:ok, a_expires_at, 0} = DateTime.from_iso8601(a_expires_at)
|
||||
assert DateTime.diff(expires_at, a_expires_at) == 0
|
||||
|
||||
assert DateTime.diff(
|
||||
DateTime.truncate(expires_at, :second),
|
||||
DateTime.truncate(a_expires_at, :second)
|
||||
) == 0
|
||||
|
||||
%{conn: conn} = oauth_access(["read:statuses"])
|
||||
|
||||
|
|
|
|||
|
|
@ -149,6 +149,31 @@ defmodule Pleroma.Web.MastodonAPI.TimelineControllerTest do
|
|||
|> get("/api/v1/timelines/home?remote=true&local=true")
|
||||
|> json_response_and_validate_schema(200) == []
|
||||
end
|
||||
|
||||
test "the home timeline excludes posts from users in exclusive lists", %{
|
||||
user: user,
|
||||
conn: conn
|
||||
} do
|
||||
other_user1 = insert(:user)
|
||||
other_user2 = insert(:user)
|
||||
|
||||
{:ok, user, other_user1} = User.follow(user, other_user1)
|
||||
{:ok, user, other_user2} = User.follow(user, other_user2)
|
||||
|
||||
{:ok, list} = Pleroma.List.create(%{title: "foo", exclusive: true}, user)
|
||||
{:ok, _list} = Pleroma.List.follow(list, other_user1)
|
||||
|
||||
{:ok, _activity} = CommonAPI.post(other_user1, %{status: "hi"})
|
||||
{:ok, %{id: activity2_id}} = CommonAPI.post(other_user2, %{status: "hi too"})
|
||||
|
||||
response =
|
||||
conn
|
||||
|> assign(:user, user)
|
||||
|> get("/api/v1/timelines/home")
|
||||
|> json_response_and_validate_schema(200)
|
||||
|
||||
assert [%{"id" => ^activity2_id}] = response
|
||||
end
|
||||
end
|
||||
|
||||
describe "public" do
|
||||
|
|
@ -606,7 +631,7 @@ defmodule Pleroma.Web.MastodonAPI.TimelineControllerTest do
|
|||
{:ok, activity_two} = CommonAPI.post(other_user, %{status: "Marisa is stupid."})
|
||||
{:ok, _} = CommonAPI.repeat(activity_one.id, other_user)
|
||||
|
||||
{:ok, list} = Pleroma.List.create("name", user)
|
||||
{:ok, list} = Pleroma.List.create(%{title: "name"}, user)
|
||||
{:ok, list} = Pleroma.List.follow(list, other_user)
|
||||
|
||||
conn = get(conn, "/api/v1/timelines/list/#{list.id}")
|
||||
|
|
@ -618,7 +643,7 @@ defmodule Pleroma.Web.MastodonAPI.TimelineControllerTest do
|
|||
|
||||
test "works with pagination", %{user: user, conn: conn} do
|
||||
other_user = insert(:user)
|
||||
{:ok, list} = Pleroma.List.create("name", user)
|
||||
{:ok, list} = Pleroma.List.create(%{title: "name"}, user)
|
||||
{:ok, list} = Pleroma.List.follow(list, other_user)
|
||||
|
||||
Enum.each(1..30, fn i ->
|
||||
|
|
@ -644,7 +669,7 @@ defmodule Pleroma.Web.MastodonAPI.TimelineControllerTest do
|
|||
other_user = insert(:user)
|
||||
{:ok, _activity_one} = CommonAPI.post(user, %{status: "Marisa is cute."})
|
||||
{:ok, activity_two} = CommonAPI.post(other_user, %{status: "Marisa is cute."})
|
||||
{:ok, list} = Pleroma.List.create("name", user)
|
||||
{:ok, list} = Pleroma.List.create(%{title: "name"}, user)
|
||||
{:ok, list} = Pleroma.List.follow(list, other_user)
|
||||
|
||||
conn = get(conn, "/api/v1/timelines/list/#{list.id}")
|
||||
|
|
@ -667,7 +692,7 @@ defmodule Pleroma.Web.MastodonAPI.TimelineControllerTest do
|
|||
visibility: "private"
|
||||
})
|
||||
|
||||
{:ok, list} = Pleroma.List.create("name", user)
|
||||
{:ok, list} = Pleroma.List.create(%{title: "name"}, user)
|
||||
{:ok, list} = Pleroma.List.follow(list, other_user)
|
||||
|
||||
conn = get(conn, "/api/v1/timelines/list/#{list.id}")
|
||||
|
|
@ -685,7 +710,7 @@ defmodule Pleroma.Web.MastodonAPI.TimelineControllerTest do
|
|||
{:ok, _} = CommonAPI.react_with_emoji(activity.id, user3, "🎅")
|
||||
User.mute(user, user3)
|
||||
|
||||
{:ok, list} = Pleroma.List.create("name", user)
|
||||
{:ok, list} = Pleroma.List.create(%{title: "name"}, user)
|
||||
{:ok, list} = Pleroma.List.follow(list, user2)
|
||||
|
||||
result =
|
||||
|
|
@ -716,7 +741,7 @@ defmodule Pleroma.Web.MastodonAPI.TimelineControllerTest do
|
|||
end
|
||||
|
||||
test "filtering", %{user: user, conn: conn} do
|
||||
{:ok, list} = Pleroma.List.create("name", user)
|
||||
{:ok, list} = Pleroma.List.create(%{title: "name"}, user)
|
||||
|
||||
local_user = insert(:user)
|
||||
{:ok, local_activity} = CommonAPI.post(local_user, %{status: "Marisa is stupid."})
|
||||
|
|
|
|||
|
|
@ -10,11 +10,12 @@ defmodule Pleroma.Web.MastodonAPI.ListViewTest do
|
|||
test "show" do
|
||||
user = insert(:user)
|
||||
title = "mortal enemies"
|
||||
{:ok, list} = Pleroma.List.create(title, user)
|
||||
{:ok, list} = Pleroma.List.create(%{title: title}, user)
|
||||
|
||||
expected = %{
|
||||
id: to_string(list.id),
|
||||
title: title
|
||||
title: title,
|
||||
exclusive: false
|
||||
}
|
||||
|
||||
assert expected == ListView.render("show.json", %{list: list})
|
||||
|
|
@ -23,10 +24,13 @@ defmodule Pleroma.Web.MastodonAPI.ListViewTest do
|
|||
test "index" do
|
||||
user = insert(:user)
|
||||
|
||||
{:ok, list} = Pleroma.List.create("my list", user)
|
||||
{:ok, list2} = Pleroma.List.create("cofe", user)
|
||||
{:ok, list} = Pleroma.List.create(%{title: "my list", exclusive: false}, user)
|
||||
{:ok, list2} = Pleroma.List.create(%{title: "cofe", exclusive: true}, user)
|
||||
|
||||
assert [%{id: _, title: "my list"}, %{id: _, title: "cofe"}] =
|
||||
assert [
|
||||
%{id: _, title: "my list", exclusive: false},
|
||||
%{id: _, title: "cofe", exclusive: true}
|
||||
] =
|
||||
ListView.render("index.json", lists: [list, list2])
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -167,10 +167,192 @@ defmodule Pleroma.Web.MastodonAPI.PollViewTest do
|
|||
} = PollView.render("show.json", %{object: object})
|
||||
end
|
||||
|
||||
test "that poll is non anonymous" do
|
||||
test "displays correct voters count" do
|
||||
object = Object.normalize("https://friends.grishka.me/posts/54642", fetch: true)
|
||||
result = PollView.render("show.json", %{object: object})
|
||||
|
||||
assert result[:voters_count] == 14
|
||||
end
|
||||
|
||||
test "detects that poll is non anonymous" do
|
||||
object = Object.normalize("https://friends.grishka.me/posts/54642", fetch: true)
|
||||
result = PollView.render("show.json", %{object: object})
|
||||
|
||||
assert result[:pleroma][:non_anonymous] == true
|
||||
end
|
||||
|
||||
test "prefers votersCount over voters list when both are present" do
|
||||
user = insert(:user)
|
||||
|
||||
{:ok, activity} =
|
||||
CommonAPI.post(user, %{
|
||||
status: "Which flavor?",
|
||||
poll: %{options: ["chocolate", "vanilla"], expires_in: 20}
|
||||
})
|
||||
|
||||
object = Object.normalize(activity, fetch: false)
|
||||
|
||||
voter = insert(:user)
|
||||
{:ok, _, object} = CommonAPI.vote(object, voter, [0])
|
||||
|
||||
assert object.data["votersCount"] == 1
|
||||
assert length(object.data["voters"]) == 1
|
||||
|
||||
object = %{
|
||||
object
|
||||
| data: Map.put(object.data, "votersCount", 42)
|
||||
}
|
||||
|
||||
result = PollView.render("show.json", %{object: object})
|
||||
|
||||
assert result[:voters_count] == 42
|
||||
end
|
||||
|
||||
test "falls back to voters list when votersCount is absent" do
|
||||
user = insert(:user)
|
||||
|
||||
{:ok, activity} =
|
||||
CommonAPI.post(user, %{
|
||||
status: "Which flavor?",
|
||||
poll: %{options: ["chocolate", "vanilla"], expires_in: 20}
|
||||
})
|
||||
|
||||
object = Object.normalize(activity, fetch: false)
|
||||
|
||||
voter = insert(:user)
|
||||
{:ok, _, object} = CommonAPI.vote(object, voter, [0])
|
||||
|
||||
assert length(object.data["voters"]) == 1
|
||||
|
||||
data = Map.delete(object.data, "votersCount")
|
||||
object = %{object | data: data}
|
||||
|
||||
result = PollView.render("show.json", %{object: object})
|
||||
|
||||
assert result[:voters_count] == 1
|
||||
end
|
||||
|
||||
test "returns 0 when both votersCount and voters are absent" do
|
||||
user = insert(:user)
|
||||
|
||||
{:ok, activity} =
|
||||
CommonAPI.post(user, %{
|
||||
status: "Which flavor?",
|
||||
poll: %{options: ["chocolate", "vanilla"], expires_in: 20}
|
||||
})
|
||||
|
||||
object = Object.normalize(activity, fetch: false)
|
||||
|
||||
data =
|
||||
object.data
|
||||
|> Map.delete("votersCount")
|
||||
|> Map.delete("voters")
|
||||
|
||||
object = %{object | data: data}
|
||||
|
||||
result = PollView.render("show.json", %{object: object})
|
||||
|
||||
assert result[:voters_count] == 0
|
||||
end
|
||||
|
||||
test "returns 0 when voters list is empty" do
|
||||
user = insert(:user)
|
||||
|
||||
{:ok, activity} =
|
||||
CommonAPI.post(user, %{
|
||||
status: "Which flavor?",
|
||||
poll: %{options: ["chocolate", "vanilla"], expires_in: 20}
|
||||
})
|
||||
|
||||
object = Object.normalize(activity, fetch: false)
|
||||
|
||||
data =
|
||||
object.data
|
||||
|> Map.delete("votersCount")
|
||||
|> Map.put("voters", [])
|
||||
|
||||
object = %{object | data: data}
|
||||
|
||||
result = PollView.render("show.json", %{object: object})
|
||||
|
||||
assert result[:voters_count] == 0
|
||||
end
|
||||
|
||||
test "does not inflate votersCount when same voter picks multiple options" do
|
||||
user = insert(:user)
|
||||
|
||||
{:ok, activity} =
|
||||
CommonAPI.post(user, %{
|
||||
status: "Pick several",
|
||||
poll: %{options: ["a", "b", "c"], expires_in: 20, multiple: true}
|
||||
})
|
||||
|
||||
object = Object.normalize(activity, fetch: false)
|
||||
|
||||
voter = insert(:user)
|
||||
{:ok, _, object} = CommonAPI.vote(object, voter, [0, 2])
|
||||
|
||||
assert object.data["votersCount"] == 1
|
||||
assert length(object.data["voters"]) == 1
|
||||
end
|
||||
|
||||
test "preserves votersCount from remote source when existing voter picks another option" do
|
||||
user = insert(:user)
|
||||
|
||||
{:ok, activity} =
|
||||
CommonAPI.post(user, %{
|
||||
status: "Pick several",
|
||||
poll: %{options: ["a", "b"], expires_in: 20, multiple: true}
|
||||
})
|
||||
|
||||
object = Object.normalize(activity, fetch: false)
|
||||
|
||||
voter = insert(:user)
|
||||
{:ok, _, object} = CommonAPI.vote(object, voter, [0, 1])
|
||||
|
||||
object = %{object | data: Map.put(object.data, "votersCount", 14)}
|
||||
|
||||
result = PollView.render("show.json", %{object: object})
|
||||
|
||||
assert result[:voters_count] == 14
|
||||
end
|
||||
|
||||
test "returns 0 when votersCount is explicitly 0" do
|
||||
user = insert(:user)
|
||||
|
||||
{:ok, activity} =
|
||||
CommonAPI.post(user, %{
|
||||
status: "Pick one",
|
||||
poll: %{options: ["a", "b"], expires_in: 20}
|
||||
})
|
||||
|
||||
object = Object.normalize(activity, fetch: false)
|
||||
|
||||
object = %{object | data: Map.put(object.data, "votersCount", 0)}
|
||||
|
||||
result = PollView.render("show.json", %{object: object})
|
||||
|
||||
assert result[:voters_count] == 0
|
||||
end
|
||||
|
||||
test "falls back to voters list when votersCount is nil" do
|
||||
user = insert(:user)
|
||||
|
||||
{:ok, activity} =
|
||||
CommonAPI.post(user, %{
|
||||
status: "Pick one",
|
||||
poll: %{options: ["a", "b"], expires_in: 20}
|
||||
})
|
||||
|
||||
object = Object.normalize(activity, fetch: false)
|
||||
|
||||
voter = insert(:user)
|
||||
{:ok, _, object} = CommonAPI.vote(object, voter, [0])
|
||||
|
||||
object = %{object | data: Map.put(object.data, "votersCount", nil)}
|
||||
|
||||
result = PollView.render("show.json", %{object: object})
|
||||
|
||||
assert result[:voters_count] == length(object.data["voters"])
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -909,7 +909,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusViewTest do
|
|||
test "visibility/list" do
|
||||
user = insert(:user)
|
||||
|
||||
{:ok, list} = Pleroma.List.create("foo", user)
|
||||
{:ok, list} = Pleroma.List.create(%{title: "foo"}, user)
|
||||
|
||||
{:ok, activity} = CommonAPI.post(user, %{status: "foobar", visibility: "list:#{list.id}"})
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.TwitterAPI.PasswordControllerTest do
|
||||
defmodule Pleroma.Web.OAuth.PasswordControllerTest do
|
||||
use Pleroma.Web.ConnCase
|
||||
|
||||
alias Pleroma.Config
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.TwitterAPI.ControllerTest do
|
||||
defmodule Pleroma.Web.OAuth.TokenControllerTest do
|
||||
use Pleroma.Web.ConnCase, async: true
|
||||
|
||||
alias Pleroma.Repo
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.TwitterAPI.UtilControllerTest do
|
||||
defmodule Pleroma.Web.PleromaAPI.UtilControllerTest do
|
||||
use Pleroma.Web.ConnCase
|
||||
use Oban.Testing, repo: Pleroma.Repo
|
||||
|
||||
|
|
@ -182,153 +182,6 @@ defmodule Pleroma.Web.TwitterAPI.UtilControllerTest do
|
|||
end
|
||||
end
|
||||
|
||||
describe "POST /main/ostatus - remote_subscribe/2" do
|
||||
setup do: clear_config([:instance, :federating], true)
|
||||
|
||||
test "renders subscribe form", %{conn: conn} do
|
||||
user = insert(:user)
|
||||
|
||||
response =
|
||||
conn
|
||||
|> post("/main/ostatus", %{"nickname" => user.nickname, "profile" => ""})
|
||||
|> response(:ok)
|
||||
|
||||
refute response =~ "Could not find user"
|
||||
assert response =~ "Remotely follow #{user.nickname}"
|
||||
end
|
||||
|
||||
test "renders subscribe form with error when user not found", %{conn: conn} do
|
||||
response =
|
||||
conn
|
||||
|> post("/main/ostatus", %{"nickname" => "nickname", "profile" => ""})
|
||||
|> response(:ok)
|
||||
|
||||
assert response =~ "Could not find user"
|
||||
refute response =~ "Remotely follow"
|
||||
end
|
||||
|
||||
test "it redirect to webfinger url", %{conn: conn} do
|
||||
user = insert(:user)
|
||||
user2 = insert(:user, ap_id: "shp@social.heldscal.la")
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> post("/main/ostatus", %{
|
||||
"user" => %{"nickname" => user.nickname, "profile" => user2.ap_id}
|
||||
})
|
||||
|
||||
assert redirected_to(conn) ==
|
||||
"https://social.heldscal.la/main/ostatussub?profile=#{user.ap_id}"
|
||||
end
|
||||
|
||||
test "it renders form with error when user not found", %{conn: conn} do
|
||||
user2 = insert(:user, ap_id: "shp@social.heldscal.la")
|
||||
|
||||
response =
|
||||
conn
|
||||
|> post("/main/ostatus", %{"user" => %{"nickname" => "jimm", "profile" => user2.ap_id}})
|
||||
|> response(:ok)
|
||||
|
||||
assert response =~ "Something went wrong."
|
||||
end
|
||||
end
|
||||
|
||||
describe "POST /main/ostatus - remote_subscribe/2 - with statuses" do
|
||||
setup do: clear_config([:instance, :federating], true)
|
||||
|
||||
test "renders subscribe form", %{conn: conn} do
|
||||
user = insert(:user)
|
||||
status = insert(:note_activity, %{user: user})
|
||||
status_id = status.id
|
||||
|
||||
assert is_binary(status_id)
|
||||
|
||||
response =
|
||||
conn
|
||||
|> post("/main/ostatus", %{"status_id" => status_id, "profile" => ""})
|
||||
|> response(:ok)
|
||||
|
||||
refute response =~ "Could not find status"
|
||||
assert response =~ "Interacting with"
|
||||
end
|
||||
|
||||
test "renders subscribe form with error when status not found", %{conn: conn} do
|
||||
response =
|
||||
conn
|
||||
|> post("/main/ostatus", %{"status_id" => "somerandomid", "profile" => ""})
|
||||
|> response(:ok)
|
||||
|
||||
assert response =~ "Could not find status"
|
||||
refute response =~ "Interacting with"
|
||||
end
|
||||
|
||||
test "it redirect to webfinger url", %{conn: conn} do
|
||||
user = insert(:user)
|
||||
status = insert(:note_activity, %{user: user})
|
||||
status_id = status.id
|
||||
status_ap_id = status.data["object"]
|
||||
|
||||
assert is_binary(status_id)
|
||||
assert is_binary(status_ap_id)
|
||||
|
||||
user2 = insert(:user, ap_id: "shp@social.heldscal.la")
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> post("/main/ostatus", %{
|
||||
"status" => %{"status_id" => status_id, "profile" => user2.ap_id}
|
||||
})
|
||||
|
||||
assert redirected_to(conn) ==
|
||||
"https://social.heldscal.la/main/ostatussub?profile=#{status_ap_id}"
|
||||
end
|
||||
|
||||
test "it renders form with error when status not found", %{conn: conn} do
|
||||
user2 = insert(:user, ap_id: "shp@social.heldscal.la")
|
||||
|
||||
response =
|
||||
conn
|
||||
|> post("/main/ostatus", %{
|
||||
"status" => %{"status_id" => "somerandomid", "profile" => user2.ap_id}
|
||||
})
|
||||
|> response(:ok)
|
||||
|
||||
assert response =~ "Something went wrong."
|
||||
end
|
||||
end
|
||||
|
||||
describe "GET /main/ostatus - show_subscribe_form/2" do
|
||||
setup do: clear_config([:instance, :federating], true)
|
||||
|
||||
test "it works with users", %{conn: conn} do
|
||||
user = insert(:user)
|
||||
|
||||
response =
|
||||
conn
|
||||
|> get("/main/ostatus", %{"nickname" => user.nickname})
|
||||
|> response(:ok)
|
||||
|
||||
refute response =~ "Could not find user"
|
||||
assert response =~ "Remotely follow #{user.nickname}"
|
||||
end
|
||||
|
||||
test "it works with statuses", %{conn: conn} do
|
||||
user = insert(:user)
|
||||
status = insert(:note_activity, %{user: user})
|
||||
status_id = status.id
|
||||
|
||||
assert is_binary(status_id)
|
||||
|
||||
response =
|
||||
conn
|
||||
|> get("/main/ostatus", %{"status_id" => status_id})
|
||||
|> response(:ok)
|
||||
|
||||
refute response =~ "Could not find status"
|
||||
assert response =~ "Interacting with"
|
||||
end
|
||||
end
|
||||
|
||||
test "it returns new captcha", %{conn: conn} do
|
||||
with_mock Pleroma.Captcha,
|
||||
new: fn -> "test_captcha" end do
|
||||
63
test/pleroma/web/plugs/favicon_plug_test.exs
Normal file
63
test/pleroma/web/plugs/favicon_plug_test.exs
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2026 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.Plugs.FaviconPlugTest do
|
||||
use Pleroma.Web.ConnCase
|
||||
|
||||
@dir "test/tmp/favicon_static"
|
||||
|
||||
setup do
|
||||
Pleroma.Backports.mkdir_p!(@dir)
|
||||
|
||||
on_exit(fn -> File.rm_rf!(@dir) end)
|
||||
end
|
||||
|
||||
describe "default favicon" do
|
||||
test "returns favicon", %{conn: conn} do
|
||||
conn = get(conn, "/favicon.png")
|
||||
body_size = byte_size(conn.resp_body)
|
||||
|
||||
assert conn.status == 200
|
||||
assert body_size == 1583
|
||||
assert response_content_type(conn, :png)
|
||||
end
|
||||
|
||||
test "returns correct cache-control", %{conn: conn} do
|
||||
conn = get(conn, "/favicon.png")
|
||||
cache = get_resp_header(conn, "cache-control")
|
||||
|
||||
assert conn.status == 200
|
||||
assert cache == ["public, max=age=86400, immutable"]
|
||||
end
|
||||
end
|
||||
|
||||
describe "custom favicon" do
|
||||
setup do
|
||||
favicon_path = Path.join(@dir, "favicon.png")
|
||||
donor_image = "test/fixtures/image.png"
|
||||
|
||||
File.cp!(donor_image, favicon_path)
|
||||
clear_config([:instance, :static_dir], @dir)
|
||||
|
||||
on_exit(fn -> File.rm!(favicon_path) end)
|
||||
end
|
||||
|
||||
test "returns favicon", %{conn: conn} do
|
||||
conn = get(conn, "/favicon.png")
|
||||
body_size = byte_size(conn.resp_body)
|
||||
|
||||
assert conn.status == 200
|
||||
assert body_size == 104_426
|
||||
assert response_content_type(conn, :png)
|
||||
end
|
||||
|
||||
test "returns correct cache-control", %{conn: conn} do
|
||||
conn = get(conn, "/favicon.png")
|
||||
cache = get_resp_header(conn, "cache-control")
|
||||
|
||||
assert conn.status == 200
|
||||
assert cache == ["public, max=age=86400, immutable"]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -105,6 +105,7 @@ defmodule Pleroma.Web.Plugs.FrontendStaticPlugTest do
|
|||
"nodeinfo",
|
||||
"manifest.json",
|
||||
"auth",
|
||||
"embed",
|
||||
"proxy",
|
||||
"test",
|
||||
"user_exists",
|
||||
|
|
|
|||
|
|
@ -137,4 +137,47 @@ defmodule Pleroma.Web.Plugs.InstanceStaticTest do
|
|||
# It should be preserved because "image" is in the allowed_mime_types list
|
||||
assert content_type == "image/jpeg"
|
||||
end
|
||||
|
||||
describe "404s for missing files in static-only paths" do
|
||||
test "returns 404 for non-existent static-only JSON files" do
|
||||
conn = get(build_conn(), "/static/non-existent.json")
|
||||
|
||||
assert conn.status == 404
|
||||
assert ["application/json"] = get_resp_header(conn, "content-type")
|
||||
assert Jason.decode!(conn.resp_body) == %{"error" => "not found"}
|
||||
end
|
||||
|
||||
test "returns 404 for non-existent static-only non-JSON files" do
|
||||
conn = get(build_conn(), "/static/non-existent.txt")
|
||||
|
||||
assert conn.status == 404
|
||||
assert conn.resp_body == "Not found"
|
||||
assert ["text/plain"] = get_resp_header(conn, "content-type")
|
||||
end
|
||||
|
||||
test "returns 404 for non-existent .css files" do
|
||||
conn = get(build_conn(), "/static/non-existent.css")
|
||||
|
||||
assert conn.status == 404
|
||||
assert conn.resp_body == "Not found"
|
||||
# Verifies that we forced text/plain for the error body, even though the path was .css
|
||||
assert ["text/plain"] = get_resp_header(conn, "content-type")
|
||||
end
|
||||
|
||||
test "returns 404 for non-existent files without an extension" do
|
||||
conn = get(build_conn(), "/static/non-existent")
|
||||
|
||||
assert conn.status == 404
|
||||
assert conn.resp_body == "Not found"
|
||||
assert ["text/plain"] = get_resp_header(conn, "content-type")
|
||||
end
|
||||
|
||||
test "returns 200 (falls through to SPA) for non-static-only paths" do
|
||||
# /some-route is NOT in static_only_files, so it should still fall through to the SPA.
|
||||
conn = get(build_conn(), "/some-route")
|
||||
|
||||
assert conn.status == 200
|
||||
assert ["text/html; charset=utf-8"] = get_resp_header(conn, "content-type")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -47,13 +47,27 @@ defmodule Pleroma.Web.Plugs.MappedSignatureToIdentityPlugTest do
|
|||
assert %{valid_signature: false} == conn.assigns
|
||||
end
|
||||
|
||||
@tag skip: "known breakage; the testsuite presently depends on it"
|
||||
test "it considers a mapped identity to be invalid when the identity cannot be found" do
|
||||
actor = "http://niu.moe/users/rye"
|
||||
|
||||
conn =
|
||||
build_conn(:post, "/doesntmattter", %{"actor" => "http://mastodon.example.org/users/admin"})
|
||||
|> set_signature("http://niu.moe/users/rye")
|
||||
build_conn(:post, "/doesntmattter", %{"actor" => actor})
|
||||
|> set_signature(actor)
|
||||
|> MappedSignatureToIdentityPlug.call(%{})
|
||||
|
||||
assert %{valid_signature: false} == conn.assigns
|
||||
assert conn.assigns.valid_signature == false
|
||||
refute Map.has_key?(conn.assigns, :user)
|
||||
end
|
||||
|
||||
test "it considers a mapped identity to be invalid when embedded actor identity cannot be found" do
|
||||
actor = "http://niu.moe/users/rye"
|
||||
|
||||
conn =
|
||||
build_conn(:post, "/doesntmattter", %{"actor" => %{"id" => actor}})
|
||||
|> set_signature(actor)
|
||||
|> MappedSignatureToIdentityPlug.call(%{})
|
||||
|
||||
assert conn.assigns.valid_signature == false
|
||||
refute Map.has_key?(conn.assigns, :user)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -268,6 +268,23 @@ defmodule Pleroma.Web.Plugs.RateLimiterTest do
|
|||
refute {:err, :not_found} == RateLimiter.inspect_bucket(conn, limiter_name, opts)
|
||||
end
|
||||
|
||||
test "doesn't crash if rate limit scale is invalid (e.g. broken DB config)" do
|
||||
limiter_name = :test_invalid_rate_limit_config
|
||||
|
||||
clear_config([:rate_limit, limiter_name], [{"", 0}, {"", ""}])
|
||||
clear_config([Pleroma.Web.Endpoint, :http, :ip], {8, 8, 8, 8})
|
||||
|
||||
opts = RateLimiter.init(name: limiter_name)
|
||||
|
||||
conn = %{build_conn(:get, "/") | remote_ip: {127, 0, 0, 1}}
|
||||
|
||||
conn_limited = RateLimiter.call(conn, opts)
|
||||
|
||||
refute conn_limited.status == Conn.Status.code(:too_many_requests)
|
||||
refute conn_limited.resp_body
|
||||
refute conn_limited.halted
|
||||
end
|
||||
|
||||
def expire_ttl(%{remote_ip: remote_ip} = _conn, bucket_name_root) do
|
||||
bucket_name = "anon:#{bucket_name_root}" |> String.to_atom()
|
||||
key_name = "ip::#{remote_ip |> Tuple.to_list() |> Enum.join(".")}"
|
||||
|
|
|
|||
|
|
@ -2,14 +2,14 @@
|
|||
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do
|
||||
defmodule Pleroma.Web.RegistrationTest do
|
||||
use Pleroma.DataCase
|
||||
import Pleroma.Factory
|
||||
alias Pleroma.Repo
|
||||
alias Pleroma.Tests.ObanHelpers
|
||||
alias Pleroma.User
|
||||
alias Pleroma.UserInviteToken
|
||||
alias Pleroma.Web.TwitterAPI.TwitterAPI
|
||||
alias Pleroma.Web.Registration
|
||||
|
||||
setup_all do
|
||||
Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end)
|
||||
|
|
@ -25,7 +25,7 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do
|
|||
:confirm => "bear"
|
||||
}
|
||||
|
||||
{:ok, user} = TwitterAPI.register_user(data)
|
||||
{:ok, user} = Registration.register_user(data)
|
||||
|
||||
assert user == User.get_cached_by_nickname("lain")
|
||||
end
|
||||
|
|
@ -40,7 +40,7 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do
|
|||
:confirm => "bear"
|
||||
}
|
||||
|
||||
{:ok, user} = TwitterAPI.register_user(data)
|
||||
{:ok, user} = Registration.register_user(data)
|
||||
|
||||
assert user == User.get_cached_by_nickname("lain")
|
||||
end
|
||||
|
|
@ -57,7 +57,7 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do
|
|||
:confirm => "bear"
|
||||
}
|
||||
|
||||
{:ok, user} = TwitterAPI.register_user(data)
|
||||
{:ok, user} = Registration.register_user(data)
|
||||
ObanHelpers.perform_all()
|
||||
|
||||
refute user.is_confirmed
|
||||
|
|
@ -89,7 +89,7 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do
|
|||
:reason => "I love anime"
|
||||
}
|
||||
|
||||
{:ok, user} = TwitterAPI.register_user(data)
|
||||
{:ok, user} = Registration.register_user(data)
|
||||
ObanHelpers.perform_all()
|
||||
|
||||
refute user.is_approved
|
||||
|
|
@ -125,7 +125,7 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do
|
|||
:confirm => "bear"
|
||||
}
|
||||
|
||||
{:ok, user1} = TwitterAPI.register_user(data1)
|
||||
{:ok, user1} = Registration.register_user(data1)
|
||||
|
||||
data2 = %{
|
||||
:username => "lain",
|
||||
|
|
@ -136,7 +136,7 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do
|
|||
:confirm => "bear"
|
||||
}
|
||||
|
||||
{:ok, user2} = TwitterAPI.register_user(data2)
|
||||
{:ok, user2} = Registration.register_user(data2)
|
||||
|
||||
expected_text =
|
||||
~s(<span class="h-card"><a class="u-url mention" data-user="#{user1.id}" href="#{user1.ap_id}" rel="ugc">@<span>john</span></a></span> test)
|
||||
|
|
@ -160,7 +160,7 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do
|
|||
:token => invite.token
|
||||
}
|
||||
|
||||
{:ok, user} = TwitterAPI.register_user(data)
|
||||
{:ok, user} = Registration.register_user(data)
|
||||
|
||||
assert user == User.get_cached_by_nickname("vinny")
|
||||
|
||||
|
|
@ -179,7 +179,7 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do
|
|||
:token => "DudeLetMeInImAFairy"
|
||||
}
|
||||
|
||||
{:error, msg} = TwitterAPI.register_user(data)
|
||||
{:error, msg} = Registration.register_user(data)
|
||||
|
||||
assert msg == "Invalid token"
|
||||
refute User.get_cached_by_nickname("GrimReaper")
|
||||
|
|
@ -199,7 +199,7 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do
|
|||
:token => invite.token
|
||||
}
|
||||
|
||||
{:error, msg} = TwitterAPI.register_user(data)
|
||||
{:error, msg} = Registration.register_user(data)
|
||||
|
||||
assert msg == "Expired token"
|
||||
refute User.get_cached_by_nickname("GrimReaper")
|
||||
|
|
@ -221,7 +221,7 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do
|
|||
|
||||
check_fn = fn invite ->
|
||||
data = Map.put(data, :token, invite.token)
|
||||
{:ok, user} = TwitterAPI.register_user(data)
|
||||
{:ok, user} = Registration.register_user(data)
|
||||
|
||||
assert user == User.get_cached_by_nickname("vinny")
|
||||
end
|
||||
|
|
@ -254,7 +254,7 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do
|
|||
|
||||
data = Map.put(data, "token", invite.token)
|
||||
|
||||
{:error, msg} = TwitterAPI.register_user(data)
|
||||
{:error, msg} = Registration.register_user(data)
|
||||
|
||||
assert msg == "Expired token"
|
||||
refute User.get_cached_by_nickname("vinny")
|
||||
|
|
@ -282,7 +282,7 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do
|
|||
:token => invite.token
|
||||
}
|
||||
|
||||
{:ok, user} = TwitterAPI.register_user(data)
|
||||
{:ok, user} = Registration.register_user(data)
|
||||
assert user == User.get_cached_by_nickname("vinny")
|
||||
|
||||
invite = Repo.get_by(UserInviteToken, token: invite.token)
|
||||
|
|
@ -298,7 +298,7 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do
|
|||
:token => invite.token
|
||||
}
|
||||
|
||||
{:error, msg} = TwitterAPI.register_user(data)
|
||||
{:error, msg} = Registration.register_user(data)
|
||||
|
||||
assert msg == "Expired token"
|
||||
refute User.get_cached_by_nickname("GrimReaper")
|
||||
|
|
@ -321,7 +321,7 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do
|
|||
:token => invite.token
|
||||
}
|
||||
|
||||
{:ok, user} = TwitterAPI.register_user(data)
|
||||
{:ok, user} = Registration.register_user(data)
|
||||
assert user == User.get_cached_by_nickname("vinny")
|
||||
|
||||
invite = Repo.get_by(UserInviteToken, token: invite.token)
|
||||
|
|
@ -343,7 +343,7 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do
|
|||
:token => invite.token
|
||||
}
|
||||
|
||||
{:ok, user} = TwitterAPI.register_user(data)
|
||||
{:ok, user} = Registration.register_user(data)
|
||||
assert user == User.get_cached_by_nickname("vinny")
|
||||
|
||||
invite = Repo.get_by(UserInviteToken, token: invite.token)
|
||||
|
|
@ -359,7 +359,7 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do
|
|||
:token => invite.token
|
||||
}
|
||||
|
||||
{:error, msg} = TwitterAPI.register_user(data)
|
||||
{:error, msg} = Registration.register_user(data)
|
||||
|
||||
assert msg == "Expired token"
|
||||
refute User.get_cached_by_nickname("GrimReaper")
|
||||
|
|
@ -379,7 +379,7 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do
|
|||
:token => invite.token
|
||||
}
|
||||
|
||||
{:error, msg} = TwitterAPI.register_user(data)
|
||||
{:error, msg} = Registration.register_user(data)
|
||||
|
||||
assert msg == "Expired token"
|
||||
refute User.get_cached_by_nickname("GrimReaper")
|
||||
|
|
@ -401,7 +401,7 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do
|
|||
:token => invite.token
|
||||
}
|
||||
|
||||
{:error, msg} = TwitterAPI.register_user(data)
|
||||
{:error, msg} = Registration.register_user(data)
|
||||
|
||||
assert msg == "Expired token"
|
||||
refute User.get_cached_by_nickname("GrimReaper")
|
||||
|
|
@ -416,7 +416,7 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do
|
|||
:bio => "close the world."
|
||||
}
|
||||
|
||||
{:error, error} = TwitterAPI.register_user(data)
|
||||
{:error, error} = Registration.register_user(data)
|
||||
|
||||
assert is_binary(error)
|
||||
refute User.get_cached_by_nickname("lain")
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.TwitterAPI.RemoteFollowControllerTest do
|
||||
defmodule Pleroma.Web.RemoteInteraction.RemoteInteractionControllerTest do
|
||||
use Pleroma.Web.ConnCase
|
||||
|
||||
alias Pleroma.MFA
|
||||
|
|
@ -16,6 +16,11 @@ defmodule Pleroma.Web.TwitterAPI.RemoteFollowControllerTest do
|
|||
import Mox
|
||||
import Pleroma.Factory
|
||||
|
||||
setup do
|
||||
Tesla.Mock.mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
|
||||
:ok
|
||||
end
|
||||
|
||||
setup_all do: clear_config([:instance, :federating], true)
|
||||
setup do: clear_config([:user, :deny_follow_blocked])
|
||||
|
||||
|
|
@ -49,7 +54,7 @@ defmodule Pleroma.Web.TwitterAPI.RemoteFollowControllerTest do
|
|||
|
||||
assert conn
|
||||
|> get(
|
||||
remote_follow_path(conn, :follow, %{
|
||||
remote_interaction_path(conn, :follow, %{
|
||||
acct: "https://mastodon.social/users/emelie/statuses/101849165031453009"
|
||||
})
|
||||
)
|
||||
|
|
@ -78,7 +83,9 @@ defmodule Pleroma.Web.TwitterAPI.RemoteFollowControllerTest do
|
|||
|
||||
response =
|
||||
conn
|
||||
|> get(remote_follow_path(conn, :follow, %{acct: "https://mastodon.social/users/emelie"}))
|
||||
|> get(
|
||||
remote_interaction_path(conn, :follow, %{acct: "https://mastodon.social/users/emelie"})
|
||||
)
|
||||
|> html_response(200)
|
||||
|
||||
assert response =~ "Log in to follow"
|
||||
|
|
@ -109,7 +116,9 @@ defmodule Pleroma.Web.TwitterAPI.RemoteFollowControllerTest do
|
|||
response =
|
||||
conn
|
||||
|> assign(:user, user)
|
||||
|> get(remote_follow_path(conn, :follow, %{acct: "https://mastodon.social/users/emelie"}))
|
||||
|> get(
|
||||
remote_interaction_path(conn, :follow, %{acct: "https://mastodon.social/users/emelie"})
|
||||
)
|
||||
|> html_response(200)
|
||||
|
||||
assert response =~ "Remote follow"
|
||||
|
|
@ -130,7 +139,7 @@ defmodule Pleroma.Web.TwitterAPI.RemoteFollowControllerTest do
|
|||
conn
|
||||
|> assign(:user, user)
|
||||
|> get(
|
||||
remote_follow_path(conn, :follow, %{
|
||||
remote_interaction_path(conn, :follow, %{
|
||||
acct: "https://mastodon.social/users/not_found"
|
||||
})
|
||||
)
|
||||
|
|
@ -152,7 +161,9 @@ defmodule Pleroma.Web.TwitterAPI.RemoteFollowControllerTest do
|
|||
conn
|
||||
|> assign(:user, user)
|
||||
|> assign(:token, read_token)
|
||||
|> post(remote_follow_path(conn, :do_follow), %{"user" => %{"id" => user2.id}})
|
||||
|> post(remote_interaction_path(conn, :do_follow), %{
|
||||
"user" => %{"id" => user2.id}
|
||||
})
|
||||
|> response(200)
|
||||
|
||||
assert response =~ "Error following account"
|
||||
|
|
@ -167,7 +178,7 @@ defmodule Pleroma.Web.TwitterAPI.RemoteFollowControllerTest do
|
|||
conn
|
||||
|> assign(:user, user)
|
||||
|> assign(:token, insert(:oauth_token, user: user, scopes: ["write:follows"]))
|
||||
|> post(remote_follow_path(conn, :do_follow), %{"user" => %{"id" => user2.id}})
|
||||
|> post(remote_interaction_path(conn, :do_follow), %{"user" => %{"id" => user2.id}})
|
||||
|
||||
assert redirected_to(conn) == "/users/#{user2.id}"
|
||||
end
|
||||
|
|
@ -179,7 +190,7 @@ defmodule Pleroma.Web.TwitterAPI.RemoteFollowControllerTest do
|
|||
response =
|
||||
conn
|
||||
|> assign(:user, user)
|
||||
|> post(remote_follow_path(conn, :do_follow), %{"user" => %{"id" => user2.id}})
|
||||
|> post(remote_interaction_path(conn, :do_follow), %{"user" => %{"id" => user2.id}})
|
||||
|> response(200)
|
||||
|
||||
assert response =~ "Error following account"
|
||||
|
|
@ -195,7 +206,7 @@ defmodule Pleroma.Web.TwitterAPI.RemoteFollowControllerTest do
|
|||
response =
|
||||
conn
|
||||
|> assign(:user, user)
|
||||
|> post(remote_follow_path(conn, :do_follow), %{"user" => %{"id" => user2.id}})
|
||||
|> post(remote_interaction_path(conn, :do_follow), %{"user" => %{"id" => user2.id}})
|
||||
|> response(200)
|
||||
|
||||
assert response =~ "Error following account"
|
||||
|
|
@ -207,7 +218,7 @@ defmodule Pleroma.Web.TwitterAPI.RemoteFollowControllerTest do
|
|||
response =
|
||||
conn
|
||||
|> assign(:user, user)
|
||||
|> post(remote_follow_path(conn, :do_follow), %{"user" => %{"id" => "jimm"}})
|
||||
|> post(remote_interaction_path(conn, :do_follow), %{"user" => %{"id" => "jimm"}})
|
||||
|> response(200)
|
||||
|
||||
assert response =~ "Error following account"
|
||||
|
|
@ -222,7 +233,7 @@ defmodule Pleroma.Web.TwitterAPI.RemoteFollowControllerTest do
|
|||
conn
|
||||
|> assign(:user, refresh_record(user))
|
||||
|> assign(:token, insert(:oauth_token, user: user, scopes: ["write:follows"]))
|
||||
|> post(remote_follow_path(conn, :do_follow), %{"user" => %{"id" => user2.id}})
|
||||
|> post(remote_interaction_path(conn, :do_follow), %{"user" => %{"id" => user2.id}})
|
||||
|
||||
assert redirected_to(conn) == "/users/#{user2.id}"
|
||||
end
|
||||
|
|
@ -244,7 +255,7 @@ defmodule Pleroma.Web.TwitterAPI.RemoteFollowControllerTest do
|
|||
|
||||
response =
|
||||
conn
|
||||
|> post(remote_follow_path(conn, :do_follow), %{
|
||||
|> post(remote_interaction_path(conn, :do_follow), %{
|
||||
"authorization" => %{"name" => user.nickname, "password" => "test", "id" => user2.id}
|
||||
})
|
||||
|> response(200)
|
||||
|
|
@ -272,7 +283,7 @@ defmodule Pleroma.Web.TwitterAPI.RemoteFollowControllerTest do
|
|||
|
||||
response =
|
||||
conn
|
||||
|> post(remote_follow_path(conn, :do_follow), %{
|
||||
|> post(remote_interaction_path(conn, :do_follow), %{
|
||||
"authorization" => %{"name" => user.nickname, "password" => "test1", "id" => user2.id}
|
||||
})
|
||||
|> response(200)
|
||||
|
|
@ -300,7 +311,7 @@ defmodule Pleroma.Web.TwitterAPI.RemoteFollowControllerTest do
|
|||
conn =
|
||||
conn
|
||||
|> post(
|
||||
remote_follow_path(conn, :do_follow),
|
||||
remote_interaction_path(conn, :do_follow),
|
||||
%{
|
||||
"mfa" => %{"code" => otp_token, "token" => token, "id" => user2.id}
|
||||
}
|
||||
|
|
@ -329,7 +340,7 @@ defmodule Pleroma.Web.TwitterAPI.RemoteFollowControllerTest do
|
|||
response =
|
||||
conn
|
||||
|> post(
|
||||
remote_follow_path(conn, :do_follow),
|
||||
remote_interaction_path(conn, :do_follow),
|
||||
%{
|
||||
"mfa" => %{"code" => otp_token, "token" => token, "id" => user2.id}
|
||||
}
|
||||
|
|
@ -348,7 +359,7 @@ defmodule Pleroma.Web.TwitterAPI.RemoteFollowControllerTest do
|
|||
|
||||
conn =
|
||||
conn
|
||||
|> post(remote_follow_path(conn, :do_follow), %{
|
||||
|> post(remote_interaction_path(conn, :do_follow), %{
|
||||
"authorization" => %{"name" => user.nickname, "password" => "test", "id" => user2.id}
|
||||
})
|
||||
|
||||
|
|
@ -361,7 +372,7 @@ defmodule Pleroma.Web.TwitterAPI.RemoteFollowControllerTest do
|
|||
|
||||
response =
|
||||
conn
|
||||
|> post(remote_follow_path(conn, :do_follow), %{
|
||||
|> post(remote_interaction_path(conn, :do_follow), %{
|
||||
"authorization" => %{"name" => user.nickname, "password" => "test", "id" => "jimm"}
|
||||
})
|
||||
|> response(200)
|
||||
|
|
@ -374,7 +385,7 @@ defmodule Pleroma.Web.TwitterAPI.RemoteFollowControllerTest do
|
|||
|
||||
response =
|
||||
conn
|
||||
|> post(remote_follow_path(conn, :do_follow), %{
|
||||
|> post(remote_interaction_path(conn, :do_follow), %{
|
||||
"authorization" => %{"name" => "jimm", "password" => "test", "id" => user.id}
|
||||
})
|
||||
|> response(200)
|
||||
|
|
@ -388,7 +399,7 @@ defmodule Pleroma.Web.TwitterAPI.RemoteFollowControllerTest do
|
|||
|
||||
response =
|
||||
conn
|
||||
|> post(remote_follow_path(conn, :do_follow), %{
|
||||
|> post(remote_interaction_path(conn, :do_follow), %{
|
||||
"authorization" => %{"name" => user.nickname, "password" => "42", "id" => user2.id}
|
||||
})
|
||||
|> response(200)
|
||||
|
|
@ -404,7 +415,7 @@ defmodule Pleroma.Web.TwitterAPI.RemoteFollowControllerTest do
|
|||
|
||||
response =
|
||||
conn
|
||||
|> post(remote_follow_path(conn, :do_follow), %{
|
||||
|> post(remote_interaction_path(conn, :do_follow), %{
|
||||
"authorization" => %{"name" => user.nickname, "password" => "test", "id" => user2.id}
|
||||
})
|
||||
|> response(200)
|
||||
|
|
@ -423,7 +434,7 @@ defmodule Pleroma.Web.TwitterAPI.RemoteFollowControllerTest do
|
|||
avatar: %{"url" => [%{"href" => "https://remote.org/avatar.png"}]}
|
||||
})
|
||||
|
||||
avatar_url = Pleroma.Web.TwitterAPI.RemoteFollowView.avatar_url(user)
|
||||
avatar_url = Pleroma.Web.RemoteInteraction.RemoteInteractionView.avatar_url(user)
|
||||
|
||||
assert avatar_url == "https://remote.org/avatar.png"
|
||||
end
|
||||
|
|
@ -440,7 +451,7 @@ defmodule Pleroma.Web.TwitterAPI.RemoteFollowControllerTest do
|
|||
avatar: %{"url" => [%{"href" => "https://remote.org/avatar.png"}]}
|
||||
})
|
||||
|
||||
avatar_url = Pleroma.Web.TwitterAPI.RemoteFollowView.avatar_url(user)
|
||||
avatar_url = Pleroma.Web.RemoteInteraction.RemoteInteractionView.avatar_url(user)
|
||||
url = Pleroma.Web.Endpoint.url()
|
||||
|
||||
assert String.starts_with?(avatar_url, url)
|
||||
|
|
@ -455,7 +466,7 @@ defmodule Pleroma.Web.TwitterAPI.RemoteFollowControllerTest do
|
|||
avatar: %{"url" => [%{"href" => "#{Pleroma.Web.Endpoint.url()}/localuser/avatar.png"}]}
|
||||
})
|
||||
|
||||
avatar_url = Pleroma.Web.TwitterAPI.RemoteFollowView.avatar_url(user)
|
||||
avatar_url = Pleroma.Web.RemoteInteraction.RemoteInteractionView.avatar_url(user)
|
||||
|
||||
assert avatar_url == "#{Pleroma.Web.Endpoint.url()}/localuser/avatar.png"
|
||||
end
|
||||
|
|
@ -485,13 +496,162 @@ defmodule Pleroma.Web.TwitterAPI.RemoteFollowControllerTest do
|
|||
conn =
|
||||
conn
|
||||
|> get(
|
||||
remote_follow_path(conn, :authorize_interaction, %{
|
||||
remote_interaction_path(conn, :authorize_interaction, %{
|
||||
uri: "https://mastodon.social/users/emelie"
|
||||
})
|
||||
)
|
||||
|
||||
assert redirected_to(conn) ==
|
||||
remote_follow_path(conn, :follow, %{acct: "https://mastodon.social/users/emelie"})
|
||||
remote_interaction_path(conn, :follow, %{
|
||||
acct: "https://mastodon.social/users/emelie"
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
describe "POST /main/ostatus - remote_subscribe/2" do
|
||||
setup do: clear_config([:instance, :federating], true)
|
||||
|
||||
test "renders subscribe form", %{conn: conn} do
|
||||
user = insert(:user)
|
||||
|
||||
response =
|
||||
conn
|
||||
|> post("/main/ostatus", %{"nickname" => user.nickname, "profile" => ""})
|
||||
|> response(:ok)
|
||||
|
||||
refute response =~ "Could not find user"
|
||||
assert response =~ "Remotely follow #{user.nickname}"
|
||||
end
|
||||
|
||||
test "renders subscribe form with error when user not found", %{conn: conn} do
|
||||
response =
|
||||
conn
|
||||
|> post("/main/ostatus", %{"nickname" => "nickname", "profile" => ""})
|
||||
|> response(:ok)
|
||||
|
||||
assert response =~ "Could not find user"
|
||||
refute response =~ "Remotely follow"
|
||||
end
|
||||
|
||||
test "it redirect to webfinger url", %{conn: conn} do
|
||||
user = insert(:user)
|
||||
user2 = insert(:user, ap_id: "shp@social.heldscal.la")
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> post("/main/ostatus", %{
|
||||
"user" => %{"nickname" => user.nickname, "profile" => user2.ap_id}
|
||||
})
|
||||
|
||||
assert redirected_to(conn) ==
|
||||
"https://social.heldscal.la/main/ostatussub?profile=#{user.ap_id}"
|
||||
end
|
||||
|
||||
test "it renders form with error when user not found", %{conn: conn} do
|
||||
user2 = insert(:user, ap_id: "shp@social.heldscal.la")
|
||||
|
||||
response =
|
||||
conn
|
||||
|> post("/main/ostatus", %{"user" => %{"nickname" => "jimm", "profile" => user2.ap_id}})
|
||||
|> response(:ok)
|
||||
|
||||
assert response =~ "Something went wrong."
|
||||
end
|
||||
end
|
||||
|
||||
describe "POST /main/ostatus - remote_subscribe/2 - with statuses" do
|
||||
setup do: clear_config([:instance, :federating], true)
|
||||
|
||||
test "renders subscribe form", %{conn: conn} do
|
||||
user = insert(:user)
|
||||
status = insert(:note_activity, %{user: user})
|
||||
status_id = status.id
|
||||
|
||||
assert is_binary(status_id)
|
||||
|
||||
response =
|
||||
conn
|
||||
|> post("/main/ostatus", %{"status_id" => status_id, "profile" => ""})
|
||||
|> response(:ok)
|
||||
|
||||
refute response =~ "Could not find status"
|
||||
assert response =~ "Interacting with"
|
||||
end
|
||||
|
||||
test "renders subscribe form with error when status not found", %{conn: conn} do
|
||||
response =
|
||||
conn
|
||||
|> post("/main/ostatus", %{"status_id" => "somerandomid", "profile" => ""})
|
||||
|> response(:ok)
|
||||
|
||||
assert response =~ "Could not find status"
|
||||
refute response =~ "Interacting with"
|
||||
end
|
||||
|
||||
test "it redirect to webfinger url", %{conn: conn} do
|
||||
user = insert(:user)
|
||||
status = insert(:note_activity, %{user: user})
|
||||
status_id = status.id
|
||||
status_ap_id = status.data["object"]
|
||||
|
||||
assert is_binary(status_id)
|
||||
assert is_binary(status_ap_id)
|
||||
|
||||
user2 = insert(:user, ap_id: "shp@social.heldscal.la")
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> post("/main/ostatus", %{
|
||||
"status" => %{"status_id" => status_id, "profile" => user2.ap_id}
|
||||
})
|
||||
|
||||
assert redirected_to(conn) ==
|
||||
"https://social.heldscal.la/main/ostatussub?profile=#{status_ap_id}"
|
||||
end
|
||||
|
||||
test "it renders form with error when status not found", %{conn: conn} do
|
||||
user2 = insert(:user, ap_id: "shp@social.heldscal.la")
|
||||
|
||||
response =
|
||||
conn
|
||||
|> post("/main/ostatus", %{
|
||||
"status" => %{"status_id" => "somerandomid", "profile" => user2.ap_id}
|
||||
})
|
||||
|> response(:ok)
|
||||
|
||||
assert response =~ "Something went wrong."
|
||||
end
|
||||
end
|
||||
|
||||
describe "GET /main/ostatus - show_subscribe_form/2" do
|
||||
setup do: clear_config([:instance, :federating], true)
|
||||
|
||||
test "it works with users", %{conn: conn} do
|
||||
user = insert(:user)
|
||||
|
||||
response =
|
||||
conn
|
||||
|> get("/main/ostatus", %{"nickname" => user.nickname})
|
||||
|> response(:ok)
|
||||
|
||||
refute response =~ "Could not find user"
|
||||
assert response =~ "Remotely follow #{user.nickname}"
|
||||
end
|
||||
|
||||
test "it works with statuses", %{conn: conn} do
|
||||
user = insert(:user)
|
||||
status = insert(:note_activity, %{user: user})
|
||||
status_id = status.id
|
||||
|
||||
assert is_binary(status_id)
|
||||
|
||||
response =
|
||||
conn
|
||||
|> get("/main/ostatus", %{"status_id" => status_id})
|
||||
|> response(:ok)
|
||||
|
||||
refute response =~ "Could not find status"
|
||||
assert response =~ "Interacting with"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -220,7 +220,7 @@ defmodule Pleroma.Web.StreamerTest do
|
|||
} do
|
||||
%{token: read_lists_token} = oauth_access(["read:lists"], user: user)
|
||||
%{token: invalid_token} = oauth_access(["irrelevant:scope"], user: user)
|
||||
{:ok, list} = List.create("Test", user)
|
||||
{:ok, list} = List.create(%{title: "Test"}, user)
|
||||
|
||||
assert {:error, _} = Streamer.get_topic("list:#{list.id}", user, read_oauth_token)
|
||||
|
||||
|
|
@ -233,7 +233,7 @@ defmodule Pleroma.Web.StreamerTest do
|
|||
|
||||
test "disallows list stream that are not owned by the user", %{user: user, token: oauth_token} do
|
||||
another_user = insert(:user)
|
||||
{:ok, list} = List.create("Test", another_user)
|
||||
{:ok, list} = List.create(%{title: "Test"}, another_user)
|
||||
|
||||
assert {:error, _} = Streamer.get_topic("list:#{list.id}", user, oauth_token)
|
||||
assert {:error, _} = Streamer.get_topic("list", user, oauth_token, %{"list" => list.id})
|
||||
|
|
@ -803,7 +803,7 @@ defmodule Pleroma.Web.StreamerTest do
|
|||
|
||||
{:ok, user_a, user_b} = User.follow(user_a, user_b)
|
||||
|
||||
{:ok, list} = List.create("Test", user_a)
|
||||
{:ok, list} = List.create(%{title: "Test"}, user_a)
|
||||
{:ok, list} = List.follow(list, user_b)
|
||||
|
||||
Streamer.get_topic_and_add_socket("list", user_a, user_a_token, %{"list" => list.id})
|
||||
|
|
@ -820,7 +820,7 @@ defmodule Pleroma.Web.StreamerTest do
|
|||
test "it doesn't send unwanted private posts to list", %{user: user_a, token: user_a_token} do
|
||||
user_b = insert(:user)
|
||||
|
||||
{:ok, list} = List.create("Test", user_a)
|
||||
{:ok, list} = List.create(%{title: "Test"}, user_a)
|
||||
{:ok, list} = List.follow(list, user_b)
|
||||
|
||||
Streamer.get_topic_and_add_socket("list", user_a, user_a_token, %{"list" => list.id})
|
||||
|
|
@ -839,7 +839,7 @@ defmodule Pleroma.Web.StreamerTest do
|
|||
|
||||
{:ok, user_a, user_b} = User.follow(user_a, user_b)
|
||||
|
||||
{:ok, list} = List.create("Test", user_a)
|
||||
{:ok, list} = List.create(%{title: "Test"}, user_a)
|
||||
{:ok, list} = List.follow(list, user_b)
|
||||
|
||||
Streamer.get_topic_and_add_socket("list", user_a, user_a_token, %{"list" => list.id})
|
||||
|
|
|
|||
|
|
@ -11,9 +11,27 @@ defmodule Pleroma.Workers.ReceiverWorkerTest do
|
|||
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.CommonAPI
|
||||
alias Pleroma.Web.Federator
|
||||
alias Pleroma.Workers.ReceiverWorker
|
||||
|
||||
defp signature_headers_for(%User{} = signer) do
|
||||
[
|
||||
{"host", "local.test"},
|
||||
{"date", "Thu, 25 Jul 2024 13:33:31 GMT"},
|
||||
{"digest", "SHA-256=fake-digest"},
|
||||
{"content-type", "application/activity+json"},
|
||||
{
|
||||
"signature",
|
||||
"keyId=\"#{signer.ap_id}#main-key\",algorithm=\"rsa-sha256\",headers=\"(request-target) host date digest content-type\",signature=\"fake-signature\""
|
||||
}
|
||||
]
|
||||
end
|
||||
|
||||
defp perform_incoming(params) do
|
||||
ReceiverWorker.perform(%Oban.Job{
|
||||
args: %{"op" => "incoming_ap_doc", "params" => params}
|
||||
})
|
||||
end
|
||||
|
||||
test "it does not retry MRF reject" do
|
||||
params = insert(:note).data
|
||||
|
||||
|
|
@ -81,16 +99,7 @@ defmodule Pleroma.Workers.ReceiverWorkerTest do
|
|||
insert(:note_activity).data
|
||||
|> Map.put("actor", "https://springfield.social/users/bart")
|
||||
|
||||
{:ok, oban_job} =
|
||||
Federator.incoming_ap_doc(%{
|
||||
method: "POST",
|
||||
req_headers: [],
|
||||
request_path: "/inbox",
|
||||
params: params,
|
||||
query_string: ""
|
||||
})
|
||||
|
||||
assert {:cancel, {:error, :forbidden}} = ReceiverWorker.perform(oban_job)
|
||||
assert {:cancel, {:error, :forbidden}} = perform_incoming(params)
|
||||
end
|
||||
|
||||
test "when request returns a 404" do
|
||||
|
|
@ -98,16 +107,7 @@ defmodule Pleroma.Workers.ReceiverWorkerTest do
|
|||
insert(:note_activity).data
|
||||
|> Map.put("actor", "https://springfield.social/users/troymcclure")
|
||||
|
||||
{:ok, oban_job} =
|
||||
Federator.incoming_ap_doc(%{
|
||||
method: "POST",
|
||||
req_headers: [],
|
||||
request_path: "/inbox",
|
||||
params: params,
|
||||
query_string: ""
|
||||
})
|
||||
|
||||
assert {:cancel, {:error, :not_found}} = ReceiverWorker.perform(oban_job)
|
||||
assert {:cancel, {:error, :not_found}} = perform_incoming(params)
|
||||
end
|
||||
|
||||
test "when request returns a 410" do
|
||||
|
|
@ -115,16 +115,7 @@ defmodule Pleroma.Workers.ReceiverWorkerTest do
|
|||
insert(:note_activity).data
|
||||
|> Map.put("actor", "https://springfield.social/users/hankscorpio")
|
||||
|
||||
{:ok, oban_job} =
|
||||
Federator.incoming_ap_doc(%{
|
||||
method: "POST",
|
||||
req_headers: [],
|
||||
request_path: "/inbox",
|
||||
params: params,
|
||||
query_string: ""
|
||||
})
|
||||
|
||||
assert {:cancel, {:error, :not_found}} = ReceiverWorker.perform(oban_job)
|
||||
assert {:cancel, {:error, :not_found}} = perform_incoming(params)
|
||||
end
|
||||
|
||||
test "when user account is disabled" do
|
||||
|
|
@ -138,86 +129,16 @@ defmodule Pleroma.Workers.ReceiverWorkerTest do
|
|||
|
||||
{:ok, %User{}} = User.set_activation(user, false)
|
||||
|
||||
{:ok, oban_job} =
|
||||
Federator.incoming_ap_doc(%{
|
||||
method: "POST",
|
||||
req_headers: [],
|
||||
request_path: "/inbox",
|
||||
params: params,
|
||||
query_string: ""
|
||||
})
|
||||
|
||||
assert {:cancel, {:user_active, false}} = ReceiverWorker.perform(oban_job)
|
||||
assert {:cancel, {:user_active, false}} = perform_incoming(params)
|
||||
end
|
||||
end
|
||||
|
||||
test "it can validate the signature" do
|
||||
Tesla.Mock.mock(fn
|
||||
%{url: "https://phpc.social/users/denniskoch"} ->
|
||||
%Tesla.Env{
|
||||
status: 200,
|
||||
body: File.read!("test/fixtures/denniskoch.json"),
|
||||
headers: [{"content-type", "application/activity+json"}]
|
||||
}
|
||||
|
||||
%{url: "https://phpc.social/users/denniskoch/collections/featured"} ->
|
||||
%Tesla.Env{
|
||||
status: 200,
|
||||
headers: [{"content-type", "application/activity+json"}],
|
||||
body:
|
||||
File.read!("test/fixtures/users_mock/masto_featured.json")
|
||||
|> String.replace("{{domain}}", "phpc.social")
|
||||
|> String.replace("{{nickname}}", "denniskoch")
|
||||
}
|
||||
end)
|
||||
|
||||
params =
|
||||
File.read!("test/fixtures/receiver_worker_signature_activity.json") |> Jason.decode!()
|
||||
|
||||
req_headers = [
|
||||
["accept-encoding", "gzip"],
|
||||
["content-length", "5184"],
|
||||
["content-type", "application/activity+json"],
|
||||
["date", "Thu, 25 Jul 2024 13:33:31 GMT"],
|
||||
["digest", "SHA-256=ouge/6HP2/QryG6F3JNtZ6vzs/hSwMk67xdxe87eH7A="],
|
||||
["host", "bikeshed.party"],
|
||||
[
|
||||
"signature",
|
||||
"keyId=\"https://mastodon.social/users/bastianallgeier#main-key\",algorithm=\"rsa-sha256\",headers=\"(request-target) host date digest content-type\",signature=\"ymE3vn5Iw50N6ukSp8oIuXJB5SBjGAGjBasdTDvn+ahZIzq2SIJfmVCsIIzyqIROnhWyQoTbavTclVojEqdaeOx+Ejz2wBnRBmhz5oemJLk4RnnCH0lwMWyzeY98YAvxi9Rq57Gojuv/1lBqyGa+rDzynyJpAMyFk17XIZpjMKuTNMCbjMDy76ILHqArykAIL/v1zxkgwxY/+ELzxqMpNqtZ+kQ29znNMUBB3eVZ/mNAHAz6o33Y9VKxM2jw+08vtuIZOusXyiHbRiaj2g5HtN2WBUw1MzzfRfHF2/yy7rcipobeoyk5RvP5SyHV3WrIeZ3iyoNfmv33y8fxllF0EA==\""
|
||||
],
|
||||
[
|
||||
"user-agent",
|
||||
"http.rb/5.2.0 (Mastodon/4.3.0-nightly.2024-07-25; +https://mastodon.social/)"
|
||||
]
|
||||
]
|
||||
|
||||
{:ok, oban_job} =
|
||||
Federator.incoming_ap_doc(%{
|
||||
method: "POST",
|
||||
req_headers: req_headers,
|
||||
request_path: "/inbox",
|
||||
params: params,
|
||||
query_string: ""
|
||||
})
|
||||
|
||||
assert {:ok, %Pleroma.Activity{}} = ReceiverWorker.perform(oban_job)
|
||||
end
|
||||
|
||||
test "cancels due to origin containment" do
|
||||
params =
|
||||
insert(:note_activity).data
|
||||
|> Map.put("id", "https://notorigindomain.com/activity")
|
||||
|
||||
{:ok, oban_job} =
|
||||
Federator.incoming_ap_doc(%{
|
||||
method: "POST",
|
||||
req_headers: [],
|
||||
request_path: "/inbox",
|
||||
params: params,
|
||||
query_string: ""
|
||||
})
|
||||
|
||||
assert {:cancel, :origin_containment_failed} = ReceiverWorker.perform(oban_job)
|
||||
assert {:cancel, :origin_containment_failed} = perform_incoming(params)
|
||||
end
|
||||
|
||||
test "canceled due to deleted object" do
|
||||
|
|
@ -233,16 +154,114 @@ defmodule Pleroma.Workers.ReceiverWorkerTest do
|
|||
}
|
||||
end)
|
||||
|
||||
{:ok, oban_job} =
|
||||
Federator.incoming_ap_doc(%{
|
||||
method: "POST",
|
||||
req_headers: [],
|
||||
request_path: "/inbox",
|
||||
params: params,
|
||||
query_string: ""
|
||||
})
|
||||
assert {:cancel, _} = perform_incoming(params)
|
||||
end
|
||||
|
||||
assert {:cancel, _} = ReceiverWorker.perform(oban_job)
|
||||
test "delegates legacy failed-signature metadata jobs instead of processing them as trusted" do
|
||||
alice = insert(:user, local: false, ap_id: "https://one.com/users/alice")
|
||||
bob = insert(:user, local: false, ap_id: "https://two.com/users/bob")
|
||||
object_id = "https://two.com/objects/legacy-forged-note"
|
||||
|
||||
create = %{
|
||||
"type" => "Create",
|
||||
"actor" => bob.ap_id,
|
||||
"id" => "https://two.com/activities/legacy-forged-create",
|
||||
"context" => "https://two.com/contexts/legacy-forged-create",
|
||||
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||
"cc" => [],
|
||||
"object" => %{
|
||||
"type" => "Note",
|
||||
"id" => object_id,
|
||||
"actor" => bob.ap_id,
|
||||
"attributedTo" => bob.ap_id,
|
||||
"context" => "https://two.com/contexts/legacy-forged-create",
|
||||
"content" => "forged post",
|
||||
"published" => "2024-07-25T13:33:31Z",
|
||||
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||
"cc" => []
|
||||
}
|
||||
}
|
||||
|
||||
assert {:cancel, :actor_signature_mismatch} =
|
||||
ReceiverWorker.perform(%Oban.Job{
|
||||
args: %{
|
||||
"op" => "incoming_ap_doc",
|
||||
"method" => "POST",
|
||||
"params" => create,
|
||||
"req_headers" => signature_headers_for(alice),
|
||||
"request_path" => "/inbox",
|
||||
"query_string" => ""
|
||||
}
|
||||
})
|
||||
|
||||
refute Pleroma.Activity.get_by_ap_id(create["id"])
|
||||
refute Pleroma.Object.get_by_ap_id(object_id)
|
||||
end
|
||||
|
||||
test "fails closed for the old persisted failed-signature job shape" do
|
||||
alice = insert(:user, local: false, ap_id: "https://one.com/users/alice")
|
||||
bob = insert(:user, local: false, ap_id: "https://two.com/users/bob")
|
||||
object_id = "https://two.com/objects/old-shape-forged-note"
|
||||
|
||||
create = %{
|
||||
"type" => "Create",
|
||||
"actor" => bob.ap_id,
|
||||
"id" => "https://two.com/activities/old-shape-forged-create",
|
||||
"context" => "https://two.com/contexts/old-shape-forged-create",
|
||||
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||
"cc" => [],
|
||||
"object" => %{
|
||||
"type" => "Note",
|
||||
"id" => object_id,
|
||||
"actor" => bob.ap_id,
|
||||
"attributedTo" => bob.ap_id,
|
||||
"context" => "https://two.com/contexts/old-shape-forged-create",
|
||||
"content" => "forged post",
|
||||
"published" => "2024-07-25T13:33:31Z",
|
||||
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||
"cc" => []
|
||||
}
|
||||
}
|
||||
|
||||
assert {:cancel, :missing_signature_retry_metadata} =
|
||||
ReceiverWorker.perform(%Oban.Job{
|
||||
args: %{
|
||||
"op" => "incoming_ap_doc",
|
||||
"params" => create,
|
||||
"req_headers" => signature_headers_for(alice),
|
||||
"timeout" => 20_000
|
||||
}
|
||||
})
|
||||
|
||||
refute Pleroma.Activity.get_by_ap_id(create["id"])
|
||||
refute Pleroma.Object.get_by_ap_id(object_id)
|
||||
end
|
||||
|
||||
test "fails closed for legacy retry jobs missing one metadata field" do
|
||||
alice = insert(:user, local: false, ap_id: "https://one.com/users/alice")
|
||||
params = insert(:note_activity).data
|
||||
|
||||
assert {:cancel, :missing_signature_retry_metadata} =
|
||||
ReceiverWorker.perform(%Oban.Job{
|
||||
args: %{
|
||||
"op" => "incoming_ap_doc",
|
||||
"method" => "POST",
|
||||
"params" => params,
|
||||
"req_headers" => signature_headers_for(alice),
|
||||
"request_path" => "/inbox"
|
||||
}
|
||||
})
|
||||
end
|
||||
|
||||
test "fails closed for malformed legacy metadata jobs without params" do
|
||||
assert {:cancel, :missing_signature_retry_metadata} =
|
||||
ReceiverWorker.perform(%Oban.Job{
|
||||
args: %{
|
||||
"op" => "incoming_ap_doc",
|
||||
"req_headers" => [],
|
||||
"timeout" => 20_000
|
||||
}
|
||||
})
|
||||
end
|
||||
|
||||
describe "Server reachability:" do
|
||||
|
|
|
|||
574
test/pleroma/workers/signature_retry_worker_test.exs
Normal file
574
test/pleroma/workers/signature_retry_worker_test.exs
Normal file
|
|
@ -0,0 +1,574 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Workers.SignatureRetryWorkerTest do
|
||||
use Pleroma.DataCase, async: false
|
||||
use Oban.Testing, repo: Pleroma.Repo
|
||||
|
||||
import ExUnit.CaptureLog
|
||||
import Pleroma.Factory
|
||||
|
||||
@moduletag capture_log: true
|
||||
|
||||
alias Pleroma.Activity
|
||||
alias Pleroma.Object
|
||||
alias Pleroma.Signature
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.ActivityPub.UserView
|
||||
alias Pleroma.Web.Federator
|
||||
alias Pleroma.Workers.SignatureRetryWorker
|
||||
|
||||
defp signature_headers_for(%User{} = signer) do
|
||||
[
|
||||
{"host", "local.test"},
|
||||
{"date", "Thu, 25 Jul 2024 13:33:31 GMT"},
|
||||
{"digest", "SHA-256=fake-digest"},
|
||||
{"content-type", "application/activity+json"},
|
||||
{
|
||||
"signature",
|
||||
"keyId=\"#{signer.ap_id}#main-key\",algorithm=\"rsa-sha256\",headers=\"(request-target) host date digest content-type\",signature=\"fake-signature\""
|
||||
}
|
||||
]
|
||||
end
|
||||
|
||||
defp stub_actor_fetch(%User{} = signer) do
|
||||
signer_json = UserView.render("user.json", %{user: signer}) |> Map.delete("featured")
|
||||
|
||||
Tesla.Mock.mock(fn
|
||||
%{url: url} when url == signer.ap_id ->
|
||||
%Tesla.Env{
|
||||
status: 200,
|
||||
body: Jason.encode!(signer_json),
|
||||
headers: HttpRequestMock.activitypub_object_headers()
|
||||
}
|
||||
end)
|
||||
end
|
||||
|
||||
defp expect_signature_from(%User{} = signer) do
|
||||
stub_actor_fetch(signer)
|
||||
Mox.expect(Pleroma.StubbedHTTPSignaturesMock, :validate_conn, fn _conn -> true end)
|
||||
end
|
||||
|
||||
defp enqueue_failed_signature(params, signer) do
|
||||
Federator.incoming_failed_signature_ap_doc(%{
|
||||
method: "POST",
|
||||
req_headers: signature_headers_for(signer),
|
||||
request_path: "/inbox",
|
||||
params: params,
|
||||
query_string: ""
|
||||
})
|
||||
end
|
||||
|
||||
defp failed_signature_job(params, req_headers, opts \\ []) do
|
||||
%Oban.Job{
|
||||
args: %{
|
||||
"op" => "incoming_failed_signature_ap_doc",
|
||||
"method" => Keyword.get(opts, :method, "POST"),
|
||||
"req_headers" => req_headers,
|
||||
"request_path" => Keyword.get(opts, :request_path, "/inbox"),
|
||||
"params" => params,
|
||||
"query_string" => Keyword.get(opts, :query_string, "")
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
defp assert_mismatched_signature_cancelled(params, signer) do
|
||||
assert {:ok, oban_job} = enqueue_failed_signature(params, signer)
|
||||
|
||||
capture_log([level: :warning], fn ->
|
||||
assert {:cancel, :actor_signature_mismatch} = SignatureRetryWorker.perform(oban_job)
|
||||
end)
|
||||
end
|
||||
|
||||
test "Federator preserves request metadata for failed-signature retry jobs" do
|
||||
params = insert(:note_activity).data
|
||||
|
||||
req_headers = [
|
||||
{"host", "local.test"},
|
||||
{"signature", "keyId=\"https://one.com/users/alice#main-key\""}
|
||||
]
|
||||
|
||||
assert {:ok, oban_job} =
|
||||
Federator.incoming_failed_signature_ap_doc(%{
|
||||
method: "POST",
|
||||
req_headers: req_headers,
|
||||
request_path: "/inbox",
|
||||
params: params,
|
||||
query_string: "foo=bar"
|
||||
})
|
||||
|
||||
assert oban_job.worker == "Pleroma.Workers.SignatureRetryWorker"
|
||||
|
||||
assert %{
|
||||
"op" => "incoming_failed_signature_ap_doc",
|
||||
"method" => "POST",
|
||||
"req_headers" => ^req_headers,
|
||||
"request_path" => "/inbox",
|
||||
"params" => ^params,
|
||||
"query_string" => "foo=bar"
|
||||
} = oban_job.args
|
||||
end
|
||||
|
||||
test "cancels retry jobs without request metadata" do
|
||||
params = insert(:note_activity).data
|
||||
|
||||
log =
|
||||
capture_log([level: :warning], fn ->
|
||||
assert {:cancel, :missing_signature_retry_metadata} =
|
||||
SignatureRetryWorker.perform(%Oban.Job{
|
||||
args: %{"op" => "incoming_failed_signature_ap_doc", "params" => params}
|
||||
})
|
||||
end)
|
||||
|
||||
assert log =~ "Failed-signature inbox retry rejected"
|
||||
assert log =~ "reason=:missing_signature_retry_metadata"
|
||||
assert log =~ "payload_actor=#{inspect(params["actor"])}"
|
||||
assert log =~ "activity_id=#{inspect(params["id"])}"
|
||||
assert log =~ "type=#{inspect(params["type"])}"
|
||||
assert log =~ "request_path=nil"
|
||||
end
|
||||
|
||||
test "cancels retry jobs with malformed serialized request headers" do
|
||||
params = insert(:note_activity).data
|
||||
|
||||
log =
|
||||
capture_log([level: :warning], fn ->
|
||||
assert {:cancel, :invalid_signature_retry_metadata} =
|
||||
SignatureRetryWorker.perform(failed_signature_job(params, [["signature"]]))
|
||||
end)
|
||||
|
||||
assert log =~ "Failed-signature inbox retry rejected"
|
||||
assert log =~ "reason=:invalid_signature_retry_metadata"
|
||||
assert log =~ "signature_actor=nil"
|
||||
assert log =~ "request_path=\"/inbox\""
|
||||
end
|
||||
|
||||
test "cancels retry jobs without a signature header" do
|
||||
alice = insert(:user, local: false, ap_id: "https://one.com/users/alice")
|
||||
params = insert(:note_activity, user: alice).data
|
||||
|
||||
log =
|
||||
capture_log([level: :warning], fn ->
|
||||
assert {:cancel, :invalid_signature} =
|
||||
SignatureRetryWorker.perform(
|
||||
failed_signature_job(params, [{"host", "local.test"}])
|
||||
)
|
||||
end)
|
||||
|
||||
assert log =~ "Failed-signature inbox retry rejected"
|
||||
assert log =~ "reason=:invalid_signature"
|
||||
assert log =~ "payload_actor=#{inspect(params["actor"])}"
|
||||
assert log =~ "signature_actor=nil"
|
||||
assert log =~ "request_path=\"/inbox\""
|
||||
end
|
||||
|
||||
test "cancels missing signature before fetching an unavailable payload actor" do
|
||||
params =
|
||||
insert(:note_activity).data
|
||||
|> Map.put("actor", "https://unavailable.example/users/bob")
|
||||
|
||||
assert {:cancel, :invalid_signature} =
|
||||
SignatureRetryWorker.perform(failed_signature_job(params, [{"host", "local.test"}]))
|
||||
end
|
||||
|
||||
test "cancels signer mismatch before fetching an unavailable payload actor" do
|
||||
alice = insert(:user, local: false, ap_id: "https://one.com/users/alice")
|
||||
|
||||
params =
|
||||
insert(:note_activity).data
|
||||
|> Map.put("actor", "https://unavailable.example/users/bob")
|
||||
|
||||
assert {:cancel, :actor_signature_mismatch} =
|
||||
SignatureRetryWorker.perform(
|
||||
failed_signature_job(params, signature_headers_for(alice))
|
||||
)
|
||||
end
|
||||
|
||||
test "cancels retry jobs with a signature header without keyId" do
|
||||
alice = insert(:user, local: false, ap_id: "https://one.com/users/alice")
|
||||
params = insert(:note_activity, user: alice).data
|
||||
|
||||
req_headers = [{"signature", "algorithm=\"rsa-sha256\",signature=\"fake-signature\""}]
|
||||
|
||||
assert {:cancel, :invalid_signature} =
|
||||
SignatureRetryWorker.perform(failed_signature_job(params, req_headers))
|
||||
end
|
||||
|
||||
test "cancels retry jobs with an unparsable signature keyId" do
|
||||
alice = insert(:user, local: false, ap_id: "https://one.com/users/alice")
|
||||
params = insert(:note_activity, user: alice).data
|
||||
req_headers = [{"signature", "keyId=\"not an activitypub id\",signature=\"fake-signature\""}]
|
||||
|
||||
assert {:cancel, :invalid_signature} =
|
||||
SignatureRetryWorker.perform(failed_signature_job(params, req_headers))
|
||||
end
|
||||
|
||||
test "cancels when the refetched key still cannot validate the signature" do
|
||||
alice = insert(:user, local: false, ap_id: "https://one.com/users/alice")
|
||||
|
||||
create = %{
|
||||
"type" => "Create",
|
||||
"actor" => alice.ap_id,
|
||||
"id" => "https://one.com/activities/invalid-signature-create",
|
||||
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||
"cc" => [],
|
||||
"object" => %{
|
||||
"type" => "Note",
|
||||
"id" => "https://one.com/objects/invalid-signature-note",
|
||||
"actor" => alice.ap_id,
|
||||
"attributedTo" => alice.ap_id,
|
||||
"content" => "forged post",
|
||||
"published" => "2024-07-25T13:33:31Z",
|
||||
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||
"cc" => []
|
||||
}
|
||||
}
|
||||
|
||||
stub_actor_fetch(alice)
|
||||
|
||||
assert {:ok, oban_job} = enqueue_failed_signature(create, alice)
|
||||
|
||||
log =
|
||||
capture_log([level: :warning], fn ->
|
||||
assert {:cancel, :invalid_signature} = SignatureRetryWorker.perform(oban_job)
|
||||
end)
|
||||
|
||||
assert log =~ "Failed-signature inbox retry rejected"
|
||||
assert log =~ "reason=:invalid_signature"
|
||||
assert log =~ "payload_actor=\"https://one.com/users/alice\""
|
||||
assert log =~ "signature_actor=\"https://one.com/users/alice\""
|
||||
assert log =~ "activity_id=\"https://one.com/activities/invalid-signature-create\""
|
||||
assert log =~ "type=\"Create\""
|
||||
assert log =~ "request_path=\"/inbox\""
|
||||
|
||||
refute Activity.get_by_ap_id(create["id"])
|
||||
end
|
||||
|
||||
test "processes the activity after refetching a valid matching signature" do
|
||||
alice = insert(:user, local: false, ap_id: "https://one.com/users/alice")
|
||||
|
||||
create = %{
|
||||
"type" => "Create",
|
||||
"actor" => alice.ap_id,
|
||||
"id" => "https://one.com/activities/valid-signature-create",
|
||||
"context" => "https://one.com/contexts/valid-signature-create",
|
||||
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||
"cc" => [],
|
||||
"object" => %{
|
||||
"type" => "Note",
|
||||
"id" => "https://one.com/objects/valid-signature-note",
|
||||
"actor" => alice.ap_id,
|
||||
"attributedTo" => alice.ap_id,
|
||||
"context" => "https://one.com/contexts/valid-signature-create",
|
||||
"content" => "valid post",
|
||||
"published" => "2024-07-25T13:33:31Z",
|
||||
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||
"cc" => []
|
||||
}
|
||||
}
|
||||
|
||||
expect_signature_from(alice)
|
||||
|
||||
assert {:ok, oban_job} = enqueue_failed_signature(create, alice)
|
||||
assert {:ok, %Activity{}} = SignatureRetryWorker.perform(oban_job)
|
||||
assert Activity.get_by_ap_id(create["id"])
|
||||
end
|
||||
|
||||
test "processes the activity when a real signature validates with a query string" do
|
||||
alice = insert(:user, local: false, ap_id: "https://one.com/users/alice")
|
||||
|
||||
create = %{
|
||||
"type" => "Create",
|
||||
"actor" => alice.ap_id,
|
||||
"id" => "https://one.com/activities/valid-query-signature-create",
|
||||
"context" => "https://one.com/contexts/valid-query-signature-create",
|
||||
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||
"cc" => [],
|
||||
"object" => %{
|
||||
"type" => "Note",
|
||||
"id" => "https://one.com/objects/valid-query-signature-note",
|
||||
"actor" => alice.ap_id,
|
||||
"attributedTo" => alice.ap_id,
|
||||
"context" => "https://one.com/contexts/valid-query-signature-create",
|
||||
"content" => "valid signed post",
|
||||
"published" => "2024-07-25T13:33:31Z",
|
||||
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||
"cc" => []
|
||||
}
|
||||
}
|
||||
|
||||
stub_actor_fetch(alice)
|
||||
|
||||
date = "Thu, 25 Jul 2024 13:33:31 GMT"
|
||||
digest = "SHA-256=fake-digest"
|
||||
|
||||
signature =
|
||||
Signature.sign(alice, %{
|
||||
"(request-target)" => "post /inbox?foo=bar",
|
||||
"content-type" => "application/activity+json",
|
||||
date: date,
|
||||
digest: digest,
|
||||
host: "local.test"
|
||||
})
|
||||
|
||||
req_headers = [
|
||||
["host", "local.test"],
|
||||
["date", date],
|
||||
["digest", digest],
|
||||
["content-type", "application/activity+json"],
|
||||
["signature", signature]
|
||||
]
|
||||
|
||||
assert {:ok, %Activity{}} =
|
||||
SignatureRetryWorker.perform(
|
||||
failed_signature_job(create, req_headers, query_string: "foo=bar")
|
||||
)
|
||||
|
||||
assert Activity.get_by_ap_id(create["id"])
|
||||
end
|
||||
|
||||
test "cancels when signature actor does not match payload actor" do
|
||||
alice = insert(:user, local: false, ap_id: "https://one.com/users/alice")
|
||||
bob = insert(:user, local: false, ap_id: "https://two.com/users/bob")
|
||||
|
||||
note =
|
||||
insert(:note,
|
||||
user: bob,
|
||||
object_local: false,
|
||||
data: %{"id" => "https://two.com/objects/malicious-update-note"}
|
||||
)
|
||||
|
||||
update = %{
|
||||
"type" => "Update",
|
||||
"actor" => bob.ap_id,
|
||||
"id" => "https://two.com/activities/malicious-update",
|
||||
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||
"cc" => [],
|
||||
"object" => note.data
|
||||
}
|
||||
|
||||
assert_mismatched_signature_cancelled(update, alice)
|
||||
end
|
||||
|
||||
test "cancels signature actor mismatch through Federator-created jobs" do
|
||||
alice = insert(:user, local: false, ap_id: "https://one.com/users/alice")
|
||||
bob = insert(:user, local: false, ap_id: "https://two.com/users/bob")
|
||||
|
||||
note =
|
||||
insert(:note,
|
||||
user: bob,
|
||||
object_local: false,
|
||||
data: %{"id" => "https://two.com/objects/federator-malicious-note"}
|
||||
)
|
||||
|
||||
update = %{
|
||||
"type" => "Update",
|
||||
"actor" => bob.ap_id,
|
||||
"id" => "https://two.com/activities/federator-malicious-update",
|
||||
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||
"cc" => [],
|
||||
"object" => note.data
|
||||
}
|
||||
|
||||
assert_mismatched_signature_cancelled(update, alice)
|
||||
end
|
||||
|
||||
test "cancels signature actor mismatch before processing a forged Create" do
|
||||
alice = insert(:user, local: false, ap_id: "https://one.com/users/alice")
|
||||
bob = insert(:user, local: false, ap_id: "https://two.com/users/bob")
|
||||
|
||||
create = %{
|
||||
"type" => "Create",
|
||||
"actor" => bob.ap_id,
|
||||
"id" => "https://two.com/activities/forged-create",
|
||||
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||
"cc" => [],
|
||||
"object" => %{
|
||||
"type" => "Note",
|
||||
"id" => "https://two.com/objects/forged-note",
|
||||
"actor" => bob.ap_id,
|
||||
"attributedTo" => bob.ap_id,
|
||||
"content" => "forged post",
|
||||
"published" => "2024-07-25T13:33:31Z",
|
||||
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||
"cc" => []
|
||||
}
|
||||
}
|
||||
|
||||
assert_mismatched_signature_cancelled(create, alice)
|
||||
end
|
||||
|
||||
test "cancels signature actor mismatch when payload actor is embedded" do
|
||||
alice = insert(:user, local: false, ap_id: "https://one.com/users/alice")
|
||||
bob = insert(:user, local: false, ap_id: "https://two.com/users/bob")
|
||||
|
||||
create = %{
|
||||
"type" => "Create",
|
||||
"actor" => %{"id" => bob.ap_id},
|
||||
"id" => "https://two.com/activities/embedded-actor-forged-create",
|
||||
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||
"cc" => [],
|
||||
"object" => %{
|
||||
"type" => "Note",
|
||||
"id" => "https://two.com/objects/embedded-actor-forged-note",
|
||||
"actor" => bob.ap_id,
|
||||
"attributedTo" => bob.ap_id,
|
||||
"content" => "forged post",
|
||||
"published" => "2024-07-25T13:33:31Z",
|
||||
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||
"cc" => []
|
||||
}
|
||||
}
|
||||
|
||||
assert_mismatched_signature_cancelled(create, alice)
|
||||
end
|
||||
|
||||
test "logs signature actor mismatch retry rejections" do
|
||||
alice = insert(:user, local: false, ap_id: "https://one.com/users/alice")
|
||||
bob = insert(:user, local: false, ap_id: "https://two.com/users/bob")
|
||||
|
||||
create = %{
|
||||
"type" => "Create",
|
||||
"actor" => bob.ap_id,
|
||||
"id" => "https://two.com/activities/logged-forged-create",
|
||||
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||
"cc" => [],
|
||||
"object" => %{
|
||||
"type" => "Note",
|
||||
"id" => "https://two.com/objects/logged-forged-note",
|
||||
"actor" => bob.ap_id,
|
||||
"attributedTo" => bob.ap_id,
|
||||
"content" => "forged post",
|
||||
"published" => "2024-07-25T13:33:31Z",
|
||||
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||
"cc" => []
|
||||
}
|
||||
}
|
||||
|
||||
log = assert_mismatched_signature_cancelled(create, alice)
|
||||
|
||||
assert log =~ "Failed-signature inbox retry rejected"
|
||||
assert log =~ "reason=:actor_signature_mismatch"
|
||||
assert log =~ "payload_actor=\"https://two.com/users/bob\""
|
||||
assert log =~ "signature_actor=\"https://one.com/users/alice\""
|
||||
assert log =~ "activity_id=\"https://two.com/activities/logged-forged-create\""
|
||||
assert log =~ "type=\"Create\""
|
||||
assert log =~ "request_path=\"/inbox\""
|
||||
end
|
||||
|
||||
test "cancels signature actor mismatch before actually creating a forged post" do
|
||||
alice = insert(:user, local: false, ap_id: "https://one.com/users/alice")
|
||||
bob = insert(:user, local: false, ap_id: "https://two.com/users/bob")
|
||||
|
||||
object_id = "https://two.com/objects/actually-forged-note"
|
||||
|
||||
create = %{
|
||||
"type" => "Create",
|
||||
"actor" => bob.ap_id,
|
||||
"id" => "https://two.com/activities/actually-forged-create",
|
||||
"context" => "https://two.com/contexts/actually-forged-create",
|
||||
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||
"cc" => [],
|
||||
"object" => %{
|
||||
"type" => "Note",
|
||||
"id" => object_id,
|
||||
"actor" => bob.ap_id,
|
||||
"attributedTo" => bob.ap_id,
|
||||
"context" => "https://two.com/contexts/actually-forged-create",
|
||||
"content" => "forged post",
|
||||
"published" => "2024-07-25T13:33:31Z",
|
||||
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||
"cc" => []
|
||||
}
|
||||
}
|
||||
|
||||
assert_mismatched_signature_cancelled(create, alice)
|
||||
refute Object.get_by_ap_id(object_id)
|
||||
end
|
||||
|
||||
test "cancels signature actor mismatch before processing a forged Like" do
|
||||
alice = insert(:user, local: false, ap_id: "https://one.com/users/alice")
|
||||
bob = insert(:user, local: false, ap_id: "https://two.com/users/bob")
|
||||
note = insert(:note)
|
||||
|
||||
like = %{
|
||||
"type" => "Like",
|
||||
"actor" => bob.ap_id,
|
||||
"id" => "https://two.com/activities/forged-like",
|
||||
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||
"cc" => [],
|
||||
"object" => note.data["id"]
|
||||
}
|
||||
|
||||
assert_mismatched_signature_cancelled(like, alice)
|
||||
end
|
||||
|
||||
test "cancels signature actor mismatch before actually creating a forged Like" do
|
||||
alice = insert(:user, local: false, ap_id: "https://one.com/users/alice")
|
||||
bob = insert(:user, local: false, ap_id: "https://two.com/users/bob")
|
||||
note = insert(:note)
|
||||
|
||||
like = %{
|
||||
"type" => "Like",
|
||||
"actor" => bob.ap_id,
|
||||
"id" => "https://two.com/activities/actually-forged-like",
|
||||
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||
"cc" => [],
|
||||
"object" => note.data["id"]
|
||||
}
|
||||
|
||||
assert_mismatched_signature_cancelled(like, alice)
|
||||
refute Activity.get_by_ap_id(like["id"])
|
||||
end
|
||||
|
||||
test "cancels signature actor mismatch before processing a forged Announce" do
|
||||
alice = insert(:user, local: false, ap_id: "https://one.com/users/alice")
|
||||
bob = insert(:user, local: false, ap_id: "https://two.com/users/bob")
|
||||
note = insert(:note)
|
||||
|
||||
announce = %{
|
||||
"type" => "Announce",
|
||||
"actor" => bob.ap_id,
|
||||
"id" => "https://two.com/activities/forged-announce",
|
||||
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||
"cc" => [],
|
||||
"object" => note.data["id"]
|
||||
}
|
||||
|
||||
assert_mismatched_signature_cancelled(announce, alice)
|
||||
end
|
||||
|
||||
test "cancels signature actor mismatch before processing a forged Follow" do
|
||||
alice = insert(:user, local: false, ap_id: "https://one.com/users/alice")
|
||||
bob = insert(:user, local: false, ap_id: "https://two.com/users/bob")
|
||||
followed = insert(:user)
|
||||
|
||||
follow = %{
|
||||
"type" => "Follow",
|
||||
"actor" => bob.ap_id,
|
||||
"id" => "https://two.com/activities/forged-follow",
|
||||
"to" => [followed.ap_id],
|
||||
"cc" => [],
|
||||
"object" => followed.ap_id
|
||||
}
|
||||
|
||||
assert_mismatched_signature_cancelled(follow, alice)
|
||||
end
|
||||
|
||||
test "cancels signature actor mismatch before processing a forged Undo" do
|
||||
alice = insert(:user, local: false, ap_id: "https://one.com/users/alice")
|
||||
bob = insert(:user, local: false, ap_id: "https://two.com/users/bob")
|
||||
|
||||
undo = %{
|
||||
"type" => "Undo",
|
||||
"actor" => bob.ap_id,
|
||||
"id" => "https://two.com/activities/forged-undo",
|
||||
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||
"cc" => [],
|
||||
"object" => "https://two.com/activities/existing-bob-activity"
|
||||
}
|
||||
|
||||
assert_mismatched_signature_cancelled(undo, alice)
|
||||
end
|
||||
end
|
||||
Loading…
Add table
Add a link
Reference in a new issue