logo

pleroma

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

user.ex (13065B)


  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 Mix.Tasks.Pleroma.User do
  5. use Mix.Task
  6. import Mix.Pleroma
  7. alias Ecto.Changeset
  8. alias Pleroma.User
  9. alias Pleroma.UserInviteToken
  10. alias Pleroma.Web.ActivityPub.Builder
  11. alias Pleroma.Web.ActivityPub.Pipeline
  12. @shortdoc "Manages Pleroma users"
  13. @moduledoc File.read!("docs/administration/CLI_tasks/user.md")
  14. def run(["new", nickname, email | rest]) do
  15. {options, [], []} =
  16. OptionParser.parse(
  17. rest,
  18. strict: [
  19. name: :string,
  20. bio: :string,
  21. password: :string,
  22. moderator: :boolean,
  23. admin: :boolean,
  24. assume_yes: :boolean
  25. ],
  26. aliases: [
  27. y: :assume_yes
  28. ]
  29. )
  30. name = Keyword.get(options, :name, nickname)
  31. bio = Keyword.get(options, :bio, "")
  32. {password, generated_password?} =
  33. case Keyword.get(options, :password) do
  34. nil ->
  35. {:crypto.strong_rand_bytes(16) |> Base.encode64(), true}
  36. password ->
  37. {password, false}
  38. end
  39. moderator? = Keyword.get(options, :moderator, false)
  40. admin? = Keyword.get(options, :admin, false)
  41. assume_yes? = Keyword.get(options, :assume_yes, false)
  42. shell_info("""
  43. A user will be created with the following information:
  44. - nickname: #{nickname}
  45. - email: #{email}
  46. - password: #{if(generated_password?, do: "[generated; a reset link will be created]", else: password)}
  47. - name: #{name}
  48. - bio: #{bio}
  49. - moderator: #{if(moderator?, do: "true", else: "false")}
  50. - admin: #{if(admin?, do: "true", else: "false")}
  51. """)
  52. proceed? = assume_yes? or shell_prompt("Continue?", "n") in ~w(Yn Y y)
  53. if proceed? do
  54. start_pleroma()
  55. params = %{
  56. nickname: nickname,
  57. email: email,
  58. password: password,
  59. password_confirmation: password,
  60. name: name,
  61. bio: bio
  62. }
  63. changeset = User.register_changeset(%User{}, params, is_confirmed: true)
  64. {:ok, _user} = User.register(changeset)
  65. shell_info("User #{nickname} created")
  66. if moderator? do
  67. run(["set", nickname, "--moderator"])
  68. end
  69. if admin? do
  70. run(["set", nickname, "--admin"])
  71. end
  72. if generated_password? do
  73. run(["reset_password", nickname])
  74. end
  75. else
  76. shell_info("User will not be created.")
  77. end
  78. end
  79. def run(["rm", nickname]) do
  80. start_pleroma()
  81. with %User{local: true} = user <- User.get_cached_by_nickname(nickname),
  82. {:ok, delete_data, _} <- Builder.delete(user, user.ap_id),
  83. {:ok, _delete, _} <- Pipeline.common_pipeline(delete_data, local: true) do
  84. shell_info("User #{nickname} deleted.")
  85. else
  86. _ -> shell_error("No local user #{nickname}")
  87. end
  88. end
  89. def run(["reset_password", nickname]) do
  90. start_pleroma()
  91. with %User{local: true} = user <- User.get_cached_by_nickname(nickname),
  92. {:ok, token} <- Pleroma.PasswordResetToken.create_token(user) do
  93. shell_info("Generated password reset token for #{user.nickname}")
  94. url =
  95. Pleroma.Web.Router.Helpers.reset_password_url(Pleroma.Web.Endpoint, :reset, token.token)
  96. IO.puts("URL: #{url}")
  97. else
  98. _ ->
  99. shell_error("No local user #{nickname}")
  100. end
  101. end
  102. def run(["reset_mfa", nickname]) do
  103. start_pleroma()
  104. with %User{local: true} = user <- User.get_cached_by_nickname(nickname),
  105. {:ok, _token} <- Pleroma.MFA.disable(user) do
  106. shell_info("Multi-Factor Authentication disabled for #{user.nickname}")
  107. else
  108. _ ->
  109. shell_error("No local user #{nickname}")
  110. end
  111. end
  112. def run(["activate", nickname]) do
  113. start_pleroma()
  114. with %User{} = user <- User.get_cached_by_nickname(nickname),
  115. false <- user.is_active do
  116. User.set_activation(user, true)
  117. :timer.sleep(500)
  118. shell_info("Successfully activated #{nickname}")
  119. else
  120. true ->
  121. shell_info("User #{nickname} already activated")
  122. _ ->
  123. shell_error("No user #{nickname}")
  124. end
  125. end
  126. def run(["deactivate", nickname]) do
  127. start_pleroma()
  128. with %User{} = user <- User.get_cached_by_nickname(nickname),
  129. true <- user.is_active do
  130. User.set_activation(user, false)
  131. :timer.sleep(500)
  132. user = User.get_cached_by_id(user.id)
  133. if Enum.empty?(Enum.filter(User.get_friends(user), & &1.local)) do
  134. shell_info("Successfully deactivated #{nickname} and unsubscribed all local followers")
  135. end
  136. else
  137. false ->
  138. shell_info("User #{nickname} already deactivated")
  139. _ ->
  140. shell_error("No user #{nickname}")
  141. end
  142. end
  143. def run(["deactivate_all_from_instance", instance]) do
  144. start_pleroma()
  145. Pleroma.User.Query.build(%{nickname: "@#{instance}"})
  146. |> Pleroma.Repo.chunk_stream(500, :batches)
  147. |> Stream.each(fn users ->
  148. users
  149. |> Enum.each(fn user ->
  150. run(["deactivate", user.nickname])
  151. end)
  152. end)
  153. |> Stream.run()
  154. end
  155. def run(["set", nickname | rest]) do
  156. start_pleroma()
  157. {options, [], []} =
  158. OptionParser.parse(
  159. rest,
  160. strict: [
  161. admin: :boolean,
  162. confirmed: :boolean,
  163. locked: :boolean,
  164. moderator: :boolean
  165. ]
  166. )
  167. with %User{local: true} = user <- User.get_cached_by_nickname(nickname) do
  168. user =
  169. case Keyword.get(options, :admin) do
  170. nil -> user
  171. value -> set_admin(user, value)
  172. end
  173. user =
  174. case Keyword.get(options, :confirmed) do
  175. nil -> user
  176. value -> set_confirmation(user, value)
  177. end
  178. user =
  179. case Keyword.get(options, :locked) do
  180. nil -> user
  181. value -> set_locked(user, value)
  182. end
  183. _user =
  184. case Keyword.get(options, :moderator) do
  185. nil -> user
  186. value -> set_moderator(user, value)
  187. end
  188. else
  189. _ ->
  190. shell_error("No local user #{nickname}")
  191. end
  192. end
  193. def run(["tag", nickname | tags]) do
  194. start_pleroma()
  195. with %User{} = user <- User.get_cached_by_nickname(nickname) do
  196. user = user |> User.tag(tags)
  197. shell_info("Tags of #{user.nickname}: #{inspect(user.tags)}")
  198. else
  199. _ ->
  200. shell_error("Could not change user tags for #{nickname}")
  201. end
  202. end
  203. def run(["untag", nickname | tags]) do
  204. start_pleroma()
  205. with %User{} = user <- User.get_cached_by_nickname(nickname) do
  206. user = user |> User.untag(tags)
  207. shell_info("Tags of #{user.nickname}: #{inspect(user.tags)}")
  208. else
  209. _ ->
  210. shell_error("Could not change user tags for #{nickname}")
  211. end
  212. end
  213. def run(["invite" | rest]) do
  214. {options, [], []} =
  215. OptionParser.parse(rest,
  216. strict: [
  217. expires_at: :string,
  218. max_use: :integer
  219. ]
  220. )
  221. options =
  222. options
  223. |> Keyword.update(:expires_at, {:ok, nil}, fn
  224. nil -> {:ok, nil}
  225. val -> Date.from_iso8601(val)
  226. end)
  227. |> Enum.into(%{})
  228. start_pleroma()
  229. with {:ok, val} <- options[:expires_at],
  230. options = Map.put(options, :expires_at, val),
  231. {:ok, invite} <- UserInviteToken.create_invite(options) do
  232. shell_info("Generated user invite token " <> String.replace(invite.invite_type, "_", " "))
  233. url =
  234. Pleroma.Web.Router.Helpers.redirect_url(
  235. Pleroma.Web.Endpoint,
  236. :registration_page,
  237. invite.token
  238. )
  239. IO.puts(url)
  240. else
  241. error ->
  242. shell_error("Could not create invite token: #{inspect(error)}")
  243. end
  244. end
  245. def run(["invites"]) do
  246. start_pleroma()
  247. shell_info("Invites list:")
  248. UserInviteToken.list_invites()
  249. |> Enum.each(fn invite ->
  250. expire_info =
  251. with expires_at when not is_nil(expires_at) <- invite.expires_at do
  252. " | Expires at: #{Date.to_string(expires_at)}"
  253. end
  254. using_info =
  255. with max_use when not is_nil(max_use) <- invite.max_use do
  256. " | Max use: #{max_use} Left use: #{max_use - invite.uses}"
  257. end
  258. shell_info(
  259. "ID: #{invite.id} | Token: #{invite.token} | Token type: #{invite.invite_type} | Used: #{invite.used}#{expire_info}#{using_info}"
  260. )
  261. end)
  262. end
  263. def run(["revoke_invite", token]) do
  264. start_pleroma()
  265. with {:ok, invite} <- UserInviteToken.find_by_token(token),
  266. {:ok, _} <- UserInviteToken.update_invite(invite, %{used: true}) do
  267. shell_info("Invite for token #{token} was revoked.")
  268. else
  269. _ -> shell_error("No invite found with token #{token}")
  270. end
  271. end
  272. def run(["delete_activities", nickname]) do
  273. start_pleroma()
  274. with %User{local: true} = user <- User.get_cached_by_nickname(nickname) do
  275. User.delete_user_activities(user)
  276. shell_info("User #{nickname} statuses deleted.")
  277. else
  278. _ ->
  279. shell_error("No local user #{nickname}")
  280. end
  281. end
  282. def run(["confirm", nickname]) do
  283. start_pleroma()
  284. with %User{} = user <- User.get_cached_by_nickname(nickname) do
  285. {:ok, user} = User.confirm(user)
  286. message = if !user.is_confirmed, do: "needs", else: "doesn't need"
  287. shell_info("#{nickname} #{message} confirmation.")
  288. else
  289. _ ->
  290. shell_error("No local user #{nickname}")
  291. end
  292. end
  293. def run(["confirm_all"]) do
  294. start_pleroma()
  295. Pleroma.User.Query.build(%{
  296. local: true,
  297. is_active: true,
  298. is_moderator: false,
  299. is_admin: false,
  300. invisible: false
  301. })
  302. |> Pleroma.Repo.chunk_stream(500, :batches)
  303. |> Stream.each(fn users ->
  304. users
  305. |> Enum.each(fn user -> User.set_confirmation(user, true) end)
  306. end)
  307. |> Stream.run()
  308. end
  309. def run(["unconfirm_all"]) do
  310. start_pleroma()
  311. Pleroma.User.Query.build(%{
  312. local: true,
  313. is_active: true,
  314. is_moderator: false,
  315. is_admin: false,
  316. invisible: false
  317. })
  318. |> Pleroma.Repo.chunk_stream(500, :batches)
  319. |> Stream.each(fn users ->
  320. users
  321. |> Enum.each(fn user -> User.set_confirmation(user, false) end)
  322. end)
  323. |> Stream.run()
  324. end
  325. def run(["sign_out", nickname]) do
  326. start_pleroma()
  327. with %User{local: true} = user <- User.get_cached_by_nickname(nickname) do
  328. User.global_sign_out(user)
  329. shell_info("#{nickname} signed out from all apps.")
  330. else
  331. _ ->
  332. shell_error("No local user #{nickname}")
  333. end
  334. end
  335. def run(["list"]) do
  336. start_pleroma()
  337. Pleroma.User.Query.build(%{local: true})
  338. |> Pleroma.Repo.chunk_stream(500, :batches)
  339. |> Stream.each(fn users ->
  340. users
  341. |> Enum.each(fn user ->
  342. shell_info(
  343. "#{user.nickname} moderator: #{user.is_moderator}, admin: #{user.is_admin}, locked: #{user.is_locked}, is_active: #{user.is_active}"
  344. )
  345. end)
  346. end)
  347. |> Stream.run()
  348. end
  349. def run(["fix_follow_state", local_user, remote_user]) do
  350. start_pleroma()
  351. with {:local, %User{} = local} <- {:local, User.get_by_nickname(local_user)},
  352. {:remote, %User{} = remote} <- {:remote, User.get_by_nickname(remote_user)},
  353. {:follow_data, %{data: %{"state" => request_state}}} <-
  354. {:follow_data, Pleroma.Web.ActivityPub.Utils.fetch_latest_follow(local, remote)} do
  355. calculated_state = User.following?(local, remote)
  356. shell_info(
  357. "Request state is #{request_state}, vs calculated state of following=#{calculated_state}"
  358. )
  359. if calculated_state == false && request_state == "accept" do
  360. shell_info("Discrepancy found, fixing")
  361. Pleroma.Web.CommonAPI.reject_follow_request(local, remote)
  362. shell_info("Relationship fixed")
  363. else
  364. shell_info("No discrepancy found")
  365. end
  366. else
  367. {:local, _} ->
  368. shell_error("No local user #{local_user}")
  369. {:remote, _} ->
  370. shell_error("No remote user #{remote_user}")
  371. {:follow_data, _} ->
  372. shell_error("No follow data for #{local_user} and #{remote_user}")
  373. end
  374. end
  375. defp set_moderator(user, value) do
  376. {:ok, user} =
  377. user
  378. |> Changeset.change(%{is_moderator: value})
  379. |> User.update_and_set_cache()
  380. shell_info("Moderator status of #{user.nickname}: #{user.is_moderator}")
  381. user
  382. end
  383. defp set_admin(user, value) do
  384. {:ok, user} = User.admin_api_update(user, %{is_admin: value})
  385. shell_info("Admin status of #{user.nickname}: #{user.is_admin}")
  386. user
  387. end
  388. defp set_locked(user, value) do
  389. {:ok, user} =
  390. user
  391. |> Changeset.change(%{is_locked: value})
  392. |> User.update_and_set_cache()
  393. shell_info("Locked status of #{user.nickname}: #{user.is_locked}")
  394. user
  395. end
  396. defp set_confirmation(user, value) do
  397. {:ok, user} = User.set_confirmation(user, value)
  398. shell_info("Confirmation status of #{user.nickname}: #{user.is_confirmed}")
  399. user
  400. end
  401. end