X-Git-Url: http://git.squeep.com/?a=blobdiff_plain;f=lib%2Fpleroma%2Fweb%2Fadmin_api%2Fadmin_api_controller.ex;h=4421b30c8d533332053b87f31781e60eba49bc8a;hb=f89728ad95ff317b68cd5bfe1d35e98e94a9f42d;hp=c436715d5d67227116f16743011c508223819ef3;hpb=0cd4b6024d043efb096a353d6bd84ea0aeb74a6f;p=akkoma diff --git a/lib/pleroma/web/admin_api/admin_api_controller.ex b/lib/pleroma/web/admin_api/admin_api_controller.ex index c436715d5..4421b30c8 100644 --- a/lib/pleroma/web/admin_api/admin_api_controller.ex +++ b/lib/pleroma/web/admin_api/admin_api_controller.ex @@ -4,11 +4,20 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do use Pleroma.Web, :controller + alias Pleroma.Activity + alias Pleroma.ModerationLog alias Pleroma.User alias Pleroma.UserInviteToken + alias Pleroma.Web.ActivityPub.ActivityPub alias Pleroma.Web.ActivityPub.Relay alias Pleroma.Web.AdminAPI.AccountView + alias Pleroma.Web.AdminAPI.Config + alias Pleroma.Web.AdminAPI.ConfigView + alias Pleroma.Web.AdminAPI.ModerationLogView + alias Pleroma.Web.AdminAPI.ReportView alias Pleroma.Web.AdminAPI.Search + alias Pleroma.Web.CommonAPI + alias Pleroma.Web.MastodonAPI.StatusView import Pleroma.Web.ControllerHelper, only: [json_response: 3] @@ -18,56 +27,117 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do action_fallback(:errors) - def user_delete(conn, %{"nickname" => nickname}) do - User.get_by_nickname(nickname) - |> User.delete() + def user_delete(%{assigns: %{user: admin}} = conn, %{"nickname" => nickname}) do + user = User.get_cached_by_nickname(nickname) + User.delete(user) + + ModerationLog.insert_log(%{ + actor: admin, + subject: user, + action: "delete" + }) conn |> json(nickname) end - def user_follow(conn, %{"follower" => follower_nick, "followed" => followed_nick}) do - with %User{} = follower <- User.get_by_nickname(follower_nick), - %User{} = followed <- User.get_by_nickname(followed_nick) do + 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(conn, %{"follower" => follower_nick, "followed" => followed_nick}) do - with %User{} = follower <- User.get_by_nickname(follower_nick), - %User{} = followed <- User.get_by_nickname(followed_nick) do + 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 user_create( - conn, - %{"nickname" => nickname, "email" => email, "password" => password} - ) do - user_data = %{ - nickname: nickname, - name: nickname, - email: email, - password: password, - password_confirmation: password, - bio: "." - } + 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: "." + } - changeset = User.register_changeset(%User{}, user_data, confirmed: true) - {:ok, user} = User.register(changeset) + 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(user.nickname) + 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_by_nickname(nickname) do + with %User{} = user <- User.get_cached_by_nickname_or_id(nickname) do conn |> json(AccountView.render("show.json", %{user: user})) else @@ -75,23 +145,66 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do end end - def user_toggle_activation(conn, %{"nickname" => nickname}) do - user = User.get_by_nickname(nickname) + def list_user_statuses(conn, %{"nickname" => nickname} = params) do + godmode = params["godmode"] == "true" || params["godmode"] == true + + with %User{} = user <- User.get_cached_by_nickname_or_id(nickname) do + {_, page_size} = page_params(params) + + activities = + ActivityPub.fetch_user_activities(user, nil, %{ + "limit" => page_size, + "godmode" => godmode + }) + + conn + |> json(StatusView.render("index.json", %{activities: activities, 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.info.deactivated) + action = if user.info.deactivated, do: "activate", else: "deactivate" + + ModerationLog.insert_log(%{ + actor: admin, + subject: user, + action: action + }) + conn |> json(AccountView.render("show.json", %{user: updated_user})) end - def tag_users(conn, %{"nicknames" => nicknames, "tags" => tags}) do - with {:ok, _} <- User.tag(nicknames, tags), - do: json_response(conn, :no_content, "") + def tag_users(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames, "tags" => tags}) do + with {:ok, _} <- User.tag(nicknames, tags) do + ModerationLog.insert_log(%{ + actor: admin, + nicknames: nicknames, + tags: tags, + action: "tag" + }) + + json_response(conn, :no_content, "") + end end - def untag_users(conn, %{"nicknames" => nicknames, "tags" => tags}) do - with {:ok, _} <- User.untag(nicknames, tags), - do: json_response(conn, :no_content, "") + def untag_users(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames, "tags" => tags}) do + with {:ok, _} <- User.untag(nicknames, tags) do + ModerationLog.insert_log(%{ + actor: admin, + nicknames: nicknames, + tags: tags, + action: "untag" + }) + + json_response(conn, :no_content, "") + end end def list_users(conn, params) do @@ -101,7 +214,10 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do search_params = %{ query: params["query"], page: page, - page_size: page_size + page_size: page_size, + tags: params["tags"], + name: params["name"], + email: params["email"] } with {:ok, users, count} <- Search.user(Map.merge(search_params, filters)), @@ -116,11 +232,11 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do ) end - @filters ~w(local external active deactivated) + @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: %{} - @spec maybe_parse_filters(String.t()) :: %{required(String.t()) => true} | %{} defp maybe_parse_filters(filters) do filters |> String.split(",") @@ -129,9 +245,12 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do |> Enum.into(%{}, &{&1, true}) end - def right_add(conn, %{"permission_group" => permission_group, "nickname" => nickname}) + def right_add(%{assigns: %{user: admin}} = conn, %{ + "permission_group" => permission_group, + "nickname" => nickname + }) when permission_group in ["moderator", "admin"] do - user = User.get_by_nickname(nickname) + user = User.get_cached_by_nickname(nickname) info = %{} @@ -144,19 +263,24 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do |> Ecto.Changeset.change() |> Ecto.Changeset.put_embed(:info, info_cng) + ModerationLog.insert_log(%{ + action: "grant", + actor: admin, + subject: user, + permission: permission_group + }) + {:ok, _user} = User.update_and_set_cache(cng) json(conn, info) end def right_add(conn, _) do - conn - |> put_status(404) - |> json(%{error: "No such permission_group"}) + render_error(conn, :not_found, "No such permission_group") end def right_get(conn, %{"nickname" => nickname}) do - user = User.get_by_nickname(nickname) + user = User.get_cached_by_nickname(nickname) conn |> json(%{ @@ -166,7 +290,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do end def right_delete( - %{assigns: %{user: %User{:nickname => admin_nickname}}} = conn, + %{assigns: %{user: %User{:nickname => admin_nickname} = admin}} = conn, %{ "permission_group" => permission_group, "nickname" => nickname @@ -174,11 +298,9 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do ) when permission_group in ["moderator", "admin"] do if admin_nickname == nickname do - conn - |> put_status(403) - |> json(%{error: "You can't revoke your own admin status."}) + render_error(conn, :forbidden, "You can't revoke your own admin status.") else - user = User.get_by_nickname(nickname) + user = User.get_cached_by_nickname(nickname) info = %{} @@ -192,25 +314,48 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do {:ok, _user} = User.update_and_set_cache(cng) + ModerationLog.insert_log(%{ + action: "revoke", + actor: admin, + subject: user, + permission: permission_group + }) + json(conn, info) end end def right_delete(conn, _) do - conn - |> put_status(404) - |> json(%{error: "No such permission_group"}) + render_error(conn, :not_found, "No such permission_group") end - def set_activation_status(conn, %{"nickname" => nickname, "status" => status}) do + def set_activation_status(%{assigns: %{user: admin}} = conn, %{ + "nickname" => nickname, + "status" => status + }) do with {:ok, status} <- Ecto.Type.cast(:boolean, status), - %User{} = user <- User.get_by_nickname(nickname), - {:ok, _} <- User.deactivate(user, !status), - do: json_response(conn, :no_content, "") + %User{} = user <- User.get_cached_by_nickname(nickname), + {:ok, _} <- User.deactivate(user, !status) do + action = if(user.info.deactivated, do: "activate", else: "deactivate") + + ModerationLog.insert_log(%{ + actor: admin, + subject: user, + action: action + }) + + json_response(conn, :no_content, "") + end end - def relay_follow(conn, %{"relay_url" => target}) do + 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 _ -> @@ -220,8 +365,14 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do end end - def relay_unfollow(conn, %{"relay_url" => target}) do + 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 _ -> @@ -268,38 +419,203 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do @doc "Revokes invite by token" def revoke_invite(conn, %{"token" => token}) do - invite = UserInviteToken.find_by_token!(token) - {:ok, updated_invite} = UserInviteToken.update_invite(invite, %{used: true}) - - conn - |> json(AccountView.render("invite.json", %{invite: updated_invite})) + with {:ok, invite} <- UserInviteToken.find_by_token(token), + {:ok, updated_invite} = UserInviteToken.update_invite(invite, %{used: true}) do + conn + |> json(AccountView.render("invite.json", %{invite: updated_invite})) + else + nil -> {:error, :not_found} + 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_by_nickname(nickname) + (%User{local: true} = user) = User.get_cached_by_nickname(nickname) {:ok, token} = Pleroma.PasswordResetToken.create_token(user) + host = Pleroma.Config.get([Pleroma.Web.Endpoint, :url, :host]) + protocol = Pleroma.Config.get([Pleroma.Web.Endpoint, :protocol]) + + conn + |> json(%{ + token: token.token, + link: "#{protocol}://#{host}/api/pleroma/password_reset/#{token}" + }) + end + + def list_reports(conn, params) do + params = + params + |> Map.put("type", "Flag") + |> Map.put("skip_preload", true) + |> Map.put("total", true) + + reports = ActivityPub.fetch_activities([], params) + + conn + |> put_view(ReportView) + |> render("index.json", %{reports: reports}) + end + + def report_show(conn, %{"id" => id}) do + with %Activity{} = report <- Activity.get_by_id(id) do + conn + |> put_view(ReportView) + |> render("show.json", %{report: report}) + else + _ -> {:error, :not_found} + end + end + + def report_update_state(%{assigns: %{user: admin}} = conn, %{"id" => id, "state" => state}) do + with {:ok, report} <- CommonAPI.update_report_state(id, state) do + ModerationLog.insert_log(%{ + action: "report_update", + actor: admin, + subject: report + }) + + conn + |> put_view(ReportView) + |> render("show.json", %{report: report}) + end + end + + def report_respond(%{assigns: %{user: user}} = conn, %{"id" => id} = params) do + with false <- is_nil(params["status"]), + %Activity{} <- Activity.get_by_id(id) do + params = + params + |> Map.put("in_reply_to_status_id", id) + |> Map.put("visibility", "direct") + + {:ok, activity} = CommonAPI.post(user, params) + + ModerationLog.insert_log(%{ + action: "report_response", + actor: user, + subject: activity, + text: params["status"] + }) + + conn + |> put_view(StatusView) + |> render("status.json", %{activity: activity}) + else + true -> + {:param_cast, nil} + + nil -> + {:error, :not_found} + end + end + + def status_update(%{assigns: %{user: admin}} = conn, %{"id" => id} = params) do + with {:ok, activity} <- CommonAPI.update_activity_scope(id, params) do + {:ok, sensitive} = Ecto.Type.cast(:boolean, params["sensitive"]) + + ModerationLog.insert_log(%{ + action: "status_update", + actor: admin, + subject: activity, + sensitive: sensitive, + visibility: params["visibility"] + }) + + conn + |> put_view(StatusView) + |> render("status.json", %{activity: activity}) + end + end + + def status_delete(%{assigns: %{user: user}} = conn, %{"id" => id}) do + with {:ok, %Activity{}} <- CommonAPI.delete(id, user) do + ModerationLog.insert_log(%{ + action: "status_delete", + actor: user, + subject_id: id + }) + + json(conn, %{}) + end + end + + def list_log(conn, params) do + {page, page_size} = page_params(params) + + log = ModerationLog.get_all(page, page_size) conn - |> json(token.token) + |> put_view(ModerationLogView) + |> render("index.json", %{log: log}) + end + + def migrate_to_db(conn, _params) do + Mix.Tasks.Pleroma.Config.run(["migrate_to_db"]) + json(conn, %{}) + end + + def migrate_from_db(conn, _params) do + Mix.Tasks.Pleroma.Config.run(["migrate_from_db", Pleroma.Config.get(:env), "true"]) + json(conn, %{}) + end + + def config_show(conn, _params) do + configs = Pleroma.Repo.all(Config) + + conn + |> put_view(ConfigView) + |> render("index.json", %{configs: configs}) + end + + def config_update(conn, %{"configs" => configs}) do + updated = + if Pleroma.Config.get([:instance, :dynamic_configuration]) do + updated = + Enum.map(configs, fn + %{"group" => group, "key" => key, "delete" => "true"} = params -> + {:ok, config} = Config.delete(%{group: group, key: key, subkeys: params["subkeys"]}) + config + + %{"group" => group, "key" => key, "value" => value} -> + {:ok, config} = Config.update_or_create(%{group: group, key: key, value: value}) + config + end) + |> Enum.reject(&is_nil(&1)) + + Pleroma.Config.TransferTask.load_and_update_env() + Mix.Tasks.Pleroma.Config.run(["migrate_from_db", Pleroma.Config.get(:env), "false"]) + updated + else + [] + end + + conn + |> put_view(ConfigView) + |> render("index.json", %{configs: updated}) end def errors(conn, {:error, :not_found}) do conn - |> put_status(404) - |> json("Not found") + |> put_status(:not_found) + |> json(dgettext("errors", "Not found")) + end + + def errors(conn, {:error, reason}) do + conn + |> put_status(:bad_request) + |> json(reason) end def errors(conn, {:param_cast, _}) do conn - |> put_status(400) - |> json("Invalid parameters") + |> put_status(:bad_request) + |> json(dgettext("errors", "Invalid parameters")) end def errors(conn, _) do conn - |> put_status(500) - |> json("Something went wrong") + |> put_status(:internal_server_error) + |> json(dgettext("errors", "Something went wrong")) end defp page_params(params) do