Fork of Pleroma with site-specific changes and feature branches https://git.pleroma.social/pleroma/pleroma
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

3644 lines
115KB

  1. # Pleroma: A lightweight social networking server
  2. # Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
  3. # SPDX-License-Identifier: AGPL-3.0-only
  4. defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
  5. use Pleroma.Web.ConnCase
  6. use Oban.Testing, repo: Pleroma.Repo
  7. import Pleroma.Factory
  8. import ExUnit.CaptureLog
  9. alias Pleroma.Activity
  10. alias Pleroma.Config
  11. alias Pleroma.ConfigDB
  12. alias Pleroma.HTML
  13. alias Pleroma.ModerationLog
  14. alias Pleroma.Repo
  15. alias Pleroma.ReportNote
  16. alias Pleroma.Tests.ObanHelpers
  17. alias Pleroma.User
  18. alias Pleroma.UserInviteToken
  19. alias Pleroma.Web.ActivityPub.Relay
  20. alias Pleroma.Web.CommonAPI
  21. alias Pleroma.Web.MastodonAPI.StatusView
  22. alias Pleroma.Web.MediaProxy
  23. setup_all do
  24. Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end)
  25. :ok
  26. end
  27. setup do
  28. admin = insert(:user, is_admin: true)
  29. token = insert(:oauth_admin_token, user: admin)
  30. conn =
  31. build_conn()
  32. |> assign(:user, admin)
  33. |> assign(:token, token)
  34. {:ok, %{admin: admin, token: token, conn: conn}}
  35. end
  36. describe "with [:auth, :enforce_oauth_admin_scope_usage]," do
  37. clear_config([:auth, :enforce_oauth_admin_scope_usage]) do
  38. Config.put([:auth, :enforce_oauth_admin_scope_usage], true)
  39. end
  40. test "GET /api/pleroma/admin/users/:nickname requires admin:read:accounts or broader scope",
  41. %{admin: admin} do
  42. user = insert(:user)
  43. url = "/api/pleroma/admin/users/#{user.nickname}"
  44. good_token1 = insert(:oauth_token, user: admin, scopes: ["admin"])
  45. good_token2 = insert(:oauth_token, user: admin, scopes: ["admin:read"])
  46. good_token3 = insert(:oauth_token, user: admin, scopes: ["admin:read:accounts"])
  47. bad_token1 = insert(:oauth_token, user: admin, scopes: ["read:accounts"])
  48. bad_token2 = insert(:oauth_token, user: admin, scopes: ["admin:read:accounts:partial"])
  49. bad_token3 = nil
  50. for good_token <- [good_token1, good_token2, good_token3] do
  51. conn =
  52. build_conn()
  53. |> assign(:user, admin)
  54. |> assign(:token, good_token)
  55. |> get(url)
  56. assert json_response(conn, 200)
  57. end
  58. for good_token <- [good_token1, good_token2, good_token3] do
  59. conn =
  60. build_conn()
  61. |> assign(:user, nil)
  62. |> assign(:token, good_token)
  63. |> get(url)
  64. assert json_response(conn, :forbidden)
  65. end
  66. for bad_token <- [bad_token1, bad_token2, bad_token3] do
  67. conn =
  68. build_conn()
  69. |> assign(:user, admin)
  70. |> assign(:token, bad_token)
  71. |> get(url)
  72. assert json_response(conn, :forbidden)
  73. end
  74. end
  75. end
  76. describe "unless [:auth, :enforce_oauth_admin_scope_usage]," do
  77. clear_config([:auth, :enforce_oauth_admin_scope_usage]) do
  78. Config.put([:auth, :enforce_oauth_admin_scope_usage], false)
  79. end
  80. test "GET /api/pleroma/admin/users/:nickname requires " <>
  81. "read:accounts or admin:read:accounts or broader scope",
  82. %{admin: admin} do
  83. user = insert(:user)
  84. url = "/api/pleroma/admin/users/#{user.nickname}"
  85. good_token1 = insert(:oauth_token, user: admin, scopes: ["admin"])
  86. good_token2 = insert(:oauth_token, user: admin, scopes: ["admin:read"])
  87. good_token3 = insert(:oauth_token, user: admin, scopes: ["admin:read:accounts"])
  88. good_token4 = insert(:oauth_token, user: admin, scopes: ["read:accounts"])
  89. good_token5 = insert(:oauth_token, user: admin, scopes: ["read"])
  90. good_tokens = [good_token1, good_token2, good_token3, good_token4, good_token5]
  91. bad_token1 = insert(:oauth_token, user: admin, scopes: ["read:accounts:partial"])
  92. bad_token2 = insert(:oauth_token, user: admin, scopes: ["admin:read:accounts:partial"])
  93. bad_token3 = nil
  94. for good_token <- good_tokens do
  95. conn =
  96. build_conn()
  97. |> assign(:user, admin)
  98. |> assign(:token, good_token)
  99. |> get(url)
  100. assert json_response(conn, 200)
  101. end
  102. for good_token <- good_tokens do
  103. conn =
  104. build_conn()
  105. |> assign(:user, nil)
  106. |> assign(:token, good_token)
  107. |> get(url)
  108. assert json_response(conn, :forbidden)
  109. end
  110. for bad_token <- [bad_token1, bad_token2, bad_token3] do
  111. conn =
  112. build_conn()
  113. |> assign(:user, admin)
  114. |> assign(:token, bad_token)
  115. |> get(url)
  116. assert json_response(conn, :forbidden)
  117. end
  118. end
  119. end
  120. describe "DELETE /api/pleroma/admin/users" do
  121. test "single user", %{admin: admin, conn: conn} do
  122. user = insert(:user)
  123. conn =
  124. conn
  125. |> put_req_header("accept", "application/json")
  126. |> delete("/api/pleroma/admin/users?nickname=#{user.nickname}")
  127. log_entry = Repo.one(ModerationLog)
  128. assert ModerationLog.get_log_entry_message(log_entry) ==
  129. "@#{admin.nickname} deleted users: @#{user.nickname}"
  130. assert json_response(conn, 200) == user.nickname
  131. end
  132. test "multiple users", %{admin: admin, conn: conn} do
  133. user_one = insert(:user)
  134. user_two = insert(:user)
  135. conn =
  136. conn
  137. |> put_req_header("accept", "application/json")
  138. |> delete("/api/pleroma/admin/users", %{
  139. nicknames: [user_one.nickname, user_two.nickname]
  140. })
  141. log_entry = Repo.one(ModerationLog)
  142. assert ModerationLog.get_log_entry_message(log_entry) ==
  143. "@#{admin.nickname} deleted users: @#{user_one.nickname}, @#{user_two.nickname}"
  144. response = json_response(conn, 200)
  145. assert response -- [user_one.nickname, user_two.nickname] == []
  146. end
  147. end
  148. describe "/api/pleroma/admin/users" do
  149. test "Create", %{conn: conn} do
  150. conn =
  151. conn
  152. |> put_req_header("accept", "application/json")
  153. |> post("/api/pleroma/admin/users", %{
  154. "users" => [
  155. %{
  156. "nickname" => "lain",
  157. "email" => "lain@example.org",
  158. "password" => "test"
  159. },
  160. %{
  161. "nickname" => "lain2",
  162. "email" => "lain2@example.org",
  163. "password" => "test"
  164. }
  165. ]
  166. })
  167. response = json_response(conn, 200) |> Enum.map(&Map.get(&1, "type"))
  168. assert response == ["success", "success"]
  169. log_entry = Repo.one(ModerationLog)
  170. assert ["lain", "lain2"] -- Enum.map(log_entry.data["subjects"], & &1["nickname"]) == []
  171. end
  172. test "Cannot create user with existing email", %{conn: conn} do
  173. user = insert(:user)
  174. conn =
  175. conn
  176. |> put_req_header("accept", "application/json")
  177. |> post("/api/pleroma/admin/users", %{
  178. "users" => [
  179. %{
  180. "nickname" => "lain",
  181. "email" => user.email,
  182. "password" => "test"
  183. }
  184. ]
  185. })
  186. assert json_response(conn, 409) == [
  187. %{
  188. "code" => 409,
  189. "data" => %{
  190. "email" => user.email,
  191. "nickname" => "lain"
  192. },
  193. "error" => "email has already been taken",
  194. "type" => "error"
  195. }
  196. ]
  197. end
  198. test "Cannot create user with existing nickname", %{conn: conn} do
  199. user = insert(:user)
  200. conn =
  201. conn
  202. |> put_req_header("accept", "application/json")
  203. |> post("/api/pleroma/admin/users", %{
  204. "users" => [
  205. %{
  206. "nickname" => user.nickname,
  207. "email" => "someuser@plerama.social",
  208. "password" => "test"
  209. }
  210. ]
  211. })
  212. assert json_response(conn, 409) == [
  213. %{
  214. "code" => 409,
  215. "data" => %{
  216. "email" => "someuser@plerama.social",
  217. "nickname" => user.nickname
  218. },
  219. "error" => "nickname has already been taken",
  220. "type" => "error"
  221. }
  222. ]
  223. end
  224. test "Multiple user creation works in transaction", %{conn: conn} do
  225. user = insert(:user)
  226. conn =
  227. conn
  228. |> put_req_header("accept", "application/json")
  229. |> post("/api/pleroma/admin/users", %{
  230. "users" => [
  231. %{
  232. "nickname" => "newuser",
  233. "email" => "newuser@pleroma.social",
  234. "password" => "test"
  235. },
  236. %{
  237. "nickname" => "lain",
  238. "email" => user.email,
  239. "password" => "test"
  240. }
  241. ]
  242. })
  243. assert json_response(conn, 409) == [
  244. %{
  245. "code" => 409,
  246. "data" => %{
  247. "email" => user.email,
  248. "nickname" => "lain"
  249. },
  250. "error" => "email has already been taken",
  251. "type" => "error"
  252. },
  253. %{
  254. "code" => 409,
  255. "data" => %{
  256. "email" => "newuser@pleroma.social",
  257. "nickname" => "newuser"
  258. },
  259. "error" => "",
  260. "type" => "error"
  261. }
  262. ]
  263. assert User.get_by_nickname("newuser") === nil
  264. end
  265. end
  266. describe "/api/pleroma/admin/users/:nickname" do
  267. test "Show", %{conn: conn} do
  268. user = insert(:user)
  269. conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}")
  270. expected = %{
  271. "deactivated" => false,
  272. "id" => to_string(user.id),
  273. "local" => true,
  274. "nickname" => user.nickname,
  275. "roles" => %{"admin" => false, "moderator" => false},
  276. "tags" => [],
  277. "avatar" => User.avatar_url(user) |> MediaProxy.url(),
  278. "display_name" => HTML.strip_tags(user.name || user.nickname),
  279. "confirmation_pending" => false
  280. }
  281. assert expected == json_response(conn, 200)
  282. end
  283. test "when the user doesn't exist", %{conn: conn} do
  284. user = build(:user)
  285. conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}")
  286. assert "Not found" == json_response(conn, 404)
  287. end
  288. end
  289. describe "/api/pleroma/admin/users/follow" do
  290. test "allows to force-follow another user", %{admin: admin, conn: conn} do
  291. user = insert(:user)
  292. follower = insert(:user)
  293. conn
  294. |> put_req_header("accept", "application/json")
  295. |> post("/api/pleroma/admin/users/follow", %{
  296. "follower" => follower.nickname,
  297. "followed" => user.nickname
  298. })
  299. user = User.get_cached_by_id(user.id)
  300. follower = User.get_cached_by_id(follower.id)
  301. assert User.following?(follower, user)
  302. log_entry = Repo.one(ModerationLog)
  303. assert ModerationLog.get_log_entry_message(log_entry) ==
  304. "@#{admin.nickname} made @#{follower.nickname} follow @#{user.nickname}"
  305. end
  306. end
  307. describe "/api/pleroma/admin/users/unfollow" do
  308. test "allows to force-unfollow another user", %{admin: admin, conn: conn} do
  309. user = insert(:user)
  310. follower = insert(:user)
  311. User.follow(follower, user)
  312. conn
  313. |> put_req_header("accept", "application/json")
  314. |> post("/api/pleroma/admin/users/unfollow", %{
  315. "follower" => follower.nickname,
  316. "followed" => user.nickname
  317. })
  318. user = User.get_cached_by_id(user.id)
  319. follower = User.get_cached_by_id(follower.id)
  320. refute User.following?(follower, user)
  321. log_entry = Repo.one(ModerationLog)
  322. assert ModerationLog.get_log_entry_message(log_entry) ==
  323. "@#{admin.nickname} made @#{follower.nickname} unfollow @#{user.nickname}"
  324. end
  325. end
  326. describe "PUT /api/pleroma/admin/users/tag" do
  327. setup %{conn: conn} do
  328. user1 = insert(:user, %{tags: ["x"]})
  329. user2 = insert(:user, %{tags: ["y"]})
  330. user3 = insert(:user, %{tags: ["unchanged"]})
  331. conn =
  332. conn
  333. |> put_req_header("accept", "application/json")
  334. |> put(
  335. "/api/pleroma/admin/users/tag?nicknames[]=#{user1.nickname}&nicknames[]=" <>
  336. "#{user2.nickname}&tags[]=foo&tags[]=bar"
  337. )
  338. %{conn: conn, user1: user1, user2: user2, user3: user3}
  339. end
  340. test "it appends specified tags to users with specified nicknames", %{
  341. conn: conn,
  342. admin: admin,
  343. user1: user1,
  344. user2: user2
  345. } do
  346. assert json_response(conn, :no_content)
  347. assert User.get_cached_by_id(user1.id).tags == ["x", "foo", "bar"]
  348. assert User.get_cached_by_id(user2.id).tags == ["y", "foo", "bar"]
  349. log_entry = Repo.one(ModerationLog)
  350. users =
  351. [user1.nickname, user2.nickname]
  352. |> Enum.map(&"@#{&1}")
  353. |> Enum.join(", ")
  354. tags = ["foo", "bar"] |> Enum.join(", ")
  355. assert ModerationLog.get_log_entry_message(log_entry) ==
  356. "@#{admin.nickname} added tags: #{tags} to users: #{users}"
  357. end
  358. test "it does not modify tags of not specified users", %{conn: conn, user3: user3} do
  359. assert json_response(conn, :no_content)
  360. assert User.get_cached_by_id(user3.id).tags == ["unchanged"]
  361. end
  362. end
  363. describe "DELETE /api/pleroma/admin/users/tag" do
  364. setup %{conn: conn} do
  365. user1 = insert(:user, %{tags: ["x"]})
  366. user2 = insert(:user, %{tags: ["y", "z"]})
  367. user3 = insert(:user, %{tags: ["unchanged"]})
  368. conn =
  369. conn
  370. |> put_req_header("accept", "application/json")
  371. |> delete(
  372. "/api/pleroma/admin/users/tag?nicknames[]=#{user1.nickname}&nicknames[]=" <>
  373. "#{user2.nickname}&tags[]=x&tags[]=z"
  374. )
  375. %{conn: conn, user1: user1, user2: user2, user3: user3}
  376. end
  377. test "it removes specified tags from users with specified nicknames", %{
  378. conn: conn,
  379. admin: admin,
  380. user1: user1,
  381. user2: user2
  382. } do
  383. assert json_response(conn, :no_content)
  384. assert User.get_cached_by_id(user1.id).tags == []
  385. assert User.get_cached_by_id(user2.id).tags == ["y"]
  386. log_entry = Repo.one(ModerationLog)
  387. users =
  388. [user1.nickname, user2.nickname]
  389. |> Enum.map(&"@#{&1}")
  390. |> Enum.join(", ")
  391. tags = ["x", "z"] |> Enum.join(", ")
  392. assert ModerationLog.get_log_entry_message(log_entry) ==
  393. "@#{admin.nickname} removed tags: #{tags} from users: #{users}"
  394. end
  395. test "it does not modify tags of not specified users", %{conn: conn, user3: user3} do
  396. assert json_response(conn, :no_content)
  397. assert User.get_cached_by_id(user3.id).tags == ["unchanged"]
  398. end
  399. end
  400. describe "/api/pleroma/admin/users/:nickname/permission_group" do
  401. test "GET is giving user_info", %{admin: admin, conn: conn} do
  402. conn =
  403. conn
  404. |> put_req_header("accept", "application/json")
  405. |> get("/api/pleroma/admin/users/#{admin.nickname}/permission_group/")
  406. assert json_response(conn, 200) == %{
  407. "is_admin" => true,
  408. "is_moderator" => false
  409. }
  410. end
  411. test "/:right POST, can add to a permission group", %{admin: admin, conn: conn} do
  412. user = insert(:user)
  413. conn =
  414. conn
  415. |> put_req_header("accept", "application/json")
  416. |> post("/api/pleroma/admin/users/#{user.nickname}/permission_group/admin")
  417. assert json_response(conn, 200) == %{
  418. "is_admin" => true
  419. }
  420. log_entry = Repo.one(ModerationLog)
  421. assert ModerationLog.get_log_entry_message(log_entry) ==
  422. "@#{admin.nickname} made @#{user.nickname} admin"
  423. end
  424. test "/:right POST, can add to a permission group (multiple)", %{admin: admin, conn: conn} do
  425. user_one = insert(:user)
  426. user_two = insert(:user)
  427. conn =
  428. conn
  429. |> put_req_header("accept", "application/json")
  430. |> post("/api/pleroma/admin/users/permission_group/admin", %{
  431. nicknames: [user_one.nickname, user_two.nickname]
  432. })
  433. assert json_response(conn, 200) == %{"is_admin" => true}
  434. log_entry = Repo.one(ModerationLog)
  435. assert ModerationLog.get_log_entry_message(log_entry) ==
  436. "@#{admin.nickname} made @#{user_one.nickname}, @#{user_two.nickname} admin"
  437. end
  438. test "/:right DELETE, can remove from a permission group", %{admin: admin, conn: conn} do
  439. user = insert(:user, is_admin: true)
  440. conn =
  441. conn
  442. |> put_req_header("accept", "application/json")
  443. |> delete("/api/pleroma/admin/users/#{user.nickname}/permission_group/admin")
  444. assert json_response(conn, 200) == %{"is_admin" => false}
  445. log_entry = Repo.one(ModerationLog)
  446. assert ModerationLog.get_log_entry_message(log_entry) ==
  447. "@#{admin.nickname} revoked admin role from @#{user.nickname}"
  448. end
  449. test "/:right DELETE, can remove from a permission group (multiple)", %{
  450. admin: admin,
  451. conn: conn
  452. } do
  453. user_one = insert(:user, is_admin: true)
  454. user_two = insert(:user, is_admin: true)
  455. conn =
  456. conn
  457. |> put_req_header("accept", "application/json")
  458. |> delete("/api/pleroma/admin/users/permission_group/admin", %{
  459. nicknames: [user_one.nickname, user_two.nickname]
  460. })
  461. assert json_response(conn, 200) == %{"is_admin" => false}
  462. log_entry = Repo.one(ModerationLog)
  463. assert ModerationLog.get_log_entry_message(log_entry) ==
  464. "@#{admin.nickname} revoked admin role from @#{user_one.nickname}, @#{
  465. user_two.nickname
  466. }"
  467. end
  468. end
  469. describe "POST /api/pleroma/admin/email_invite, with valid config" do
  470. clear_config([:instance, :registrations_open]) do
  471. Config.put([:instance, :registrations_open], false)
  472. end
  473. clear_config([:instance, :invites_enabled]) do
  474. Config.put([:instance, :invites_enabled], true)
  475. end
  476. test "sends invitation and returns 204", %{admin: admin, conn: conn} do
  477. recipient_email = "foo@bar.com"
  478. recipient_name = "J. D."
  479. conn =
  480. post(
  481. conn,
  482. "/api/pleroma/admin/users/email_invite?email=#{recipient_email}&name=#{recipient_name}"
  483. )
  484. assert json_response(conn, :no_content)
  485. token_record = List.last(Repo.all(Pleroma.UserInviteToken))
  486. assert token_record
  487. refute token_record.used
  488. notify_email = Config.get([:instance, :notify_email])
  489. instance_name = Config.get([:instance, :name])
  490. email =
  491. Pleroma.Emails.UserEmail.user_invitation_email(
  492. admin,
  493. token_record,
  494. recipient_email,
  495. recipient_name
  496. )
  497. Swoosh.TestAssertions.assert_email_sent(
  498. from: {instance_name, notify_email},
  499. to: {recipient_name, recipient_email},
  500. html_body: email.html_body
  501. )
  502. end
  503. test "it returns 403 if requested by a non-admin" do
  504. non_admin_user = insert(:user)
  505. token = insert(:oauth_token, user: non_admin_user)
  506. conn =
  507. build_conn()
  508. |> assign(:user, non_admin_user)
  509. |> assign(:token, token)
  510. |> post("/api/pleroma/admin/users/email_invite?email=foo@bar.com&name=JD")
  511. assert json_response(conn, :forbidden)
  512. end
  513. end
  514. describe "POST /api/pleroma/admin/users/email_invite, with invalid config" do
  515. clear_config([:instance, :registrations_open])
  516. clear_config([:instance, :invites_enabled])
  517. test "it returns 500 if `invites_enabled` is not enabled", %{conn: conn} do
  518. Config.put([:instance, :registrations_open], false)
  519. Config.put([:instance, :invites_enabled], false)
  520. conn = post(conn, "/api/pleroma/admin/users/email_invite?email=foo@bar.com&name=JD")
  521. assert json_response(conn, :internal_server_error)
  522. end
  523. test "it returns 500 if `registrations_open` is enabled", %{conn: conn} do
  524. Config.put([:instance, :registrations_open], true)
  525. Config.put([:instance, :invites_enabled], true)
  526. conn = post(conn, "/api/pleroma/admin/users/email_invite?email=foo@bar.com&name=JD")
  527. assert json_response(conn, :internal_server_error)
  528. end
  529. end
  530. test "/api/pleroma/admin/users/:nickname/password_reset", %{conn: conn} do
  531. user = insert(:user)
  532. conn =
  533. conn
  534. |> put_req_header("accept", "application/json")
  535. |> get("/api/pleroma/admin/users/#{user.nickname}/password_reset")
  536. resp = json_response(conn, 200)
  537. assert Regex.match?(~r/(http:\/\/|https:\/\/)/, resp["link"])
  538. end
  539. describe "GET /api/pleroma/admin/users" do
  540. test "renders users array for the first page", %{conn: conn, admin: admin} do
  541. user = insert(:user, local: false, tags: ["foo", "bar"])
  542. conn = get(conn, "/api/pleroma/admin/users?page=1")
  543. users =
  544. [
  545. %{
  546. "deactivated" => admin.deactivated,
  547. "id" => admin.id,
  548. "nickname" => admin.nickname,
  549. "roles" => %{"admin" => true, "moderator" => false},
  550. "local" => true,
  551. "tags" => [],
  552. "avatar" => User.avatar_url(admin) |> MediaProxy.url(),
  553. "display_name" => HTML.strip_tags(admin.name || admin.nickname),
  554. "confirmation_pending" => false
  555. },
  556. %{
  557. "deactivated" => user.deactivated,
  558. "id" => user.id,
  559. "nickname" => user.nickname,
  560. "roles" => %{"admin" => false, "moderator" => false},
  561. "local" => false,
  562. "tags" => ["foo", "bar"],
  563. "avatar" => User.avatar_url(user) |> MediaProxy.url(),
  564. "display_name" => HTML.strip_tags(user.name || user.nickname),
  565. "confirmation_pending" => false
  566. }
  567. ]
  568. |> Enum.sort_by(& &1["nickname"])
  569. assert json_response(conn, 200) == %{
  570. "count" => 2,
  571. "page_size" => 50,
  572. "users" => users
  573. }
  574. end
  575. test "renders empty array for the second page", %{conn: conn} do
  576. insert(:user)
  577. conn = get(conn, "/api/pleroma/admin/users?page=2")
  578. assert json_response(conn, 200) == %{
  579. "count" => 2,
  580. "page_size" => 50,
  581. "users" => []
  582. }
  583. end
  584. test "regular search", %{conn: conn} do
  585. user = insert(:user, nickname: "bob")
  586. conn = get(conn, "/api/pleroma/admin/users?query=bo")
  587. assert json_response(conn, 200) == %{
  588. "count" => 1,
  589. "page_size" => 50,
  590. "users" => [
  591. %{
  592. "deactivated" => user.deactivated,
  593. "id" => user.id,
  594. "nickname" => user.nickname,
  595. "roles" => %{"admin" => false, "moderator" => false},
  596. "local" => true,
  597. "tags" => [],
  598. "avatar" => User.avatar_url(user) |> MediaProxy.url(),
  599. "display_name" => HTML.strip_tags(user.name || user.nickname),
  600. "confirmation_pending" => false
  601. }
  602. ]
  603. }
  604. end
  605. test "search by domain", %{conn: conn} do
  606. user = insert(:user, nickname: "nickname@domain.com")
  607. insert(:user)
  608. conn = get(conn, "/api/pleroma/admin/users?query=domain.com")
  609. assert json_response(conn, 200) == %{
  610. "count" => 1,
  611. "page_size" => 50,
  612. "users" => [
  613. %{
  614. "deactivated" => user.deactivated,
  615. "id" => user.id,
  616. "nickname" => user.nickname,
  617. "roles" => %{"admin" => false, "moderator" => false},
  618. "local" => true,
  619. "tags" => [],
  620. "avatar" => User.avatar_url(user) |> MediaProxy.url(),
  621. "display_name" => HTML.strip_tags(user.name || user.nickname),
  622. "confirmation_pending" => false
  623. }
  624. ]
  625. }
  626. end
  627. test "search by full nickname", %{conn: conn} do
  628. user = insert(:user, nickname: "nickname@domain.com")
  629. insert(:user)
  630. conn = get(conn, "/api/pleroma/admin/users?query=nickname@domain.com")
  631. assert json_response(conn, 200) == %{
  632. "count" => 1,
  633. "page_size" => 50,
  634. "users" => [
  635. %{
  636. "deactivated" => user.deactivated,
  637. "id" => user.id,
  638. "nickname" => user.nickname,
  639. "roles" => %{"admin" => false, "moderator" => false},
  640. "local" => true,
  641. "tags" => [],
  642. "avatar" => User.avatar_url(user) |> MediaProxy.url(),
  643. "display_name" => HTML.strip_tags(user.name || user.nickname),
  644. "confirmation_pending" => false
  645. }
  646. ]
  647. }
  648. end
  649. test "search by display name", %{conn: conn} do
  650. user = insert(:user, name: "Display name")
  651. insert(:user)
  652. conn = get(conn, "/api/pleroma/admin/users?name=display")
  653. assert json_response(conn, 200) == %{
  654. "count" => 1,
  655. "page_size" => 50,
  656. "users" => [
  657. %{
  658. "deactivated" => user.deactivated,
  659. "id" => user.id,
  660. "nickname" => user.nickname,
  661. "roles" => %{"admin" => false, "moderator" => false},
  662. "local" => true,
  663. "tags" => [],
  664. "avatar" => User.avatar_url(user) |> MediaProxy.url(),
  665. "display_name" => HTML.strip_tags(user.name || user.nickname),
  666. "confirmation_pending" => false
  667. }
  668. ]
  669. }
  670. end
  671. test "search by email", %{conn: conn} do
  672. user = insert(:user, email: "email@example.com")
  673. insert(:user)
  674. conn = get(conn, "/api/pleroma/admin/users?email=email@example.com")
  675. assert json_response(conn, 200) == %{
  676. "count" => 1,
  677. "page_size" => 50,
  678. "users" => [
  679. %{
  680. "deactivated" => user.deactivated,
  681. "id" => user.id,
  682. "nickname" => user.nickname,
  683. "roles" => %{"admin" => false, "moderator" => false},
  684. "local" => true,
  685. "tags" => [],
  686. "avatar" => User.avatar_url(user) |> MediaProxy.url(),
  687. "display_name" => HTML.strip_tags(user.name || user.nickname),
  688. "confirmation_pending" => false
  689. }
  690. ]
  691. }
  692. end
  693. test "regular search with page size", %{conn: conn} do
  694. user = insert(:user, nickname: "aalice")
  695. user2 = insert(:user, nickname: "alice")
  696. conn1 = get(conn, "/api/pleroma/admin/users?query=a&page_size=1&page=1")
  697. assert json_response(conn1, 200) == %{
  698. "count" => 2,
  699. "page_size" => 1,
  700. "users" => [
  701. %{
  702. "deactivated" => user.deactivated,
  703. "id" => user.id,
  704. "nickname" => user.nickname,
  705. "roles" => %{"admin" => false, "moderator" => false},
  706. "local" => true,
  707. "tags" => [],
  708. "avatar" => User.avatar_url(user) |> MediaProxy.url(),
  709. "display_name" => HTML.strip_tags(user.name || user.nickname),
  710. "confirmation_pending" => false
  711. }
  712. ]
  713. }
  714. conn2 = get(conn, "/api/pleroma/admin/users?query=a&page_size=1&page=2")
  715. assert json_response(conn2, 200) == %{
  716. "count" => 2,
  717. "page_size" => 1,
  718. "users" => [
  719. %{
  720. "deactivated" => user2.deactivated,
  721. "id" => user2.id,
  722. "nickname" => user2.nickname,
  723. "roles" => %{"admin" => false, "moderator" => false},
  724. "local" => true,
  725. "tags" => [],
  726. "avatar" => User.avatar_url(user2) |> MediaProxy.url(),
  727. "display_name" => HTML.strip_tags(user2.name || user2.nickname),
  728. "confirmation_pending" => false
  729. }
  730. ]
  731. }
  732. end
  733. test "only local users" do
  734. admin = insert(:user, is_admin: true, nickname: "john")
  735. token = insert(:oauth_admin_token, user: admin)
  736. user = insert(:user, nickname: "bob")
  737. insert(:user, nickname: "bobb", local: false)
  738. conn =
  739. build_conn()
  740. |> assign(:user, admin)
  741. |> assign(:token, token)
  742. |> get("/api/pleroma/admin/users?query=bo&filters=local")
  743. assert json_response(conn, 200) == %{
  744. "count" => 1,
  745. "page_size" => 50,
  746. "users" => [
  747. %{
  748. "deactivated" => user.deactivated,
  749. "id" => user.id,
  750. "nickname" => user.nickname,
  751. "roles" => %{"admin" => false, "moderator" => false},
  752. "local" => true,
  753. "tags" => [],
  754. "avatar" => User.avatar_url(user) |> MediaProxy.url(),
  755. "display_name" => HTML.strip_tags(user.name || user.nickname),
  756. "confirmation_pending" => false
  757. }
  758. ]
  759. }
  760. end
  761. test "only local users with no query", %{conn: conn, admin: old_admin} do
  762. admin = insert(:user, is_admin: true, nickname: "john")
  763. user = insert(:user, nickname: "bob")
  764. insert(:user, nickname: "bobb", local: false)
  765. conn = get(conn, "/api/pleroma/admin/users?filters=local")
  766. users =
  767. [
  768. %{
  769. "deactivated" => user.deactivated,
  770. "id" => user.id,
  771. "nickname" => user.nickname,
  772. "roles" => %{"admin" => false, "moderator" => false},
  773. "local" => true,
  774. "tags" => [],
  775. "avatar" => User.avatar_url(user) |> MediaProxy.url(),
  776. "display_name" => HTML.strip_tags(user.name || user.nickname),
  777. "confirmation_pending" => false
  778. },
  779. %{
  780. "deactivated" => admin.deactivated,
  781. "id" => admin.id,
  782. "nickname" => admin.nickname,
  783. "roles" => %{"admin" => true, "moderator" => false},
  784. "local" => true,
  785. "tags" => [],
  786. "avatar" => User.avatar_url(admin) |> MediaProxy.url(),
  787. "display_name" => HTML.strip_tags(admin.name || admin.nickname),
  788. "confirmation_pending" => false
  789. },
  790. %{
  791. "deactivated" => false,
  792. "id" => old_admin.id,
  793. "local" => true,
  794. "nickname" => old_admin.nickname,
  795. "roles" => %{"admin" => true, "moderator" => false},
  796. "tags" => [],
  797. "avatar" => User.avatar_url(old_admin) |> MediaProxy.url(),
  798. "display_name" => HTML.strip_tags(old_admin.name || old_admin.nickname),
  799. "confirmation_pending" => false
  800. }
  801. ]
  802. |> Enum.sort_by(& &1["nickname"])
  803. assert json_response(conn, 200) == %{
  804. "count" => 3,
  805. "page_size" => 50,
  806. "users" => users
  807. }
  808. end
  809. test "load only admins", %{conn: conn, admin: admin} do
  810. second_admin = insert(:user, is_admin: true)
  811. insert(:user)
  812. insert(:user)
  813. conn = get(conn, "/api/pleroma/admin/users?filters=is_admin")
  814. users =
  815. [
  816. %{
  817. "deactivated" => false,
  818. "id" => admin.id,
  819. "nickname" => admin.nickname,
  820. "roles" => %{"admin" => true, "moderator" => false},
  821. "local" => admin.local,
  822. "tags" => [],
  823. "avatar" => User.avatar_url(admin) |> MediaProxy.url(),
  824. "display_name" => HTML.strip_tags(admin.name || admin.nickname),
  825. "confirmation_pending" => false
  826. },
  827. %{
  828. "deactivated" => false,
  829. "id" => second_admin.id,
  830. "nickname" => second_admin.nickname,
  831. "roles" => %{"admin" => true, "moderator" => false},
  832. "local" => second_admin.local,
  833. "tags" => [],
  834. "avatar" => User.avatar_url(second_admin) |> MediaProxy.url(),
  835. "display_name" => HTML.strip_tags(second_admin.name || second_admin.nickname),
  836. "confirmation_pending" => false
  837. }
  838. ]
  839. |> Enum.sort_by(& &1["nickname"])
  840. assert json_response(conn, 200) == %{
  841. "count" => 2,
  842. "page_size" => 50,
  843. "users" => users
  844. }
  845. end
  846. test "load only moderators", %{conn: conn} do
  847. moderator = insert(:user, is_moderator: true)
  848. insert(:user)
  849. insert(:user)
  850. conn = get(conn, "/api/pleroma/admin/users?filters=is_moderator")
  851. assert json_response(conn, 200) == %{
  852. "count" => 1,
  853. "page_size" => 50,
  854. "users" => [
  855. %{
  856. "deactivated" => false,
  857. "id" => moderator.id,
  858. "nickname" => moderator.nickname,
  859. "roles" => %{"admin" => false, "moderator" => true},
  860. "local" => moderator.local,
  861. "tags" => [],
  862. "avatar" => User.avatar_url(moderator) |> MediaProxy.url(),
  863. "display_name" => HTML.strip_tags(moderator.name || moderator.nickname),
  864. "confirmation_pending" => false
  865. }
  866. ]
  867. }
  868. end
  869. test "load users with tags list", %{conn: conn} do
  870. user1 = insert(:user, tags: ["first"])
  871. user2 = insert(:user, tags: ["second"])
  872. insert(:user)
  873. insert(:user)
  874. conn = get(conn, "/api/pleroma/admin/users?tags[]=first&tags[]=second")
  875. users =
  876. [
  877. %{
  878. "deactivated" => false,
  879. "id" => user1.id,
  880. "nickname" => user1.nickname,
  881. "roles" => %{"admin" => false, "moderator" => false},
  882. "local" => user1.local,
  883. "tags" => ["first"],
  884. "avatar" => User.avatar_url(user1) |> MediaProxy.url(),
  885. "display_name" => HTML.strip_tags(user1.name || user1.nickname),
  886. "confirmation_pending" => false
  887. },
  888. %{
  889. "deactivated" => false,
  890. "id" => user2.id,
  891. "nickname" => user2.nickname,
  892. "roles" => %{"admin" => false, "moderator" => false},
  893. "local" => user2.local,
  894. "tags" => ["second"],
  895. "avatar" => User.avatar_url(user2) |> MediaProxy.url(),
  896. "display_name" => HTML.strip_tags(user2.name || user2.nickname),
  897. "confirmation_pending" => false
  898. }
  899. ]
  900. |> Enum.sort_by(& &1["nickname"])
  901. assert json_response(conn, 200) == %{
  902. "count" => 2,
  903. "page_size" => 50,
  904. "users" => users
  905. }
  906. end
  907. test "it works with multiple filters" do
  908. admin = insert(:user, nickname: "john", is_admin: true)
  909. token = insert(:oauth_admin_token, user: admin)
  910. user = insert(:user, nickname: "bob", local: false, deactivated: true)
  911. insert(:user, nickname: "ken", local: true, deactivated: true)
  912. insert(:user, nickname: "bobb", local: false, deactivated: false)
  913. conn =
  914. build_conn()
  915. |> assign(:user, admin)
  916. |> assign(:token, token)
  917. |> get("/api/pleroma/admin/users?filters=deactivated,external")
  918. assert json_response(conn, 200) == %{
  919. "count" => 1,
  920. "page_size" => 50,
  921. "users" => [
  922. %{
  923. "deactivated" => user.deactivated,
  924. "id" => user.id,
  925. "nickname" => user.nickname,
  926. "roles" => %{"admin" => false, "moderator" => false},
  927. "local" => user.local,
  928. "tags" => [],
  929. "avatar" => User.avatar_url(user) |> MediaProxy.url(),
  930. "display_name" => HTML.strip_tags(user.name || user.nickname),
  931. "confirmation_pending" => false
  932. }
  933. ]
  934. }
  935. end
  936. test "it omits relay user", %{admin: admin, conn: conn} do
  937. assert %User{} = Relay.get_actor()
  938. conn = get(conn, "/api/pleroma/admin/users")
  939. assert json_response(conn, 200) == %{
  940. "count" => 1,
  941. "page_size" => 50,
  942. "users" => [
  943. %{
  944. "deactivated" => admin.deactivated,
  945. "id" => admin.id,
  946. "nickname" => admin.nickname,
  947. "roles" => %{"admin" => true, "moderator" => false},
  948. "local" => true,
  949. "tags" => [],
  950. "avatar" => User.avatar_url(admin) |> MediaProxy.url(),
  951. "display_name" => HTML.strip_tags(admin.name || admin.nickname),
  952. "confirmation_pending" => false
  953. }
  954. ]
  955. }
  956. end
  957. end
  958. test "PATCH /api/pleroma/admin/users/activate", %{admin: admin, conn: conn} do
  959. user_one = insert(:user, deactivated: true)
  960. user_two = insert(:user, deactivated: true)
  961. conn =
  962. patch(
  963. conn,
  964. "/api/pleroma/admin/users/activate",
  965. %{nicknames: [user_one.nickname, user_two.nickname]}
  966. )
  967. response = json_response(conn, 200)
  968. assert Enum.map(response["users"], & &1["deactivated"]) == [false, false]
  969. log_entry = Repo.one(ModerationLog)
  970. assert ModerationLog.get_log_entry_message(log_entry) ==
  971. "@#{admin.nickname} activated users: @#{user_one.nickname}, @#{user_two.nickname}"
  972. end
  973. test "PATCH /api/pleroma/admin/users/deactivate", %{admin: admin, conn: conn} do
  974. user_one = insert(:user, deactivated: false)
  975. user_two = insert(:user, deactivated: false)
  976. conn =
  977. patch(
  978. conn,
  979. "/api/pleroma/admin/users/deactivate",
  980. %{nicknames: [user_one.nickname, user_two.nickname]}
  981. )
  982. response = json_response(conn, 200)
  983. assert Enum.map(response["users"], & &1["deactivated"]) == [true, true]
  984. log_entry = Repo.one(ModerationLog)
  985. assert ModerationLog.get_log_entry_message(log_entry) ==
  986. "@#{admin.nickname} deactivated users: @#{user_one.nickname}, @#{user_two.nickname}"
  987. end
  988. test "PATCH /api/pleroma/admin/users/:nickname/toggle_activation", %{admin: admin, conn: conn} do
  989. user = insert(:user)
  990. conn = patch(conn, "/api/pleroma/admin/users/#{user.nickname}/toggle_activation")
  991. assert json_response(conn, 200) ==
  992. %{
  993. "deactivated" => !user.deactivated,
  994. "id" => user.id,
  995. "nickname" => user.nickname,
  996. "roles" => %{"admin" => false, "moderator" => false},
  997. "local" => true,
  998. "tags" => [],
  999. "avatar" => User.avatar_url(user) |> MediaProxy.url(),
  1000. "display_name" => HTML.strip_tags(user.name || user.nickname),
  1001. "confirmation_pending" => false
  1002. }
  1003. log_entry = Repo.one(ModerationLog)
  1004. assert ModerationLog.get_log_entry_message(log_entry) ==
  1005. "@#{admin.nickname} deactivated users: @#{user.nickname}"
  1006. end
  1007. describe "POST /api/pleroma/admin/users/invite_token" do
  1008. test "without options", %{conn: conn} do
  1009. conn = post(conn, "/api/pleroma/admin/users/invite_token")
  1010. invite_json = json_response(conn, 200)
  1011. invite = UserInviteToken.find_by_token!(invite_json["token"])
  1012. refute invite.used
  1013. refute invite.expires_at
  1014. refute invite.max_use
  1015. assert invite.invite_type == "one_time"
  1016. end
  1017. test "with expires_at", %{conn: conn} do
  1018. conn =
  1019. post(conn, "/api/pleroma/admin/users/invite_token", %{
  1020. "expires_at" => Date.to_string(Date.utc_today())
  1021. })
  1022. invite_json = json_response(conn, 200)
  1023. invite = UserInviteToken.find_by_token!(invite_json["token"])
  1024. refute invite.used
  1025. assert invite.expires_at == Date.utc_today()
  1026. refute invite.max_use
  1027. assert invite.invite_type == "date_limited"
  1028. end
  1029. test "with max_use", %{conn: conn} do
  1030. conn = post(conn, "/api/pleroma/admin/users/invite_token", %{"max_use" => 150})
  1031. invite_json = json_response(conn, 200)
  1032. invite = UserInviteToken.find_by_token!(invite_json["token"])
  1033. refute invite.used
  1034. refute invite.expires_at
  1035. assert invite.max_use == 150
  1036. assert invite.invite_type == "reusable"
  1037. end
  1038. test "with max use and expires_at", %{conn: conn} do
  1039. conn =
  1040. post(conn, "/api/pleroma/admin/users/invite_token", %{
  1041. "max_use" => 150,
  1042. "expires_at" => Date.to_string(Date.utc_today())
  1043. })
  1044. invite_json = json_response(conn, 200)
  1045. invite = UserInviteToken.find_by_token!(invite_json["token"])
  1046. refute invite.used
  1047. assert invite.expires_at == Date.utc_today()
  1048. assert invite.max_use == 150
  1049. assert invite.invite_type == "reusable_date_limited"
  1050. end
  1051. end
  1052. describe "GET /api/pleroma/admin/users/invites" do
  1053. test "no invites", %{conn: conn} do
  1054. conn = get(conn, "/api/pleroma/admin/users/invites")
  1055. assert json_response(conn, 200) == %{"invites" => []}
  1056. end
  1057. test "with invite", %{conn: conn} do
  1058. {:ok, invite} = UserInviteToken.create_invite()
  1059. conn = get(conn, "/api/pleroma/admin/users/invites")
  1060. assert json_response(conn, 200) == %{
  1061. "invites" => [
  1062. %{
  1063. "expires_at" => nil,
  1064. "id" => invite.id,
  1065. "invite_type" => "one_time",
  1066. "max_use" => nil,
  1067. "token" => invite.token,
  1068. "used" => false,
  1069. "uses" => 0
  1070. }
  1071. ]
  1072. }
  1073. end
  1074. end
  1075. describe "POST /api/pleroma/admin/users/revoke_invite" do
  1076. test "with token", %{conn: conn} do
  1077. {:ok, invite} = UserInviteToken.create_invite()
  1078. conn = post(conn, "/api/pleroma/admin/users/revoke_invite", %{"token" => invite.token})
  1079. assert json_response(conn, 200) == %{
  1080. "expires_at" => nil,
  1081. "id" => invite.id,
  1082. "invite_type" => "one_time",
  1083. "max_use" => nil,
  1084. "token" => invite.token,
  1085. "used" => true,
  1086. "uses" => 0
  1087. }
  1088. end
  1089. test "with invalid token", %{conn: conn} do
  1090. conn = post(conn, "/api/pleroma/admin/users/revoke_invite", %{"token" => "foo"})
  1091. assert json_response(conn, :not_found) == "Not found"
  1092. end
  1093. end
  1094. describe "GET /api/pleroma/admin/reports/:id" do
  1095. test "returns report by its id", %{conn: conn} do
  1096. [reporter, target_user] = insert_pair(:user)
  1097. activity = insert(:note_activity, user: target_user)
  1098. {:ok, %{id: report_id}} =
  1099. CommonAPI.report(reporter, %{
  1100. "account_id" => target_user.id,
  1101. "comment" => "I feel offended",
  1102. "status_ids" => [activity.id]
  1103. })
  1104. response =
  1105. conn
  1106. |> get("/api/pleroma/admin/reports/#{report_id}")
  1107. |> json_response(:ok)
  1108. assert response["id"] == report_id
  1109. end
  1110. test "returns 404 when report id is invalid", %{conn: conn} do
  1111. conn = get(conn, "/api/pleroma/admin/reports/test")
  1112. assert json_response(conn, :not_found) == "Not found"
  1113. end
  1114. end
  1115. describe "PATCH /api/pleroma/admin/reports" do
  1116. setup do
  1117. [reporter, target_user] = insert_pair(:user)
  1118. activity = insert(:note_activity, user: target_user)
  1119. {:ok, %{id: report_id}} =
  1120. CommonAPI.report(reporter, %{
  1121. "account_id" => target_user.id,
  1122. "comment" => "I feel offended",
  1123. "status_ids" => [activity.id]
  1124. })
  1125. {:ok, %{id: second_report_id}} =
  1126. CommonAPI.report(reporter, %{
  1127. "account_id" => target_user.id,
  1128. "comment" => "I feel very offended",
  1129. "status_ids" => [activity.id]
  1130. })
  1131. %{
  1132. id: report_id,
  1133. second_report_id: second_report_id
  1134. }
  1135. end
  1136. test "requires admin:write:reports scope", %{conn: conn, id: id, admin: admin} do
  1137. read_token = insert(:oauth_token, user: admin, scopes: ["admin:read"])
  1138. write_token = insert(:oauth_token, user: admin, scopes: ["admin:write:reports"])
  1139. response =
  1140. conn
  1141. |> assign(:token, read_token)
  1142. |> patch("/api/pleroma/admin/reports", %{
  1143. "reports" => [%{"state" => "resolved", "id" => id}]
  1144. })
  1145. |> json_response(403)
  1146. assert response == %{
  1147. "error" => "Insufficient permissions: admin:write:reports."
  1148. }
  1149. conn
  1150. |> assign(:token, write_token)
  1151. |> patch("/api/pleroma/admin/reports", %{
  1152. "reports" => [%{"state" => "resolved", "id" => id}]
  1153. })
  1154. |> json_response(:no_content)
  1155. end
  1156. test "mark report as resolved", %{conn: conn, id: id, admin: admin} do
  1157. conn
  1158. |> patch("/api/pleroma/admin/reports", %{
  1159. "reports" => [
  1160. %{"state" => "resolved", "id" => id}
  1161. ]
  1162. })
  1163. |> json_response(:no_content)
  1164. activity = Activity.get_by_id(id)
  1165. assert activity.data["state"] == "resolved"
  1166. log_entry = Repo.one(ModerationLog)
  1167. assert ModerationLog.get_log_entry_message(log_entry) ==
  1168. "@#{admin.nickname} updated report ##{id} with 'resolved' state"
  1169. end
  1170. test "closes report", %{conn: conn, id: id, admin: admin} do
  1171. conn
  1172. |> patch("/api/pleroma/admin/reports", %{
  1173. "reports" => [
  1174. %{"state" => "closed", "id" => id}
  1175. ]
  1176. })
  1177. |> json_response(:no_content)
  1178. activity = Activity.get_by_id(id)
  1179. assert activity.data["state"] == "closed"
  1180. log_entry = Repo.one(ModerationLog)
  1181. assert ModerationLog.get_log_entry_message(log_entry) ==
  1182. "@#{admin.nickname} updated report ##{id} with 'closed' state"
  1183. end
  1184. test "returns 400 when state is unknown", %{conn: conn, id: id} do
  1185. conn =
  1186. conn
  1187. |> patch("/api/pleroma/admin/reports", %{
  1188. "reports" => [
  1189. %{"state" => "test", "id" => id}
  1190. ]
  1191. })
  1192. assert hd(json_response(conn, :bad_request))["error"] == "Unsupported state"
  1193. end
  1194. test "returns 404 when report is not exist", %{conn: conn} do
  1195. conn =
  1196. conn
  1197. |> patch("/api/pleroma/admin/reports", %{
  1198. "reports" => [
  1199. %{"state" => "closed", "id" => "test"}
  1200. ]
  1201. })
  1202. assert hd(json_response(conn, :bad_request))["error"] == "not_found"
  1203. end
  1204. test "updates state of multiple reports", %{
  1205. conn: conn,
  1206. id: id,
  1207. admin: admin,
  1208. second_report_id: second_report_id
  1209. } do
  1210. conn
  1211. |> patch("/api/pleroma/admin/reports", %{
  1212. "reports" => [
  1213. %{"state" => "resolved", "id" => id},
  1214. %{"state" => "closed", "id" => second_report_id}
  1215. ]
  1216. })
  1217. |> json_response(:no_content)
  1218. activity = Activity.get_by_id(id)
  1219. second_activity = Activity.get_by_id(second_report_id)
  1220. assert activity.data["state"] == "resolved"
  1221. assert second_activity.data["state"] == "closed"
  1222. [first_log_entry, second_log_entry] = Repo.all(ModerationLog)
  1223. assert ModerationLog.get_log_entry_message(first_log_entry) ==
  1224. "@#{admin.nickname} updated report ##{id} with 'resolved' state"
  1225. assert ModerationLog.get_log_entry_message(second_log_entry) ==
  1226. "@#{admin.nickname} updated report ##{second_report_id} with 'closed' state"
  1227. end
  1228. end
  1229. describe "GET /api/pleroma/admin/reports" do
  1230. test "returns empty response when no reports created", %{conn: conn} do
  1231. response =
  1232. conn
  1233. |> get("/api/pleroma/admin/reports")
  1234. |> json_response(:ok)
  1235. assert Enum.empty?(response["reports"])
  1236. assert response["total"] == 0
  1237. end
  1238. test "returns reports", %{conn: conn} do
  1239. [reporter, target_user] = insert_pair(:user)
  1240. activity = insert(:note_activity, user: target_user)
  1241. {:ok, %{id: report_id}} =
  1242. CommonAPI.report(reporter, %{
  1243. "account_id" => target_user.id,
  1244. "comment" => "I feel offended",
  1245. "status_ids" => [activity.id]
  1246. })
  1247. response =
  1248. conn
  1249. |> get("/api/pleroma/admin/reports")
  1250. |> json_response(:ok)
  1251. [report] = response["reports"]
  1252. assert length(response["reports"]) == 1
  1253. assert report["id"] == report_id
  1254. assert response["total"] == 1
  1255. end
  1256. test "returns reports with specified state", %{conn: conn} do
  1257. [reporter, target_user] = insert_pair(:user)
  1258. activity = insert(:note_activity, user: target_user)
  1259. {:ok, %{id: first_report_id}} =
  1260. CommonAPI.report(reporter, %{
  1261. "account_id" => target_user.id,
  1262. "comment" => "I feel offended",
  1263. "status_ids" => [activity.id]
  1264. })
  1265. {:ok, %{id: second_report_id}} =
  1266. CommonAPI.report(reporter, %{
  1267. "account_id" => target_user.id,
  1268. "comment" => "I don't like this user"
  1269. })
  1270. CommonAPI.update_report_state(second_report_id, "closed")
  1271. response =
  1272. conn
  1273. |> get("/api/pleroma/admin/reports", %{
  1274. "state" => "open"
  1275. })
  1276. |> json_response(:ok)
  1277. [open_report] = response["reports"]
  1278. assert length(response["reports"]) == 1
  1279. assert open_report["id"] == first_report_id
  1280. assert response["total"] == 1
  1281. response =
  1282. conn
  1283. |> get("/api/pleroma/admin/reports", %{
  1284. "state" => "closed"
  1285. })
  1286. |> json_response(:ok)
  1287. [closed_report] = response["reports"]
  1288. assert length(response["reports"]) == 1
  1289. assert closed_report["id"] == second_report_id
  1290. assert response["total"] == 1
  1291. response =
  1292. conn
  1293. |> get("/api/pleroma/admin/reports", %{
  1294. "state" => "resolved"
  1295. })
  1296. |> json_response(:ok)
  1297. assert Enum.empty?(response["reports"])
  1298. assert response["total"] == 0
  1299. end
  1300. test "returns 403 when requested by a non-admin" do
  1301. user = insert(:user)
  1302. token = insert(:oauth_token, user: user)
  1303. conn =
  1304. build_conn()
  1305. |> assign(:user, user)
  1306. |> assign(:token, token)
  1307. |> get("/api/pleroma/admin/reports")
  1308. assert json_response(conn, :forbidden) ==
  1309. %{"error" => "User is not an admin or OAuth admin scope is not granted."}
  1310. end
  1311. test "returns 403 when requested by anonymous" do
  1312. conn = get(build_conn(), "/api/pleroma/admin/reports")
  1313. assert json_response(conn, :forbidden) == %{"error" => "Invalid credentials."}
  1314. end
  1315. end
  1316. describe "GET /api/pleroma/admin/grouped_reports" do
  1317. setup do
  1318. [reporter, target_user] = insert_pair(:user)
  1319. date1 = (DateTime.to_unix(DateTime.utc_now()) + 1000) |> DateTime.from_unix!()
  1320. date2 = (DateTime.to_unix(DateTime.utc_now()) + 2000) |> DateTime.from_unix!()
  1321. date3 = (DateTime.to_unix(DateTime.utc_now()) + 3000) |> DateTime.from_unix!()
  1322. first_status =
  1323. insert(:note_activity, user: target_user, data_attrs: %{"published" => date1})
  1324. second_status =
  1325. insert(:note_activity, user: target_user, data_attrs: %{"published" => date2})
  1326. third_status =
  1327. insert(:note_activity, user: target_user, data_attrs: %{"published" => date3})
  1328. {:ok, first_report} =
  1329. CommonAPI.report(reporter, %{
  1330. "account_id" => target_user.id,
  1331. "status_ids" => [first_status.id, second_status.id, third_status.id]
  1332. })
  1333. {:ok, second_report} =
  1334. CommonAPI.report(reporter, %{
  1335. "account_id" => target_user.id,
  1336. "status_ids" => [first_status.id, second_status.id]
  1337. })
  1338. {:ok, third_report} =
  1339. CommonAPI.report(reporter, %{
  1340. "account_id" => target_user.id,
  1341. "status_ids" => [first_status.id]
  1342. })
  1343. %{
  1344. first_status: Activity.get_by_ap_id_with_object(first_status.data["id"]),
  1345. second_status: Activity.get_by_ap_id_with_object(second_status.data["id"]),
  1346. third_status: Activity.get_by_ap_id_with_object(third_status.data["id"]),
  1347. first_report: first_report,
  1348. first_status_reports: [first_report, second_report, third_report],
  1349. second_status_reports: [first_report, second_report],
  1350. third_status_reports: [first_report],
  1351. target_user: target_user,
  1352. reporter: reporter
  1353. }
  1354. end
  1355. test "returns reports grouped by status", %{
  1356. conn: conn,
  1357. first_status: first_status,
  1358. second_status: second_status,
  1359. third_status: third_status,
  1360. first_status_reports: first_status_reports,
  1361. second_status_reports: second_status_reports,
  1362. third_status_reports: third_status_reports,
  1363. target_user: target_user,
  1364. reporter: reporter
  1365. } do
  1366. response =
  1367. conn
  1368. |> get("/api/pleroma/admin/grouped_reports")
  1369. |> json_response(:ok)
  1370. assert length(response["reports"]) == 3
  1371. first_group = Enum.find(response["reports"], &(&1["status"]["id"] == first_status.id))
  1372. second_group = Enum.find(response["reports"], &(&1["status"]["id"] == second_status.id))
  1373. third_group = Enum.find(response["reports"], &(&1["status"]["id"] == third_status.id))
  1374. assert length(first_group["reports"]) == 3
  1375. assert length(second_group["reports"]) == 2
  1376. assert length(third_group["reports"]) == 1
  1377. assert first_group["date"] ==
  1378. Enum.max_by(first_status_reports, fn act ->
  1379. NaiveDateTime.from_iso8601!(act.data["published"])
  1380. end).data["published"]
  1381. assert first_group["status"] ==
  1382. Map.put(
  1383. stringify_keys(StatusView.render("show.json", %{activity: first_status})),
  1384. "deleted",
  1385. false
  1386. )
  1387. assert(first_group["account"]["id"] == target_user.id)
  1388. assert length(first_group["actors"]) == 1
  1389. assert hd(first_group["actors"])["id"] == reporter.id
  1390. assert Enum.map(first_group["reports"], & &1["id"]) --
  1391. Enum.map(first_status_reports, & &1.id) == []
  1392. assert second_group["date"] ==
  1393. Enum.max_by(second_status_reports, fn act ->
  1394. NaiveDateTime.from_iso8601!(act.data["published"])
  1395. end).data["published"]
  1396. assert second_group["status"] ==
  1397. Map.put(
  1398. stringify_keys(StatusView.render("show.json", %{activity: second_status})),
  1399. "deleted",
  1400. false
  1401. )
  1402. assert second_group["account"]["id"] == target_user.id
  1403. assert length(second_group["actors"]) == 1
  1404. assert hd(second_group["actors"])["id"] == reporter.id
  1405. assert Enum.map(second_group["reports"], & &1["id"]) --
  1406. Enum.map(second_status_reports, & &1.id) == []
  1407. assert third_group["date"] ==
  1408. Enum.max_by(third_status_reports, fn act ->
  1409. NaiveDateTime.from_iso8601!(act.data["published"])
  1410. end).data["published"]
  1411. assert third_group["status"] ==
  1412. Map.put(
  1413. stringify_keys(StatusView.render("show.json", %{activity: third_status})),
  1414. "deleted",
  1415. false
  1416. )
  1417. assert third_group["account"]["id"] == target_user.id
  1418. assert length(third_group["actors"]) == 1
  1419. assert hd(third_group["actors"])["id"] == reporter.id
  1420. assert Enum.map(third_group["reports"], & &1["id"]) --
  1421. Enum.map(third_status_reports, & &1.id) == []
  1422. end
  1423. test "reopened report renders status data", %{
  1424. conn: conn,
  1425. first_report: first_report,
  1426. first_status: first_status
  1427. } do
  1428. {:ok, _} = CommonAPI.update_report_state(first_report.id, "resolved")
  1429. response =
  1430. conn
  1431. |> get("/api/pleroma/admin/grouped_reports")
  1432. |> json_response(:ok)
  1433. first_group = Enum.find(response["reports"], &(&1["status"]["id"] == first_status.id))
  1434. assert first_group["status"] ==
  1435. Map.put(
  1436. stringify_keys(StatusView.render("show.json", %{activity: first_status})),
  1437. "deleted",
  1438. false
  1439. )
  1440. end
  1441. test "reopened report does not render status data if status has been deleted", %{
  1442. conn: conn,
  1443. first_report: first_report,
  1444. first_status: first_status,
  1445. target_user: target_user
  1446. } do
  1447. {:ok, _} = CommonAPI.update_report_state(first_report.id, "resolved")
  1448. {:ok, _} = CommonAPI.delete(first_status.id, target_user)
  1449. refute Activity.get_by_ap_id(first_status.id)
  1450. response =
  1451. conn
  1452. |> get("/api/pleroma/admin/grouped_reports")
  1453. |> json_response(:ok)
  1454. assert Enum.find(response["reports"], &(&1["status"]["deleted"] == true))["status"][
  1455. "deleted"
  1456. ] == true
  1457. assert length(Enum.filter(response["reports"], &(&1["status"]["deleted"] == false))) == 2
  1458. end
  1459. test "account not empty if status was deleted", %{
  1460. conn: conn,
  1461. first_report: first_report,
  1462. first_status: first_status,
  1463. target_user: target_user
  1464. } do
  1465. {:ok, _} = CommonAPI.update_report_state(first_report.id, "resolved")
  1466. {:ok, _} = CommonAPI.delete(first_status.id, target_user)
  1467. refute Activity.get_by_ap_id(first_status.id)
  1468. response =
  1469. conn
  1470. |> get("/api/pleroma/admin/grouped_reports")
  1471. |> json_response(:ok)
  1472. assert Enum.find(response["reports"], &(&1["status"]["deleted"] == true))["account"]
  1473. end
  1474. end
  1475. describe "PUT /api/pleroma/admin/statuses/:id" do
  1476. setup do
  1477. activity = insert(:note_activity)
  1478. %{id: activity.id}
  1479. end
  1480. test "toggle sensitive flag", %{conn: conn, id: id, admin: admin} do
  1481. response =
  1482. conn
  1483. |> put("/api/pleroma/admin/statuses/#{id}", %{"sensitive" => "true"})
  1484. |> json_response(:ok)
  1485. assert response["sensitive"]
  1486. log_entry = Repo.one(ModerationLog)
  1487. assert ModerationLog.get_log_entry_message(log_entry) ==
  1488. "@#{admin.nickname} updated status ##{id}, set sensitive: 'true'"
  1489. response =
  1490. conn
  1491. |> put("/api/pleroma/admin/statuses/#{id}", %{"sensitive" => "false"})
  1492. |> json_response(:ok)
  1493. refute response["sensitive"]
  1494. end
  1495. test "change visibility flag", %{conn: conn, id: id, admin: admin} do
  1496. response =
  1497. conn
  1498. |> put("/api/pleroma/admin/statuses/#{id}", %{"visibility" => "public"})
  1499. |> json_response(:ok)
  1500. assert response["visibility"] == "public"
  1501. log_entry = Repo.one(ModerationLog)
  1502. assert ModerationLog.get_log_entry_message(log_entry) ==
  1503. "@#{admin.nickname} updated status ##{id}, set visibility: 'public'"
  1504. response =
  1505. conn
  1506. |> put("/api/pleroma/admin/statuses/#{id}", %{"visibility" => "private"})
  1507. |> json_response(:ok)
  1508. assert response["visibility"] == "private"
  1509. response =
  1510. conn
  1511. |> put("/api/pleroma/admin/statuses/#{id}", %{"visibility" => "unlisted"})
  1512. |> json_response(:ok)
  1513. assert response["visibility"] == "unlisted"
  1514. end
  1515. test "returns 400 when visibility is unknown", %{conn: conn, id: id} do
  1516. conn = put(conn, "/api/pleroma/admin/statuses/#{id}", %{"visibility" => "test"})
  1517. assert json_response(conn, :bad_request) == "Unsupported visibility"
  1518. end
  1519. end
  1520. describe "DELETE /api/pleroma/admin/statuses/:id" do
  1521. setup do
  1522. activity = insert(:note_activity)
  1523. %{id: activity.id}
  1524. end
  1525. test "deletes status", %{conn: conn, id: id, admin: admin} do
  1526. conn
  1527. |> delete("/api/pleroma/admin/statuses/#{id}")
  1528. |> json_response(:ok)
  1529. refute Activity.get_by_id(id)
  1530. log_entry = Repo.one(ModerationLog)
  1531. assert ModerationLog.get_log_entry_message(log_entry) ==
  1532. "@#{admin.nickname} deleted status ##{id}"
  1533. end
  1534. test "returns 404 when the status does not exist", %{conn: conn} do
  1535. conn = delete(conn, "/api/pleroma/admin/statuses/test")
  1536. assert json_response(conn, :not_found) == "Not found"
  1537. end
  1538. end
  1539. describe "GET /api/pleroma/admin/config" do
  1540. clear_config(:configurable_from_database) do
  1541. Config.put(:configurable_from_database, true)
  1542. end
  1543. test "when configuration from database is off", %{conn: conn} do
  1544. Config.put(:configurable_from_database, false)
  1545. conn = get(conn, "/api/pleroma/admin/config")
  1546. assert json_response(conn, 400) ==
  1547. "To use this endpoint you need to enable configuration from database."
  1548. end
  1549. test "with settings only in db", %{conn: conn} do
  1550. config1 = insert(:config)
  1551. config2 = insert(:config)
  1552. conn = get(conn, "/api/pleroma/admin/config", %{"only_db" => true})
  1553. %{
  1554. "configs" => [
  1555. %{
  1556. "group" => ":pleroma",
  1557. "key" => key1,
  1558. "value" => _
  1559. },
  1560. %{
  1561. "group" => ":pleroma",
  1562. "key" => key2,
  1563. "value" => _
  1564. }
  1565. ]
  1566. } = json_response(conn, 200)
  1567. assert key1 == config1.key
  1568. assert key2 == config2.key
  1569. end
  1570. test "db is added to settings that are in db", %{conn: conn} do
  1571. _config = insert(:config, key: ":instance", value: ConfigDB.to_binary(name: "Some name"))
  1572. %{"configs" => configs} =
  1573. conn
  1574. |> get("/api/pleroma/admin/config")
  1575. |> json_response(200)
  1576. [instance_config] =
  1577. Enum.filter(configs, fn %{"group" => group, "key" => key} ->
  1578. group == ":pleroma" and key == ":instance"
  1579. end)
  1580. assert instance_config["db"] == [":name"]
  1581. end
  1582. test "merged default setting with db settings", %{conn: conn} do
  1583. config1 = insert(:config)
  1584. config2 = insert(:config)
  1585. config3 =
  1586. insert(:config,
  1587. value: ConfigDB.to_binary(k1: :v1, k2: :v2)
  1588. )
  1589. %{"configs" => configs} =
  1590. conn
  1591. |> get("/api/pleroma/admin/config")
  1592. |> json_response(200)
  1593. assert length(configs) > 3
  1594. received_configs =
  1595. Enum.filter(configs, fn %{"group" => group, "key" => key} ->
  1596. group == ":pleroma" and key in [config1.key, config2.key, config3.key]
  1597. end)
  1598. assert length(received_configs) == 3
  1599. db_keys =
  1600. config3.value
  1601. |> ConfigDB.from_binary()
  1602. |> Keyword.keys()
  1603. |> ConfigDB.convert()
  1604. Enum.each(received_configs, fn %{"value" => value, "db" => db} ->
  1605. assert db in [[config1.key], [config2.key], db_keys]
  1606. assert value in [
  1607. ConfigDB.from_binary_with_convert(config1.value),
  1608. ConfigDB.from_binary_with_convert(config2.value),
  1609. ConfigDB.from_binary_with_convert(config3.value)
  1610. ]
  1611. end)
  1612. end
  1613. test "subkeys with full update right merge", %{conn: conn} do
  1614. config1 =
  1615. insert(:config,
  1616. key: ":emoji",
  1617. value: ConfigDB.to_binary(groups: [a: 1, b: 2], key: [a: 1])
  1618. )
  1619. config2 =
  1620. insert(:config,
  1621. key: ":assets",
  1622. value: ConfigDB.to_binary(mascots: [a: 1, b: 2], key: [a: 1])
  1623. )
  1624. %{"configs" => configs} =
  1625. conn
  1626. |> get("/api/pleroma/admin/config")
  1627. |> json_response(200)
  1628. vals =
  1629. Enum.filter(configs, fn %{"group" => group, "key" => key} ->
  1630. group == ":pleroma" and key in [config1.key, config2.key]
  1631. end)
  1632. emoji = Enum.find(vals, fn %{"key" => key} -> key == ":emoji" end)
  1633. assets = Enum.find(vals, fn %{"key" => key} -> key == ":assets" end)
  1634. emoji_val = ConfigDB.transform_with_out_binary(emoji["value"])
  1635. assets_val = ConfigDB.transform_with_out_binary(assets["value"])
  1636. assert emoji_val[:groups] == [a: 1, b: 2]
  1637. assert assets_val[:mascots] == [a: 1, b: 2]
  1638. end
  1639. end
  1640. test "POST /api/pleroma/admin/config error", %{conn: conn} do
  1641. conn = post(conn, "/api/pleroma/admin/config", %{"configs" => []})
  1642. assert json_response(conn, 400) ==
  1643. "To use this endpoint you need to enable configuration from database."
  1644. end
  1645. describe "POST /api/pleroma/admin/config" do
  1646. setup do
  1647. http = Application.get_env(:pleroma, :http)
  1648. on_exit(fn ->
  1649. Application.delete_env(:pleroma, :key1)
  1650. Application.delete_env(:pleroma, :key2)
  1651. Application.delete_env(:pleroma, :key3)
  1652. Application.delete_env(:pleroma, :key4)
  1653. Application.delete_env(:pleroma, :keyaa1)
  1654. Application.delete_env(:pleroma, :keyaa2)
  1655. Application.delete_env(:pleroma, Pleroma.Web.Endpoint.NotReal)
  1656. Application.delete_env(:pleroma, Pleroma.Captcha.NotReal)
  1657. Application.put_env(:pleroma, :http, http)
  1658. Application.put_env(:tesla, :adapter, Tesla.Mock)
  1659. Restarter.Pleroma.refresh()
  1660. end)
  1661. end
  1662. clear_config(:configurable_from_database) do
  1663. Config.put(:configurable_from_database, true)
  1664. end
  1665. @tag capture_log: true
  1666. test "create new config setting in db", %{conn: conn} do
  1667. ueberauth = Application.get_env(:ueberauth, Ueberauth)
  1668. on_exit(fn -> Application.put_env(:ueberauth, Ueberauth, ueberauth) end)
  1669. conn =
  1670. post(conn, "/api/pleroma/admin/config", %{
  1671. configs: [
  1672. %{group: ":pleroma", key: ":key1", value: "value1"},
  1673. %{
  1674. group: ":ueberauth",
  1675. key: "Ueberauth",
  1676. value: [%{"tuple" => [":consumer_secret", "aaaa"]}]
  1677. },
  1678. %{
  1679. group: ":pleroma",
  1680. key: ":key2",
  1681. value: %{
  1682. ":nested_1" => "nested_value1",
  1683. ":nested_2" => [
  1684. %{":nested_22" => "nested_value222"},
  1685. %{":nested_33" => %{":nested_44" => "nested_444"}}
  1686. ]
  1687. }
  1688. },
  1689. %{
  1690. group: ":pleroma",
  1691. key: ":key3",
  1692. value: [
  1693. %{"nested_3" => ":nested_3", "nested_33" => "nested_33"},
  1694. %{"nested_4" => true}
  1695. ]
  1696. },
  1697. %{
  1698. group: ":pleroma",
  1699. key: ":key4",
  1700. value: %{":nested_5" => ":upload", "endpoint" => "https://example.com"}
  1701. },
  1702. %{
  1703. group: ":idna",
  1704. key: ":key5",
  1705. value: %{"tuple" => ["string", "Pleroma.Captcha.NotReal", []]}
  1706. }
  1707. ]
  1708. })
  1709. assert json_response(conn, 200) == %{
  1710. "configs" => [
  1711. %{
  1712. "group" => ":pleroma",
  1713. "key" => ":key1",
  1714. "value" => "value1",
  1715. "db" => [":key1"]
  1716. },
  1717. %{
  1718. "group" => ":ueberauth",
  1719. "key" => "Ueberauth",
  1720. "value" => [%{"tuple" => [":consumer_secret", "aaaa"]}],
  1721. "db" => [":consumer_secret"]
  1722. },
  1723. %{
  1724. "group" => ":pleroma",
  1725. "key" => ":key2",
  1726. "value" => %{
  1727. ":nested_1" => "nested_value1",
  1728. ":nested_2" => [
  1729. %{":nested_22" => "nested_value222"},
  1730. %{":nested_33" => %{":nested_44" => "nested_444"}}
  1731. ]
  1732. },
  1733. "db" => [":key2"]
  1734. },
  1735. %{
  1736. "group" => ":pleroma",
  1737. "key" => ":key3",
  1738. "value" => [
  1739. %{"nested_3" => ":nested_3", "nested_33" => "nested_33"},
  1740. %{"nested_4" => true}
  1741. ],
  1742. "db" => [":key3"]
  1743. },
  1744. %{
  1745. "group" => ":pleroma",
  1746. "key" => ":key4",
  1747. "value" => %{"endpoint" => "https://example.com", ":nested_5" => ":upload"},
  1748. "db" => [":key4"]
  1749. },
  1750. %{
  1751. "group" => ":idna",
  1752. "key" => ":key5",
  1753. "value" => %{"tuple" => ["string", "Pleroma.Captcha.NotReal", []]},
  1754. "db" => [":key5"]
  1755. }
  1756. ]
  1757. }
  1758. assert Application.get_env(:pleroma, :key1) == "value1"
  1759. assert Application.get_env(:pleroma, :key2) == %{
  1760. nested_1: "nested_value1",
  1761. nested_2: [
  1762. %{nested_22: "nested_value222"},
  1763. %{nested_33: %{nested_44: "nested_444"}}
  1764. ]
  1765. }
  1766. assert Application.get_env(:pleroma, :key3) == [
  1767. %{"nested_3" => :nested_3, "nested_33" => "nested_33"},
  1768. %{"nested_4" => true}
  1769. ]
  1770. assert Application.get_env(:pleroma, :key4) == %{
  1771. "endpoint" => "https://example.com",
  1772. nested_5: :upload
  1773. }
  1774. assert Application.get_env(:idna, :key5) == {"string", Pleroma.Captcha.NotReal, []}
  1775. end
  1776. test "save configs setting without explicit key", %{conn: conn} do
  1777. level = Application.get_env(:quack, :level)
  1778. meta = Application.get_env(:quack, :meta)
  1779. webhook_url = Application.get_env(:quack, :webhook_url)
  1780. on_exit(fn ->
  1781. Application.put_env(:quack, :level, level)
  1782. Application.put_env(:quack, :meta, meta)
  1783. Application.put_env(:quack, :webhook_url, webhook_url)
  1784. end)
  1785. conn =
  1786. post(conn, "/api/pleroma/admin/config", %{
  1787. configs: [
  1788. %{
  1789. group: ":quack",
  1790. key: ":level",
  1791. value: ":info"
  1792. },
  1793. %{
  1794. group: ":quack",
  1795. key: ":meta",
  1796. value: [":none"]
  1797. },
  1798. %{
  1799. group: ":quack",
  1800. key: ":webhook_url",
  1801. value: "https://hooks.slack.com/services/KEY"
  1802. }
  1803. ]
  1804. })
  1805. assert json_response(conn, 200) == %{
  1806. "configs" => [
  1807. %{
  1808. "group" => ":quack",
  1809. "key" => ":level",
  1810. "value" => ":info",
  1811. "db" => [":level"]
  1812. },
  1813. %{
  1814. "group" => ":quack",
  1815. "key" => ":meta",
  1816. "value" => [":none"],
  1817. "db" => [":meta"]
  1818. },
  1819. %{
  1820. "group" => ":quack",
  1821. "key" => ":webhook_url",
  1822. "value" => "https://hooks.slack.com/services/KEY",
  1823. "db" => [":webhook_url"]
  1824. }
  1825. ]
  1826. }
  1827. assert Application.get_env(:quack, :level) == :info
  1828. assert Application.get_env(:quack, :meta) == [:none]
  1829. assert Application.get_env(:quack, :webhook_url) == "https://hooks.slack.com/services/KEY"
  1830. end
  1831. test "saving config with partial update", %{conn: conn} do
  1832. config = insert(:config, key: ":key1", value: :erlang.term_to_binary(key1: 1, key2: 2))
  1833. conn =
  1834. post(conn, "/api/pleroma/admin/config", %{
  1835. configs: [
  1836. %{group: config.group, key: config.key, value: [%{"tuple" => [":key3", 3]}]}
  1837. ]
  1838. })
  1839. assert json_response(conn, 200) == %{
  1840. "configs" => [
  1841. %{
  1842. "group" => ":pleroma",
  1843. "key" => ":key1",
  1844. "value" => [
  1845. %{"tuple" => [":key1", 1]},
  1846. %{"tuple" => [":key2", 2]},
  1847. %{"tuple" => [":key3", 3]}
  1848. ],
  1849. "db" => [":key1", ":key2", ":key3"]
  1850. }
  1851. ]
  1852. }
  1853. end
  1854. test "saving config which need pleroma reboot", %{conn: conn} do
  1855. chat = Config.get(:chat)
  1856. on_exit(fn -> Config.put(:chat, chat) end)
  1857. assert post(
  1858. conn,
  1859. "/api/pleroma/admin/config",
  1860. %{
  1861. configs: [
  1862. %{group: ":pleroma", key: ":chat", value: [%{"tuple" => [":enabled", true]}]}
  1863. ]
  1864. }
  1865. )
  1866. |> json_response(200) == %{
  1867. "configs" => [
  1868. %{
  1869. "db" => [":enabled"],
  1870. "group" => ":pleroma",
  1871. "key" => ":chat",
  1872. "value" => [%{"tuple" => [":enabled", true]}]
  1873. }
  1874. ],
  1875. "need_reboot" => true
  1876. }
  1877. configs =
  1878. conn
  1879. |> get("/api/pleroma/admin/config")
  1880. |> json_response(200)
  1881. assert configs["need_reboot"]
  1882. capture_log(fn ->
  1883. assert conn |> get("/api/pleroma/admin/restart") |> json_response(200) == %{}
  1884. end) =~ "pleroma restarted"
  1885. configs =
  1886. conn
  1887. |> get("/api/pleroma/admin/config")
  1888. |> json_response(200)
  1889. refute Map.has_key?(configs, "need_reboot")
  1890. end
  1891. test "update setting which need reboot, don't change reboot flag until reboot", %{conn: conn} do
  1892. chat = Config.get(:chat)
  1893. on_exit(fn -> Config.put(:chat, chat) end)
  1894. assert post(
  1895. conn,
  1896. "/api/pleroma/admin/config",
  1897. %{
  1898. configs: [
  1899. %{group: ":pleroma", key: ":chat", value: [%{"tuple" => [":enabled", true]}]}
  1900. ]
  1901. }
  1902. )
  1903. |> json_response(200) == %{
  1904. "configs" => [
  1905. %{
  1906. "db" => [":enabled"],
  1907. "group" => ":pleroma",
  1908. "key" => ":chat",
  1909. "value" => [%{"tuple" => [":enabled", true]}]
  1910. }
  1911. ],
  1912. "need_reboot" => true
  1913. }
  1914. assert post(conn, "/api/pleroma/admin/config", %{
  1915. configs: [
  1916. %{group: ":pleroma", key: ":key1", value: [%{"tuple" => [":key3", 3]}]}
  1917. ]
  1918. })
  1919. |> json_response(200) == %{
  1920. "configs" => [
  1921. %{
  1922. "group" => ":pleroma",
  1923. "key" => ":key1",
  1924. "value" => [
  1925. %{"tuple" => [":key3", 3]}
  1926. ],
  1927. "db" => [":key3"]
  1928. }
  1929. ],
  1930. "need_reboot" => true
  1931. }
  1932. capture_log(fn ->
  1933. assert conn |> get("/api/pleroma/admin/restart") |> json_response(200) == %{}
  1934. end) =~ "pleroma restarted"
  1935. configs =
  1936. conn
  1937. |> get("/api/pleroma/admin/config")
  1938. |> json_response(200)
  1939. refute Map.has_key?(configs, "need_reboot")
  1940. end
  1941. test "saving config with nested merge", %{conn: conn} do
  1942. config =
  1943. insert(:config, key: ":key1", value: :erlang.term_to_binary(key1: 1, key2: [k1: 1, k2: 2]))
  1944. conn =
  1945. post(conn, "/api/pleroma/admin/config", %{
  1946. configs: [
  1947. %{
  1948. group: config.group,
  1949. key: config.key,
  1950. value: [
  1951. %{"tuple" => [":key3", 3]},
  1952. %{
  1953. "tuple" => [
  1954. ":key2",
  1955. [
  1956. %{"tuple" => [":k2", 1]},
  1957. %{"tuple" => [":k3", 3]}
  1958. ]
  1959. ]
  1960. }
  1961. ]
  1962. }
  1963. ]
  1964. })
  1965. assert json_response(conn, 200) == %{
  1966. "configs" => [
  1967. %{
  1968. "group" => ":pleroma",
  1969. "key" => ":key1",
  1970. "value" => [
  1971. %{"tuple" => [":key1", 1]},
  1972. %{"tuple" => [":key3", 3]},
  1973. %{
  1974. "tuple" => [
  1975. ":key2",
  1976. [
  1977. %{"tuple" => [":k1", 1]},
  1978. %{"tuple" => [":k2", 1]},
  1979. %{"tuple" => [":k3", 3]}
  1980. ]
  1981. ]
  1982. }
  1983. ],
  1984. "db" => [":key1", ":key3", ":key2"]
  1985. }
  1986. ]
  1987. }
  1988. end
  1989. test "saving special atoms", %{conn: conn} do
  1990. conn =
  1991. post(conn, "/api/pleroma/admin/config", %{
  1992. "configs" => [
  1993. %{
  1994. "group" => ":pleroma",
  1995. "key" => ":key1",
  1996. "value" => [
  1997. %{
  1998. "tuple" => [
  1999. ":ssl_options",
  2000. [%{"tuple" => [":versions", [":tlsv1", ":tlsv1.1", ":tlsv1.2"]]}]
  2001. ]
  2002. }
  2003. ]
  2004. }
  2005. ]
  2006. })
  2007. assert json_response(conn, 200) == %{
  2008. "configs" => [
  2009. %{
  2010. "group" => ":pleroma",
  2011. "key" => ":key1",
  2012. "value" => [
  2013. %{
  2014. "tuple" => [
  2015. ":ssl_options",
  2016. [%{"tuple" => [":versions", [":tlsv1", ":tlsv1.1", ":tlsv1.2"]]}]
  2017. ]
  2018. }
  2019. ],
  2020. "db" => [":ssl_options"]
  2021. }
  2022. ]
  2023. }
  2024. assert Application.get_env(:pleroma, :key1) == [
  2025. ssl_options: [versions: [:tlsv1, :"tlsv1.1", :"tlsv1.2"]]
  2026. ]
  2027. end
  2028. test "saving full setting if value is in full_key_update list", %{conn: conn} do
  2029. backends = Application.get_env(:logger, :backends)
  2030. on_exit(fn -> Application.put_env(:logger, :backends, backends) end)
  2031. config =
  2032. insert(:config,
  2033. group: ":logger",
  2034. key: ":backends",
  2035. value: :erlang.term_to_binary([])
  2036. )
  2037. conn =
  2038. post(conn, "/api/pleroma/admin/config", %{
  2039. configs: [
  2040. %{
  2041. group: config.group,
  2042. key: config.key,
  2043. value: [":console", %{"tuple" => ["ExSyslogger", ":ex_syslogger"]}]
  2044. }
  2045. ]
  2046. })
  2047. assert json_response(conn, 200) == %{
  2048. "configs" => [
  2049. %{
  2050. "group" => ":logger",
  2051. "key" => ":backends",
  2052. "value" => [
  2053. ":console",
  2054. %{"tuple" => ["ExSyslogger", ":ex_syslogger"]}
  2055. ],
  2056. "db" => [":backends"]
  2057. }
  2058. ]
  2059. }
  2060. assert Application.get_env(:logger, :backends) == [
  2061. :console,
  2062. {ExSyslogger, :ex_syslogger}
  2063. ]
  2064. capture_log(fn ->
  2065. require Logger
  2066. Logger.warn("Ooops...")
  2067. end) =~ "Ooops..."
  2068. end
  2069. test "saving full setting if value is not keyword", %{conn: conn} do
  2070. config =
  2071. insert(:config,
  2072. group: ":tesla",
  2073. key: ":adapter",
  2074. value: :erlang.term_to_binary(Tesla.Adapter.Hackey)
  2075. )
  2076. conn =
  2077. post(conn, "/api/pleroma/admin/config", %{
  2078. configs: [
  2079. %{group: config.group, key: config.key, value: "Tesla.Adapter.Httpc"}
  2080. ]
  2081. })
  2082. assert json_response(conn, 200) == %{
  2083. "configs" => [
  2084. %{
  2085. "group" => ":tesla",
  2086. "key" => ":adapter",
  2087. "value" => "Tesla.Adapter.Httpc",
  2088. "db" => [":adapter"]
  2089. }
  2090. ],
  2091. "need_reboot" => true
  2092. }
  2093. end
  2094. test "update config setting & delete with fallback to default value", %{
  2095. conn: conn,
  2096. admin: admin,
  2097. token: token
  2098. } do
  2099. ueberauth = Application.get_env(:ueberauth, Ueberauth)
  2100. config1 = insert(:config, key: ":keyaa1")
  2101. config2 = insert(:config, key: ":keyaa2")
  2102. config3 =
  2103. insert(:config,
  2104. group: ":ueberauth",
  2105. key: "Ueberauth"
  2106. )
  2107. conn =
  2108. post(conn, "/api/pleroma/admin/config", %{
  2109. configs: [
  2110. %{group: config1.group, key: config1.key, value: "another_value"},
  2111. %{group: config2.group, key: config2.key, value: "another_value"}
  2112. ]
  2113. })
  2114. assert json_response(conn, 200) == %{
  2115. "configs" => [
  2116. %{
  2117. "group" => ":pleroma",
  2118. "key" => config1.key,
  2119. "value" => "another_value",
  2120. "db" => [":keyaa1"]
  2121. },
  2122. %{
  2123. "group" => ":pleroma",
  2124. "key" => config2.key,
  2125. "value" => "another_value",
  2126. "db" => [":keyaa2"]
  2127. }
  2128. ]
  2129. }
  2130. assert Application.get_env(:pleroma, :keyaa1) == "another_value"
  2131. assert Application.get_env(:pleroma, :keyaa2) == "another_value"
  2132. assert Application.get_env(:ueberauth, Ueberauth) == ConfigDB.from_binary(config3.value)
  2133. conn =
  2134. build_conn()
  2135. |> assign(:user, admin)
  2136. |> assign(:token, token)
  2137. |> post("/api/pleroma/admin/config", %{
  2138. configs: [
  2139. %{group: config2.group, key: config2.key, delete: true},
  2140. %{
  2141. group: ":ueberauth",
  2142. key: "Ueberauth",
  2143. delete: true
  2144. }
  2145. ]
  2146. })
  2147. assert json_response(conn, 200) == %{
  2148. "configs" => []
  2149. }
  2150. assert Application.get_env(:ueberauth, Ueberauth) == ueberauth
  2151. refute Keyword.has_key?(Application.get_all_env(:pleroma), :keyaa2)
  2152. end
  2153. test "common config example", %{conn: conn} do
  2154. adapter = Application.get_env(:tesla, :adapter)
  2155. on_exit(fn -> Application.put_env(:tesla, :adapter, adapter) end)
  2156. conn =
  2157. post(conn, "/api/pleroma/admin/config", %{
  2158. configs: [
  2159. %{
  2160. "group" => ":pleroma",
  2161. "key" => "Pleroma.Captcha.NotReal",
  2162. "value" => [
  2163. %{"tuple" => [":enabled", false]},
  2164. %{"tuple" => [":method", "Pleroma.Captcha.Kocaptcha"]},
  2165. %{"tuple" => [":seconds_valid", 60]},
  2166. %{"tuple" => [":path", ""]},
  2167. %{"tuple" => [":key1", nil]},
  2168. %{"tuple" => [":regex1", "~r/https:\/\/example.com/"]},
  2169. %{"tuple" => [":regex2", "~r/https:\/\/example.com/u"]},
  2170. %{"tuple" => [":regex3", "~r/https:\/\/example.com/i"]},
  2171. %{"tuple" => [":regex4", "~r/https:\/\/example.com/s"]},
  2172. %{"tuple" => [":name", "Pleroma"]}
  2173. ]
  2174. },
  2175. %{
  2176. "group" => ":tesla",
  2177. "key" => ":adapter",
  2178. "value" => "Tesla.Adapter.Httpc"
  2179. }
  2180. ]
  2181. })
  2182. assert Application.get_env(:tesla, :adapter) == Tesla.Adapter.Httpc
  2183. assert Config.get([Pleroma.Captcha.NotReal, :name]) == "Pleroma"
  2184. assert json_response(conn, 200) == %{
  2185. "configs" => [
  2186. %{
  2187. "group" => ":pleroma",
  2188. "key" => "Pleroma.Captcha.NotReal",
  2189. "value" => [
  2190. %{"tuple" => [":enabled", false]},
  2191. %{"tuple" => [":method", "Pleroma.Captcha.Kocaptcha"]},
  2192. %{"tuple" => [":seconds_valid", 60]},
  2193. %{"tuple" => [":path", ""]},
  2194. %{"tuple" => [":key1", nil]},
  2195. %{"tuple" => [":regex1", "~r/https:\\/\\/example.com/"]},
  2196. %{"tuple" => [":regex2", "~r/https:\\/\\/example.com/u"]},
  2197. %{"tuple" => [":regex3", "~r/https:\\/\\/example.com/i"]},
  2198. %{"tuple" => [":regex4", "~r/https:\\/\\/example.com/s"]},
  2199. %{"tuple" => [":name", "Pleroma"]}
  2200. ],
  2201. "db" => [
  2202. ":enabled",
  2203. ":method",
  2204. ":seconds_valid",
  2205. ":path",
  2206. ":key1",
  2207. ":regex1",
  2208. ":regex2",
  2209. ":regex3",
  2210. ":regex4",
  2211. ":name"
  2212. ]
  2213. },
  2214. %{
  2215. "group" => ":tesla",
  2216. "key" => ":adapter",
  2217. "value" => "Tesla.Adapter.Httpc",
  2218. "db" => [":adapter"]
  2219. }
  2220. ],
  2221. "need_reboot" => true
  2222. }
  2223. end
  2224. test "tuples with more than two values", %{conn: conn} do
  2225. conn =
  2226. post(conn, "/api/pleroma/admin/config", %{
  2227. configs: [
  2228. %{
  2229. "group" => ":pleroma",
  2230. "key" => "Pleroma.Web.Endpoint.NotReal",
  2231. "value" => [
  2232. %{
  2233. "tuple" => [
  2234. ":http",
  2235. [
  2236. %{
  2237. "tuple" => [
  2238. ":key2",
  2239. [
  2240. %{
  2241. "tuple" => [
  2242. ":_",
  2243. [
  2244. %{
  2245. "tuple" => [
  2246. "/api/v1/streaming",
  2247. "Pleroma.Web.MastodonAPI.WebsocketHandler",
  2248. []
  2249. ]
  2250. },
  2251. %{
  2252. "tuple" => [
  2253. "/websocket",
  2254. "Phoenix.Endpoint.CowboyWebSocket",
  2255. %{
  2256. "tuple" => [
  2257. "Phoenix.Transports.WebSocket",
  2258. %{
  2259. "tuple" => [
  2260. "Pleroma.Web.Endpoint",
  2261. "Pleroma.Web.UserSocket",
  2262. []
  2263. ]
  2264. }
  2265. ]
  2266. }
  2267. ]
  2268. },
  2269. %{
  2270. "tuple" => [
  2271. ":_",
  2272. "Phoenix.Endpoint.Cowboy2Handler",
  2273. %{"tuple" => ["Pleroma.Web.Endpoint", []]}
  2274. ]
  2275. }
  2276. ]
  2277. ]
  2278. }
  2279. ]
  2280. ]
  2281. }
  2282. ]
  2283. ]
  2284. }
  2285. ]
  2286. }
  2287. ]
  2288. })
  2289. assert json_response(conn, 200) == %{
  2290. "configs" => [
  2291. %{
  2292. "group" => ":pleroma",
  2293. "key" => "Pleroma.Web.Endpoint.NotReal",
  2294. "value" => [
  2295. %{
  2296. "tuple" => [
  2297. ":http",
  2298. [
  2299. %{
  2300. "tuple" => [
  2301. ":key2",
  2302. [
  2303. %{
  2304. "tuple" => [
  2305. ":_",
  2306. [
  2307. %{
  2308. "tuple" => [
  2309. "/api/v1/streaming",
  2310. "Pleroma.Web.MastodonAPI.WebsocketHandler",
  2311. []
  2312. ]
  2313. },
  2314. %{
  2315. "tuple" => [
  2316. "/websocket",
  2317. "Phoenix.Endpoint.CowboyWebSocket",
  2318. %{
  2319. "tuple" => [
  2320. "Phoenix.Transports.WebSocket",
  2321. %{
  2322. "tuple" => [
  2323. "Pleroma.Web.Endpoint",
  2324. "Pleroma.Web.UserSocket",
  2325. []
  2326. ]
  2327. }
  2328. ]
  2329. }
  2330. ]
  2331. },
  2332. %{
  2333. "tuple" => [
  2334. ":_",
  2335. "Phoenix.Endpoint.Cowboy2Handler",
  2336. %{"tuple" => ["Pleroma.Web.Endpoint", []]}
  2337. ]
  2338. }
  2339. ]
  2340. ]
  2341. }
  2342. ]
  2343. ]
  2344. }
  2345. ]
  2346. ]
  2347. }
  2348. ],
  2349. "db" => [":http"]
  2350. }
  2351. ]
  2352. }
  2353. end
  2354. test "settings with nesting map", %{conn: conn} do
  2355. conn =
  2356. post(conn, "/api/pleroma/admin/config", %{
  2357. configs: [
  2358. %{
  2359. "group" => ":pleroma",
  2360. "key" => ":key1",
  2361. "value" => [
  2362. %{"tuple" => [":key2", "some_val"]},
  2363. %{
  2364. "tuple" => [
  2365. ":key3",
  2366. %{
  2367. ":max_options" => 20,
  2368. ":max_option_chars" => 200,
  2369. ":min_expiration" => 0,
  2370. ":max_expiration" => 31_536_000,
  2371. "nested" => %{
  2372. ":max_options" => 20,
  2373. ":max_option_chars" => 200,
  2374. ":min_expiration" => 0,
  2375. ":max_expiration" => 31_536_000
  2376. }
  2377. }
  2378. ]
  2379. }
  2380. ]
  2381. }
  2382. ]
  2383. })
  2384. assert json_response(conn, 200) ==
  2385. %{
  2386. "configs" => [
  2387. %{
  2388. "group" => ":pleroma",
  2389. "key" => ":key1",
  2390. "value" => [
  2391. %{"tuple" => [":key2", "some_val"]},
  2392. %{
  2393. "tuple" => [
  2394. ":key3",
  2395. %{
  2396. ":max_expiration" => 31_536_000,
  2397. ":max_option_chars" => 200,
  2398. ":max_options" => 20,
  2399. ":min_expiration" => 0,
  2400. "nested" => %{
  2401. ":max_expiration" => 31_536_000,
  2402. ":max_option_chars" => 200,
  2403. ":max_options" => 20,
  2404. ":min_expiration" => 0
  2405. }
  2406. }
  2407. ]
  2408. }
  2409. ],
  2410. "db" => [":key2", ":key3"]
  2411. }
  2412. ]
  2413. }
  2414. end
  2415. test "value as map", %{conn: conn} do
  2416. conn =
  2417. post(conn, "/api/pleroma/admin/config", %{
  2418. configs: [
  2419. %{
  2420. "group" => ":pleroma",
  2421. "key" => ":key1",
  2422. "value" => %{"key" => "some_val"}
  2423. }
  2424. ]
  2425. })
  2426. assert json_response(conn, 200) ==
  2427. %{
  2428. "configs" => [
  2429. %{
  2430. "group" => ":pleroma",
  2431. "key" => ":key1",
  2432. "value" => %{"key" => "some_val"},
  2433. "db" => [":key1"]
  2434. }
  2435. ]
  2436. }
  2437. end
  2438. test "queues key as atom", %{conn: conn} do
  2439. conn =
  2440. post(conn, "/api/pleroma/admin/config", %{
  2441. configs: [
  2442. %{
  2443. "group" => ":oban",
  2444. "key" => ":queues",
  2445. "value" => [
  2446. %{"tuple" => [":federator_incoming", 50]},
  2447. %{"tuple" => [":federator_outgoing", 50]},
  2448. %{"tuple" => [":web_push", 50]},
  2449. %{"tuple" => [":mailer", 10]},
  2450. %{"tuple" => [":transmogrifier", 20]},
  2451. %{"tuple" => [":scheduled_activities", 10]},
  2452. %{"tuple" => [":background", 5]}
  2453. ]
  2454. }
  2455. ]
  2456. })
  2457. assert json_response(conn, 200) == %{
  2458. "configs" => [
  2459. %{
  2460. "group" => ":oban",
  2461. "key" => ":queues",
  2462. "value" => [
  2463. %{"tuple" => [":federator_incoming", 50]},
  2464. %{"tuple" => [":federator_outgoing", 50]},
  2465. %{"tuple" => [":web_push", 50]},
  2466. %{"tuple" => [":mailer", 10]},
  2467. %{"tuple" => [":transmogrifier", 20]},
  2468. %{"tuple" => [":scheduled_activities", 10]},
  2469. %{"tuple" => [":background", 5]}
  2470. ],
  2471. "db" => [
  2472. ":federator_incoming",
  2473. ":federator_outgoing",
  2474. ":web_push",
  2475. ":mailer",
  2476. ":transmogrifier",
  2477. ":scheduled_activities",
  2478. ":background"
  2479. ]
  2480. }
  2481. ]
  2482. }
  2483. end
  2484. test "delete part of settings by atom subkeys", %{conn: conn} do
  2485. config =
  2486. insert(:config,
  2487. key: ":keyaa1",
  2488. value: :erlang.term_to_binary(subkey1: "val1", subkey2: "val2", subkey3: "val3")
  2489. )
  2490. conn =
  2491. post(conn, "/api/pleroma/admin/config", %{
  2492. configs: [
  2493. %{
  2494. group: config.group,
  2495. key: config.key,
  2496. subkeys: [":subkey1", ":subkey3"],
  2497. delete: true
  2498. }
  2499. ]
  2500. })
  2501. assert json_response(conn, 200) == %{
  2502. "configs" => [
  2503. %{
  2504. "group" => ":pleroma",
  2505. "key" => ":keyaa1",
  2506. "value" => [%{"tuple" => [":subkey2", "val2"]}],
  2507. "db" => [":subkey2"]
  2508. }
  2509. ]
  2510. }
  2511. end
  2512. test "proxy tuple localhost", %{conn: conn} do
  2513. conn =
  2514. post(conn, "/api/pleroma/admin/config", %{
  2515. configs: [
  2516. %{
  2517. group: ":pleroma",
  2518. key: ":http",
  2519. value: [
  2520. %{"tuple" => [":proxy_url", %{"tuple" => [":socks5", "localhost", 1234]}]},
  2521. %{"tuple" => [":send_user_agent", false]}
  2522. ]
  2523. }
  2524. ]
  2525. })
  2526. assert json_response(conn, 200) == %{
  2527. "configs" => [
  2528. %{
  2529. "group" => ":pleroma",
  2530. "key" => ":http",
  2531. "value" => [
  2532. %{"tuple" => [":proxy_url", %{"tuple" => [":socks5", "localhost", 1234]}]},
  2533. %{"tuple" => [":send_user_agent", false]}
  2534. ],
  2535. "db" => [":proxy_url", ":send_user_agent"]
  2536. }
  2537. ]
  2538. }
  2539. end
  2540. test "proxy tuple domain", %{conn: conn} do
  2541. conn =
  2542. post(conn, "/api/pleroma/admin/config", %{
  2543. configs: [
  2544. %{
  2545. group: ":pleroma",
  2546. key: ":http",
  2547. value: [
  2548. %{"tuple" => [":proxy_url", %{"tuple" => [":socks5", "domain.com", 1234]}]},
  2549. %{"tuple" => [":send_user_agent", false]}
  2550. ]
  2551. }
  2552. ]
  2553. })
  2554. assert json_response(conn, 200) == %{
  2555. "configs" => [
  2556. %{
  2557. "group" => ":pleroma",
  2558. "key" => ":http",
  2559. "value" => [
  2560. %{"tuple" => [":proxy_url", %{"tuple" => [":socks5", "domain.com", 1234]}]},
  2561. %{"tuple" => [":send_user_agent", false]}
  2562. ],
  2563. "db" => [":proxy_url", ":send_user_agent"]
  2564. }
  2565. ]
  2566. }
  2567. end
  2568. test "proxy tuple ip", %{conn: conn} do
  2569. conn =
  2570. post(conn, "/api/pleroma/admin/config", %{
  2571. configs: [
  2572. %{
  2573. group: ":pleroma",
  2574. key: ":http",
  2575. value: [
  2576. %{"tuple" => [":proxy_url", %{"tuple" => [":socks5", "127.0.0.1", 1234]}]},
  2577. %{"tuple" => [":send_user_agent", false]}
  2578. ]
  2579. }
  2580. ]
  2581. })
  2582. assert json_response(conn, 200) == %{
  2583. "configs" => [
  2584. %{
  2585. "group" => ":pleroma",
  2586. "key" => ":http",
  2587. "value" => [
  2588. %{"tuple" => [":proxy_url", %{"tuple" => [":socks5", "127.0.0.1", 1234]}]},
  2589. %{"tuple" => [":send_user_agent", false]}
  2590. ],
  2591. "db" => [":proxy_url", ":send_user_agent"]
  2592. }
  2593. ]
  2594. }
  2595. end
  2596. end
  2597. describe "GET /api/pleroma/admin/restart" do
  2598. clear_config(:configurable_from_database) do
  2599. Config.put(:configurable_from_database, true)
  2600. end
  2601. test "pleroma restarts", %{conn: conn} do
  2602. capture_log(fn ->
  2603. assert conn |> get("/api/pleroma/admin/restart") |> json_response(200) == %{}
  2604. end) =~ "pleroma restarted"
  2605. refute Restarter.Pleroma.need_reboot?()
  2606. end
  2607. end
  2608. describe "GET /api/pleroma/admin/statuses" do
  2609. test "returns all public, unlisted, and direct statuses", %{conn: conn, admin: admin} do
  2610. blocked = insert(:user)
  2611. user = insert(:user)
  2612. User.block(admin, blocked)
  2613. {:ok, _} =
  2614. CommonAPI.post(user, %{"status" => "@#{admin.nickname}", "visibility" => "direct"})
  2615. {:ok, _} = CommonAPI.post(user, %{"status" => ".", "visibility" => "unlisted"})
  2616. {:ok, _} = CommonAPI.post(user, %{"status" => ".", "visibility" => "private"})
  2617. {:ok, _} = CommonAPI.post(user, %{"status" => ".", "visibility" => "public"})
  2618. {:ok, _} = CommonAPI.post(blocked, %{"status" => ".", "visibility" => "public"})
  2619. response =
  2620. conn
  2621. |> get("/api/pleroma/admin/statuses")
  2622. |> json_response(200)
  2623. refute "private" in Enum.map(response, & &1["visibility"])
  2624. assert length(response) == 4
  2625. end
  2626. test "returns only local statuses with local_only on", %{conn: conn} do
  2627. user = insert(:user)
  2628. remote_user = insert(:user, local: false, nickname: "archaeme@archae.me")
  2629. insert(:note_activity, user: user, local: true)
  2630. insert(:note_activity, user: remote_user, local: false)
  2631. response =
  2632. conn
  2633. |> get("/api/pleroma/admin/statuses?local_only=true")
  2634. |> json_response(200)
  2635. assert length(response) == 1
  2636. end
  2637. test "returns private statuses with godmode on", %{conn: conn} do
  2638. user = insert(:user)
  2639. {:ok, _} = CommonAPI.post(user, %{"status" => ".", "visibility" => "private"})
  2640. {:ok, _} = CommonAPI.post(user, %{"status" => ".", "visibility" => "public"})
  2641. conn = get(conn, "/api/pleroma/admin/statuses?godmode=true")
  2642. assert json_response(conn, 200) |> length() == 2
  2643. end
  2644. end
  2645. describe "GET /api/pleroma/admin/users/:nickname/statuses" do
  2646. setup do
  2647. user = insert(:user)
  2648. date1 = (DateTime.to_unix(DateTime.utc_now()) + 2000) |> DateTime.from_unix!()
  2649. date2 = (DateTime.to_unix(DateTime.utc_now()) + 1000) |> DateTime.from_unix!()
  2650. date3 = (DateTime.to_unix(DateTime.utc_now()) + 3000) |> DateTime.from_unix!()
  2651. insert(:note_activity, user: user, published: date1)
  2652. insert(:note_activity, user: user, published: date2)
  2653. insert(:note_activity, user: user, published: date3)
  2654. %{user: user}
  2655. end
  2656. test "renders user's statuses", %{conn: conn, user: user} do
  2657. conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/statuses")
  2658. assert json_response(conn, 200) |> length() == 3
  2659. end
  2660. test "renders user's statuses with a limit", %{conn: conn, user: user} do
  2661. conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/statuses?page_size=2")
  2662. assert json_response(conn, 200) |> length() == 2
  2663. end
  2664. test "doesn't return private statuses by default", %{conn: conn, user: user} do
  2665. {:ok, _private_status} =
  2666. CommonAPI.post(user, %{"status" => "private", "visibility" => "private"})
  2667. {:ok, _public_status} =
  2668. CommonAPI.post(user, %{"status" => "public", "visibility" => "public"})
  2669. conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/statuses")
  2670. assert json_response(conn, 200) |> length() == 4
  2671. end
  2672. test "returns private statuses with godmode on", %{conn: conn, user: user} do
  2673. {:ok, _private_status} =
  2674. CommonAPI.post(user, %{"status" => "private", "visibility" => "private"})
  2675. {:ok, _public_status} =
  2676. CommonAPI.post(user, %{"status" => "public", "visibility" => "public"})
  2677. conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/statuses?godmode=true")
  2678. assert json_response(conn, 200) |> length() == 5
  2679. end
  2680. test "excludes reblogs by default", %{conn: conn, user: user} do
  2681. other_user = insert(:user)
  2682. {:ok, activity} = CommonAPI.post(user, %{"status" => "."})
  2683. {:ok, %Activity{}, _} = CommonAPI.repeat(activity.id, other_user)
  2684. conn_res = get(conn, "/api/pleroma/admin/users/#{other_user.nickname}/statuses")
  2685. assert json_response(conn_res, 200) |> length() == 0
  2686. conn_res =
  2687. get(conn, "/api/pleroma/admin/users/#{other_user.nickname}/statuses?with_reblogs=true")
  2688. assert json_response(conn_res, 200) |> length() == 1
  2689. end
  2690. end
  2691. describe "GET /api/pleroma/admin/moderation_log" do
  2692. setup do
  2693. moderator = insert(:user, is_moderator: true)
  2694. %{moderator: moderator}
  2695. end
  2696. test "returns the log", %{conn: conn, admin: admin} do
  2697. Repo.insert(%ModerationLog{
  2698. data: %{
  2699. actor: %{
  2700. "id" => admin.id,
  2701. "nickname" => admin.nickname,
  2702. "type" => "user"
  2703. },
  2704. action: "relay_follow",
  2705. target: "https://example.org/relay"
  2706. },
  2707. inserted_at: NaiveDateTime.truncate(~N[2017-08-15 15:47:06.597036], :second)
  2708. })
  2709. Repo.insert(%ModerationLog{
  2710. data: %{
  2711. actor: %{
  2712. "id" => admin.id,
  2713. "nickname" => admin.nickname,
  2714. "type" => "user"
  2715. },
  2716. action: "relay_unfollow",
  2717. target: "https://example.org/relay"
  2718. },
  2719. inserted_at: NaiveDateTime.truncate(~N[2017-08-16 15:47:06.597036], :second)
  2720. })
  2721. conn = get(conn, "/api/pleroma/admin/moderation_log")
  2722. response = json_response(conn, 200)
  2723. [first_entry, second_entry] = response["items"]
  2724. assert response["total"] == 2
  2725. assert first_entry["data"]["action"] == "relay_unfollow"
  2726. assert first_entry["message"] ==
  2727. "@#{admin.nickname} unfollowed relay: https://example.org/relay"
  2728. assert second_entry["data"]["action"] == "relay_follow"
  2729. assert second_entry["message"] ==
  2730. "@#{admin.nickname} followed relay: https://example.org/relay"
  2731. end
  2732. test "returns the log with pagination", %{conn: conn, admin: admin} do
  2733. Repo.insert(%ModerationLog{
  2734. data: %{
  2735. actor: %{
  2736. "id" => admin.id,
  2737. "nickname" => admin.nickname,
  2738. "type" => "user"
  2739. },
  2740. action: "relay_follow",
  2741. target: "https://example.org/relay"
  2742. },
  2743. inserted_at: NaiveDateTime.truncate(~N[2017-08-15 15:47:06.597036], :second)
  2744. })
  2745. Repo.insert(%ModerationLog{
  2746. data: %{
  2747. actor: %{
  2748. "id" => admin.id,
  2749. "nickname" => admin.nickname,
  2750. "type" => "user"
  2751. },
  2752. action: "relay_unfollow",
  2753. target: "https://example.org/relay"
  2754. },
  2755. inserted_at: NaiveDateTime.truncate(~N[2017-08-16 15:47:06.597036], :second)
  2756. })
  2757. conn1 = get(conn, "/api/pleroma/admin/moderation_log?page_size=1&page=1")
  2758. response1 = json_response(conn1, 200)
  2759. [first_entry] = response1["items"]
  2760. assert response1["total"] == 2
  2761. assert response1["items"] |> length() == 1
  2762. assert first_entry["data"]["action"] == "relay_unfollow"
  2763. assert first_entry["message"] ==
  2764. "@#{admin.nickname} unfollowed relay: https://example.org/relay"
  2765. conn2 = get(conn, "/api/pleroma/admin/moderation_log?page_size=1&page=2")
  2766. response2 = json_response(conn2, 200)
  2767. [second_entry] = response2["items"]
  2768. assert response2["total"] == 2
  2769. assert response2["items"] |> length() == 1
  2770. assert second_entry["data"]["action"] == "relay_follow"
  2771. assert second_entry["message"] ==
  2772. "@#{admin.nickname} followed relay: https://example.org/relay"
  2773. end
  2774. test "filters log by date", %{conn: conn, admin: admin} do
  2775. first_date = "2017-08-15T15:47:06Z"
  2776. second_date = "2017-08-20T15:47:06Z"
  2777. Repo.insert(%ModerationLog{
  2778. data: %{
  2779. actor: %{
  2780. "id" => admin.id,
  2781. "nickname" => admin.nickname,
  2782. "type" => "user"
  2783. },
  2784. action: "relay_follow",
  2785. target: "https://example.org/relay"
  2786. },
  2787. inserted_at: NaiveDateTime.from_iso8601!(first_date)
  2788. })
  2789. Repo.insert(%ModerationLog{
  2790. data: %{
  2791. actor: %{
  2792. "id" => admin.id,
  2793. "nickname" => admin.nickname,
  2794. "type" => "user"
  2795. },
  2796. action: "relay_unfollow",
  2797. target: "https://example.org/relay"
  2798. },
  2799. inserted_at: NaiveDateTime.from_iso8601!(second_date)
  2800. })
  2801. conn1 =
  2802. get(
  2803. conn,
  2804. "/api/pleroma/admin/moderation_log?start_date=#{second_date}"
  2805. )
  2806. response1 = json_response(conn1, 200)
  2807. [first_entry] = response1["items"]
  2808. assert response1["total"] == 1
  2809. assert first_entry["data"]["action"] == "relay_unfollow"
  2810. assert first_entry["message"] ==
  2811. "@#{admin.nickname} unfollowed relay: https://example.org/relay"
  2812. end
  2813. test "returns log filtered by user", %{conn: conn, admin: admin, moderator: moderator} do
  2814. Repo.insert(%ModerationLog{
  2815. data: %{
  2816. actor: %{
  2817. "id" => admin.id,
  2818. "nickname" => admin.nickname,
  2819. "type" => "user"
  2820. },
  2821. action: "relay_follow",
  2822. target: "https://example.org/relay"
  2823. }
  2824. })
  2825. Repo.insert(%ModerationLog{
  2826. data: %{
  2827. actor: %{
  2828. "id" => moderator.id,
  2829. "nickname" => moderator.nickname,
  2830. "type" => "user"
  2831. },
  2832. action: "relay_unfollow",
  2833. target: "https://example.org/relay"
  2834. }
  2835. })
  2836. conn1 = get(conn, "/api/pleroma/admin/moderation_log?user_id=#{moderator.id}")
  2837. response1 = json_response(conn1, 200)
  2838. [first_entry] = response1["items"]
  2839. assert response1["total"] == 1
  2840. assert get_in(first_entry, ["data", "actor", "id"]) == moderator.id
  2841. end
  2842. test "returns log filtered by search", %{conn: conn, moderator: moderator} do
  2843. ModerationLog.insert_log(%{
  2844. actor: moderator,
  2845. action: "relay_follow",
  2846. target: "https://example.org/relay"
  2847. })
  2848. ModerationLog.insert_log(%{
  2849. actor: moderator,
  2850. action: "relay_unfollow",
  2851. target: "https://example.org/relay"
  2852. })
  2853. conn1 = get(conn, "/api/pleroma/admin/moderation_log?search=unfo")
  2854. response1 = json_response(conn1, 200)
  2855. [first_entry] = response1["items"]
  2856. assert response1["total"] == 1
  2857. assert get_in(first_entry, ["data", "message"]) ==
  2858. "@#{moderator.nickname} unfollowed relay: https://example.org/relay"
  2859. end
  2860. end
  2861. describe "PATCH /users/:nickname/force_password_reset" do
  2862. test "sets password_reset_pending to true", %{conn: conn} do
  2863. user = insert(:user)
  2864. assert user.password_reset_pending == false
  2865. conn =
  2866. patch(conn, "/api/pleroma/admin/users/force_password_reset", %{nicknames: [user.nickname]})
  2867. assert json_response(conn, 204) == ""
  2868. ObanHelpers.perform_all()
  2869. assert User.get_by_id(user.id).password_reset_pending == true
  2870. end
  2871. end
  2872. describe "relays" do
  2873. test "POST /relay", %{conn: conn, admin: admin} do
  2874. conn =
  2875. post(conn, "/api/pleroma/admin/relay", %{
  2876. relay_url: "http://mastodon.example.org/users/admin"
  2877. })
  2878. assert json_response(conn, 200) == "http://mastodon.example.org/users/admin"
  2879. log_entry = Repo.one(ModerationLog)
  2880. assert ModerationLog.get_log_entry_message(log_entry) ==
  2881. "@#{admin.nickname} followed relay: http://mastodon.example.org/users/admin"
  2882. end
  2883. test "GET /relay", %{conn: conn} do
  2884. relay_user = Pleroma.Web.ActivityPub.Relay.get_actor()
  2885. ["http://mastodon.example.org/users/admin", "https://mstdn.io/users/mayuutann"]
  2886. |> Enum.each(fn ap_id ->
  2887. {:ok, user} = User.get_or_fetch_by_ap_id(ap_id)
  2888. User.follow(relay_user, user)
  2889. end)
  2890. conn = get(conn, "/api/pleroma/admin/relay")
  2891. assert json_response(conn, 200)["relays"] -- ["mastodon.example.org", "mstdn.io"] == []
  2892. end
  2893. test "DELETE /relay", %{conn: conn, admin: admin} do
  2894. post(conn, "/api/pleroma/admin/relay", %{
  2895. relay_url: "http://mastodon.example.org/users/admin"
  2896. })
  2897. conn =
  2898. delete(conn, "/api/pleroma/admin/relay", %{
  2899. relay_url: "http://mastodon.example.org/users/admin"
  2900. })
  2901. assert json_response(conn, 200) == "http://mastodon.example.org/users/admin"
  2902. [log_entry_one, log_entry_two] = Repo.all(ModerationLog)
  2903. assert ModerationLog.get_log_entry_message(log_entry_one) ==
  2904. "@#{admin.nickname} followed relay: http://mastodon.example.org/users/admin"
  2905. assert ModerationLog.get_log_entry_message(log_entry_two) ==
  2906. "@#{admin.nickname} unfollowed relay: http://mastodon.example.org/users/admin"
  2907. end
  2908. end
  2909. describe "instances" do
  2910. test "GET /instances/:instance/statuses", %{conn: conn} do
  2911. user = insert(:user, local: false, nickname: "archaeme@archae.me")
  2912. user2 = insert(:user, local: false, nickname: "test@test.com")
  2913. insert_pair(:note_activity, user: user)
  2914. activity = insert(:note_activity, user: user2)
  2915. ret_conn = get(conn, "/api/pleroma/admin/instances/archae.me/statuses")
  2916. response = json_response(ret_conn, 200)
  2917. assert length(response) == 2
  2918. ret_conn = get(conn, "/api/pleroma/admin/instances/test.com/statuses")
  2919. response = json_response(ret_conn, 200)
  2920. assert length(response) == 1
  2921. ret_conn = get(conn, "/api/pleroma/admin/instances/nonexistent.com/statuses")
  2922. response = json_response(ret_conn, 200)
  2923. assert Enum.empty?(response)
  2924. CommonAPI.repeat(activity.id, user)
  2925. ret_conn = get(conn, "/api/pleroma/admin/instances/archae.me/statuses")
  2926. response = json_response(ret_conn, 200)
  2927. assert length(response) == 2
  2928. ret_conn = get(conn, "/api/pleroma/admin/instances/archae.me/statuses?with_reblogs=true")
  2929. response = json_response(ret_conn, 200)
  2930. assert length(response) == 3
  2931. end
  2932. end
  2933. describe "PATCH /confirm_email" do
  2934. test "it confirms emails of two users", %{conn: conn, admin: admin} do
  2935. [first_user, second_user] = insert_pair(:user, confirmation_pending: true)
  2936. assert first_user.confirmation_pending == true
  2937. assert second_user.confirmation_pending == true
  2938. ret_conn =
  2939. patch(conn, "/api/pleroma/admin/users/confirm_email", %{
  2940. nicknames: [
  2941. first_user.nickname,
  2942. second_user.nickname
  2943. ]
  2944. })
  2945. assert ret_conn.status == 200
  2946. assert first_user.confirmation_pending == true
  2947. assert second_user.confirmation_pending == true
  2948. log_entry = Repo.one(ModerationLog)
  2949. assert ModerationLog.get_log_entry_message(log_entry) ==
  2950. "@#{admin.nickname} confirmed email for users: @#{first_user.nickname}, @#{
  2951. second_user.nickname
  2952. }"
  2953. end
  2954. end
  2955. describe "PATCH /resend_confirmation_email" do
  2956. test "it resend emails for two users", %{conn: conn, admin: admin} do
  2957. [first_user, second_user] = insert_pair(:user, confirmation_pending: true)
  2958. ret_conn =
  2959. patch(conn, "/api/pleroma/admin/users/resend_confirmation_email", %{
  2960. nicknames: [
  2961. first_user.nickname,
  2962. second_user.nickname
  2963. ]
  2964. })
  2965. assert ret_conn.status == 200
  2966. log_entry = Repo.one(ModerationLog)
  2967. assert ModerationLog.get_log_entry_message(log_entry) ==
  2968. "@#{admin.nickname} re-sent confirmation email for users: @#{first_user.nickname}, @#{
  2969. second_user.nickname
  2970. }"
  2971. end
  2972. end
  2973. describe "POST /reports/:id/notes" do
  2974. setup %{conn: conn, admin: admin} do
  2975. [reporter, target_user] = insert_pair(:user)
  2976. activity = insert(:note_activity, user: target_user)
  2977. {:ok, %{id: report_id}} =
  2978. CommonAPI.report(reporter, %{
  2979. "account_id" => target_user.id,
  2980. "comment" => "I feel offended",
  2981. "status_ids" => [activity.id]
  2982. })
  2983. post(conn, "/api/pleroma/admin/reports/#{report_id}/notes", %{
  2984. content: "this is disgusting!"
  2985. })
  2986. post(conn, "/api/pleroma/admin/reports/#{report_id}/notes", %{
  2987. content: "this is disgusting2!"
  2988. })
  2989. %{
  2990. admin_id: admin.id,
  2991. report_id: report_id
  2992. }
  2993. end
  2994. test "it creates report note", %{admin_id: admin_id, report_id: report_id} do
  2995. [note, _] = Repo.all(ReportNote)
  2996. assert %{
  2997. activity_id: ^report_id,
  2998. content: "this is disgusting!",
  2999. user_id: ^admin_id
  3000. } = note
  3001. end
  3002. test "it returns reports with notes", %{conn: conn, admin: admin} do
  3003. conn = get(conn, "/api/pleroma/admin/reports")
  3004. response = json_response(conn, 200)
  3005. notes = hd(response["reports"])["notes"]
  3006. [note, _] = notes
  3007. assert note["user"]["nickname"] == admin.nickname
  3008. assert note["content"] == "this is disgusting!"
  3009. assert note["created_at"]
  3010. assert response["total"] == 1
  3011. end
  3012. test "it deletes the note", %{conn: conn, report_id: report_id} do
  3013. assert ReportNote |> Repo.all() |> length() == 2
  3014. [note, _] = Repo.all(ReportNote)
  3015. delete(conn, "/api/pleroma/admin/reports/#{report_id}/notes/#{note.id}")
  3016. assert ReportNote |> Repo.all() |> length() == 1
  3017. end
  3018. end
  3019. test "GET /api/pleroma/admin/config/descriptions", %{conn: conn} do
  3020. admin = insert(:user, is_admin: true)
  3021. conn =
  3022. assign(conn, :user, admin)
  3023. |> get("/api/pleroma/admin/config/descriptions")
  3024. assert [child | _others] = json_response(conn, 200)
  3025. assert child["children"]
  3026. assert child["key"]
  3027. assert String.starts_with?(child["group"], ":")
  3028. assert child["description"]
  3029. end
  3030. describe "/api/pleroma/admin/stats" do
  3031. test "status visibility count", %{conn: conn} do
  3032. admin = insert(:user, is_admin: true)
  3033. user = insert(:user)
  3034. CommonAPI.post(user, %{"visibility" => "public", "status" => "hey"})
  3035. CommonAPI.post(user, %{"visibility" => "unlisted", "status" => "hey"})
  3036. CommonAPI.post(user, %{"visibility" => "unlisted", "status" => "hey"})
  3037. response =
  3038. conn
  3039. |> assign(:user, admin)
  3040. |> get("/api/pleroma/admin/stats")
  3041. |> json_response(200)
  3042. assert %{"direct" => 0, "private" => 0, "public" => 1, "unlisted" => 2} =
  3043. response["status_visibility"]
  3044. end
  3045. end
  3046. end
  3047. # Needed for testing
  3048. defmodule Pleroma.Web.Endpoint.NotReal do
  3049. end
  3050. defmodule Pleroma.Captcha.NotReal do
  3051. end