Merge branch 'fix-admin-api-scope' into 'develop'
[akkoma] / lib / pleroma / web / twitter_api / controllers / util_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.TwitterAPI.UtilController do
6 use Pleroma.Web, :controller
7
8 require Logger
9
10 alias Pleroma.Config
11 alias Pleroma.Emoji
12 alias Pleroma.Healthcheck
13 alias Pleroma.Notification
14 alias Pleroma.Plugs.OAuthScopesPlug
15 alias Pleroma.User
16 alias Pleroma.Web
17 alias Pleroma.Web.CommonAPI
18 alias Pleroma.Web.WebFinger
19
20 plug(
21 OAuthScopesPlug,
22 %{scopes: ["follow", "write:follows"]}
23 when action == :follow_import
24 )
25
26 # Note: follower can submit the form (with password auth) not being signed in (having no token)
27 plug(
28 OAuthScopesPlug,
29 %{fallback: :proceed_unauthenticated, scopes: ["follow", "write:follows"]}
30 when action == :do_remote_follow
31 )
32
33 plug(OAuthScopesPlug, %{scopes: ["follow", "write:blocks"]} when action == :blocks_import)
34
35 plug(
36 OAuthScopesPlug,
37 %{scopes: ["write:accounts"]}
38 when action in [
39 :change_email,
40 :change_password,
41 :delete_account,
42 :update_notificaton_settings,
43 :disable_account
44 ]
45 )
46
47 plug(OAuthScopesPlug, %{scopes: ["write:notifications"]} when action == :notifications_read)
48
49 plug(Pleroma.Plugs.SetFormatPlug when action in [:config, :version])
50
51 def help_test(conn, _params) do
52 json(conn, "ok")
53 end
54
55 def remote_subscribe(conn, %{"nickname" => nick, "profile" => _}) do
56 with %User{} = user <- User.get_cached_by_nickname(nick),
57 avatar = User.avatar_url(user) do
58 conn
59 |> render("subscribe.html", %{nickname: nick, avatar: avatar, error: false})
60 else
61 _e ->
62 render(conn, "subscribe.html", %{
63 nickname: nick,
64 avatar: nil,
65 error: "Could not find user"
66 })
67 end
68 end
69
70 def remote_subscribe(conn, %{"user" => %{"nickname" => nick, "profile" => profile}}) do
71 with {:ok, %{"subscribe_address" => template}} <- WebFinger.finger(profile),
72 %User{ap_id: ap_id} <- User.get_cached_by_nickname(nick) do
73 conn
74 |> Phoenix.Controller.redirect(external: String.replace(template, "{uri}", ap_id))
75 else
76 _e ->
77 render(conn, "subscribe.html", %{
78 nickname: nick,
79 avatar: nil,
80 error: "Something went wrong."
81 })
82 end
83 end
84
85 def notifications_read(%{assigns: %{user: user}} = conn, %{"id" => notification_id}) do
86 with {:ok, _} <- Notification.read_one(user, notification_id) do
87 json(conn, %{status: "success"})
88 else
89 {:error, message} ->
90 conn
91 |> put_resp_content_type("application/json")
92 |> send_resp(403, Jason.encode!(%{"error" => message}))
93 end
94 end
95
96 def config(%{assigns: %{format: "xml"}} = conn, _params) do
97 instance = Pleroma.Config.get(:instance)
98
99 response = """
100 <config>
101 <site>
102 <name>#{Keyword.get(instance, :name)}</name>
103 <site>#{Web.base_url()}</site>
104 <textlimit>#{Keyword.get(instance, :limit)}</textlimit>
105 <closed>#{!Keyword.get(instance, :registrations_open)}</closed>
106 </site>
107 </config>
108 """
109
110 conn
111 |> put_resp_content_type("application/xml")
112 |> send_resp(200, response)
113 end
114
115 def config(conn, _params) do
116 instance = Pleroma.Config.get(:instance)
117
118 vapid_public_key = Keyword.get(Pleroma.Web.Push.vapid_config(), :public_key)
119
120 uploadlimit = %{
121 uploadlimit: to_string(Keyword.get(instance, :upload_limit)),
122 avatarlimit: to_string(Keyword.get(instance, :avatar_upload_limit)),
123 backgroundlimit: to_string(Keyword.get(instance, :background_upload_limit)),
124 bannerlimit: to_string(Keyword.get(instance, :banner_upload_limit))
125 }
126
127 data = %{
128 name: Keyword.get(instance, :name),
129 description: Keyword.get(instance, :description),
130 server: Web.base_url(),
131 textlimit: to_string(Keyword.get(instance, :limit)),
132 uploadlimit: uploadlimit,
133 closed: bool_to_val(Keyword.get(instance, :registrations_open), "0", "1"),
134 private: bool_to_val(Keyword.get(instance, :public, true), "0", "1"),
135 vapidPublicKey: vapid_public_key,
136 accountActivationRequired:
137 bool_to_val(Keyword.get(instance, :account_activation_required, false)),
138 invitesEnabled: bool_to_val(Keyword.get(instance, :invites_enabled, false)),
139 safeDMMentionsEnabled: bool_to_val(Pleroma.Config.get([:instance, :safe_dm_mentions]))
140 }
141
142 managed_config = Keyword.get(instance, :managed_config)
143
144 data =
145 if managed_config do
146 pleroma_fe = Pleroma.Config.get([:frontend_configurations, :pleroma_fe])
147 Map.put(data, "pleromafe", pleroma_fe)
148 else
149 data
150 end
151
152 json(conn, %{site: data})
153 end
154
155 defp bool_to_val(true), do: "1"
156 defp bool_to_val(_), do: "0"
157 defp bool_to_val(true, val, _), do: val
158 defp bool_to_val(_, _, val), do: val
159
160 def frontend_configurations(conn, _params) do
161 config =
162 Pleroma.Config.get(:frontend_configurations, %{})
163 |> Enum.into(%{})
164
165 json(conn, config)
166 end
167
168 def version(%{assigns: %{format: "xml"}} = conn, _params) do
169 version = Pleroma.Application.named_version()
170
171 conn
172 |> put_resp_content_type("application/xml")
173 |> send_resp(200, "<version>#{version}</version>")
174 end
175
176 def version(conn, _params) do
177 json(conn, Pleroma.Application.named_version())
178 end
179
180 def emoji(conn, _params) do
181 emoji =
182 Enum.reduce(Emoji.get_all(), %{}, fn {code, %Emoji{file: file, tags: tags}}, acc ->
183 Map.put(acc, code, %{image_url: file, tags: tags})
184 end)
185
186 json(conn, emoji)
187 end
188
189 def update_notificaton_settings(%{assigns: %{user: user}} = conn, params) do
190 with {:ok, _} <- User.update_notification_settings(user, params) do
191 json(conn, %{status: "success"})
192 end
193 end
194
195 def follow_import(conn, %{"list" => %Plug.Upload{} = listfile}) do
196 follow_import(conn, %{"list" => File.read!(listfile.path)})
197 end
198
199 def follow_import(%{assigns: %{user: follower}} = conn, %{"list" => list}) do
200 with lines <- String.split(list, "\n"),
201 followed_identifiers <-
202 Enum.map(lines, fn line ->
203 String.split(line, ",") |> List.first()
204 end)
205 |> List.delete("Account address") do
206 User.follow_import(follower, followed_identifiers)
207 json(conn, "job started")
208 end
209 end
210
211 def blocks_import(conn, %{"list" => %Plug.Upload{} = listfile}) do
212 blocks_import(conn, %{"list" => File.read!(listfile.path)})
213 end
214
215 def blocks_import(%{assigns: %{user: blocker}} = conn, %{"list" => list}) do
216 with blocked_identifiers <- String.split(list) do
217 User.blocks_import(blocker, blocked_identifiers)
218 json(conn, "job started")
219 end
220 end
221
222 def change_password(%{assigns: %{user: user}} = conn, params) do
223 case CommonAPI.Utils.confirm_current_password(user, params["password"]) do
224 {:ok, user} ->
225 with {:ok, _user} <-
226 User.reset_password(user, %{
227 password: params["new_password"],
228 password_confirmation: params["new_password_confirmation"]
229 }) do
230 json(conn, %{status: "success"})
231 else
232 {:error, changeset} ->
233 {_, {error, _}} = Enum.at(changeset.errors, 0)
234 json(conn, %{error: "New password #{error}."})
235
236 _ ->
237 json(conn, %{error: "Unable to change password."})
238 end
239
240 {:error, msg} ->
241 json(conn, %{error: msg})
242 end
243 end
244
245 def change_email(%{assigns: %{user: user}} = conn, params) do
246 case CommonAPI.Utils.confirm_current_password(user, params["password"]) do
247 {:ok, user} ->
248 with {:ok, _user} <- User.change_email(user, params["email"]) do
249 json(conn, %{status: "success"})
250 else
251 {:error, changeset} ->
252 {_, {error, _}} = Enum.at(changeset.errors, 0)
253 json(conn, %{error: "Email #{error}."})
254
255 _ ->
256 json(conn, %{error: "Unable to change email."})
257 end
258
259 {:error, msg} ->
260 json(conn, %{error: msg})
261 end
262 end
263
264 def delete_account(%{assigns: %{user: user}} = conn, params) do
265 password = params["password"] || ""
266
267 case CommonAPI.Utils.confirm_current_password(user, password) do
268 {:ok, user} ->
269 User.delete(user)
270 json(conn, %{status: "success"})
271
272 {:error, msg} ->
273 json(conn, %{error: msg})
274 end
275 end
276
277 def disable_account(%{assigns: %{user: user}} = conn, params) do
278 case CommonAPI.Utils.confirm_current_password(user, params["password"]) do
279 {:ok, user} ->
280 User.deactivate_async(user)
281 json(conn, %{status: "success"})
282
283 {:error, msg} ->
284 json(conn, %{error: msg})
285 end
286 end
287
288 def captcha(conn, _params) do
289 json(conn, Pleroma.Captcha.new())
290 end
291
292 def healthcheck(conn, _params) do
293 with true <- Config.get([:instance, :healthcheck]),
294 %{healthy: true} = info <- Healthcheck.system_info() do
295 json(conn, info)
296 else
297 %{healthy: false} = info ->
298 service_unavailable(conn, info)
299
300 _ ->
301 service_unavailable(conn, %{})
302 end
303 end
304
305 defp service_unavailable(conn, info) do
306 conn
307 |> put_status(:service_unavailable)
308 |> json(info)
309 end
310 end