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