50aa294f0ff0aed48ac6089323a47a4a6e2353b7
[akkoma] / lib / pleroma / web / admin_api / controllers / admin_api_controller.ex
1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
4
5 defmodule Pleroma.Web.AdminAPI.AdminAPIController do
6 use Pleroma.Web, :controller
7
8 import Pleroma.Web.ControllerHelper,
9 only: [json_response: 3, fetch_integer_param: 3]
10
11 alias Pleroma.Config
12 alias Pleroma.MFA
13 alias Pleroma.ModerationLog
14 alias Pleroma.Stats
15 alias Pleroma.User
16 alias Pleroma.Web.ActivityPub.ActivityPub
17 alias Pleroma.Web.AdminAPI
18 alias Pleroma.Web.AdminAPI.AccountView
19 alias Pleroma.Web.AdminAPI.ModerationLogView
20 alias Pleroma.Web.Endpoint
21 alias Pleroma.Web.Plugs.OAuthScopesPlug
22 alias Pleroma.Web.Router
23
24 @users_page_size 50
25
26 plug(
27 OAuthScopesPlug,
28 %{scopes: ["admin:read:accounts"]}
29 when action in [:right_get, :show_user_credentials, :create_backup]
30 )
31
32 plug(
33 OAuthScopesPlug,
34 %{scopes: ["admin:write:accounts"]}
35 when action in [
36 :get_password_reset,
37 :force_password_reset,
38 :tag_users,
39 :untag_users,
40 :right_add,
41 :right_add_multiple,
42 :right_delete,
43 :disable_mfa,
44 :right_delete_multiple,
45 :update_user_credentials
46 ]
47 )
48
49 plug(
50 OAuthScopesPlug,
51 %{scopes: ["admin:read:statuses"]}
52 when action in [:list_user_statuses]
53 )
54
55 plug(
56 OAuthScopesPlug,
57 %{scopes: ["admin:read:chats"]}
58 when action in [:list_user_chats]
59 )
60
61 plug(
62 OAuthScopesPlug,
63 %{scopes: ["admin:read"]}
64 when action in [
65 :list_log,
66 :stats,
67 :need_reboot
68 ]
69 )
70
71 plug(
72 OAuthScopesPlug,
73 %{scopes: ["admin:write"]}
74 when action in [
75 :restart,
76 :resend_confirmation_email,
77 :confirm_email,
78 :reload_emoji
79 ]
80 )
81
82 action_fallback(AdminAPI.FallbackController)
83
84 def list_user_statuses(%{assigns: %{user: admin}} = conn, %{"nickname" => nickname} = params) do
85 with_reblogs = params["with_reblogs"] == "true" || params["with_reblogs"] == true
86 godmode = params["godmode"] == "true" || params["godmode"] == true
87
88 with %User{} = user <- User.get_cached_by_nickname_or_id(nickname, for: admin) do
89 {page, page_size} = page_params(params)
90
91 result =
92 ActivityPub.fetch_user_activities(user, nil, %{
93 limit: page_size,
94 offset: (page - 1) * page_size,
95 godmode: godmode,
96 exclude_reblogs: not with_reblogs,
97 pagination_type: :offset,
98 total: true
99 })
100
101 conn
102 |> put_view(AdminAPI.StatusView)
103 |> render("index.json", %{total: result[:total], activities: result[:items], as: :activity})
104 else
105 _ -> {:error, :not_found}
106 end
107 end
108
109 def list_user_chats(%{assigns: %{user: admin}} = conn, %{"nickname" => nickname} = _params) do
110 with %User{id: user_id} <- User.get_cached_by_nickname_or_id(nickname, for: admin) do
111 chats =
112 Pleroma.Chat.for_user_query(user_id)
113 |> Pleroma.Repo.all()
114
115 conn
116 |> put_view(AdminAPI.ChatView)
117 |> render("index.json", chats: chats)
118 else
119 _ -> {:error, :not_found}
120 end
121 end
122
123 def tag_users(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames, "tags" => tags}) do
124 with {:ok, _} <- User.tag(nicknames, tags) do
125 ModerationLog.insert_log(%{
126 actor: admin,
127 nicknames: nicknames,
128 tags: tags,
129 action: "tag"
130 })
131
132 json_response(conn, :no_content, "")
133 end
134 end
135
136 def untag_users(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames, "tags" => tags}) do
137 with {:ok, _} <- User.untag(nicknames, tags) do
138 ModerationLog.insert_log(%{
139 actor: admin,
140 nicknames: nicknames,
141 tags: tags,
142 action: "untag"
143 })
144
145 json_response(conn, :no_content, "")
146 end
147 end
148
149 def right_add_multiple(%{assigns: %{user: admin}} = conn, %{
150 "permission_group" => permission_group,
151 "nicknames" => nicknames
152 })
153 when permission_group in ["moderator", "admin"] do
154 update = %{:"is_#{permission_group}" => true}
155
156 users = nicknames |> Enum.map(&User.get_cached_by_nickname/1)
157
158 for u <- users, do: User.admin_api_update(u, update)
159
160 ModerationLog.insert_log(%{
161 action: "grant",
162 actor: admin,
163 subject: users,
164 permission: permission_group
165 })
166
167 json(conn, update)
168 end
169
170 def right_add_multiple(conn, _) do
171 render_error(conn, :not_found, "No such permission_group")
172 end
173
174 def right_add(%{assigns: %{user: admin}} = conn, %{
175 "permission_group" => permission_group,
176 "nickname" => nickname
177 })
178 when permission_group in ["moderator", "admin"] do
179 fields = %{:"is_#{permission_group}" => true}
180
181 {:ok, user} =
182 nickname
183 |> User.get_cached_by_nickname()
184 |> User.admin_api_update(fields)
185
186 ModerationLog.insert_log(%{
187 action: "grant",
188 actor: admin,
189 subject: [user],
190 permission: permission_group
191 })
192
193 json(conn, fields)
194 end
195
196 def right_add(conn, _) do
197 render_error(conn, :not_found, "No such permission_group")
198 end
199
200 def right_get(conn, %{"nickname" => nickname}) do
201 user = User.get_cached_by_nickname(nickname)
202
203 conn
204 |> json(%{
205 is_moderator: user.is_moderator,
206 is_admin: user.is_admin
207 })
208 end
209
210 def right_delete_multiple(
211 %{assigns: %{user: %{nickname: admin_nickname} = admin}} = conn,
212 %{
213 "permission_group" => permission_group,
214 "nicknames" => nicknames
215 }
216 )
217 when permission_group in ["moderator", "admin"] do
218 with false <- Enum.member?(nicknames, admin_nickname) do
219 update = %{:"is_#{permission_group}" => false}
220
221 users = nicknames |> Enum.map(&User.get_cached_by_nickname/1)
222
223 for u <- users, do: User.admin_api_update(u, update)
224
225 ModerationLog.insert_log(%{
226 action: "revoke",
227 actor: admin,
228 subject: users,
229 permission: permission_group
230 })
231
232 json(conn, update)
233 else
234 _ -> render_error(conn, :forbidden, "You can't revoke your own admin/moderator status.")
235 end
236 end
237
238 def right_delete_multiple(conn, _) do
239 render_error(conn, :not_found, "No such permission_group")
240 end
241
242 def right_delete(
243 %{assigns: %{user: admin}} = conn,
244 %{
245 "permission_group" => permission_group,
246 "nickname" => nickname
247 }
248 )
249 when permission_group in ["moderator", "admin"] do
250 fields = %{:"is_#{permission_group}" => false}
251
252 {:ok, user} =
253 nickname
254 |> User.get_cached_by_nickname()
255 |> User.admin_api_update(fields)
256
257 ModerationLog.insert_log(%{
258 action: "revoke",
259 actor: admin,
260 subject: [user],
261 permission: permission_group
262 })
263
264 json(conn, fields)
265 end
266
267 def right_delete(%{assigns: %{user: %{nickname: nickname}}} = conn, %{"nickname" => nickname}) do
268 render_error(conn, :forbidden, "You can't revoke your own admin status.")
269 end
270
271 @doc "Get a password reset token (base64 string) for given nickname"
272 def get_password_reset(conn, %{"nickname" => nickname}) do
273 (%User{local: true} = user) = User.get_cached_by_nickname(nickname)
274 {:ok, token} = Pleroma.PasswordResetToken.create_token(user)
275
276 conn
277 |> json(%{
278 token: token.token,
279 link: Router.Helpers.reset_password_url(Endpoint, :reset, token.token)
280 })
281 end
282
283 @doc "Force password reset for a given user"
284 def force_password_reset(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do
285 users = nicknames |> Enum.map(&User.get_cached_by_nickname/1)
286
287 Enum.each(users, &User.force_password_reset_async/1)
288
289 ModerationLog.insert_log(%{
290 actor: admin,
291 subject: users,
292 action: "force_password_reset"
293 })
294
295 json_response(conn, :no_content, "")
296 end
297
298 @doc "Disable mfa for user's account."
299 def disable_mfa(conn, %{"nickname" => nickname}) do
300 case User.get_by_nickname(nickname) do
301 %User{} = user ->
302 MFA.disable(user)
303 json(conn, nickname)
304
305 _ ->
306 {:error, :not_found}
307 end
308 end
309
310 @doc "Show a given user's credentials"
311 def show_user_credentials(%{assigns: %{user: admin}} = conn, %{"nickname" => nickname}) do
312 with %User{} = user <- User.get_cached_by_nickname_or_id(nickname, for: admin) do
313 conn
314 |> put_view(AccountView)
315 |> render("credentials.json", %{user: user, for: admin})
316 else
317 _ -> {:error, :not_found}
318 end
319 end
320
321 @doc "Updates a given user"
322 def update_user_credentials(
323 %{assigns: %{user: admin}} = conn,
324 %{"nickname" => nickname} = params
325 ) do
326 with {_, %User{} = user} <- {:user, User.get_cached_by_nickname(nickname)},
327 {:ok, _user} <-
328 User.update_as_admin(user, params) do
329 ModerationLog.insert_log(%{
330 actor: admin,
331 subject: [user],
332 action: "updated_users"
333 })
334
335 if params["password"] do
336 User.force_password_reset_async(user)
337 end
338
339 ModerationLog.insert_log(%{
340 actor: admin,
341 subject: [user],
342 action: "force_password_reset"
343 })
344
345 json(conn, %{status: "success"})
346 else
347 {:error, changeset} ->
348 errors = Map.new(changeset.errors, fn {key, {error, _}} -> {key, error} end)
349
350 {:errors, errors}
351
352 _ ->
353 {:error, :not_found}
354 end
355 end
356
357 def list_log(conn, params) do
358 {page, page_size} = page_params(params)
359
360 log =
361 ModerationLog.get_all(%{
362 page: page,
363 page_size: page_size,
364 start_date: params["start_date"],
365 end_date: params["end_date"],
366 user_id: params["user_id"],
367 search: params["search"]
368 })
369
370 conn
371 |> put_view(ModerationLogView)
372 |> render("index.json", %{log: log})
373 end
374
375 def restart(conn, _params) do
376 with :ok <- configurable_from_database() do
377 Restarter.Pleroma.restart(Config.get(:env), 50)
378
379 json(conn, %{})
380 end
381 end
382
383 def need_reboot(conn, _params) do
384 json(conn, %{need_reboot: Restarter.Pleroma.need_reboot?()})
385 end
386
387 defp configurable_from_database do
388 if Config.get(:configurable_from_database) do
389 :ok
390 else
391 {:error, "You must enable configurable_from_database in your config file."}
392 end
393 end
394
395 def reload_emoji(conn, _params) do
396 Pleroma.Emoji.reload()
397
398 json(conn, "ok")
399 end
400
401 def confirm_email(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do
402 users = Enum.map(nicknames, &User.get_cached_by_nickname/1)
403
404 User.confirm(users)
405
406 ModerationLog.insert_log(%{actor: admin, subject: users, action: "confirm_email"})
407
408 json(conn, "")
409 end
410
411 def resend_confirmation_email(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do
412 users =
413 Enum.map(nicknames, fn nickname ->
414 nickname
415 |> User.get_cached_by_nickname()
416 |> User.send_confirmation_email()
417 end)
418
419 ModerationLog.insert_log(%{actor: admin, subject: users, action: "resend_confirmation_email"})
420
421 json(conn, "")
422 end
423
424 def stats(conn, params) do
425 counters = Stats.get_status_visibility_count(params["instance"])
426
427 json(conn, %{"status_visibility" => counters})
428 end
429
430 def create_backup(%{assigns: %{user: admin}} = conn, %{"nickname" => nickname}) do
431 with %User{} = user <- User.get_by_nickname(nickname),
432 {:ok, _} <- Pleroma.User.Backup.create(user, admin.id) do
433 ModerationLog.insert_log(%{actor: admin, subject: user, action: "create_backup"})
434
435 json(conn, "")
436 end
437 end
438
439 defp page_params(params) do
440 {
441 fetch_integer_param(params, "page", 1),
442 fetch_integer_param(params, "page_size", @users_page_size)
443 }
444 end
445 end