X-Git-Url: http://git.squeep.com/?a=blobdiff_plain;f=lib%2Fpleroma%2Fweb%2Fadmin_api%2Fcontrollers%2Fadmin_api_controller.ex;h=db2413dfe6015c6b916bc2bb862e2d8e035d2874;hb=a61207cf8f218b34998ab8eec0db6b7f59b3ba8d;hp=467d05375e3e98c53510f5c492bfdddb69b22d07;hpb=d6693a91028ab3ee7865f530f3ad2532542fc2d2;p=akkoma diff --git a/lib/pleroma/web/admin_api/controllers/admin_api_controller.ex b/lib/pleroma/web/admin_api/controllers/admin_api_controller.ex index 467d05375..50aa294f0 100644 --- a/lib/pleroma/web/admin_api/controllers/admin_api_controller.ex +++ b/lib/pleroma/web/admin_api/controllers/admin_api_controller.ex @@ -1,53 +1,40 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2020 Pleroma Authors +# Copyright © 2017-2021 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.AdminAPI.AdminAPIController do use Pleroma.Web, :controller - import Pleroma.Web.ControllerHelper, only: [json_response: 3] + import Pleroma.Web.ControllerHelper, + only: [json_response: 3, fetch_integer_param: 3] alias Pleroma.Config - alias Pleroma.ConfigDB alias Pleroma.MFA alias Pleroma.ModerationLog - alias Pleroma.Plugs.OAuthScopesPlug alias Pleroma.Stats alias Pleroma.User alias Pleroma.Web.ActivityPub.ActivityPub - alias Pleroma.Web.ActivityPub.Builder - alias Pleroma.Web.ActivityPub.Pipeline - alias Pleroma.Web.ActivityPub.Relay alias Pleroma.Web.AdminAPI alias Pleroma.Web.AdminAPI.AccountView - alias Pleroma.Web.AdminAPI.ConfigView alias Pleroma.Web.AdminAPI.ModerationLogView - alias Pleroma.Web.AdminAPI.Search alias Pleroma.Web.Endpoint + alias Pleroma.Web.Plugs.OAuthScopesPlug alias Pleroma.Web.Router - require Logger - - @descriptions Pleroma.Docs.JSON.compile() @users_page_size 50 plug( OAuthScopesPlug, - %{scopes: ["read:accounts"], admin: true} - when action in [:list_users, :user_show, :right_get, :show_user_credentials] + %{scopes: ["admin:read:accounts"]} + when action in [:right_get, :show_user_credentials, :create_backup] ) plug( OAuthScopesPlug, - %{scopes: ["write:accounts"], admin: true} + %{scopes: ["admin:write:accounts"]} when action in [ :get_password_reset, :force_password_reset, - :user_delete, - :users_create, - :user_toggle_activation, - :user_activate, - :user_deactivate, :tag_users, :untag_users, :right_add, @@ -61,35 +48,31 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do plug( OAuthScopesPlug, - %{scopes: ["write:follows"], admin: true} - when action in [:user_follow, :user_unfollow, :relay_follow, :relay_unfollow] + %{scopes: ["admin:read:statuses"]} + when action in [:list_user_statuses] ) plug( OAuthScopesPlug, - %{scopes: ["read:statuses"], admin: true} - when action in [:list_user_statuses, :list_instance_statuses] + %{scopes: ["admin:read:chats"]} + when action in [:list_user_chats] ) plug( OAuthScopesPlug, - %{scopes: ["read"], admin: true} + %{scopes: ["admin:read"]} when action in [ - :config_show, :list_log, :stats, - :relay_list, - :config_descriptions, :need_reboot ] ) plug( OAuthScopesPlug, - %{scopes: ["write"], admin: true} + %{scopes: ["admin:write"]} when action in [ :restart, - :config_update, :resend_confirmation_email, :confirm_email, :reload_emoji @@ -98,221 +81,43 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do action_fallback(AdminAPI.FallbackController) - def user_delete(conn, %{"nickname" => nickname}) do - user_delete(conn, %{"nicknames" => [nickname]}) - end - - def user_delete(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do - users = - nicknames - |> Enum.map(&User.get_cached_by_nickname/1) - - users - |> Enum.each(fn user -> - {:ok, delete_data, _} = Builder.delete(admin, user.ap_id) - Pipeline.common_pipeline(delete_data, local: true) - end) - - ModerationLog.insert_log(%{ - actor: admin, - subject: users, - action: "delete" - }) - - conn - |> json(nicknames) - end - - def user_follow(%{assigns: %{user: admin}} = conn, %{ - "follower" => follower_nick, - "followed" => followed_nick - }) do - with %User{} = follower <- User.get_cached_by_nickname(follower_nick), - %User{} = followed <- User.get_cached_by_nickname(followed_nick) do - User.follow(follower, followed) - - ModerationLog.insert_log(%{ - actor: admin, - followed: followed, - follower: follower, - action: "follow" - }) - end - - conn - |> json("ok") - end - - def user_unfollow(%{assigns: %{user: admin}} = conn, %{ - "follower" => follower_nick, - "followed" => followed_nick - }) do - with %User{} = follower <- User.get_cached_by_nickname(follower_nick), - %User{} = followed <- User.get_cached_by_nickname(followed_nick) do - User.unfollow(follower, followed) - - ModerationLog.insert_log(%{ - actor: admin, - followed: followed, - follower: follower, - action: "unfollow" - }) - end - - conn - |> json("ok") - end - - def users_create(%{assigns: %{user: admin}} = conn, %{"users" => users}) do - changesets = - Enum.map(users, fn %{"nickname" => nickname, "email" => email, "password" => password} -> - user_data = %{ - nickname: nickname, - name: nickname, - email: email, - password: password, - password_confirmation: password, - bio: "." - } - - User.register_changeset(%User{}, user_data, need_confirmation: false) - end) - |> Enum.reduce(Ecto.Multi.new(), fn changeset, multi -> - Ecto.Multi.insert(multi, Ecto.UUID.generate(), changeset) - end) - - case Pleroma.Repo.transaction(changesets) do - {:ok, users} -> - res = - users - |> Map.values() - |> Enum.map(fn user -> - {:ok, user} = User.post_register_action(user) - - user - end) - |> Enum.map(&AccountView.render("created.json", %{user: &1})) - - ModerationLog.insert_log(%{ - actor: admin, - subjects: Map.values(users), - action: "create" - }) - - conn - |> json(res) - - {:error, id, changeset, _} -> - res = - Enum.map(changesets.operations, fn - {current_id, {:changeset, _current_changeset, _}} when current_id == id -> - AccountView.render("create-error.json", %{changeset: changeset}) - - {_, {:changeset, current_changeset, _}} -> - AccountView.render("create-error.json", %{changeset: current_changeset}) - end) - - conn - |> put_status(:conflict) - |> json(res) - end - end - - def user_show(conn, %{"nickname" => nickname}) do - with %User{} = user <- User.get_cached_by_nickname_or_id(nickname) do - conn - |> put_view(AccountView) - |> render("show.json", %{user: user}) - else - _ -> {:error, :not_found} - end - end - - def list_instance_statuses(conn, %{"instance" => instance} = params) do - with_reblogs = params["with_reblogs"] == "true" || params["with_reblogs"] == true - {page, page_size} = page_params(params) - - activities = - ActivityPub.fetch_statuses(nil, %{ - "instance" => instance, - "limit" => page_size, - "offset" => (page - 1) * page_size, - "exclude_reblogs" => !with_reblogs && "true" - }) - - conn - |> put_view(AdminAPI.StatusView) - |> render("index.json", %{activities: activities, as: :activity}) - end - - def list_user_statuses(conn, %{"nickname" => nickname} = params) do + def list_user_statuses(%{assigns: %{user: admin}} = conn, %{"nickname" => nickname} = params) do with_reblogs = params["with_reblogs"] == "true" || params["with_reblogs"] == true godmode = params["godmode"] == "true" || params["godmode"] == true - with %User{} = user <- User.get_cached_by_nickname_or_id(nickname) do - {_, page_size} = page_params(params) + with %User{} = user <- User.get_cached_by_nickname_or_id(nickname, for: admin) do + {page, page_size} = page_params(params) - activities = + result = ActivityPub.fetch_user_activities(user, nil, %{ - "limit" => page_size, - "godmode" => godmode, - "exclude_reblogs" => !with_reblogs && "true" + limit: page_size, + offset: (page - 1) * page_size, + godmode: godmode, + exclude_reblogs: not with_reblogs, + pagination_type: :offset, + total: true }) conn |> put_view(AdminAPI.StatusView) - |> render("index.json", %{activities: activities, as: :activity}) + |> render("index.json", %{total: result[:total], activities: result[:items], as: :activity}) else _ -> {:error, :not_found} end end - def user_toggle_activation(%{assigns: %{user: admin}} = conn, %{"nickname" => nickname}) do - user = User.get_cached_by_nickname(nickname) - - {:ok, updated_user} = User.deactivate(user, !user.deactivated) - - action = if user.deactivated, do: "activate", else: "deactivate" - - ModerationLog.insert_log(%{ - actor: admin, - subject: [user], - action: action - }) + def list_user_chats(%{assigns: %{user: admin}} = conn, %{"nickname" => nickname} = _params) do + with %User{id: user_id} <- User.get_cached_by_nickname_or_id(nickname, for: admin) do + chats = + Pleroma.Chat.for_user_query(user_id) + |> Pleroma.Repo.all() - conn - |> put_view(AccountView) - |> render("show.json", %{user: updated_user}) - end - - def user_activate(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do - users = Enum.map(nicknames, &User.get_cached_by_nickname/1) - {:ok, updated_users} = User.deactivate(users, false) - - ModerationLog.insert_log(%{ - actor: admin, - subject: users, - action: "activate" - }) - - conn - |> put_view(AccountView) - |> render("index.json", %{users: Keyword.values(updated_users)}) - end - - def user_deactivate(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do - users = Enum.map(nicknames, &User.get_cached_by_nickname/1) - {:ok, updated_users} = User.deactivate(users, true) - - ModerationLog.insert_log(%{ - actor: admin, - subject: users, - action: "deactivate" - }) - - conn - |> put_view(AccountView) - |> render("index.json", %{users: Keyword.values(updated_users)}) + conn + |> put_view(AdminAPI.ChatView) + |> render("index.json", chats: chats) + else + _ -> {:error, :not_found} + end end def tag_users(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames, "tags" => tags}) do @@ -341,40 +146,6 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do end end - def list_users(conn, params) do - {page, page_size} = page_params(params) - filters = maybe_parse_filters(params["filters"]) - - search_params = %{ - query: params["query"], - page: page, - page_size: page_size, - tags: params["tags"], - name: params["name"], - email: params["email"] - } - - with {:ok, users, count} <- Search.user(Map.merge(search_params, filters)) do - json( - conn, - AccountView.render("index.json", users: users, count: count, page_size: page_size) - ) - end - end - - @filters ~w(local external active deactivated is_admin is_moderator) - - @spec maybe_parse_filters(String.t()) :: %{required(String.t()) => true} | %{} - defp maybe_parse_filters(filters) when is_nil(filters) or filters == "", do: %{} - - defp maybe_parse_filters(filters) do - filters - |> String.split(",") - |> Enum.filter(&Enum.member?(@filters, &1)) - |> Enum.map(&String.to_atom(&1)) - |> Enum.into(%{}, &{&1, true}) - end - def right_add_multiple(%{assigns: %{user: admin}} = conn, %{ "permission_group" => permission_group, "nicknames" => nicknames @@ -497,50 +268,6 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do render_error(conn, :forbidden, "You can't revoke your own admin status.") end - def relay_list(conn, _params) do - with {:ok, list} <- Relay.list() do - json(conn, %{relays: list}) - else - _ -> - conn - |> put_status(500) - end - end - - def relay_follow(%{assigns: %{user: admin}} = conn, %{"relay_url" => target}) do - with {:ok, _message} <- Relay.follow(target) do - ModerationLog.insert_log(%{ - action: "relay_follow", - actor: admin, - target: target - }) - - json(conn, target) - else - _ -> - conn - |> put_status(500) - |> json(target) - end - end - - def relay_unfollow(%{assigns: %{user: admin}} = conn, %{"relay_url" => target}) do - with {:ok, _message} <- Relay.unfollow(target) do - ModerationLog.insert_log(%{ - action: "relay_unfollow", - actor: admin, - target: target - }) - - json(conn, target) - else - _ -> - conn - |> put_status(500) - |> json(target) - end - end - @doc "Get a password reset token (base64 string) for given nickname" def get_password_reset(conn, %{"nickname" => nickname}) do (%User{local: true} = user) = User.get_cached_by_nickname(nickname) @@ -582,7 +309,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do @doc "Show a given user's credentials" def show_user_credentials(%{assigns: %{user: admin}} = conn, %{"nickname" => nickname}) do - with %User{} = user <- User.get_cached_by_nickname_or_id(nickname) do + with %User{} = user <- User.get_cached_by_nickname_or_id(nickname, for: admin) do conn |> put_view(AccountView) |> render("credentials.json", %{user: user, for: admin}) @@ -620,10 +347,10 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do {:error, changeset} -> errors = Map.new(changeset.errors, fn {key, {error, _}} -> {key, error} end) - json(conn, %{errors: errors}) + {:errors, errors} _ -> - json(conn, %{error: "Unable to update user."}) + {:error, :not_found} end end @@ -645,105 +372,6 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do |> render("index.json", %{log: log}) end - def config_descriptions(conn, _params) do - descriptions = Enum.filter(@descriptions, &whitelisted_config?/1) - - json(conn, descriptions) - end - - def config_show(conn, %{"only_db" => true}) do - with :ok <- configurable_from_database() do - configs = Pleroma.Repo.all(ConfigDB) - - conn - |> put_view(ConfigView) - |> render("index.json", %{configs: configs}) - end - end - - def config_show(conn, _params) do - with :ok <- configurable_from_database() do - configs = ConfigDB.get_all_as_keyword() - - merged = - Config.Holder.default_config() - |> ConfigDB.merge(configs) - |> Enum.map(fn {group, values} -> - Enum.map(values, fn {key, value} -> - db = - if configs[group][key] do - ConfigDB.get_db_keys(configs[group][key], key) - end - - db_value = configs[group][key] - - merged_value = - if !is_nil(db_value) and Keyword.keyword?(db_value) and - ConfigDB.sub_key_full_update?(group, key, Keyword.keys(db_value)) do - ConfigDB.merge_group(group, key, value, db_value) - else - value - end - - setting = %{ - group: ConfigDB.convert(group), - key: ConfigDB.convert(key), - value: ConfigDB.convert(merged_value) - } - - if db, do: Map.put(setting, :db, db), else: setting - end) - end) - |> List.flatten() - - json(conn, %{configs: merged, need_reboot: Restarter.Pleroma.need_reboot?()}) - end - end - - def config_update(conn, %{"configs" => configs}) do - with :ok <- configurable_from_database() do - {_errors, results} = - configs - |> Enum.filter(&whitelisted_config?/1) - |> Enum.map(fn - %{"group" => group, "key" => key, "delete" => true} = params -> - ConfigDB.delete(%{group: group, key: key, subkeys: params["subkeys"]}) - - %{"group" => group, "key" => key, "value" => value} -> - ConfigDB.update_or_create(%{group: group, key: key, value: value}) - end) - |> Enum.split_with(fn result -> elem(result, 0) == :error end) - - {deleted, updated} = - results - |> Enum.map(fn {:ok, config} -> - Map.put(config, :db, ConfigDB.get_db_keys(config)) - end) - |> Enum.split_with(fn config -> - Ecto.get_meta(config, :state) == :deleted - end) - - Config.TransferTask.load_and_update_env(deleted, false) - - if !Restarter.Pleroma.need_reboot?() do - changed_reboot_settings? = - (updated ++ deleted) - |> Enum.any?(fn config -> - group = ConfigDB.from_string(config.group) - key = ConfigDB.from_string(config.key) - value = ConfigDB.from_binary(config.value) - Config.TransferTask.pleroma_need_restart?(group, key, value) - end) - - if changed_reboot_settings?, do: Restarter.Pleroma.need_reboot() - end - - conn - |> put_view(ConfigView) - |> render("index.json", %{configs: updated, need_reboot: Restarter.Pleroma.need_reboot?()}) - end - end - def restart(conn, _params) do with :ok <- configurable_from_database() do Restarter.Pleroma.restart(Config.get(:env), 50) @@ -760,92 +388,58 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do if Config.get(:configurable_from_database) do :ok else - {:error, "To use this endpoint you need to enable configuration from database."} + {:error, "You must enable configurable_from_database in your config file."} end end - defp whitelisted_config?(group, key) do - if whitelisted_configs = Config.get(:database_config_whitelist) do - Enum.any?(whitelisted_configs, fn - {whitelisted_group} -> - group == inspect(whitelisted_group) - - {whitelisted_group, whitelisted_key} -> - group == inspect(whitelisted_group) && key == inspect(whitelisted_key) - end) - else - true - end - end - - defp whitelisted_config?(%{"group" => group, "key" => key}) do - whitelisted_config?(group, key) - end - - defp whitelisted_config?(%{:group => group} = config) do - whitelisted_config?(group, config[:key]) - end - def reload_emoji(conn, _params) do Pleroma.Emoji.reload() - conn |> json("ok") + json(conn, "ok") end def confirm_email(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do - users = nicknames |> Enum.map(&User.get_cached_by_nickname/1) + users = Enum.map(nicknames, &User.get_cached_by_nickname/1) - User.toggle_confirmation(users) + User.confirm(users) - ModerationLog.insert_log(%{ - actor: admin, - subject: users, - action: "confirm_email" - }) + ModerationLog.insert_log(%{actor: admin, subject: users, action: "confirm_email"}) - conn |> json("") + json(conn, "") end def resend_confirmation_email(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do - users = nicknames |> Enum.map(&User.get_cached_by_nickname/1) - - User.try_send_confirmation_email(users) + users = + Enum.map(nicknames, fn nickname -> + nickname + |> User.get_cached_by_nickname() + |> User.send_confirmation_email() + end) - ModerationLog.insert_log(%{ - actor: admin, - subject: users, - action: "resend_confirmation_email" - }) + ModerationLog.insert_log(%{actor: admin, subject: users, action: "resend_confirmation_email"}) - conn |> json("") + json(conn, "") end - def stats(conn, _) do - count = Stats.get_status_visibility_count() + def stats(conn, params) do + counters = Stats.get_status_visibility_count(params["instance"]) - conn - |> json(%{"status_visibility" => count}) + json(conn, %{"status_visibility" => counters}) end - defp page_params(params) do - {get_page(params["page"]), get_page_size(params["page_size"])} - end + def create_backup(%{assigns: %{user: admin}} = conn, %{"nickname" => nickname}) do + with %User{} = user <- User.get_by_nickname(nickname), + {:ok, _} <- Pleroma.User.Backup.create(user, admin.id) do + ModerationLog.insert_log(%{actor: admin, subject: user, action: "create_backup"}) - defp get_page(page_string) when is_nil(page_string), do: 1 - - defp get_page(page_string) do - case Integer.parse(page_string) do - {page, _} -> page - :error -> 1 + json(conn, "") end end - defp get_page_size(page_size_string) when is_nil(page_size_string), do: @users_page_size - - defp get_page_size(page_size_string) do - case Integer.parse(page_size_string) do - {page_size, _} -> page_size - :error -> @users_page_size - end + defp page_params(params) do + { + fetch_integer_param(params, "page", 1), + fetch_integer_param(params, "page_size", @users_page_size) + } end end