user.ex 8.64 KB
Newer Older
kaniini's avatar
kaniini committed
1 2 3 4
# Pleroma: A lightweight social networking server
# Copyright © 2017-2018 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only

Jorty's avatar
Jorty committed
5 6
defmodule Mix.Tasks.Pleroma.User do
  use Mix.Task
7
  import Ecto.Changeset
8
  alias Mix.Tasks.Pleroma.Common
Haelwenn's avatar
Haelwenn committed
9 10
  alias Pleroma.Repo
  alias Pleroma.User
Jorty's avatar
Jorty committed
11 12 13 14 15 16 17 18 19 20 21 22 23 24

  @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
25
  - `--admin`/`--no-admin` - whether the user is an admin
26
  - `-y`, `--assume-yes`/`--no-assume-yes` - whether to assume yes to all questions 
Rin Toshaka's avatar
Rin Toshaka committed
27

Rin Toshaka's avatar
Rin Toshaka committed
28
  ## Generate an invite link.
Maksim's avatar
Maksim committed
29

30
      mix pleroma.user invite
Jorty's avatar
Jorty committed
31 32 33 34 35 36 37 38

  ## Delete the user's account.

      mix pleroma.user rm NICKNAME

  ## Deactivate or activate the user's account.

      mix pleroma.user toggle_activated NICKNAME
rinpatch's avatar
Oops  
rinpatch committed
39

40
  ## Unsubscribe local users from user's account and deactivate it
Maksim's avatar
Maksim committed
41

42
      mix pleroma.user unsubscribe NICKNAME
Jorty's avatar
Jorty committed
43 44 45 46 47 48 49 50 51 52 53 54

  ## 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
55
  - `--admin`/`--no-admin` - whether the user is an admin
kaniini's avatar
kaniini committed
56 57 58 59 60 61 62 63

  ## Add tags to a user.

      mix pleroma.user tag NICKNAME TAGS

  ## Delete tags from a user.

      mix pleroma.user untag NICKNAME TAGS
Jorty's avatar
Jorty committed
64 65 66 67 68 69 70 71 72
  """
  def run(["new", nickname, email | rest]) do
    {options, [], []} =
      OptionParser.parse(
        rest,
        strict: [
          name: :string,
          bio: :string,
          password: :string,
73
          moderator: :boolean,
74 75 76 77 78
          admin: :boolean,
          assume_yes: :boolean
        ],
        aliases: [
          y: :assume_yes
Jorty's avatar
Jorty committed
79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94
        ]
      )

    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)
95
    admin? = Keyword.get(options, :admin, false)
96
    assume_yes? = Keyword.get(options, :assume_yes, false)
Jorty's avatar
Jorty committed
97 98 99 100 101 102 103 104 105 106 107

    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")}
108
      - admin: #{if(admin?, do: "true", else: "false")}
Jorty's avatar
Jorty committed
109 110
    """)

111
    proceed? = assume_yes? or Mix.shell().yes?("Continue?")
Jorty's avatar
Jorty committed
112 113

    unless not proceed? do
114
      Common.start_pleroma()
Jorty's avatar
Jorty committed
115

116 117 118 119 120 121 122 123
      params = %{
        nickname: nickname,
        email: email,
        password: password,
        password_confirmation: password,
        name: name,
        bio: bio
      }
Jorty's avatar
Jorty committed
124

125 126
      changeset = User.register_changeset(%User{}, params, confirmed: true)
      {:ok, _user} = User.register(changeset)
Jorty's avatar
Jorty committed
127 128 129 130 131 132 133

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

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

134 135 136 137
      if admin? do
        run(["set", nickname, "--admin"])
      end

Jorty's avatar
Jorty committed
138 139 140 141 142 143 144 145 146
      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
147
    Common.start_pleroma()
Jorty's avatar
Jorty committed
148 149 150

    with %User{local: true} = user <- User.get_by_nickname(nickname) do
      User.delete(user)
151 152 153 154
      Mix.shell().info("User #{nickname} deleted.")
    else
      _ ->
        Mix.shell().error("No local user #{nickname}")
Jorty's avatar
Jorty committed
155 156 157 158
    end
  end

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

