logo

pleroma

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

util_controller.ex (11272B)


  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.CommonAPI
  14. alias Pleroma.Web.Plugs.OAuthScopesPlug
  15. alias Pleroma.Web.WebFinger
  16. plug(
  17. Pleroma.Web.ApiSpec.CastAndValidate,
  18. [replace_params: false]
  19. when action != :remote_subscribe and action != :show_subscribe_form
  20. )
  21. plug(
  22. Pleroma.Web.Plugs.FederatingPlug
  23. when action == :remote_subscribe
  24. when action == :show_subscribe_form
  25. )
  26. plug(
  27. OAuthScopesPlug,
  28. %{scopes: ["write:accounts"]}
  29. when action in [
  30. :change_email,
  31. :change_password,
  32. :delete_account,
  33. :update_notification_settings,
  34. :disable_account,
  35. :move_account,
  36. :add_alias,
  37. :delete_alias
  38. ]
  39. )
  40. plug(
  41. OAuthScopesPlug,
  42. %{scopes: ["read:accounts"]}
  43. when action in [
  44. :list_aliases
  45. ]
  46. )
  47. defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.TwitterUtilOperation
  48. def show_subscribe_form(conn, %{"nickname" => nick}) do
  49. with %User{} = user <- User.get_cached_by_nickname(nick),
  50. avatar = User.avatar_url(user) do
  51. conn
  52. |> render("subscribe.html", %{nickname: nick, avatar: avatar, error: false})
  53. else
  54. _e ->
  55. render(conn, "subscribe.html", %{
  56. nickname: nick,
  57. avatar: nil,
  58. error:
  59. Pleroma.Web.Gettext.dpgettext(
  60. "static_pages",
  61. "remote follow error message - user not found",
  62. "Could not find user"
  63. )
  64. })
  65. end
  66. end
  67. def show_subscribe_form(conn, %{"status_id" => id}) do
  68. with %Activity{} = activity <- Activity.get_by_id(id),
  69. {:ok, ap_id} <- get_ap_id(activity),
  70. %User{} = user <- User.get_cached_by_ap_id(activity.actor),
  71. avatar = User.avatar_url(user) do
  72. conn
  73. |> render("status_interact.html", %{
  74. status_link: ap_id,
  75. status_id: id,
  76. nickname: user.nickname,
  77. avatar: avatar,
  78. error: false
  79. })
  80. else
  81. _e ->
  82. render(conn, "status_interact.html", %{
  83. status_id: id,
  84. avatar: nil,
  85. error:
  86. Pleroma.Web.Gettext.dpgettext(
  87. "static_pages",
  88. "status interact error message - status not found",
  89. "Could not find status"
  90. )
  91. })
  92. end
  93. end
  94. def remote_subscribe(conn, %{"nickname" => nick, "profile" => _}) do
  95. show_subscribe_form(conn, %{"nickname" => nick})
  96. end
  97. def remote_subscribe(conn, %{"status_id" => id, "profile" => _}) do
  98. show_subscribe_form(conn, %{"status_id" => id})
  99. end
  100. def remote_subscribe(conn, %{"user" => %{"nickname" => nick, "profile" => profile}}) do
  101. with {:ok, %{"subscribe_address" => template}} <- WebFinger.finger(profile),
  102. %User{ap_id: ap_id} <- User.get_cached_by_nickname(nick) do
  103. conn
  104. |> Phoenix.Controller.redirect(external: String.replace(template, "{uri}", ap_id))
  105. else
  106. _e ->
  107. render(conn, "subscribe.html", %{
  108. nickname: nick,
  109. avatar: nil,
  110. error:
  111. Pleroma.Web.Gettext.dpgettext(
  112. "static_pages",
  113. "remote follow error message - unknown error",
  114. "Something went wrong."
  115. )
  116. })
  117. end
  118. end
  119. def remote_subscribe(conn, %{"status" => %{"status_id" => id, "profile" => profile}}) do
  120. with {:ok, %{"subscribe_address" => template}} <- WebFinger.finger(profile),
  121. %Activity{} = activity <- Activity.get_by_id(id),
  122. {:ok, ap_id} <- get_ap_id(activity) do
  123. conn
  124. |> Phoenix.Controller.redirect(external: String.replace(template, "{uri}", ap_id))
  125. else
  126. _e ->
  127. render(conn, "status_interact.html", %{
  128. status_id: id,
  129. avatar: nil,
  130. error:
  131. Pleroma.Web.Gettext.dpgettext(
  132. "static_pages",
  133. "status interact error message - unknown error",
  134. "Something went wrong."
  135. )
  136. })
  137. end
  138. end
  139. def remote_interaction(
  140. %{private: %{open_api_spex: %{body_params: %{ap_id: ap_id, profile: profile}}}} = conn,
  141. _params
  142. ) do
  143. with {:ok, %{"subscribe_address" => template}} <- WebFinger.finger(profile) do
  144. conn
  145. |> json(%{url: String.replace(template, "{uri}", ap_id)})
  146. else
  147. _e -> json(conn, %{error: "Couldn't find user"})
  148. end
  149. end
  150. defp get_ap_id(activity) do
  151. object = Pleroma.Object.normalize(activity, fetch: false)
  152. case object do
  153. %{data: %{"id" => ap_id}} -> {:ok, ap_id}
  154. _ -> {:no_ap_id, nil}
  155. end
  156. end
  157. def frontend_configurations(conn, _params) do
  158. render(conn, "frontend_configurations.json")
  159. end
  160. def emoji(conn, _params) do
  161. emoji =
  162. Enum.reduce(Emoji.get_all(), %{}, fn {code, %Emoji{file: file, tags: tags}}, acc ->
  163. Map.put(acc, code, %{image_url: file, tags: tags})
  164. end)
  165. json(conn, emoji)
  166. end
  167. def update_notification_settings(%{assigns: %{user: user}} = conn, params) do
  168. with {:ok, _} <- User.update_notification_settings(user, params) do
  169. json(conn, %{status: "success"})
  170. end
  171. end
  172. def change_password(
  173. %{assigns: %{user: user}, private: %{open_api_spex: %{body_params: body_params}}} = conn,
  174. _
  175. ) do
  176. case CommonAPI.Utils.confirm_current_password(user, body_params.password) do
  177. {:ok, user} ->
  178. with {:ok, _user} <-
  179. User.reset_password(user, %{
  180. password: body_params.new_password,
  181. password_confirmation: body_params.new_password_confirmation
  182. }) do
  183. json(conn, %{status: "success"})
  184. else
  185. {:error, changeset} ->
  186. {_, {error, _}} = Enum.at(changeset.errors, 0)
  187. json(conn, %{error: "New password #{error}."})
  188. _ ->
  189. json(conn, %{error: "Unable to change password."})
  190. end
  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