user.ex 7.29 KB
Newer Older
Jorty's avatar
Jorty committed
1
2
defmodule Mix.Tasks.Pleroma.User do
  use Mix.Task
3
  import Ecto.Changeset
Jorty's avatar
Jorty committed
4
  alias Pleroma.{Repo, User}
5
  alias Mix.Tasks.Pleroma.Common
Jorty's avatar
Jorty committed
6
7
8
9
10
11
12
13
14
15
16
17
18
19

  @shortdoc "Manages Pleroma users"
  @moduledoc """
  Manages Pleroma users.

  ## Create a new user.

      mix pleroma.user new NICKNAME EMAIL [OPTION...]

  Options:
  - `--name NAME` - the user's name (i.e., "Lain Iwakura")
  - `--bio BIO` - the user's bio
  - `--password PASSWORD` - the user's password
  - `--moderator`/`--no-moderator` - whether the user is a moderator
20
  - `--admin`/`--no-admin` - whether the user is an admin
Rin Toshaka's avatar
Rin Toshaka committed
21

Rin Toshaka's avatar
Rin Toshaka committed
22
23
  ## Generate an invite link.
    
24
      mix pleroma.user invite
Jorty's avatar
Jorty committed
25
26
27
28
29
30
31
32

  ## Delete the user's account.

      mix pleroma.user rm NICKNAME

  ## Deactivate or activate the user's account.

      mix pleroma.user toggle_activated NICKNAME
33
34
35
36
  
  ## Unsubscribe local users from user's account and deactivate it
     
      mix pleroma.user unsubscribe NICKNAME
Jorty's avatar
Jorty committed
37
38
39
40
41
42
43
44
45
46
47
48

  ## Create a password reset link.

      mix pleroma.user reset_password NICKNAME

  ## Set the value of the given user's settings.

      mix pleroma.user set NICKNAME [OPTION...]

  Options:
  - `--locked`/`--no-locked` - whether the user's account is locked
  - `--moderator`/`--no-moderator` - whether the user is a moderator
49
  - `--admin`/`--no-admin` - whether the user is an admin
Jorty's avatar
Jorty committed
50
51
52
53
54
55
56
57
58
  """
  def run(["new", nickname, email | rest]) do
    {options, [], []} =
      OptionParser.parse(
        rest,
        strict: [
          name: :string,
          bio: :string,
          password: :string,
59
60
          moderator: :boolean,
          admin: :boolean
Jorty's avatar
Jorty committed
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
        ]
      )

    name = Keyword.get(options, :name, nickname)
    bio = Keyword.get(options, :bio, "")

    {password, generated_password?} =
      case Keyword.get(options, :password) do
        nil ->
          {:crypto.strong_rand_bytes(16) |> Base.encode64(), true}

        password ->
          {password, false}
      end

    moderator? = Keyword.get(options, :moderator, false)
77
    admin? = Keyword.get(options, :admin, false)
Jorty's avatar
Jorty committed
78
79
80
81
82
83
84
85
86
87
88

    Mix.shell().info("""
    A user will be created with the following information:
      - nickname: #{nickname}
      - email: #{email}
      - password: #{
      if(generated_password?, do: "[generated; a reset link will be created]", else: password)
    }
      - name: #{name}
      - bio: #{bio}
      - moderator: #{if(moderator?, do: "true", else: "false")}
89
      - admin: #{if(admin?, do: "true", else: "false")}
Jorty's avatar
Jorty committed
90
91
92
93
94
    """)

    proceed? = Mix.shell().yes?("Continue?")

    unless not proceed? do
95
      Common.start_pleroma()
Jorty's avatar
Jorty committed
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116

      params =
        %{
          nickname: nickname,
          email: email,
          password: password,
          password_confirmation: password,
          name: name,
          bio: bio
        }
        |> IO.inspect()

      user = User.register_changeset(%User{}, params)
      Repo.insert!(user)

      Mix.shell().info("User #{nickname} created")

      if moderator? do
        run(["set", nickname, "--moderator"])
      end

117
118
119
120
      if admin? do
        run(["set", nickname, "--admin"])
      end

Jorty's avatar
Jorty committed
121
122
123
124
125
126
127
128
129
      if generated_password? do
        run(["reset_password", nickname])
      end
    else
      Mix.shell().info("User will not be created.")
    end
  end

  def run(["rm", nickname]) do
130
    Common.start_pleroma()
Jorty's avatar
Jorty committed
131
132
133

    with %User{local: true} = user <- User.get_by_nickname(nickname) do
      User.delete(user)
134
135
136
137
      Mix.shell().info("User #{nickname} deleted.")
    else
      _ ->
        Mix.shell().error("No local user #{nickname}")
Jorty's avatar
Jorty committed
138
139
140
141
    end
  end

  def run(["toggle_activated", nickname]) do
142
    Common.start_pleroma()
Jorty's avatar
Jorty committed
143

144
    with %User{} = user <- User.get_by_nickname(nickname) do
145
      User.deactivate(user, !user.info["deactivated"])
146
147
148
      Mix.shell().info("Activation status of #{nickname}: #{user.info["deactivated"]}")
    else
      _ ->
