From 7c22c9afb410668d87dcd4a90651d62d9a1e9e4d Mon Sep 17 00:00:00 2001
From: Egor Kislitsyn <egor@kislitsyn.com>
Date: Fri, 18 Sep 2020 22:18:34 +0400
Subject: [PATCH] Allow admins request user backups

---
 lib/pleroma/backup.ex                         |  4 ++--
 lib/pleroma/emails/user_email.ex              | 20 +++++++++++++-----
 .../controllers/admin_api_controller.ex       | 12 ++++++++++-
 lib/pleroma/web/router.ex                     |  2 ++
 lib/pleroma/workers/backup_worker.ex          | 10 +++++----
 .../controllers/admin_api_controller_test.exs | 21 +++++++++++++++++++
 6 files changed, 57 insertions(+), 12 deletions(-)

diff --git a/lib/pleroma/backup.ex b/lib/pleroma/backup.ex
index 450dd5b84..d589f12f1 100644
--- a/lib/pleroma/backup.ex
+++ b/lib/pleroma/backup.ex
@@ -30,12 +30,12 @@ defmodule Pleroma.Backup do
     timestamps()
   end
 
-  def create(user) do
+  def create(user, admin_user_id \\ nil) do
     with :ok <- validate_email_enabled(),
          :ok <- validate_user_email(user),
          :ok <- validate_limit(user),
          {:ok, backup} <- user |> new() |> Repo.insert() do
-      BackupWorker.process(backup)
+      BackupWorker.process(backup, admin_user_id)
     end
   end
 
diff --git a/lib/pleroma/emails/user_email.ex b/lib/pleroma/emails/user_email.ex
index f943dda0d..5745794ec 100644
--- a/lib/pleroma/emails/user_email.ex
+++ b/lib/pleroma/emails/user_email.ex
@@ -190,14 +190,24 @@ defmodule Pleroma.Emails.UserEmail do
     Router.Helpers.subscription_url(Endpoint, :unsubscribe, token)
   end
 
-  def backup_is_ready_email(backup) do
+  def backup_is_ready_email(backup, admin_user_id \\ nil) do
     %{user: user} = Pleroma.Repo.preload(backup, :user)
     download_url = Pleroma.Web.PleromaAPI.BackupView.download_url(backup)
 
-    html_body = """
-    <p>You requested a full backup of your Pleroma account. It's ready for download:</p>
-    <p><a href="#{download_url}"></a></p>
-    """
+    html_body =
+      if is_nil(admin_user_id) do
+        """
+        <p>You requested a full backup of your Pleroma account. It's ready for download:</p>
+        <p><a href="#{download_url}"></a></p>
+        """
+      else
+        admin = Pleroma.Repo.get(User, admin_user_id)
+
+        """
+        <p>Admin @#{admin.nickname} requested a full backup of your Pleroma account. It's ready for download:</p>
+        <p><a href="#{download_url}"></a></p>
+        """
+      end
 
     new()
     |> to(recipient(user))
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 d5713c3dd..f7d2fe5b1 100644
--- a/lib/pleroma/web/admin_api/controllers/admin_api_controller.ex
+++ b/lib/pleroma/web/admin_api/controllers/admin_api_controller.ex
@@ -23,12 +23,14 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
   alias Pleroma.Web.Endpoint
   alias Pleroma.Web.Router
 
+  require Logger
+
   @users_page_size 50
 
   plug(
     OAuthScopesPlug,
     %{scopes: ["read:accounts"], admin: true}
-    when action in [:list_users, :user_show, :right_get, :show_user_credentials]
+    when action in [:list_users, :user_show, :right_get, :show_user_credentials, :create_backup]
   )
 
   plug(
@@ -681,6 +683,14 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
     json(conn, %{"status_visibility" => counters})
   end
 
+  def create_backup(%{assigns: %{user: admin}} = conn, %{"nickname" => nickname}) do
+    with %User{} = user <- User.get_by_nickname(nickname),
+         {:ok, _} <- Pleroma.Backup.create(user, admin.id) do
+      Logger.info("Admin @#{admin.nickname} requested account backup for @{nickname}")
+      json(conn, "")
+    end
+  end
+
   defp page_params(params) do
     {get_page(params["page"]), get_page_size(params["page_size"])}
   end
diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex
index a1a5a1cb5..e539eeeeb 100644
--- a/lib/pleroma/web/router.ex
+++ b/lib/pleroma/web/router.ex
@@ -129,6 +129,8 @@ defmodule Pleroma.Web.Router do
   scope "/api/pleroma/admin", Pleroma.Web.AdminAPI do
     pipe_through(:admin_api)
 
+    post("/backups", AdminAPIController, :create_backup)
+
     post("/users/follow", AdminAPIController, :user_follow)
     post("/users/unfollow", AdminAPIController, :user_unfollow)
 
diff --git a/lib/pleroma/workers/backup_worker.ex b/lib/pleroma/workers/backup_worker.ex
index 405d55269..65754b6a2 100644
--- a/lib/pleroma/workers/backup_worker.ex
+++ b/lib/pleroma/workers/backup_worker.ex
@@ -8,8 +8,8 @@ defmodule Pleroma.Workers.BackupWorker do
   alias Oban.Job
   alias Pleroma.Backup
 
-  def process(backup) do
-    %{"op" => "process", "backup_id" => backup.id}
+  def process(backup, admin_user_id \\ nil) do
+    %{"op" => "process", "backup_id" => backup.id, "admin_user_id" => admin_user_id}
     |> new()
     |> Oban.insert()
   end
@@ -30,14 +30,16 @@ defmodule Pleroma.Workers.BackupWorker do
     |> Oban.insert()
   end
 
-  def perform(%Job{args: %{"op" => "process", "backup_id" => backup_id}}) do
+  def perform(%Job{
+        args: %{"op" => "process", "backup_id" => backup_id, "admin_user_id" => admin_user_id}
+      }) do
     with {:ok, %Backup{} = backup} <-
            backup_id |> Backup.get() |> Backup.process(),
          {:ok, _job} <- schedule_deletion(backup),
          :ok <- Backup.remove_outdated(backup),
          {:ok, _} <-
            backup
-           |> Pleroma.Emails.UserEmail.backup_is_ready_email()
+           |> Pleroma.Emails.UserEmail.backup_is_ready_email(admin_user_id)
            |> Pleroma.Emails.Mailer.deliver() do
       {:ok, backup}
     end
diff --git a/test/web/admin_api/controllers/admin_api_controller_test.exs b/test/web/admin_api/controllers/admin_api_controller_test.exs
index cba6b43d3..4d331779e 100644
--- a/test/web/admin_api/controllers/admin_api_controller_test.exs
+++ b/test/web/admin_api/controllers/admin_api_controller_test.exs
@@ -2024,6 +2024,27 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
                response["status_visibility"]
     end
   end
+
+  describe "/api/pleroma/backups" do
+    test "it creates a backup", %{conn: conn} do
+      admin = insert(:user, is_admin: true)
+      token = insert(:oauth_admin_token, user: admin)
+      user = insert(:user)
+
+      assert "" ==
+               conn
+               |> assign(:user, admin)
+               |> assign(:token, token)
+               |> post("/api/pleroma/admin/backups", %{nickname: user.nickname})
+               |> json_response(200)
+
+      assert [backup] = Repo.all(Pleroma.Backup)
+
+      ObanHelpers.perform_all()
+
+      assert_email_sent(Pleroma.Emails.UserEmail.backup_is_ready_email(backup, admin.id))
+    end
+  end
 end
 
 # Needed for testing