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