Merge branch 'develop' of https://git.pleroma.social/pleroma/pleroma into develop
[akkoma] / lib / pleroma / web / admin_api / admin_api_controller.ex
1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2019 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 alias Pleroma.Activity
8 alias Pleroma.User
9 alias Pleroma.UserInviteToken
10 alias Pleroma.Web.ActivityPub.ActivityPub
11 alias Pleroma.Web.ActivityPub.Relay
12 alias Pleroma.Web.AdminAPI.AccountView
13 alias Pleroma.Web.AdminAPI.Config
14 alias Pleroma.Web.AdminAPI.ConfigView
15 alias Pleroma.Web.AdminAPI.ReportView
16 alias Pleroma.Web.AdminAPI.Search
17 alias Pleroma.Web.CommonAPI
18 alias Pleroma.Web.MastodonAPI.StatusView
19
20 import Pleroma.Web.ControllerHelper, only: [json_response: 3]
21
22 require Logger
23
24 @users_page_size 50
25
26 action_fallback(:errors)
27
28 def user_delete(conn, %{"nickname" => nickname}) do
29 User.get_cached_by_nickname(nickname)
30 |> User.delete()
31
32 conn
33 |> json(nickname)
34 end
35
36 def user_follow(conn, %{"follower" => follower_nick, "followed" => followed_nick}) do
37 with %User{} = follower <- User.get_cached_by_nickname(follower_nick),
38 %User{} = followed <- User.get_cached_by_nickname(followed_nick) do
39 User.follow(follower, followed)
40 end
41
42 conn
43 |> json("ok")
44 end
45
46 def user_unfollow(conn, %{"follower" => follower_nick, "followed" => followed_nick}) do
47 with %User{} = follower <- User.get_cached_by_nickname(follower_nick),
48 %User{} = followed <- User.get_cached_by_nickname(followed_nick) do
49 User.unfollow(follower, followed)
50 end
51
52 conn
53 |> json("ok")
54 end
55
56 def user_create(
57 conn,
58 %{"nickname" => nickname, "email" => email, "password" => password}
59 ) do
60 user_data = %{
61 nickname: nickname,
62 name: nickname,
63 email: email,
64 password: password,
65 password_confirmation: password,
66 bio: "."
67 }
68
69 changeset = User.register_changeset(%User{}, user_data, need_confirmation: false)
70 {:ok, user} = User.register(changeset)
71
72 conn
73 |> json(user.nickname)
74 end
75
76 def user_show(conn, %{"nickname" => nickname}) do
77 with %User{} = user <- User.get_cached_by_nickname_or_id(nickname) do
78 conn
79 |> json(AccountView.render("show.json", %{user: user}))
80 else
81 _ -> {:error, :not_found}
82 end
83 end
84
85 def user_toggle_activation(conn, %{"nickname" => nickname}) do
86 user = User.get_cached_by_nickname(nickname)
87
88 {:ok, updated_user} = User.deactivate(user, !user.info.deactivated)
89
90 conn
91 |> json(AccountView.render("show.json", %{user: updated_user}))
92 end
93
94 def tag_users(conn, %{"nicknames" => nicknames, "tags" => tags}) do
95 with {:ok, _} <- User.tag(nicknames, tags),
96 do: json_response(conn, :no_content, "")
97 end
98
99 def untag_users(conn, %{"nicknames" => nicknames, "tags" => tags}) do
100 with {:ok, _} <- User.untag(nicknames, tags),
101 do: json_response(conn, :no_content, "")
102 end
103
104 def list_users(conn, params) do
105 {page, page_size} = page_params(params)
106 filters = maybe_parse_filters(params["filters"])
107
108 search_params = %{
109 query: params["query"],
110 page: page,
111 page_size: page_size,
112 tags: params["tags"],
113 name: params["name"],
114 email: params["email"]
115 }
116
117 with {:ok, users, count} <- Search.user(Map.merge(search_params, filters)),
118 do:
119 conn
120 |> json(
121 AccountView.render("index.json",
122 users: users,
123 count: count,
124 page_size: page_size
125 )
126 )
127 end
128
129 @filters ~w(local external active deactivated is_admin is_moderator)
130
131 @spec maybe_parse_filters(String.t()) :: %{required(String.t()) => true} | %{}
132 defp maybe_parse_filters(filters) when is_nil(filters) or filters == "", do: %{}
133
134 defp maybe_parse_filters(filters) do
135 filters
136 |> String.split(",")
137 |> Enum.filter(&Enum.member?(@filters, &1))
138 |> Enum.map(&String.to_atom(&1))
139 |> Enum.into(%{}, &{&1, true})
140 end
141
142 def right_add(conn, %{"permission_group" => permission_group, "nickname" => nickname})
143 when permission_group in ["moderator", "admin"] do
144 user = User.get_cached_by_nickname(nickname)
145
146 info =
147 %{}
148 |> Map.put("is_" <> permission_group, true)
149
150 info_cng = User.Info.admin_api_update(user.info, info)
151
152 cng =
153 user
154 |> Ecto.Changeset.change()
155 |> Ecto.Changeset.put_embed(:info, info_cng)
156
157 {:ok, _user} = User.update_and_set_cache(cng)
158
159 json(conn, info)
160 end
161
162 def right_add(conn, _) do
163 render_error(conn, :not_found, "No such permission_group")
164 end
165
166 def right_get(conn, %{"nickname" => nickname}) do
167 user = User.get_cached_by_nickname(nickname)
168
169 conn
170 |> json(%{
171 is_moderator: user.info.is_moderator,
172 is_admin: user.info.is_admin
173 })
174 end
175
176 def right_delete(
177 %{assigns: %{user: %User{:nickname => admin_nickname}}} = conn,
178 %{
179 "permission_group" => permission_group,
180 "nickname" => nickname
181 }
182 )
183 when permission_group in ["moderator", "admin"] do
184 if admin_nickname == nickname do
185 render_error(conn, :forbidden, "You can't revoke your own admin status.")
186 else
187 user = User.get_cached_by_nickname(nickname)
188
189 info =
190 %{}
191 |> Map.put("is_" <> permission_group, false)
192
193 info_cng = User.Info.admin_api_update(user.info, info)
194
195 cng =
196 Ecto.Changeset.change(user)
197 |> Ecto.Changeset.put_embed(:info, info_cng)
198
199 {:ok, _user} = User.update_and_set_cache(cng)
200
201 json(conn, info)
202 end
203 end
204
205 def right_delete(conn, _) do
206 render_error(conn, :not_found, "No such permission_group")
207 end
208
209 def set_activation_status(conn, %{"nickname" => nickname, "status" => status}) do
210 with {:ok, status} <- Ecto.Type.cast(:boolean, status),
211 %User{} = user <- User.get_cached_by_nickname(nickname),
212 {:ok, _} <- User.deactivate(user, !status),
213 do: json_response(conn, :no_content, "")
214 end
215
216 def relay_follow(conn, %{"relay_url" => target}) do
217 with {:ok, _message} <- Relay.follow(target) do
218 json(conn, target)
219 else
220 _ ->
221 conn
222 |> put_status(500)
223 |> json(target)
224 end
225 end
226
227 def relay_unfollow(conn, %{"relay_url" => target}) do
228 with {:ok, _message} <- Relay.unfollow(target) do
229 json(conn, target)
230 else
231 _ ->
232 conn
233 |> put_status(500)
234 |> json(target)
235 end
236 end
237
238 @doc "Sends registration invite via email"
239 def email_invite(%{assigns: %{user: user}} = conn, %{"email" => email} = params) do
240 with true <-
241 Pleroma.Config.get([:instance, :invites_enabled]) &&
242 !Pleroma.Config.get([:instance, :registrations_open]),
243 {:ok, invite_token} <- UserInviteToken.create_invite(),
244 email <-
245 Pleroma.Emails.UserEmail.user_invitation_email(
246 user,
247 invite_token,
248 email,
249 params["name"]
250 ),
251 {:ok, _} <- Pleroma.Emails.Mailer.deliver(email) do
252 json_response(conn, :no_content, "")
253 end
254 end
255
256 @doc "Get a account registeration invite token (base64 string)"
257 def get_invite_token(conn, params) do
258 options = params["invite"] || %{}
259 {:ok, invite} = UserInviteToken.create_invite(options)
260
261 conn
262 |> json(invite.token)
263 end
264
265 @doc "Get list of created invites"
266 def invites(conn, _params) do
267 invites = UserInviteToken.list_invites()
268
269 conn
270 |> json(AccountView.render("invites.json", %{invites: invites}))
271 end
272
273 @doc "Revokes invite by token"
274 def revoke_invite(conn, %{"token" => token}) do
275 with {:ok, invite} <- UserInviteToken.find_by_token(token),
276 {:ok, updated_invite} = UserInviteToken.update_invite(invite, %{used: true}) do
277 conn
278 |> json(AccountView.render("invite.json", %{invite: updated_invite}))
279 else
280 nil -> {:error, :not_found}
281 end
282 end
283
284 @doc "Get a password reset token (base64 string) for given nickname"
285 def get_password_reset(conn, %{"nickname" => nickname}) do
286 (%User{local: true} = user) = User.get_cached_by_nickname(nickname)
287 {:ok, token} = Pleroma.PasswordResetToken.create_token(user)
288
289 conn
290 |> json(token.token)
291 end
292
293 def list_reports(conn, params) do
294 params =
295 params
296 |> Map.put("type", "Flag")
297 |> Map.put("skip_preload", true)
298
299 reports =
300 []
301 |> ActivityPub.fetch_activities(params)
302 |> Enum.reverse()
303
304 conn
305 |> put_view(ReportView)
306 |> render("index.json", %{reports: reports})
307 end
308
309 def report_show(conn, %{"id" => id}) do
310 with %Activity{} = report <- Activity.get_by_id(id) do
311 conn
312 |> put_view(ReportView)
313 |> render("show.json", %{report: report})
314 else
315 _ -> {:error, :not_found}
316 end
317 end
318
319 def report_update_state(conn, %{"id" => id, "state" => state}) do
320 with {:ok, report} <- CommonAPI.update_report_state(id, state) do
321 conn
322 |> put_view(ReportView)
323 |> render("show.json", %{report: report})
324 end
325 end
326
327 def report_respond(%{assigns: %{user: user}} = conn, %{"id" => id} = params) do
328 with false <- is_nil(params["status"]),
329 %Activity{} <- Activity.get_by_id(id) do
330 params =
331 params
332 |> Map.put("in_reply_to_status_id", id)
333 |> Map.put("visibility", "direct")
334
335 {:ok, activity} = CommonAPI.post(user, params)
336
337 conn
338 |> put_view(StatusView)
339 |> render("status.json", %{activity: activity})
340 else
341 true ->
342 {:param_cast, nil}
343
344 nil ->
345 {:error, :not_found}
346 end
347 end
348
349 def status_update(conn, %{"id" => id} = params) do
350 with {:ok, activity} <- CommonAPI.update_activity_scope(id, params) do
351 conn
352 |> put_view(StatusView)
353 |> render("status.json", %{activity: activity})
354 end
355 end
356
357 def status_delete(%{assigns: %{user: user}} = conn, %{"id" => id}) do
358 with {:ok, %Activity{}} <- CommonAPI.delete(id, user) do
359 json(conn, %{})
360 end
361 end
362
363 def config_show(conn, _params) do
364 configs = Pleroma.Repo.all(Config)
365
366 conn
367 |> put_view(ConfigView)
368 |> render("index.json", %{configs: configs})
369 end
370
371 def config_update(conn, %{"configs" => configs}) do
372 updated =
373 if Pleroma.Config.get([:instance, :dynamic_configuration]) do
374 updated =
375 Enum.map(configs, fn
376 %{"group" => group, "key" => key, "delete" => "true"} ->
377 {:ok, _} = Config.delete(%{group: group, key: key})
378 nil
379
380 %{"group" => group, "key" => key, "value" => value} ->
381 {:ok, config} = Config.update_or_create(%{group: group, key: key, value: value})
382 config
383 end)
384 |> Enum.reject(&is_nil(&1))
385
386 Pleroma.Config.TransferTask.load_and_update_env()
387 Mix.Tasks.Pleroma.Config.run(["migrate_from_db", Pleroma.Config.get(:env), "false"])
388 updated
389 else
390 []
391 end
392
393 conn
394 |> put_view(ConfigView)
395 |> render("index.json", %{configs: updated})
396 end
397
398 def errors(conn, {:error, :not_found}) do
399 conn
400 |> put_status(:not_found)
401 |> json(dgettext("errors", "Not found"))
402 end
403
404 def errors(conn, {:error, reason}) do
405 conn
406 |> put_status(:bad_request)
407 |> json(reason)
408 end
409
410 def errors(conn, {:param_cast, _}) do
411 conn
412 |> put_status(:bad_request)
413 |> json(dgettext("errors", "Invalid parameters"))
414 end
415
416 def errors(conn, _) do
417 conn
418 |> put_status(:internal_server_error)
419 |> json(dgettext("errors", "Something went wrong"))
420 end
421
422 defp page_params(params) do
423 {get_page(params["page"]), get_page_size(params["page_size"])}
424 end
425
426 defp get_page(page_string) when is_nil(page_string), do: 1
427
428 defp get_page(page_string) do
429 case Integer.parse(page_string) do
430 {page, _} -> page
431 :error -> 1
432 end
433 end
434
435 defp get_page_size(page_size_string) when is_nil(page_size_string), do: @users_page_size
436
437 defp get_page_size(page_size_string) do
438 case Integer.parse(page_size_string) do
439 {page_size, _} -> page_size
440 :error -> @users_page_size
441 end
442 end
443 end