logo

pleroma

My custom branche(s) on git.pleroma.social/pleroma/pleroma git clone https://anongit.hacktivis.me/git/pleroma.git/

util_controller.ex (11265B)


  1. # Pleroma: A lightweight social networking server
  2. # Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
  3. # SPDX-License-Identifier: AGPL-3.0-only
  4. defmodule Pleroma.Web.TwitterAPI.UtilController do
  5. use Pleroma.Web, :controller
  6. require Logger
  7. alias Pleroma.Activity
  8. alias Pleroma.Config
  9. alias Pleroma.Emoji
  10. alias Pleroma.Healthcheck
  11. alias Pleroma.User
  12. alias Pleroma.Web.ActivityPub.ActivityPub
  13. alias Pleroma.Web.Auth.WrapperAuthenticator, as: Authenticator
  14. alias Pleroma.Web.CommonAPI
  15. alias Pleroma.Web.Plugs.OAuthScopesPlug
  16. alias Pleroma.Web.WebFinger
  17. plug(
  18. Pleroma.Web.ApiSpec.CastAndValidate,
  19. [replace_params: false]
  20. when action != :remote_subscribe and action != :show_subscribe_form
  21. )
  22. plug(
  23. Pleroma.Web.Plugs.FederatingPlug
  24. when action == :remote_subscribe
  25. when action == :show_subscribe_form
  26. )
  27. plug(
  28. OAuthScopesPlug,
  29. %{scopes: ["write:accounts"]}
  30. when action in [
  31. :change_email,
  32. :change_password,
  33. :delete_account,
  34. :update_notification_settings,
  35. :disable_account,
  36. :move_account,
  37. :add_alias,
  38. :delete_alias
  39. ]
  40. )
  41. plug(
  42. OAuthScopesPlug,
  43. %{scopes: ["read:accounts"]}
  44. when action in [
  45. :list_aliases
  46. ]
  47. )
  48. defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.TwitterUtilOperation
  49. def show_subscribe_form(conn, %{"nickname" => nick}) do
  50. with %User{} = user <- User.get_cached_by_nickname(nick),
  51. avatar = User.avatar_url(user) do
  52. conn
  53. |> render("subscribe.html", %{nickname: nick, avatar: avatar, error: false})
  54. else
  55. _e ->
  56. render(conn, "subscribe.html", %{
  57. nickname: nick,
  58. avatar: nil,
  59. error:
  60. Pleroma.Web.Gettext.dpgettext(
  61. "static_pages",
  62. "remote follow error message - user not found",
  63. "Could not find user"
  64. )
  65. })
  66. end
  67. end
  68. def show_subscribe_form(conn, %{"status_id" => id}) do
  69. with %Activity{} = activity <- Activity.get_by_id(id),
  70. {:ok, ap_id} <- get_ap_id(activity),
  71. %User{} = user <- User.get_cached_by_ap_id(activity.actor),
  72. avatar = User.avatar_url(user) do
  73. conn
  74. |> render("status_interact.html", %{
  75. status_link: ap_id,
  76. status_id: id,
  77. nickname: user.nickname,
  78. avatar: avatar,
  79. error: false
  80. })
  81. else
  82. _e ->
  83. render(conn, "status_interact.html", %{
  84. status_id: id,
  85. avatar: nil,
  86. error:
  87. Pleroma.Web.Gettext.dpgettext(
  88. "static_pages",
  89. "status interact error message - status not found",
  90. "Could not find status"
  91. )
  92. })
  93. end
  94. end
  95. def remote_subscribe(conn, %{"nickname" => nick, "profile" => _}) do
  96. show_subscribe_form(conn, %{"nickname" => nick})
  97. end
  98. def remote_subscribe(conn, %{"status_id" => id, "profile" => _}) do
  99. show_subscribe_form(conn, %{"status_id" => id})
  100. end
  101. def remote_subscribe(conn, %{"user" => %{"nickname" => nick, "profile" => profile}}) do
  102. with {:ok, %{"subscribe_address" => template}} <- WebFinger.finger(profile),
  103. %User{ap_id: ap_id} <- User.get_cached_by_nickname(nick) do
  104. conn
  105. |> Phoenix.Controller.redirect(external: String.replace(template, "{uri}", ap_id))
  106. else
  107. _e ->
  108. render(conn, "subscribe.html", %{
  109. nickname: nick,
  110. avatar: nil,
  111. error:
  112. Pleroma.Web.Gettext.dpgettext(
  113. "static_pages",
  114. "remote follow error message - unknown error",
  115. "Something went wrong."
  116. )
  117. })
  118. end
  119. end
  120. def remote_subscribe(conn, %{"status" => %{"status_id" => id, "profile" => profile}}) do
  121. with {:ok, %{"subscribe_address" => template}} <- WebFinger.finger(profile),
  122. %Activity{} = activity <- Activity.get_by_id(id),
  123. {:ok, ap_id} <- get_ap_id(activity) do
  124. conn
  125. |> Phoenix.Controller.redirect(external: String.replace(template, "{uri}", ap_id))
  126. else
  127. _e ->
  128. render(conn, "status_interact.html", %{
  129. status_id: id,
  130. avatar: nil,
  131. error:
  132. Pleroma.Web.Gettext.dpgettext(
  133. "static_pages",
  134. "status interact error message - unknown error",
  135. "Something went wrong."
  136. )
  137. })
  138. end
  139. end
  140. def remote_interaction(
  141. %{private: %{open_api_spex: %{body_params: %{ap_id: ap_id, profile: profile}}}} = conn,
  142. _params
  143. ) do
  144. with {:ok, %{"subscribe_address" => template}} <- WebFinger.finger(profile) do
  145. conn
  146. |> json(%{url: String.replace(template, "{uri}", ap_id)})
  147. else
  148. _e -> json(conn, %{error: "Couldn't find user"})
  149. end
  150. end
  151. defp get_ap_id(activity) do
  152. object = Pleroma.Object.normalize(activity, fetch: false)
  153. case object do
  154. %{data: %{"id" => ap_id}} -> {:ok, ap_id}
  155. _ -> {:no_ap_id, nil}
  156. end
  157. end
  158. def frontend_configurations(conn, _params) do
  159. render(conn, "frontend_configurations.json")
  160. end
  161. def emoji(conn, _params) do
  162. emoji =
  163. Enum.reduce(Emoji.get_all(), %{}, fn {code, %Emoji{file: file, tags: tags}}, acc ->
  164. Map.put(acc, code, %{image_url: file, tags: tags})
  165. end)
  166. json(conn, emoji)
  167. end
  168. def update_notification_settings(%{assigns: %{user: user}} = conn, params) do
  169. with {:ok, _} <- User.update_notification_settings(user, params) do
  170. json(conn, %{status: "success"})
  171. end
  172. end
  173. def change_password(
  174. %{assigns: %{user: user}, private: %{open_api_spex: %{body_params: body_params}}} = conn,
  175. _
  176. ) do
  177. with {:ok, %User{}} <-
  178. Authenticator.change_password(
  179. user,
  180. body_params.password,
  181. body_params.new_password,
  182. body_params.new_password_confirmation
  183. ) do
  184. json(conn, %{status: "success"})
  185. else
  186. {:error, %Ecto.Changeset{} = changeset} ->
  187. {_, {error, _}} = Enum.at(changeset.errors, 0)
  188. json(conn, %{error: "New password #{error}."})
  189. {:error, :password_confirmation} ->
  190. json(conn, %{error: "New password does not match confirmation."})
  191. {:error, msg} ->
  192. json(conn, %{error: msg})
  193. end
  194. end
  195. def change_email(
  196. %{assigns: %{user: user}, private: %{open_api_spex: %{body_params: body_params}}} = conn,
  197. _
  198. ) do
  199. case CommonAPI.Utils.confirm_current_password(user, body_params.password) do
  200. {:ok, user} ->
  201. with {:ok, _user} <- User.change_email(user, body_params.email) do
  202. json(conn, %{status: "success"})
  203. else
  204. {:error, changeset} ->
  205. {_, {error, _}} = Enum.at(changeset.errors, 0)
  206. json(conn, %{error: "Email #{error}."})
  207. _ ->
  208. json(conn, %{error: "Unable to change email."})
  209. end
  210. {:error, msg} ->
  211. json(conn, %{error: msg})
  212. end
  213. end
  214. def delete_account(
  215. %{
  216. assigns: %{user: user},
  217. private: %{open_api_spex: %{body_params: body_params, params: params}}
  218. } = conn,
  219. _
  220. ) do
  221. # This endpoint can accept a query param or JSON body for backwards-compatibility.
  222. # Submitting a JSON body is recommended, so passwords don't end up in server logs.
  223. password = body_params[:password] || params[:password] || ""
  224. case CommonAPI.Utils.confirm_current_password(user, password) do
  225. {:ok, user} ->
  226. User.delete(user)
  227. json(conn, %{status: "success"})
  228. {:error, msg} ->
  229. json(conn, %{error: msg})
  230. end
  231. end
  232. def disable_account(
  233. %{assigns: %{user: user}, private: %{open_api_spex: %{params: params}}} = conn,
  234. _
  235. ) do
  236. case CommonAPI.Utils.confirm_current_password(user, params[:password]) do
  237. {:ok, user} ->
  238. User.set_activation_async(user, false)
  239. json(conn, %{status: "success"})
  240. {:error, msg} ->
  241. json(conn, %{error: msg})
  242. end
  243. end
  244. def move_account(
  245. %{assigns: %{user: user}, private: %{open_api_spex: %{body_params: body_params}}} = conn,
  246. _
  247. ) do
  248. case CommonAPI.Utils.confirm_current_password(user, body_params.password) do
  249. {:ok, user} ->
  250. with {:ok, target_user} <- find_or_fetch_user_by_nickname(body_params.target_account),
  251. {:ok, _user} <- ActivityPub.move(user, target_user) do
  252. json(conn, %{status: "success"})
  253. else
  254. {:not_found, _} ->
  255. conn
  256. |> put_status(404)
  257. |> json(%{error: "Target account not found."})
  258. {:error, error} ->
  259. json(conn, %{error: error})
  260. end
  261. {:error, msg} ->
  262. json(conn, %{error: msg})
  263. end
  264. end
  265. def add_alias(
  266. %{assigns: %{user: user}, private: %{open_api_spex: %{body_params: body_params}}} = conn,
  267. _
  268. ) do
  269. with {:ok, alias_user} <- find_user_by_nickname(body_params.alias),
  270. {:ok, _user} <- user |> User.add_alias(alias_user) do
  271. json(conn, %{status: "success"})
  272. else
  273. {:not_found, _} ->
  274. conn
  275. |> put_status(404)
  276. |> json(%{error: "Target account does not exist."})
  277. {:error, error} ->
  278. json(conn, %{error: error})
  279. end
  280. end
  281. def delete_alias(
  282. %{assigns: %{user: user}, private: %{open_api_spex: %{body_params: body_params}}} = conn,
  283. _
  284. ) do
  285. with {:ok, alias_user} <- find_user_by_nickname(body_params.alias),
  286. {:ok, _user} <- user |> User.delete_alias(alias_user) do
  287. json(conn, %{status: "success"})
  288. else
  289. {:error, :no_such_alias} ->
  290. conn
  291. |> put_status(404)
  292. |> json(%{error: "Account has no such alias."})
  293. {:error, error} ->
  294. json(conn, %{error: error})
  295. end
  296. end
  297. def list_aliases(%{assigns: %{user: user}} = conn, _) do
  298. alias_nicks =
  299. user
  300. |> User.alias_users()
  301. |> Enum.map(&User.full_nickname/1)
  302. json(conn, %{aliases: alias_nicks})
  303. end
  304. defp find_user_by_nickname(nickname) do
  305. user = User.get_cached_by_nickname(nickname)
  306. if user == nil do
  307. {:error, :not_found}
  308. else
  309. {:ok, user}
  310. end
  311. end
  312. defp find_or_fetch_user_by_nickname(nickname) do
  313. user = User.get_by_nickname(nickname)
  314. if user != nil and user.local do
  315. {:ok, user}
  316. else
  317. with {:ok, user} <- User.fetch_by_nickname(nickname) do
  318. {:ok, user}
  319. else
  320. _ ->
  321. {:not_found, nil}
  322. end
  323. end
  324. end
  325. def captcha(conn, _params) do
  326. json(conn, Pleroma.Captcha.new())
  327. end
  328. def healthcheck(conn, _params) do
  329. with {:cfg, true} <- {:cfg, Config.get([:instance, :healthcheck])},
  330. %{healthy: true} = info <- Healthcheck.system_info() do
  331. json(conn, info)
  332. else
  333. %{healthy: false} = info ->
  334. service_unavailable(conn, info)
  335. {:cfg, false} ->
  336. service_unavailable(conn, %{"error" => "Healthcheck disabled"})
  337. _ ->
  338. service_unavailable(conn, %{})
  339. end
  340. end
  341. defp service_unavailable(conn, info) do
  342. conn
  343. |> put_status(:service_unavailable)
  344. |> json(info)
  345. end
  346. end