diff options
Diffstat (limited to 'lib/mix/tasks')
| -rw-r--r-- | lib/mix/tasks/deactivate_user.ex | 19 | ||||
| -rw-r--r-- | lib/mix/tasks/generate_config.ex | 47 | ||||
| -rw-r--r-- | lib/mix/tasks/generate_invite_token.ex | 32 | ||||
| -rw-r--r-- | lib/mix/tasks/generate_password_reset.ex | 33 | ||||
| -rw-r--r-- | lib/mix/tasks/make_moderator.ex | 37 | ||||
| -rw-r--r-- | lib/mix/tasks/pleroma/gen_instance.ex | 161 | ||||
| -rw-r--r-- | lib/mix/tasks/pleroma/gen_invite.ex | 24 | ||||
| -rw-r--r-- | lib/mix/tasks/pleroma/sample_config.eex (renamed from lib/mix/tasks/sample_config.eex) | 12 | ||||
| -rw-r--r-- | lib/mix/tasks/pleroma/sample_psql.eex | 9 | ||||
| -rw-r--r-- | lib/mix/tasks/pleroma/user.ex | 207 | ||||
| -rw-r--r-- | lib/mix/tasks/register_user.ex | 30 | ||||
| -rw-r--r-- | lib/mix/tasks/rm_user.ex | 19 | ||||
| -rw-r--r-- | lib/mix/tasks/sample_psql.eex | 6 | ||||
| -rw-r--r-- | lib/mix/tasks/set_locked.ex | 39 | 
14 files changed, 409 insertions, 266 deletions
| diff --git a/lib/mix/tasks/deactivate_user.ex b/lib/mix/tasks/deactivate_user.ex deleted file mode 100644 index e71ed1ec0..000000000 --- a/lib/mix/tasks/deactivate_user.ex +++ /dev/null @@ -1,19 +0,0 @@ -defmodule Mix.Tasks.DeactivateUser do -  use Mix.Task -  alias Pleroma.User - -  @moduledoc """ -  Deactivates a user (local or remote) - -  Usage: ``mix deactivate_user <nickname>`` - -  Example: ``mix deactivate_user lain`` -  """ -  def run([nickname]) do -    Mix.Task.run("app.start") - -    with user <- User.get_by_nickname(nickname) do -      User.deactivate(user) -    end -  end -end diff --git a/lib/mix/tasks/generate_config.ex b/lib/mix/tasks/generate_config.ex deleted file mode 100644 index e3cbbf131..000000000 --- a/lib/mix/tasks/generate_config.ex +++ /dev/null @@ -1,47 +0,0 @@ -defmodule Mix.Tasks.GenerateConfig do -  use Mix.Task - -  @moduledoc """ -  Generate a new config - -  ## Usage -  ``mix generate_config`` - -  This mix task is interactive, and will overwrite the config present at ``config/generated_config.exs``. -  """ - -  def run(_) do -    IO.puts("Answer a few questions to generate a new config\n") -    IO.puts("--- THIS WILL OVERWRITE YOUR config/generated_config.exs! ---\n") -    domain = IO.gets("What is your domain name? (e.g. pleroma.soykaf.com): ") |> String.trim() -    name = IO.gets("What is the name of your instance? (e.g. Pleroma/Soykaf): ") |> String.trim() -    email = IO.gets("What's your admin email address: ") |> String.trim() - -    secret = :crypto.strong_rand_bytes(64) |> Base.encode64() |> binary_part(0, 64) -    dbpass = :crypto.strong_rand_bytes(64) |> Base.encode64() |> binary_part(0, 64) - -    resultSql = EEx.eval_file("lib/mix/tasks/sample_psql.eex", dbpass: dbpass) - -    result = -      EEx.eval_file( -        "lib/mix/tasks/sample_config.eex", -        domain: domain, -        email: email, -        name: name, -        secret: secret, -        dbpass: dbpass -      ) - -    IO.puts( -      "\nWriting config to config/generated_config.exs.\n\nCheck it and configure your database, then copy it to either config/dev.secret.exs or config/prod.secret.exs" -    ) - -    File.write("config/generated_config.exs", result) - -    IO.puts( -      "\nWriting setup_db.psql, please run it as postgre superuser, i.e.: sudo su postgres -c 'psql -f config/setup_db.psql'" -    ) - -    File.write("config/setup_db.psql", resultSql) -  end -end diff --git a/lib/mix/tasks/generate_invite_token.ex b/lib/mix/tasks/generate_invite_token.ex deleted file mode 100644 index 418ef3790..000000000 --- a/lib/mix/tasks/generate_invite_token.ex +++ /dev/null @@ -1,32 +0,0 @@ -defmodule Mix.Tasks.GenerateInviteToken do -  use Mix.Task - -  @moduledoc """ -  Generates invite token - -  This is in the form of a URL to be used by the Invited user to register themselves. - -  ## Usage -  ``mix generate_invite_token`` -  """ -  def run([]) do -    Mix.Task.run("app.start") - -    with {:ok, token} <- Pleroma.UserInviteToken.create_token() do -      IO.puts("Generated user invite token") - -      IO.puts( -        "Url: #{ -          Pleroma.Web.Router.Helpers.redirect_url( -            Pleroma.Web.Endpoint, -            :registration_page, -            token.token -          ) -        }" -      ) -    else -      _ -> -        IO.puts("Error creating token") -    end -  end -end diff --git a/lib/mix/tasks/generate_password_reset.ex b/lib/mix/tasks/generate_password_reset.ex deleted file mode 100644 index f7f4c4f59..000000000 --- a/lib/mix/tasks/generate_password_reset.ex +++ /dev/null @@ -1,33 +0,0 @@ -defmodule Mix.Tasks.GeneratePasswordReset do -  use Mix.Task -  alias Pleroma.User - -  @moduledoc """ -  Generate password reset link for user - -  Usage: ``mix generate_password_reset <nickname>`` - -  Example: ``mix generate_password_reset lain`` -  """ -  def run([nickname]) do -    Mix.Task.run("app.start") - -    with %User{local: true} = user <- User.get_by_nickname(nickname), -         {:ok, token} <- Pleroma.PasswordResetToken.create_token(user) do -      IO.puts("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 -      _ -> -        IO.puts("No local user #{nickname}") -    end -  end -end diff --git a/lib/mix/tasks/make_moderator.ex b/lib/mix/tasks/make_moderator.ex deleted file mode 100644 index 15586dc30..000000000 --- a/lib/mix/tasks/make_moderator.ex +++ /dev/null @@ -1,37 +0,0 @@ -defmodule Mix.Tasks.SetModerator do -  @moduledoc """ -  Set moderator to a local user - -  Usage: ``mix set_moderator <nickname>`` - -  Example: ``mix set_moderator lain`` -  """ - -  use Mix.Task -  import Mix.Ecto -  alias Pleroma.{Repo, User} - -  def run([nickname | rest]) do -    Application.ensure_all_started(:pleroma) - -    moderator = -      case rest do -        [moderator] -> moderator == "true" -        _ -> true -      end - -    with %User{local: true} = user <- User.get_by_nickname(nickname) do -      info = -        user.info -        |> Map.put("is_moderator", !!moderator) - -      cng = User.info_changeset(user, %{info: info}) -      {:ok, user} = User.update_and_set_cache(cng) - -      IO.puts("Moderator status of #{nickname}: #{user.info["is_moderator"]}") -    else -      _ -> -        IO.puts("No local user #{nickname}") -    end -  end -end diff --git a/lib/mix/tasks/pleroma/gen_instance.ex b/lib/mix/tasks/pleroma/gen_instance.ex new file mode 100644 index 000000000..94f2220b1 --- /dev/null +++ b/lib/mix/tasks/pleroma/gen_instance.ex @@ -0,0 +1,161 @@ +defmodule Mix.Tasks.Pleroma.Gen.Instance do +  use Mix.Task + +  @shortdoc "Generates the configuration for a new instance" +  @moduledoc """ +  Generates the configuration for a new instance. + +  If any options are left unspecified, you will be prompted interactively. This +  means the simplest invocation would be + +      mix pleroma.gen.instance + +  ## Options + +  - `-f`, `--force` - overwrite any output files +  - `-o PATH`, `--output PATH` - the output file for the generated configuration +  - `--output-psql PATH` - the output file for the generated PostgreSQL setup +  - `--domain DOMAIN` - the domain of your instance +  - `--instance-name INSTANCE_NAME` - the name of your instance +  - `--admin-email ADMIN_EMAIL` - the email address of the instance admin +  - `--dbhost HOSTNAME` - the hostname of the PostgreSQL database to use +  - `--dbname DBNAME` - the name of the database to use +  - `--dbuser DBUSER` - the user (aka role) to use for the database connection +  - `--dbpass DBPASS` - the password to use for the database connection +  """ + +  def run(rest) do +    {options, [], []} = +      OptionParser.parse( +        rest, +        strict: [ +          force: :boolean, +          output: :string, +          output_psql: :string, +          domain: :string, +          instance_name: :string, +          admin_email: :string, +          dbhost: :string, +          dbname: :string, +          dbuser: :string, +          dbpass: :string +        ], +        aliases: [ +          o: :output, +          f: :force +        ] +      ) + +    paths = +      [config_path, psql_path] = [ +        Keyword.get(options, :output, "config/generated_config.exs"), +        Keyword.get(options, :output_psql, "config/setup_db.psql") +      ] + +    will_overwrite = Enum.filter(paths, &File.exists?/1) +    proceed? = Enum.empty?(will_overwrite) or Keyword.get(options, :force, false) + +    unless not proceed? do +      domain = +        Keyword.get(options, :domain) || +          Mix.shell().prompt("What domain will your instance use? (e.g. pleroma.soykaf.com)") +          |> String.trim() + +      name = +        Keyword.get(options, :name) || +          Mix.shell().prompt("What is the name of your instance? (e.g. Pleroma/Soykaf)") +          |> String.trim() + +      email = +        Keyword.get(options, :admin_email) || +          Mix.shell().prompt("What is your admin email address?") +          |> String.trim() + +      dbhost = +        Keyword.get(options, :dbhost) || +          case Mix.shell().prompt("What is the hostname of your database? [localhost]") do +            "\n" -> "localhost" +            dbhost -> dbhost |> String.trim() +          end + +      dbname = +        Keyword.get(options, :dbname) || +          case Mix.shell().prompt("What is the name of your database? [pleroma_dev]") do +            "\n" -> "pleroma_dev" +            dbname -> dbname |> String.trim() +          end + +      dbuser = +        Keyword.get(options, :dbuser) || +          case Mix.shell().prompt("What is the user used to connect to your database? [pleroma]") do +            "\n" -> "pleroma" +            dbuser -> dbuser |> String.trim() +          end + +      dbpass = +        Keyword.get(options, :dbpass) || +          case Mix.shell().prompt( +                 "What is the password used to connect to your database? [autogenerated]" +               ) do +            "\n" -> :crypto.strong_rand_bytes(64) |> Base.encode64() |> binary_part(0, 64) +            dbpass -> dbpass |> String.trim() +          end + +      secret = :crypto.strong_rand_bytes(64) |> Base.encode64() |> binary_part(0, 64) + +      result_config = +        EEx.eval_file( +          "sample_config.eex" |> Path.expand(__DIR__), +          domain: domain, +          email: email, +          name: name, +          dbhost: dbhost, +          dbname: dbname, +          dbuser: dbuser, +          dbpass: dbpass, +          version: Pleroma.Mixfile.project() |> Keyword.get(:version), +          secret: secret +        ) + +      result_psql = +        EEx.eval_file( +          "sample_psql.eex" |> Path.expand(__DIR__), +          dbname: dbname, +          dbuser: dbuser, +          dbpass: dbpass +        ) + +      Mix.shell().info( +        "Writing config to #{config_path}. You should rename it to config/prod.secret.exs or config/dev.secret.exs." +      ) + +      File.write(config_path, result_config) +      Mix.shell().info("Writing #{psql_path}.") +      File.write(psql_path, result_psql) + +      Mix.shell().info( +        "\n" <> +          """ +          To get started: +          1. Verify the contents of the generated files. +          2. Run `sudo -u postgres psql -f #{escape_sh_path(psql_path)}`. +          """ <> +          if config_path in ["config/dev.secret.exs", "config/prod.secret.exs"] do +            "" +          else +            "3. Run `mv #{escape_sh_path(config_path)} 'config/prod.secret.exs'`." +          end +      ) +    else +      Mix.shell().error( +        "The task would have overwritten the following files:\n" <> +          (Enum.map(paths, &"- #{&1}\n") |> Enum.join("")) <> +          "Rerun with `--force` to overwrite them." +      ) +    end +  end + +  defp escape_sh_path(path) do +    ~S(') <> String.replace(path, ~S('), ~S(\')) <> ~S(') +  end +end diff --git a/lib/mix/tasks/pleroma/gen_invite.ex b/lib/mix/tasks/pleroma/gen_invite.ex new file mode 100644 index 000000000..0aa028f1e --- /dev/null +++ b/lib/mix/tasks/pleroma/gen_invite.ex @@ -0,0 +1,24 @@ +defmodule Mix.Tasks.Pleroma.Gen.Invite do +  use Mix.Task + +  @shortdoc "Generates a user invite token" +  def run([]) do +    Mix.Task.run("app.start") + +    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: #{url}") +    else +      _ -> +        Mix.shell().error("Could not create invite token.") +    end +  end +end diff --git a/lib/mix/tasks/sample_config.eex b/lib/mix/tasks/pleroma/sample_config.eex index 462c34636..df9d1ad65 100644 --- a/lib/mix/tasks/sample_config.eex +++ b/lib/mix/tasks/pleroma/sample_config.eex @@ -1,3 +1,8 @@ +# Pleroma instance configuration + +# NOTE: This file should not be committed to a repo or otherwise made public +# without removing sensitive information. +  use Mix.Config  config :pleroma, Pleroma.Web.Endpoint, @@ -16,13 +21,12 @@ config :pleroma, :media_proxy,    redirect_on_failure: true    #base_url: "https://cache.pleroma.social" -# Configure your database  config :pleroma, Pleroma.Repo,    adapter: Ecto.Adapters.Postgres, -  username: "pleroma", +  username: "<%= dbuser %>",    password: "<%= dbpass %>", -  database: "pleroma_dev", -  hostname: "localhost", +  database: "<%= dbname %>", +  hostname: "<%= dbhost %>",    pool_size: 10  # Enable Strict-Transport-Security once SSL is working: diff --git a/lib/mix/tasks/pleroma/sample_psql.eex b/lib/mix/tasks/pleroma/sample_psql.eex new file mode 100644 index 000000000..66f76752f --- /dev/null +++ b/lib/mix/tasks/pleroma/sample_psql.eex @@ -0,0 +1,9 @@ +CREATE USER <%= dbuser %> WITH ENCRYPTED PASSWORD '<%= dbpass %>' CREATEDB; +-- in case someone runs this second time accidentally +ALTER USER <%= dbuser %> WITH ENCRYPTED PASSWORD '<%= dbpass %>' CREATEDB; +CREATE DATABASE <%= dbname %>; +ALTER DATABASE <%= dbname %> OWNER TO <%= dbuser %>; +\c <%= dbname %>; +--Extensions made by ecto.migrate that need superuser access +CREATE EXTENSION IF NOT EXISTS citext; +CREATE EXTENSION IF NOT EXISTS pg_trgm; diff --git a/lib/mix/tasks/pleroma/user.ex b/lib/mix/tasks/pleroma/user.ex new file mode 100644 index 000000000..c20fecaa1 --- /dev/null +++ b/lib/mix/tasks/pleroma/user.ex @@ -0,0 +1,207 @@ +defmodule Mix.Tasks.Pleroma.User do +  use Mix.Task +  alias Pleroma.{Repo, User} + +  @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 + +  ## Delete the user's account. + +      mix pleroma.user rm NICKNAME + +  ## Deactivate or activate the user's account. + +      mix pleroma.user toggle_activated NICKNAME + +  ## 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 +  """ + +  def run(["new", nickname, email | rest]) do +    {options, [], []} = +      OptionParser.parse( +        rest, +        strict: [ +          name: :string, +          bio: :string, +          password: :string, +          moderator: :boolean +        ] +      ) + +    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) + +    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")} +    """) + +    proceed? = Mix.shell().yes?("Continue?") + +    unless not proceed? do +      Mix.Task.run("app.start") + +      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 + +      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 +    Mix.Task.run("app.start") + +    with %User{local: true} = user <- User.get_by_nickname(nickname) do +      User.delete(user) +    end + +    Mix.shell().info("User #{nickname} deleted.") +  end + +  def run(["toggle_activated", nickname]) do +    Mix.Task.run("app.start") + +    with user <- User.get_by_nickname(nickname) do +      User.deactivate(user) +    end +  end + +  def run(["reset_password", nickname]) do +    Mix.Task.run("app.start") + +    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 +  end + +  def run(["set", nickname | rest]) do +    {options, [], []} = +      OptionParser.parse( +        rest, +        strict: [ +          moderator: :boolean, +          locked: :boolean +        ] +      ) + +    case Keyword.get(options, :moderator) do +      nil -> nil +      value -> set_moderator(nickname, value) +    end + +    case Keyword.get(options, :locked) do +      nil -> nil +      value -> set_locked(nickname, value) +    end +  end + +  defp set_moderator(nickname, value) do +    Application.ensure_all_started(:pleroma) + +    with %User{local: true} = user <- User.get_by_nickname(nickname) do +      info = +        user.info +        |> Map.put("is_moderator", value) + +      cng = User.info_changeset(user, %{info: info}) +      {:ok, user} = User.update_and_set_cache(cng) + +      Mix.shell().info("Moderator status of #{nickname}: #{user.info["is_moderator"]}") +    else +      _ -> +        Mix.shell().error("No local user #{nickname}") +    end +  end + +  defp set_locked(nickname, value) do +    Mix.Ecto.ensure_started(Repo, []) + +    with %User{local: true} = user <- User.get_by_nickname(nickname) do +      info = +        user.info +        |> Map.put("locked", value) + +      cng = User.info_changeset(user, %{info: info}) +      user = Repo.update!(cng) + +      IO.puts("Locked status of #{nickname}: #{user.info["locked"]}") +    else +      _ -> +        IO.puts("No local user #{nickname}") +    end +  end +end diff --git a/lib/mix/tasks/register_user.ex b/lib/mix/tasks/register_user.ex deleted file mode 100644 index 1f5321093..000000000 --- a/lib/mix/tasks/register_user.ex +++ /dev/null @@ -1,30 +0,0 @@ -defmodule Mix.Tasks.RegisterUser do -  @moduledoc """ -  Manually register a local user - -  Usage: ``mix register_user <name> <nickname> <email> <bio> <password>`` - -  Example: ``mix register_user 仮面の告白 lain lain@example.org "blushy-crushy fediverse idol + pleroma dev" pleaseDontHeckLain`` -  """ - -  use Mix.Task -  alias Pleroma.{Repo, User} - -  @shortdoc "Register user" -  def run([name, nickname, email, bio, password]) do -    Mix.Task.run("app.start") - -    params = %{ -      name: name, -      nickname: nickname, -      email: email, -      password: password, -      password_confirmation: password, -      bio: bio -    } - -    user = User.register_changeset(%User{}, params) - -    Repo.insert!(user) -  end -end diff --git a/lib/mix/tasks/rm_user.ex b/lib/mix/tasks/rm_user.ex deleted file mode 100644 index 50463046c..000000000 --- a/lib/mix/tasks/rm_user.ex +++ /dev/null @@ -1,19 +0,0 @@ -defmodule Mix.Tasks.RmUser do -  use Mix.Task -  alias Pleroma.User - -  @moduledoc """ -  Permanently deletes a user - -  Usage: ``mix rm_user [nickname]`` - -  Example: ``mix rm_user lain`` -  """ -  def run([nickname]) do -    Mix.Task.run("app.start") - -    with %User{local: true} = user <- User.get_by_nickname(nickname) do -      {:ok, _} = User.delete(user) -    end -  end -end diff --git a/lib/mix/tasks/sample_psql.eex b/lib/mix/tasks/sample_psql.eex deleted file mode 100644 index b6f57948b..000000000 --- a/lib/mix/tasks/sample_psql.eex +++ /dev/null @@ -1,6 +0,0 @@ -CREATE USER pleroma WITH ENCRYPTED PASSWORD '<%= dbpass %>'; -CREATE DATABASE pleroma_dev OWNER pleroma; -\c pleroma_dev; ---Extensions made by ecto.migrate that need superuser access -CREATE EXTENSION IF NOT EXISTS citext; -CREATE EXTENSION IF NOT EXISTS pg_trgm; diff --git a/lib/mix/tasks/set_locked.ex b/lib/mix/tasks/set_locked.ex deleted file mode 100644 index a154595ca..000000000 --- a/lib/mix/tasks/set_locked.ex +++ /dev/null @@ -1,39 +0,0 @@ -defmodule Mix.Tasks.SetLocked do -  @moduledoc """ -  Lock a local user - -  The local user will then have to manually accept/reject followers. This can also be done by the user into their settings. - -  Usage: ``mix set_locked <username>`` - -  Example: ``mix set_locked lain`` -  """ - -  use Mix.Task -  import Mix.Ecto -  alias Pleroma.{Repo, User} - -  def run([nickname | rest]) do -    ensure_started(Repo, []) - -    locked = -      case rest do -        [locked] -> locked == "true" -        _ -> true -      end - -    with %User{local: true} = user <- User.get_by_nickname(nickname) do -      info = -        user.info -        |> Map.put("locked", !!locked) - -      cng = User.info_changeset(user, %{info: info}) -      user = Repo.update!(cng) - -      IO.puts("locked status of #{nickname}: #{user.info["locked"]}") -    else -      _ -> -        IO.puts("No local user #{nickname}") -    end -  end -end | 