161
    with %User{} = user <- User.get_by_nickname(nickname) do
162 163 164 165 166
      {:ok, user} = User.deactivate(user, !user.info.deactivated)

      Mix.shell().info(
        "Activation status of #{nickname}: #{if(user.info.deactivated, do: "de", else: "")}activated"
      )
167 168
    else
      _ ->
169
        Mix.shell().error("No user #{nickname}")
Jorty's avatar
Jorty committed
170 171 172 173
    end
  end

  def run(["reset_password", nickname]) do
174
    Common.start_pleroma()
Jorty's avatar
Jorty committed
175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192

    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
193 194 195
  end

  def run(["unsubscribe", nickname]) do
196
    Common.start_pleroma()
197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214

    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)

Haelwenn's avatar
Haelwenn committed
215
      if Enum.empty?(user.following) do
216 217 218 219 220 221
        Mix.shell().info("Successfully unsubscribed all followers from #{user.nickname}")
      end
    else
      _ ->
        Mix.shell().error("No user #{nickname}")
    end
Jorty's avatar
Jorty committed
222 223 224
  end

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

Jorty's avatar
Jorty committed
227 228 229 230 231
    {options, [], []} =
      OptionParser.parse(
        rest,
        strict: [
          moderator: :boolean,
232
          admin: :boolean,
Jorty's avatar
Jorty committed
233 234 235 236 237
          locked: :boolean
        ]
      )

    with %User{local: true} = user <- User.get_by_nickname(nickname) do
238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254
      user =
        case Keyword.get(options, :moderator) do
          nil -> user
          value -> set_moderator(user, value)
        end

      user =
        case Keyword.get(options, :locked) do
          nil -> user
          value -> set_locked(user, value)
        end

      _user =
        case Keyword.get(options, :admin) do
          nil -> user
          value -> set_admin(user, value)
        end
Jorty's avatar
Jorty committed
255 256 257 258 259 260
    else
      _ ->
        Mix.shell().error("No local user #{nickname}")
    end
  end

kaniini's avatar
kaniini committed
261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286
  def run(["tag", nickname | tags]) do
    Common.start_pleroma()

    with %User{} = user <- User.get_by_nickname(nickname) do
      user = user |> User.tag(tags)

      Mix.shell().info("Tags of #{user.nickname}: #{inspect(tags)}")
    else
      _ ->
        Mix.shell().error("Could not change user tags for #{nickname}")
    end
  end

  def run(["untag", nickname | tags]) do
    Common.start_pleroma()

    with %User{} = user <- User.get_by_nickname(nickname) do
      user = user |> User.untag(tags)

      Mix.shell().info("Tags of #{user.nickname}: #{inspect(tags)}")
    else
      _ ->
        Mix.shell().error("Could not change user tags for #{nickname}")
    end
  end

Maksim's avatar
Maksim committed
287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306
  def run(["invite"]) do
    Common.start_pleroma()

    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.")
    end
  end

307
  defp set_moderator(user, value) do
308
    info_cng = User.Info.admin_api_update(user.info, %{is_moderator: value})
Rin Toshaka's avatar
Rin Toshaka committed
309 310 311 312

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

314 315 316
    {:ok, user} = User.update_and_set_cache(user_cng)

    Mix.shell().info("Moderator status of #{user.nickname}: #{user.info.is_moderator}")
317
    user
318
  end
319

320
  defp set_admin(user, value) do
321
    info_cng = User.Info.admin_api_update(user.info, %{is_admin: value})
Rin Toshaka's avatar
Rin Toshaka committed
322 323 324 325

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

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

329 330
    Mix.shell().info("Admin status of #{user.nickname}: #{user.info.is_admin}")
    user
331 332 333
  end

  defp set_locked(user, value) do
334
    info_cng = User.Info.user_upgrade(user.info, %{locked: value})
Rin Toshaka's avatar
Rin Toshaka committed
335 336 337 338

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

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

342
    Mix.shell().info("Locked status of #{user.nickname}: #{user.info.locked}")
343
    user
344
  end
Jorty's avatar
Jorty committed
345
end