# Pleroma: A lightweight social networking server
-# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
alias Pleroma.UserInviteToken
alias Pleroma.Web.ActivityPub.Relay
alias Pleroma.Web.CommonAPI
- alias Pleroma.Web.MastodonAPI.StatusView
alias Pleroma.Web.MediaProxy
setup_all do
end
describe "with [:auth, :enforce_oauth_admin_scope_usage]," do
- clear_config([:auth, :enforce_oauth_admin_scope_usage]) do
- Config.put([:auth, :enforce_oauth_admin_scope_usage], true)
- end
+ setup do: clear_config([:auth, :enforce_oauth_admin_scope_usage], true)
test "GET /api/pleroma/admin/users/:nickname requires admin:read:accounts or broader scope",
%{admin: admin} do
end
describe "unless [:auth, :enforce_oauth_admin_scope_usage]," do
- clear_config([:auth, :enforce_oauth_admin_scope_usage]) do
- Config.put([:auth, :enforce_oauth_admin_scope_usage], false)
- end
+ setup do: clear_config([:auth, :enforce_oauth_admin_scope_usage], false)
test "GET /api/pleroma/admin/users/:nickname requires " <>
"read:accounts or admin:read:accounts or broader scope",
end
describe "POST /api/pleroma/admin/email_invite, with valid config" do
- clear_config([:instance, :registrations_open]) do
- Config.put([:instance, :registrations_open], false)
- end
-
- clear_config([:instance, :invites_enabled]) do
- Config.put([:instance, :invites_enabled], true)
- end
+ setup do: clear_config([:instance, :registrations_open], false)
+ setup do: clear_config([:instance, :invites_enabled], true)
test "sends invitation and returns 204", %{admin: admin, conn: conn} do
recipient_email = "foo@bar.com"
end
describe "POST /api/pleroma/admin/users/email_invite, with invalid config" do
- clear_config([:instance, :registrations_open])
- clear_config([:instance, :invites_enabled])
+ setup do: clear_config([:instance, :registrations_open])
+ setup do: clear_config([:instance, :invites_enabled])
test "it returns 500 if `invites_enabled` is not enabled", %{conn: conn} do
Config.put([:instance, :registrations_open], false)
end
end
- describe "GET /api/pleroma/admin/grouped_reports" do
- setup do
- [reporter, target_user] = insert_pair(:user)
-
- date1 = (DateTime.to_unix(DateTime.utc_now()) + 1000) |> DateTime.from_unix!()
- date2 = (DateTime.to_unix(DateTime.utc_now()) + 2000) |> DateTime.from_unix!()
- date3 = (DateTime.to_unix(DateTime.utc_now()) + 3000) |> DateTime.from_unix!()
-
- first_status =
- insert(:note_activity, user: target_user, data_attrs: %{"published" => date1})
-
- second_status =
- insert(:note_activity, user: target_user, data_attrs: %{"published" => date2})
-
- third_status =
- insert(:note_activity, user: target_user, data_attrs: %{"published" => date3})
-
- {:ok, first_report} =
- CommonAPI.report(reporter, %{
- "account_id" => target_user.id,
- "status_ids" => [first_status.id, second_status.id, third_status.id]
- })
-
- {:ok, second_report} =
- CommonAPI.report(reporter, %{
- "account_id" => target_user.id,
- "status_ids" => [first_status.id, second_status.id]
- })
-
- {:ok, third_report} =
- CommonAPI.report(reporter, %{
- "account_id" => target_user.id,
- "status_ids" => [first_status.id]
- })
-
- %{
- first_status: Activity.get_by_ap_id_with_object(first_status.data["id"]),
- second_status: Activity.get_by_ap_id_with_object(second_status.data["id"]),
- third_status: Activity.get_by_ap_id_with_object(third_status.data["id"]),
- first_report: first_report,
- first_status_reports: [first_report, second_report, third_report],
- second_status_reports: [first_report, second_report],
- third_status_reports: [first_report],
- target_user: target_user,
- reporter: reporter
- }
- end
-
- test "returns reports grouped by status", %{
- conn: conn,
- first_status: first_status,
- second_status: second_status,
- third_status: third_status,
- first_status_reports: first_status_reports,
- second_status_reports: second_status_reports,
- third_status_reports: third_status_reports,
- target_user: target_user,
- reporter: reporter
- } do
- response =
- conn
- |> get("/api/pleroma/admin/grouped_reports")
- |> json_response(:ok)
-
- assert length(response["reports"]) == 3
-
- first_group = Enum.find(response["reports"], &(&1["status"]["id"] == first_status.id))
-
- second_group = Enum.find(response["reports"], &(&1["status"]["id"] == second_status.id))
-
- third_group = Enum.find(response["reports"], &(&1["status"]["id"] == third_status.id))
-
- assert length(first_group["reports"]) == 3
- assert length(second_group["reports"]) == 2
- assert length(third_group["reports"]) == 1
-
- assert first_group["date"] ==
- Enum.max_by(first_status_reports, fn act ->
- NaiveDateTime.from_iso8601!(act.data["published"])
- end).data["published"]
-
- assert first_group["status"] ==
- Map.put(
- stringify_keys(StatusView.render("show.json", %{activity: first_status})),
- "deleted",
- false
- )
-
- assert(first_group["account"]["id"] == target_user.id)
-
- assert length(first_group["actors"]) == 1
- assert hd(first_group["actors"])["id"] == reporter.id
-
- assert Enum.map(first_group["reports"], & &1["id"]) --
- Enum.map(first_status_reports, & &1.id) == []
-
- assert second_group["date"] ==
- Enum.max_by(second_status_reports, fn act ->
- NaiveDateTime.from_iso8601!(act.data["published"])
- end).data["published"]
-
- assert second_group["status"] ==
- Map.put(
- stringify_keys(StatusView.render("show.json", %{activity: second_status})),
- "deleted",
- false
- )
-
- assert second_group["account"]["id"] == target_user.id
-
- assert length(second_group["actors"]) == 1
- assert hd(second_group["actors"])["id"] == reporter.id
-
- assert Enum.map(second_group["reports"], & &1["id"]) --
- Enum.map(second_status_reports, & &1.id) == []
-
- assert third_group["date"] ==
- Enum.max_by(third_status_reports, fn act ->
- NaiveDateTime.from_iso8601!(act.data["published"])
- end).data["published"]
-
- assert third_group["status"] ==
- Map.put(
- stringify_keys(StatusView.render("show.json", %{activity: third_status})),
- "deleted",
- false
- )
-
- assert third_group["account"]["id"] == target_user.id
-
- assert length(third_group["actors"]) == 1
- assert hd(third_group["actors"])["id"] == reporter.id
-
- assert Enum.map(third_group["reports"], & &1["id"]) --
- Enum.map(third_status_reports, & &1.id) == []
- end
-
- test "reopened report renders status data", %{
- conn: conn,
- first_report: first_report,
- first_status: first_status
- } do
- {:ok, _} = CommonAPI.update_report_state(first_report.id, "resolved")
-
- response =
- conn
- |> get("/api/pleroma/admin/grouped_reports")
- |> json_response(:ok)
-
- first_group = Enum.find(response["reports"], &(&1["status"]["id"] == first_status.id))
-
- assert first_group["status"] ==
- Map.put(
- stringify_keys(StatusView.render("show.json", %{activity: first_status})),
- "deleted",
- false
- )
- end
-
- test "reopened report does not render status data if status has been deleted", %{
- conn: conn,
- first_report: first_report,
- first_status: first_status,
- target_user: target_user
- } do
- {:ok, _} = CommonAPI.update_report_state(first_report.id, "resolved")
- {:ok, _} = CommonAPI.delete(first_status.id, target_user)
-
- refute Activity.get_by_ap_id(first_status.id)
-
- response =
- conn
- |> get("/api/pleroma/admin/grouped_reports")
- |> json_response(:ok)
-
- assert Enum.find(response["reports"], &(&1["status"]["deleted"] == true))["status"][
- "deleted"
- ] == true
-
- assert length(Enum.filter(response["reports"], &(&1["status"]["deleted"] == false))) == 2
- end
-
- test "account not empty if status was deleted", %{
- conn: conn,
- first_report: first_report,
- first_status: first_status,
- target_user: target_user
- } do
- {:ok, _} = CommonAPI.update_report_state(first_report.id, "resolved")
- {:ok, _} = CommonAPI.delete(first_status.id, target_user)
-
- refute Activity.get_by_ap_id(first_status.id)
-
- response =
- conn
- |> get("/api/pleroma/admin/grouped_reports")
- |> json_response(:ok)
-
- assert Enum.find(response["reports"], &(&1["status"]["deleted"] == true))["account"]
- end
- end
-
describe "PUT /api/pleroma/admin/statuses/:id" do
setup do
activity = insert(:note_activity)
"@#{admin.nickname} deleted status ##{id}"
end
- test "returns error when status is not exist", %{conn: conn} do
+ test "returns 404 when the status does not exist", %{conn: conn} do
conn = delete(conn, "/api/pleroma/admin/statuses/test")
- assert json_response(conn, :bad_request) == "Could not delete"
+ assert json_response(conn, :not_found) == "Not found"
end
end
describe "GET /api/pleroma/admin/config" do
- clear_config(:configurable_from_database) do
- Config.put(:configurable_from_database, true)
- end
+ setup do: clear_config(:configurable_from_database, true)
test "when configuration from database is off", %{conn: conn} do
Config.put(:configurable_from_database, false)
end)
end
- clear_config(:configurable_from_database) do
- Config.put(:configurable_from_database, true)
- end
+ setup do: clear_config(:configurable_from_database, true)
@tag capture_log: true
test "create new config setting in db", %{conn: conn} do
"value" => "Tesla.Adapter.Httpc",
"db" => [":adapter"]
}
- ],
- "need_reboot" => true
+ ]
}
end
end
test "common config example", %{conn: conn} do
- adapter = Application.get_env(:tesla, :adapter)
- on_exit(fn -> Application.put_env(:tesla, :adapter, adapter) end)
-
conn =
post(conn, "/api/pleroma/admin/config", %{
configs: [
%{"tuple" => [":regex4", "~r/https:\/\/example.com/s"]},
%{"tuple" => [":name", "Pleroma"]}
]
- },
- %{
- "group" => ":tesla",
- "key" => ":adapter",
- "value" => "Tesla.Adapter.Httpc"
}
]
})
- assert Application.get_env(:tesla, :adapter) == Tesla.Adapter.Httpc
assert Config.get([Pleroma.Captcha.NotReal, :name]) == "Pleroma"
assert json_response(conn, 200) == %{
":regex4",
":name"
]
- },
- %{
- "group" => ":tesla",
- "key" => ":adapter",
- "value" => "Tesla.Adapter.Httpc",
- "db" => [":adapter"]
}
- ],
- "need_reboot" => true
+ ]
}
end
end
describe "GET /api/pleroma/admin/restart" do
- clear_config(:configurable_from_database) do
- Config.put(:configurable_from_database, true)
- end
+ setup do: clear_config(:configurable_from_database, true)
test "pleroma restarts", %{conn: conn} do
capture_log(fn ->
end
end
+ describe "GET /api/pleroma/admin/statuses" do
+ test "returns all public and unlisted statuses", %{conn: conn, admin: admin} do
+ blocked = insert(:user)
+ user = insert(:user)
+ User.block(admin, blocked)
+
+ {:ok, _} =
+ CommonAPI.post(user, %{"status" => "@#{admin.nickname}", "visibility" => "direct"})
+
+ {:ok, _} = CommonAPI.post(user, %{"status" => ".", "visibility" => "unlisted"})
+ {:ok, _} = CommonAPI.post(user, %{"status" => ".", "visibility" => "private"})
+ {:ok, _} = CommonAPI.post(user, %{"status" => ".", "visibility" => "public"})
+ {:ok, _} = CommonAPI.post(blocked, %{"status" => ".", "visibility" => "public"})
+
+ response =
+ conn
+ |> get("/api/pleroma/admin/statuses")
+ |> json_response(200)
+
+ refute "private" in Enum.map(response, & &1["visibility"])
+ assert length(response) == 3
+ end
+
+ test "returns only local statuses with local_only on", %{conn: conn} do
+ user = insert(:user)
+ remote_user = insert(:user, local: false, nickname: "archaeme@archae.me")
+ insert(:note_activity, user: user, local: true)
+ insert(:note_activity, user: remote_user, local: false)
+
+ response =
+ conn
+ |> get("/api/pleroma/admin/statuses?local_only=true")
+ |> json_response(200)
+
+ assert length(response) == 1
+ end
+
+ test "returns private and direct statuses with godmode on", %{conn: conn, admin: admin} do
+ user = insert(:user)
+
+ {:ok, _} =
+ CommonAPI.post(user, %{"status" => "@#{admin.nickname}", "visibility" => "direct"})
+
+ {:ok, _} = CommonAPI.post(user, %{"status" => ".", "visibility" => "private"})
+ {:ok, _} = CommonAPI.post(user, %{"status" => ".", "visibility" => "public"})
+ conn = get(conn, "/api/pleroma/admin/statuses?godmode=true")
+ assert json_response(conn, 200) |> length() == 3
+ end
+ end
+
describe "GET /api/pleroma/admin/users/:nickname/statuses" do
setup do
user = insert(:user)
assert json_response(conn, 200) |> length() == 5
end
+
+ test "excludes reblogs by default", %{conn: conn, user: user} do
+ other_user = insert(:user)
+ {:ok, activity} = CommonAPI.post(user, %{"status" => "."})
+ {:ok, %Activity{}, _} = CommonAPI.repeat(activity.id, other_user)
+
+ conn_res = get(conn, "/api/pleroma/admin/users/#{other_user.nickname}/statuses")
+ assert json_response(conn_res, 200) |> length() == 0
+
+ conn_res =
+ get(conn, "/api/pleroma/admin/users/#{other_user.nickname}/statuses?with_reblogs=true")
+
+ assert json_response(conn_res, 200) |> length() == 1
+ end
end
describe "GET /api/pleroma/admin/moderation_log" do
end
end
+ describe "GET /users/:nickname/credentials" do
+ test "gets the user credentials", %{conn: conn} do
+ user = insert(:user)
+ conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/credentials")
+
+ response = assert json_response(conn, 200)
+ assert response["email"] == user.email
+ end
+
+ test "returns 403 if requested by a non-admin" do
+ user = insert(:user)
+
+ conn =
+ build_conn()
+ |> assign(:user, user)
+ |> get("/api/pleroma/admin/users/#{user.nickname}/credentials")
+
+ assert json_response(conn, :forbidden)
+ end
+ end
+
+ describe "PATCH /users/:nickname/credentials" do
+ test "changes password and email", %{conn: conn, admin: admin} do
+ user = insert(:user)
+ assert user.password_reset_pending == false
+
+ conn =
+ patch(conn, "/api/pleroma/admin/users/#{user.nickname}/credentials", %{
+ "password" => "new_password",
+ "email" => "new_email@example.com",
+ "name" => "new_name"
+ })
+
+ assert json_response(conn, 200) == %{"status" => "success"}
+
+ ObanHelpers.perform_all()
+
+ updated_user = User.get_by_id(user.id)
+
+ assert updated_user.email == "new_email@example.com"
+ assert updated_user.name == "new_name"
+ assert updated_user.password_hash != user.password_hash
+ assert updated_user.password_reset_pending == true
+
+ [log_entry2, log_entry1] = ModerationLog |> Repo.all() |> Enum.sort()
+
+ assert ModerationLog.get_log_entry_message(log_entry1) ==
+ "@#{admin.nickname} updated users: @#{user.nickname}"
+
+ assert ModerationLog.get_log_entry_message(log_entry2) ==
+ "@#{admin.nickname} forced password reset for users: @#{user.nickname}"
+ end
+
+ test "returns 403 if requested by a non-admin" do
+ user = insert(:user)
+
+ conn =
+ build_conn()
+ |> assign(:user, user)
+ |> patch("/api/pleroma/admin/users/#{user.nickname}/credentials", %{
+ "password" => "new_password",
+ "email" => "new_email@example.com",
+ "name" => "new_name"
+ })
+
+ assert json_response(conn, :forbidden)
+ end
+ end
+
describe "PATCH /users/:nickname/force_password_reset" do
test "sets password_reset_pending to true", %{conn: conn} do
user = insert(:user)
user = insert(:user, local: false, nickname: "archaeme@archae.me")
user2 = insert(:user, local: false, nickname: "test@test.com")
insert_pair(:note_activity, user: user)
- insert(:note_activity, user: user2)
+ activity = insert(:note_activity, user: user2)
ret_conn = get(conn, "/api/pleroma/admin/instances/archae.me/statuses")
response = json_response(ret_conn, 200)
assert Enum.empty?(response)
+
+ CommonAPI.repeat(activity.id, user)
+
+ ret_conn = get(conn, "/api/pleroma/admin/instances/archae.me/statuses")
+ response = json_response(ret_conn, 200)
+ assert length(response) == 2
+
+ ret_conn = get(conn, "/api/pleroma/admin/instances/archae.me/statuses?with_reblogs=true")
+ response = json_response(ret_conn, 200)
+ assert length(response) == 3
end
end
assert String.starts_with?(child["group"], ":")
assert child["description"]
end
+
+ describe "/api/pleroma/admin/stats" do
+ test "status visibility count", %{conn: conn} do
+ admin = insert(:user, is_admin: true)
+ user = insert(:user)
+ CommonAPI.post(user, %{"visibility" => "public", "status" => "hey"})
+ CommonAPI.post(user, %{"visibility" => "unlisted", "status" => "hey"})
+ CommonAPI.post(user, %{"visibility" => "unlisted", "status" => "hey"})
+
+ response =
+ conn
+ |> assign(:user, admin)
+ |> get("/api/pleroma/admin/stats")
+ |> json_response(200)
+
+ assert %{"direct" => 0, "private" => 0, "public" => 1, "unlisted" => 2} =
+ response["status_visibility"]
+ end
+ end
end
# Needed for testing