logo

pleroma

My custom branche(s) on git.pleroma.social/pleroma/pleroma

admin_api_controller_test.exs (123078B)


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