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