Add `account_activation_required` to /api/v1/instance
[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 setup do
3195 user = insert(:user)
3196 [user: user]
3197 end
3198
3199 test "changes password and email", %{conn: conn, admin: admin, user: user} do
3200 assert user.password_reset_pending == false
3201
3202 conn =
3203 patch(conn, "/api/pleroma/admin/users/#{user.nickname}/credentials", %{
3204 "password" => "new_password",
3205 "email" => "new_email@example.com",
3206 "name" => "new_name"
3207 })
3208
3209 assert json_response(conn, 200) == %{"status" => "success"}
3210
3211 ObanHelpers.perform_all()
3212
3213 updated_user = User.get_by_id(user.id)
3214
3215 assert updated_user.email == "new_email@example.com"
3216 assert updated_user.name == "new_name"
3217 assert updated_user.password_hash != user.password_hash
3218 assert updated_user.password_reset_pending == true
3219
3220 [log_entry2, log_entry1] = ModerationLog |> Repo.all() |> Enum.sort()
3221
3222 assert ModerationLog.get_log_entry_message(log_entry1) ==
3223 "@#{admin.nickname} updated users: @#{user.nickname}"
3224
3225 assert ModerationLog.get_log_entry_message(log_entry2) ==
3226 "@#{admin.nickname} forced password reset for users: @#{user.nickname}"
3227 end
3228
3229 test "returns 403 if requested by a non-admin", %{user: user} do
3230 conn =
3231 build_conn()
3232 |> assign(:user, user)
3233 |> patch("/api/pleroma/admin/users/#{user.nickname}/credentials", %{
3234 "password" => "new_password",
3235 "email" => "new_email@example.com",
3236 "name" => "new_name"
3237 })
3238
3239 assert json_response(conn, :forbidden)
3240 end
3241
3242 test "changes actor type from permitted list", %{conn: conn, user: user} do
3243 assert user.actor_type == "Person"
3244
3245 assert patch(conn, "/api/pleroma/admin/users/#{user.nickname}/credentials", %{
3246 "actor_type" => "Service"
3247 })
3248 |> json_response(200) == %{"status" => "success"}
3249
3250 updated_user = User.get_by_id(user.id)
3251
3252 assert updated_user.actor_type == "Service"
3253
3254 assert patch(conn, "/api/pleroma/admin/users/#{user.nickname}/credentials", %{
3255 "actor_type" => "Application"
3256 })
3257 |> json_response(200) == %{"errors" => %{"actor_type" => "is invalid"}}
3258 end
3259
3260 test "update non existing user", %{conn: conn} do
3261 assert patch(conn, "/api/pleroma/admin/users/non-existing/credentials", %{
3262 "password" => "new_password"
3263 })
3264 |> json_response(200) == %{"error" => "Unable to update user."}
3265 end
3266 end
3267
3268 describe "PATCH /users/:nickname/force_password_reset" do
3269 test "sets password_reset_pending to true", %{conn: conn} do
3270 user = insert(:user)
3271 assert user.password_reset_pending == false
3272
3273 conn =
3274 patch(conn, "/api/pleroma/admin/users/force_password_reset", %{nicknames: [user.nickname]})
3275
3276 assert json_response(conn, 204) == ""
3277
3278 ObanHelpers.perform_all()
3279
3280 assert User.get_by_id(user.id).password_reset_pending == true
3281 end
3282 end
3283
3284 describe "relays" do
3285 test "POST /relay", %{conn: conn, admin: admin} do
3286 conn =
3287 post(conn, "/api/pleroma/admin/relay", %{
3288 relay_url: "http://mastodon.example.org/users/admin"
3289 })
3290
3291 assert json_response(conn, 200) == "http://mastodon.example.org/users/admin"
3292
3293 log_entry = Repo.one(ModerationLog)
3294
3295 assert ModerationLog.get_log_entry_message(log_entry) ==
3296 "@#{admin.nickname} followed relay: http://mastodon.example.org/users/admin"
3297 end
3298
3299 test "GET /relay", %{conn: conn} do
3300 relay_user = Pleroma.Web.ActivityPub.Relay.get_actor()
3301
3302 ["http://mastodon.example.org/users/admin", "https://mstdn.io/users/mayuutann"]
3303 |> Enum.each(fn ap_id ->
3304 {:ok, user} = User.get_or_fetch_by_ap_id(ap_id)
3305 User.follow(relay_user, user)
3306 end)
3307
3308 conn = get(conn, "/api/pleroma/admin/relay")
3309
3310 assert json_response(conn, 200)["relays"] -- ["mastodon.example.org", "mstdn.io"] == []
3311 end
3312
3313 test "DELETE /relay", %{conn: conn, admin: admin} do
3314 post(conn, "/api/pleroma/admin/relay", %{
3315 relay_url: "http://mastodon.example.org/users/admin"
3316 })
3317
3318 conn =
3319 delete(conn, "/api/pleroma/admin/relay", %{
3320 relay_url: "http://mastodon.example.org/users/admin"
3321 })
3322
3323 assert json_response(conn, 200) == "http://mastodon.example.org/users/admin"
3324
3325 [log_entry_one, log_entry_two] = Repo.all(ModerationLog)
3326
3327 assert ModerationLog.get_log_entry_message(log_entry_one) ==
3328 "@#{admin.nickname} followed relay: http://mastodon.example.org/users/admin"
3329
3330 assert ModerationLog.get_log_entry_message(log_entry_two) ==
3331 "@#{admin.nickname} unfollowed relay: http://mastodon.example.org/users/admin"
3332 end
3333 end
3334
3335 describe "instances" do
3336 test "GET /instances/:instance/statuses", %{conn: conn} do
3337 user = insert(:user, local: false, nickname: "archaeme@archae.me")
3338 user2 = insert(:user, local: false, nickname: "test@test.com")
3339 insert_pair(:note_activity, user: user)
3340 activity = insert(:note_activity, user: user2)
3341
3342 ret_conn = get(conn, "/api/pleroma/admin/instances/archae.me/statuses")
3343
3344 response = json_response(ret_conn, 200)
3345
3346 assert length(response) == 2
3347
3348 ret_conn = get(conn, "/api/pleroma/admin/instances/test.com/statuses")
3349
3350 response = json_response(ret_conn, 200)
3351
3352 assert length(response) == 1
3353
3354 ret_conn = get(conn, "/api/pleroma/admin/instances/nonexistent.com/statuses")
3355
3356 response = json_response(ret_conn, 200)
3357
3358 assert Enum.empty?(response)
3359
3360 CommonAPI.repeat(activity.id, user)
3361
3362 ret_conn = get(conn, "/api/pleroma/admin/instances/archae.me/statuses")
3363 response = json_response(ret_conn, 200)
3364 assert length(response) == 2
3365
3366 ret_conn = get(conn, "/api/pleroma/admin/instances/archae.me/statuses?with_reblogs=true")
3367 response = json_response(ret_conn, 200)
3368 assert length(response) == 3
3369 end
3370 end
3371
3372 describe "PATCH /confirm_email" do
3373 test "it confirms emails of two users", %{conn: conn, admin: admin} do
3374 [first_user, second_user] = insert_pair(:user, confirmation_pending: true)
3375
3376 assert first_user.confirmation_pending == true
3377 assert second_user.confirmation_pending == true
3378
3379 ret_conn =
3380 patch(conn, "/api/pleroma/admin/users/confirm_email", %{
3381 nicknames: [
3382 first_user.nickname,
3383 second_user.nickname
3384 ]
3385 })
3386
3387 assert ret_conn.status == 200
3388
3389 assert first_user.confirmation_pending == true
3390 assert second_user.confirmation_pending == true
3391
3392 log_entry = Repo.one(ModerationLog)
3393
3394 assert ModerationLog.get_log_entry_message(log_entry) ==
3395 "@#{admin.nickname} confirmed email for users: @#{first_user.nickname}, @#{
3396 second_user.nickname
3397 }"
3398 end
3399 end
3400
3401 describe "PATCH /resend_confirmation_email" do
3402 test "it resend emails for two users", %{conn: conn, admin: admin} do
3403 [first_user, second_user] = insert_pair(:user, confirmation_pending: true)
3404
3405 ret_conn =
3406 patch(conn, "/api/pleroma/admin/users/resend_confirmation_email", %{
3407 nicknames: [
3408 first_user.nickname,
3409 second_user.nickname
3410 ]
3411 })
3412
3413 assert ret_conn.status == 200
3414
3415 log_entry = Repo.one(ModerationLog)
3416
3417 assert ModerationLog.get_log_entry_message(log_entry) ==
3418 "@#{admin.nickname} re-sent confirmation email for users: @#{first_user.nickname}, @#{
3419 second_user.nickname
3420 }"
3421 end
3422 end
3423
3424 describe "POST /reports/:id/notes" do
3425 setup %{conn: conn, admin: admin} do
3426 [reporter, target_user] = insert_pair(:user)
3427 activity = insert(:note_activity, user: target_user)
3428
3429 {:ok, %{id: report_id}} =
3430 CommonAPI.report(reporter, %{
3431 account_id: target_user.id,
3432 comment: "I feel offended",
3433 status_ids: [activity.id]
3434 })
3435
3436 post(conn, "/api/pleroma/admin/reports/#{report_id}/notes", %{
3437 content: "this is disgusting!"
3438 })
3439
3440 post(conn, "/api/pleroma/admin/reports/#{report_id}/notes", %{
3441 content: "this is disgusting2!"
3442 })
3443
3444 %{
3445 admin_id: admin.id,
3446 report_id: report_id
3447 }
3448 end
3449
3450 test "it creates report note", %{admin_id: admin_id, report_id: report_id} do
3451 [note, _] = Repo.all(ReportNote)
3452
3453 assert %{
3454 activity_id: ^report_id,
3455 content: "this is disgusting!",
3456 user_id: ^admin_id
3457 } = note
3458 end
3459
3460 test "it returns reports with notes", %{conn: conn, admin: admin} do
3461 conn = get(conn, "/api/pleroma/admin/reports")
3462
3463 response = json_response(conn, 200)
3464 notes = hd(response["reports"])["notes"]
3465 [note, _] = notes
3466
3467 assert note["user"]["nickname"] == admin.nickname
3468 assert note["content"] == "this is disgusting!"
3469 assert note["created_at"]
3470 assert response["total"] == 1
3471 end
3472
3473 test "it deletes the note", %{conn: conn, report_id: report_id} do
3474 assert ReportNote |> Repo.all() |> length() == 2
3475
3476 [note, _] = Repo.all(ReportNote)
3477
3478 delete(conn, "/api/pleroma/admin/reports/#{report_id}/notes/#{note.id}")
3479
3480 assert ReportNote |> Repo.all() |> length() == 1
3481 end
3482 end
3483
3484 describe "GET /api/pleroma/admin/config/descriptions" do
3485 test "structure", %{conn: conn} do
3486 admin = insert(:user, is_admin: true)
3487
3488 conn =
3489 assign(conn, :user, admin)
3490 |> get("/api/pleroma/admin/config/descriptions")
3491
3492 assert [child | _others] = json_response(conn, 200)
3493
3494 assert child["children"]
3495 assert child["key"]
3496 assert String.starts_with?(child["group"], ":")
3497 assert child["description"]
3498 end
3499
3500 test "filters by database configuration whitelist", %{conn: conn} do
3501 clear_config(:database_config_whitelist, [
3502 {:pleroma, :instance},
3503 {:pleroma, :activitypub},
3504 {:pleroma, Pleroma.Upload},
3505 {:esshd}
3506 ])
3507
3508 admin = insert(:user, is_admin: true)
3509
3510 conn =
3511 assign(conn, :user, admin)
3512 |> get("/api/pleroma/admin/config/descriptions")
3513
3514 children = json_response(conn, 200)
3515
3516 assert length(children) == 4
3517
3518 assert Enum.count(children, fn c -> c["group"] == ":pleroma" end) == 3
3519
3520 instance = Enum.find(children, fn c -> c["key"] == ":instance" end)
3521 assert instance["children"]
3522
3523 activitypub = Enum.find(children, fn c -> c["key"] == ":activitypub" end)
3524 assert activitypub["children"]
3525
3526 web_endpoint = Enum.find(children, fn c -> c["key"] == "Pleroma.Upload" end)
3527 assert web_endpoint["children"]
3528
3529 esshd = Enum.find(children, fn c -> c["group"] == ":esshd" end)
3530 assert esshd["children"]
3531 end
3532 end
3533
3534 describe "/api/pleroma/admin/stats" do
3535 test "status visibility count", %{conn: conn} do
3536 admin = insert(:user, is_admin: true)
3537 user = insert(:user)
3538 CommonAPI.post(user, %{visibility: "public", status: "hey"})
3539 CommonAPI.post(user, %{visibility: "unlisted", status: "hey"})
3540 CommonAPI.post(user, %{visibility: "unlisted", status: "hey"})
3541
3542 response =
3543 conn
3544 |> assign(:user, admin)
3545 |> get("/api/pleroma/admin/stats")
3546 |> json_response(200)
3547
3548 assert %{"direct" => 0, "private" => 0, "public" => 1, "unlisted" => 2} =
3549 response["status_visibility"]
3550 end
3551 end
3552
3553 describe "POST /api/pleroma/admin/oauth_app" do
3554 test "errors", %{conn: conn} do
3555 response = conn |> post("/api/pleroma/admin/oauth_app", %{}) |> json_response(200)
3556
3557 assert response == %{"name" => "can't be blank", "redirect_uris" => "can't be blank"}
3558 end
3559
3560 test "success", %{conn: conn} do
3561 base_url = Web.base_url()
3562 app_name = "Trusted app"
3563
3564 response =
3565 conn
3566 |> post("/api/pleroma/admin/oauth_app", %{
3567 name: app_name,
3568 redirect_uris: base_url
3569 })
3570 |> json_response(200)
3571
3572 assert %{
3573 "client_id" => _,
3574 "client_secret" => _,
3575 "name" => ^app_name,
3576 "redirect_uri" => ^base_url,
3577 "trusted" => false
3578 } = response
3579 end
3580
3581 test "with trusted", %{conn: conn} do
3582 base_url = Web.base_url()
3583 app_name = "Trusted app"
3584
3585 response =
3586 conn
3587 |> post("/api/pleroma/admin/oauth_app", %{
3588 name: app_name,
3589 redirect_uris: base_url,
3590 trusted: true
3591 })
3592 |> json_response(200)
3593
3594 assert %{
3595 "client_id" => _,
3596 "client_secret" => _,
3597 "name" => ^app_name,
3598 "redirect_uri" => ^base_url,
3599 "trusted" => true
3600 } = response
3601 end
3602 end
3603
3604 describe "GET /api/pleroma/admin/oauth_app" do
3605 setup do
3606 app = insert(:oauth_app)
3607 {:ok, app: app}
3608 end
3609
3610 test "list", %{conn: conn} do
3611 response =
3612 conn
3613 |> get("/api/pleroma/admin/oauth_app")
3614 |> json_response(200)
3615
3616 assert %{"apps" => apps, "count" => count, "page_size" => _} = response
3617
3618 assert length(apps) == count
3619 end
3620
3621 test "with page size", %{conn: conn} do
3622 insert(:oauth_app)
3623 page_size = 1
3624
3625 response =
3626 conn
3627 |> get("/api/pleroma/admin/oauth_app", %{page_size: to_string(page_size)})
3628 |> json_response(200)
3629
3630 assert %{"apps" => apps, "count" => _, "page_size" => ^page_size} = response
3631
3632 assert length(apps) == page_size
3633 end
3634
3635 test "search by client name", %{conn: conn, app: app} do
3636 response =
3637 conn
3638 |> get("/api/pleroma/admin/oauth_app", %{name: app.client_name})
3639 |> json_response(200)
3640
3641 assert %{"apps" => [returned], "count" => _, "page_size" => _} = response
3642
3643 assert returned["client_id"] == app.client_id
3644 assert returned["name"] == app.client_name
3645 end
3646
3647 test "search by client id", %{conn: conn, app: app} do
3648 response =
3649 conn
3650 |> get("/api/pleroma/admin/oauth_app", %{client_id: app.client_id})
3651 |> json_response(200)
3652
3653 assert %{"apps" => [returned], "count" => _, "page_size" => _} = response
3654
3655 assert returned["client_id"] == app.client_id
3656 assert returned["name"] == app.client_name
3657 end
3658
3659 test "only trusted", %{conn: conn} do
3660 app = insert(:oauth_app, trusted: true)
3661
3662 response =
3663 conn
3664 |> get("/api/pleroma/admin/oauth_app", %{trusted: true})
3665 |> json_response(200)
3666
3667 assert %{"apps" => [returned], "count" => _, "page_size" => _} = response
3668
3669 assert returned["client_id"] == app.client_id
3670 assert returned["name"] == app.client_name
3671 end
3672 end
3673
3674 describe "DELETE /api/pleroma/admin/oauth_app/:id" do
3675 test "with id", %{conn: conn} do
3676 app = insert(:oauth_app)
3677
3678 response =
3679 conn
3680 |> delete("/api/pleroma/admin/oauth_app/" <> to_string(app.id))
3681 |> json_response(:no_content)
3682
3683 assert response == ""
3684 end
3685
3686 test "with non existance id", %{conn: conn} do
3687 response =
3688 conn
3689 |> delete("/api/pleroma/admin/oauth_app/0")
3690 |> json_response(:bad_request)
3691
3692 assert response == ""
3693 end
3694 end
3695
3696 describe "PATCH /api/pleroma/admin/oauth_app/:id" do
3697 test "with id", %{conn: conn} do
3698 app = insert(:oauth_app)
3699
3700 name = "another name"
3701 url = "https://example.com"
3702 scopes = ["admin"]
3703 id = app.id
3704 website = "http://website.com"
3705
3706 response =
3707 conn
3708 |> patch("/api/pleroma/admin/oauth_app/" <> to_string(app.id), %{
3709 name: name,
3710 trusted: true,
3711 redirect_uris: url,
3712 scopes: scopes,
3713 website: website
3714 })
3715 |> json_response(200)
3716
3717 assert %{
3718 "client_id" => _,
3719 "client_secret" => _,
3720 "id" => ^id,
3721 "name" => ^name,
3722 "redirect_uri" => ^url,
3723 "trusted" => true,
3724 "website" => ^website
3725 } = response
3726 end
3727
3728 test "without id", %{conn: conn} do
3729 response =
3730 conn
3731 |> patch("/api/pleroma/admin/oauth_app/0")
3732 |> json_response(:bad_request)
3733
3734 assert response == ""
3735 end
3736 end
3737 end
3738
3739 # Needed for testing
3740 defmodule Pleroma.Web.Endpoint.NotReal do
3741 end
3742
3743 defmodule Pleroma.Captcha.NotReal do
3744 end