149
        Mix.shell().error("No user #{nickname}")
Jorty's avatar
Jorty committed
150
151
152
153
    end
  end

  def run(["reset_password", nickname]) do
154
    Common.start_pleroma()
Jorty's avatar
Jorty committed
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172

    with %User{local: true} = user <- User.get_by_nickname(nickname),
         {:ok, token} <- Pleroma.PasswordResetToken.create_token(user) do
      Mix.shell().info("Generated password reset token for #{user.nickname}")

      IO.puts(
        "URL: #{
          Pleroma.Web.Router.Helpers.util_url(
            Pleroma.Web.Endpoint,
            :show_password_reset,
            token.token
          )
        }"
      )
    else
      _ ->
        Mix.shell().error("No local user #{nickname}")
    end
173
174
175
  end

  def run(["unsubscribe", nickname]) do
176
    Common.start_pleroma()
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201

    with %User{} = user <- User.get_by_nickname(nickname) do
      Mix.shell().info("Deactivating #{user.nickname}")
      User.deactivate(user)

      {:ok, friends} = User.get_friends(user)

      Enum.each(friends, fn friend ->
        user = Repo.get(User, user.id)

        Mix.shell().info("Unsubscribing #{friend.nickname} from #{user.nickname}")
        User.unfollow(user, friend)
      end)

      :timer.sleep(500)

      user = Repo.get(User, user.id)

      if length(user.following) == 0 do
        Mix.shell().info("Successfully unsubscribed all followers from #{user.nickname}")
      end
    else
      _ ->
        Mix.shell().error("No user #{nickname}")
    end
Jorty's avatar
Jorty committed
202
203
204
  end

  def run(["set", nickname | rest]) do
205
    Common.start_pleroma()
206

Jorty's avatar
Jorty committed
207
208
209
210
211
    {options, [], []} =
      OptionParser.parse(
        rest,
        strict: [
          moderator: :boolean,
212
          admin: :boolean,
Jorty's avatar
Jorty committed
213
214
215
216
217
          locked: :boolean
        ]
      )

    with %User{local: true} = user <- User.get_by_nickname(nickname) do
218
219
220
221
      case Keyword.get(options, :moderator) do
        nil -> nil
        value -> set_moderator(user, value)
      end
Jorty's avatar
Jorty committed
222

223
224
225
226
      case Keyword.get(options, :locked) do
        nil -> nil
        value -> set_locked(user, value)
      end
Jorty's avatar
Jorty committed
227

228
229
230
231
      case Keyword.get(options, :admin) do
        nil -> nil
        value -> set_admin(user, value)
      end
Jorty's avatar
Jorty committed
232
233
234
235
236
237
    else
      _ ->
        Mix.shell().error("No local user #{nickname}")
    end
  end

238
  defp set_moderator(user, value) do
239
    info_cng = User.Info.admin_api_update(user.info, %{is_moderator: value})
Rin Toshaka's avatar
Rin Toshaka committed
240
241
242
243

    user_cng =
      Ecto.Changeset.change(user)
      |> put_embed(:info, info_cng)
244

245
246
247
    {:ok, user} = User.update_and_set_cache(user_cng)

    Mix.shell().info("Moderator status of #{user.nickname}: #{user.info.is_moderator}")
248
  end
249

250
  defp set_admin(user, value) do
251
    info_cng = User.Info.admin_api_update(user.info, %{is_admin: value})
Rin Toshaka's avatar
Rin Toshaka committed
252
253
254
255

    user_cng =
      Ecto.Changeset.change(user)
      |> put_embed(:info, info_cng)
256
257

    {:ok, user} = User.update_and_set_cache(user_cng)
258

259
    Mix.shell().info("Admin status of #{user.nickname}: #{user.info.is_moderator}")
260
261
262
  end

  defp set_locked(user, value) do
263
    info_cng = User.Info.user_upgrade(user.info, %{locked: value})
Rin Toshaka's avatar
Rin Toshaka committed
264
265
266
267

    user_cng =
      Ecto.Changeset.change(user)
      |> put_embed(:info, info_cng)
268
269

    {:ok, user} = User.update_and_set_cache(user_cng)
270

271
    Mix.shell().info("Locked status of #{user.nickname}: #{user.info.locked}")
272
273
  end

Rin Toshaka's avatar
Rin Toshaka committed
274
  def run(["invite"]) do
275
    Common.start_pleroma()
Rin Toshaka's avatar
Rin Toshaka committed
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290

    with {:ok, token} <- Pleroma.UserInviteToken.create_token() do
      Mix.shell().info("Generated user invite token")

      url =
        Pleroma.Web.Router.Helpers.redirect_url(
          Pleroma.Web.Endpoint,
          :registration_page,
          token.token
        )

      IO.puts(url)
    else
      _ ->
        Mix.shell().error("Could not create invite token.")
Rin Toshaka's avatar
Rin Toshaka committed
291
    end
Rin Toshaka's avatar
Rin Toshaka committed
292
  end
Jorty's avatar
Jorty committed
293
end