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