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