1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
5 defmodule Pleroma.Web.AdminAPI.AdminAPIController do
6 use Pleroma.Web, :controller
8 alias Pleroma.ModerationLog
10 alias Pleroma.UserInviteToken
11 alias Pleroma.Web.ActivityPub.ActivityPub
12 alias Pleroma.Web.ActivityPub.Relay
13 alias Pleroma.Web.AdminAPI.AccountView
14 alias Pleroma.Web.AdminAPI.Config
15 alias Pleroma.Web.AdminAPI.ConfigView
16 alias Pleroma.Web.AdminAPI.ModerationLogView
17 alias Pleroma.Web.AdminAPI.ReportView
18 alias Pleroma.Web.AdminAPI.Search
19 alias Pleroma.Web.CommonAPI
20 alias Pleroma.Web.Endpoint
21 alias Pleroma.Web.MastodonAPI.StatusView
22 alias Pleroma.Web.Router
24 import Pleroma.Web.ControllerHelper, only: [json_response: 3]
30 action_fallback(:errors)
32 def user_delete(%{assigns: %{user: admin}} = conn, %{"nickname" => nickname}) do
33 user = User.get_cached_by_nickname(nickname)
36 ModerationLog.insert_log(%{
46 def user_follow(%{assigns: %{user: admin}} = conn, %{
47 "follower" => follower_nick,
48 "followed" => followed_nick
50 with %User{} = follower <- User.get_cached_by_nickname(follower_nick),
51 %User{} = followed <- User.get_cached_by_nickname(followed_nick) do
52 User.follow(follower, followed)
54 ModerationLog.insert_log(%{
66 def user_unfollow(%{assigns: %{user: admin}} = conn, %{
67 "follower" => follower_nick,
68 "followed" => followed_nick
70 with %User{} = follower <- User.get_cached_by_nickname(follower_nick),
71 %User{} = followed <- User.get_cached_by_nickname(followed_nick) do
72 User.unfollow(follower, followed)
74 ModerationLog.insert_log(%{
86 def users_create(%{assigns: %{user: admin}} = conn, %{"users" => users}) do
88 Enum.map(users, fn %{"nickname" => nickname, "email" => email, "password" => password} ->
94 password_confirmation: password,
98 User.register_changeset(%User{}, user_data, need_confirmation: false)
100 |> Enum.reduce(Ecto.Multi.new(), fn changeset, multi ->
101 Ecto.Multi.insert(multi, Ecto.UUID.generate(), changeset)
104 case Pleroma.Repo.transaction(changesets) do
109 |> Enum.map(fn user ->
110 {:ok, user} = User.post_register_action(user)
114 |> Enum.map(&AccountView.render("created.json", %{user: &1}))
116 ModerationLog.insert_log(%{
118 subjects: Map.values(users),
125 {:error, id, changeset, _} ->
127 Enum.map(changesets.operations, fn
128 {current_id, {:changeset, _current_changeset, _}} when current_id == id ->
129 AccountView.render("create-error.json", %{changeset: changeset})
131 {_, {:changeset, current_changeset, _}} ->
132 AccountView.render("create-error.json", %{changeset: current_changeset})
136 |> put_status(:conflict)
141 def user_show(conn, %{"nickname" => nickname}) do
142 with %User{} = user <- User.get_cached_by_nickname_or_id(nickname) do
144 |> json(AccountView.render("show.json", %{user: user}))
146 _ -> {:error, :not_found}
150 def list_user_statuses(conn, %{"nickname" => nickname} = params) do
151 godmode = params["godmode"] == "true" || params["godmode"] == true
153 with %User{} = user <- User.get_cached_by_nickname_or_id(nickname) do
154 {_, page_size} = page_params(params)
157 ActivityPub.fetch_user_activities(user, nil, %{
158 "limit" => page_size,
163 |> json(StatusView.render("index.json", %{activities: activities, as: :activity}))
165 _ -> {:error, :not_found}
169 def user_toggle_activation(%{assigns: %{user: admin}} = conn, %{"nickname" => nickname}) do
170 user = User.get_cached_by_nickname(nickname)
172 {:ok, updated_user} = User.deactivate(user, !user.info.deactivated)
174 action = if user.info.deactivated, do: "activate", else: "deactivate"
176 ModerationLog.insert_log(%{
183 |> json(AccountView.render("show.json", %{user: updated_user}))
186 def tag_users(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames, "tags" => tags}) do
187 with {:ok, _} <- User.tag(nicknames, tags) do
188 ModerationLog.insert_log(%{
190 nicknames: nicknames,
195 json_response(conn, :no_content, "")
199 def untag_users(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames, "tags" => tags}) do
200 with {:ok, _} <- User.untag(nicknames, tags) do
201 ModerationLog.insert_log(%{
203 nicknames: nicknames,
208 json_response(conn, :no_content, "")
212 def list_users(conn, params) do
213 {page, page_size} = page_params(params)
214 filters = maybe_parse_filters(params["filters"])
217 query: params["query"],
219 page_size: page_size,
220 tags: params["tags"],
221 name: params["name"],
222 email: params["email"]
225 with {:ok, users, count} <- Search.user(Map.merge(search_params, filters)),
229 AccountView.render("index.json",
237 @filters ~w(local external active deactivated is_admin is_moderator)
239 @spec maybe_parse_filters(String.t()) :: %{required(String.t()) => true} | %{}
240 defp maybe_parse_filters(filters) when is_nil(filters) or filters == "", do: %{}
242 defp maybe_parse_filters(filters) do
245 |> Enum.filter(&Enum.member?(@filters, &1))
246 |> Enum.map(&String.to_atom(&1))
247 |> Enum.into(%{}, &{&1, true})
250 def right_add(%{assigns: %{user: admin}} = conn, %{
251 "permission_group" => permission_group,
252 "nickname" => nickname
254 when permission_group in ["moderator", "admin"] do
255 user = User.get_cached_by_nickname(nickname)
259 |> Map.put("is_" <> permission_group, true)
261 info_cng = User.Info.admin_api_update(user.info, info)
265 |> Ecto.Changeset.change()
266 |> Ecto.Changeset.put_embed(:info, info_cng)
268 ModerationLog.insert_log(%{
272 permission: permission_group
275 {:ok, _user} = User.update_and_set_cache(cng)
280 def right_add(conn, _) do
281 render_error(conn, :not_found, "No such permission_group")
284 def right_get(conn, %{"nickname" => nickname}) do
285 user = User.get_cached_by_nickname(nickname)
289 is_moderator: user.info.is_moderator,
290 is_admin: user.info.is_admin
295 %{assigns: %{user: %User{:nickname => admin_nickname} = admin}} = conn,
297 "permission_group" => permission_group,
298 "nickname" => nickname
301 when permission_group in ["moderator", "admin"] do
302 if admin_nickname == nickname do
303 render_error(conn, :forbidden, "You can't revoke your own admin status.")
305 user = User.get_cached_by_nickname(nickname)
309 |> Map.put("is_" <> permission_group, false)
311 info_cng = User.Info.admin_api_update(user.info, info)
314 Ecto.Changeset.change(user)
315 |> Ecto.Changeset.put_embed(:info, info_cng)
317 {:ok, _user} = User.update_and_set_cache(cng)
319 ModerationLog.insert_log(%{
323 permission: permission_group
330 def right_delete(conn, _) do
331 render_error(conn, :not_found, "No such permission_group")
334 def set_activation_status(%{assigns: %{user: admin}} = conn, %{
335 "nickname" => nickname,
338 with {:ok, status} <- Ecto.Type.cast(:boolean, status),
339 %User{} = user <- User.get_cached_by_nickname(nickname),
340 {:ok, _} <- User.deactivate(user, !status) do
341 action = if(user.info.deactivated, do: "activate", else: "deactivate")
343 ModerationLog.insert_log(%{
349 json_response(conn, :no_content, "")
353 def relay_follow(%{assigns: %{user: admin}} = conn, %{"relay_url" => target}) do
354 with {:ok, _message} <- Relay.follow(target) do
355 ModerationLog.insert_log(%{
356 action: "relay_follow",
370 def relay_unfollow(%{assigns: %{user: admin}} = conn, %{"relay_url" => target}) do
371 with {:ok, _message} <- Relay.unfollow(target) do
372 ModerationLog.insert_log(%{
373 action: "relay_unfollow",
387 @doc "Sends registration invite via email"
388 def email_invite(%{assigns: %{user: user}} = conn, %{"email" => email} = params) do
390 Pleroma.Config.get([:instance, :invites_enabled]) &&
391 !Pleroma.Config.get([:instance, :registrations_open]),
392 {:ok, invite_token} <- UserInviteToken.create_invite(),
394 Pleroma.Emails.UserEmail.user_invitation_email(
400 {:ok, _} <- Pleroma.Emails.Mailer.deliver(email) do
401 json_response(conn, :no_content, "")
405 @doc "Get a account registeration invite token (base64 string)"
406 def get_invite_token(conn, params) do
407 options = params["invite"] || %{}
408 {:ok, invite} = UserInviteToken.create_invite(options)
411 |> json(invite.token)
414 @doc "Get list of created invites"
415 def invites(conn, _params) do
416 invites = UserInviteToken.list_invites()
419 |> json(AccountView.render("invites.json", %{invites: invites}))
422 @doc "Revokes invite by token"
423 def revoke_invite(conn, %{"token" => token}) do
424 with {:ok, invite} <- UserInviteToken.find_by_token(token),
425 {:ok, updated_invite} = UserInviteToken.update_invite(invite, %{used: true}) do
427 |> json(AccountView.render("invite.json", %{invite: updated_invite}))
429 nil -> {:error, :not_found}
433 @doc "Get a password reset token (base64 string) for given nickname"
434 def get_password_reset(conn, %{"nickname" => nickname}) do
435 (%User{local: true} = user) = User.get_cached_by_nickname(nickname)
436 {:ok, token} = Pleroma.PasswordResetToken.create_token(user)
441 link: Router.Helpers.reset_password_url(Endpoint, :reset, token.token)
445 def list_reports(conn, params) do
448 |> Map.put("type", "Flag")
449 |> Map.put("skip_preload", true)
450 |> Map.put("total", true)
452 reports = ActivityPub.fetch_activities([], params)
455 |> put_view(ReportView)
456 |> render("index.json", %{reports: reports})
459 def report_show(conn, %{"id" => id}) do
460 with %Activity{} = report <- Activity.get_by_id(id) do
462 |> put_view(ReportView)
463 |> render("show.json", %{report: report})
465 _ -> {:error, :not_found}
469 def report_update_state(%{assigns: %{user: admin}} = conn, %{"id" => id, "state" => state}) do
470 with {:ok, report} <- CommonAPI.update_report_state(id, state) do
471 ModerationLog.insert_log(%{
472 action: "report_update",
478 |> put_view(ReportView)
479 |> render("show.json", %{report: report})
483 def report_respond(%{assigns: %{user: user}} = conn, %{"id" => id} = params) do
484 with false <- is_nil(params["status"]),
485 %Activity{} <- Activity.get_by_id(id) do
488 |> Map.put("in_reply_to_status_id", id)
489 |> Map.put("visibility", "direct")
491 {:ok, activity} = CommonAPI.post(user, params)
493 ModerationLog.insert_log(%{
494 action: "report_response",
497 text: params["status"]
501 |> put_view(StatusView)
502 |> render("status.json", %{activity: activity})
512 def status_update(%{assigns: %{user: admin}} = conn, %{"id" => id} = params) do
513 with {:ok, activity} <- CommonAPI.update_activity_scope(id, params) do
514 {:ok, sensitive} = Ecto.Type.cast(:boolean, params["sensitive"])
516 ModerationLog.insert_log(%{
517 action: "status_update",
520 sensitive: sensitive,
521 visibility: params["visibility"]
525 |> put_view(StatusView)
526 |> render("status.json", %{activity: activity})
530 def status_delete(%{assigns: %{user: user}} = conn, %{"id" => id}) do
531 with {:ok, %Activity{}} <- CommonAPI.delete(id, user) do
532 ModerationLog.insert_log(%{
533 action: "status_delete",
542 def list_log(conn, params) do
543 {page, page_size} = page_params(params)
545 log = ModerationLog.get_all(page, page_size)
548 |> put_view(ModerationLogView)
549 |> render("index.json", %{log: log})
552 def migrate_to_db(conn, _params) do
553 Mix.Tasks.Pleroma.Config.run(["migrate_to_db"])
557 def migrate_from_db(conn, _params) do
558 Mix.Tasks.Pleroma.Config.run(["migrate_from_db", Pleroma.Config.get(:env), "true"])
562 def config_show(conn, _params) do
563 configs = Pleroma.Repo.all(Config)
566 |> put_view(ConfigView)
567 |> render("index.json", %{configs: configs})
570 def config_update(conn, %{"configs" => configs}) do
572 if Pleroma.Config.get([:instance, :dynamic_configuration]) do
575 %{"group" => group, "key" => key, "delete" => "true"} = params ->
576 {:ok, config} = Config.delete(%{group: group, key: key, subkeys: params["subkeys"]})
579 %{"group" => group, "key" => key, "value" => value} ->
580 {:ok, config} = Config.update_or_create(%{group: group, key: key, value: value})
583 |> Enum.reject(&is_nil(&1))
585 Pleroma.Config.TransferTask.load_and_update_env()
586 Mix.Tasks.Pleroma.Config.run(["migrate_from_db", Pleroma.Config.get(:env), "false"])
593 |> put_view(ConfigView)
594 |> render("index.json", %{configs: updated})
597 def errors(conn, {:error, :not_found}) do
599 |> put_status(:not_found)
600 |> json(dgettext("errors", "Not found"))
603 def errors(conn, {:error, reason}) do
605 |> put_status(:bad_request)
609 def errors(conn, {:param_cast, _}) do
611 |> put_status(:bad_request)
612 |> json(dgettext("errors", "Invalid parameters"))
615 def errors(conn, _) do
617 |> put_status(:internal_server_error)
618 |> json(dgettext("errors", "Something went wrong"))
621 defp page_params(params) do
622 {get_page(params["page"]), get_page_size(params["page_size"])}
625 defp get_page(page_string) when is_nil(page_string), do: 1
627 defp get_page(page_string) do
628 case Integer.parse(page_string) do
634 defp get_page_size(page_size_string) when is_nil(page_size_string), do: @users_page_size
636 defp get_page_size(page_size_string) do
637 case Integer.parse(page_size_string) do
638 {page_size, _} -> page_size
639 :error -> @users_page_size