diff options
26 files changed, 444 insertions, 246 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 97f96ffc8..f8711f299 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -173,6 +173,7 @@ amd64:    script: &release      - mix deps.get --only prod      - mkdir release +    - export PLEROMA_BUILD_BRANCH=$CI_COMMIT_REF_NAME      - mix release --path release diff --git a/config/dev.exs b/config/dev.exs index 71b11f7c3..7e1e3b4be 100644 --- a/config/dev.exs +++ b/config/dev.exs @@ -60,5 +60,5 @@ else    )  end -if File.exists?("./config/dev.migrated.secret.exs"), -  do: import_config("./config/dev.migrated.secret.exs") +if File.exists?("./config/dev.exported_from_db.secret.exs"), +  do: import_config("dev.exported_from_db.secret.exs") diff --git a/config/prod.exs b/config/prod.exs index 42edccf64..9c205cbd2 100644 --- a/config/prod.exs +++ b/config/prod.exs @@ -64,5 +64,5 @@ config :logger, level: :warn  # which should be versioned separately.  import_config "prod.secret.exs" -if File.exists?("./config/prod.migrated.secret.exs"), -  do: import_config("./config/prod.migrated.secret.exs") +if File.exists?("./config/prod.exported_from_db.secret.exs"), +  do: import_config("prod.exported_from_db.secret.exs") diff --git a/lib/mix/tasks/pleroma/common.ex b/lib/mix/pleroma.ex index 7d50605af..548c8a0a4 100644 --- a/lib/mix/tasks/pleroma/common.ex +++ b/lib/mix/pleroma.ex @@ -2,7 +2,7 @@  # Copyright © 2017-2018 Pleroma Authors <https://pleroma.social/>  # SPDX-License-Identifier: AGPL-3.0-only -defmodule Mix.Tasks.Pleroma.Common do +defmodule Mix.Pleroma do    @doc "Common functions to be reused in mix tasks"    def start_pleroma do      Application.put_env(:phoenix, :serve_endpoints, false, persistent: true) @@ -14,7 +14,7 @@ defmodule Mix.Tasks.Pleroma.Common do    end    def shell_prompt(prompt, defval \\ nil, defname \\ nil) do -    prompt_message = "#{prompt} [#{defname || defval}]" +    prompt_message = "#{prompt} [#{defname || defval}] "      input =        if mix_shell?(), diff --git a/lib/mix/tasks/benchmark.ex b/lib/mix/tasks/pleroma/benchmark.ex index e4b1a638a..d43db7b35 100644 --- a/lib/mix/tasks/benchmark.ex +++ b/lib/mix/tasks/pleroma/benchmark.ex @@ -1,9 +1,9 @@  defmodule Mix.Tasks.Pleroma.Benchmark do +  import Mix.Pleroma    use Mix.Task -  alias Mix.Tasks.Pleroma.Common    def run(["search"]) do -    Common.start_pleroma() +    start_pleroma()      Benchee.run(%{        "search" => fn -> @@ -13,7 +13,7 @@ defmodule Mix.Tasks.Pleroma.Benchmark do    end    def run(["tag"]) do -    Common.start_pleroma() +    start_pleroma()      Benchee.run(%{        "tag" => fn -> diff --git a/lib/mix/tasks/pleroma/config.ex b/lib/mix/tasks/pleroma/config.ex index 1fe03088d..cc5425362 100644 --- a/lib/mix/tasks/pleroma/config.ex +++ b/lib/mix/tasks/pleroma/config.ex @@ -1,6 +1,6 @@  defmodule Mix.Tasks.Pleroma.Config do    use Mix.Task -  alias Mix.Tasks.Pleroma.Common +  import Mix.Pleroma    alias Pleroma.Repo    alias Pleroma.Web.AdminAPI.Config    @shortdoc "Manages the location of the config" @@ -17,7 +17,7 @@ defmodule Mix.Tasks.Pleroma.Config do    """    def run(["migrate_to_db"]) do -    Common.start_pleroma() +    start_pleroma()      if Pleroma.Config.get([:instance, :dynamic_configuration]) do        Application.get_all_env(:pleroma) @@ -37,12 +37,13 @@ defmodule Mix.Tasks.Pleroma.Config do    end    def run(["migrate_from_db", env]) do -    Common.start_pleroma() +    start_pleroma()      if Pleroma.Config.get([:instance, :dynamic_configuration]) do -      config_path = "config/#{env}.migrated.secret.exs" +      config_path = "config/#{env}.exported_from_db.secret.exs"        {:ok, file} = File.open(config_path, [:write]) +      IO.write(file, "use Mix.Config\r\n")        Repo.all(Config)        |> Enum.each(fn config -> diff --git a/lib/mix/tasks/pleroma/database.ex b/lib/mix/tasks/pleroma/database.ex index 4d480ac3f..e91fb31d1 100644 --- a/lib/mix/tasks/pleroma/database.ex +++ b/lib/mix/tasks/pleroma/database.ex @@ -3,12 +3,12 @@  # SPDX-License-Identifier: AGPL-3.0-only  defmodule Mix.Tasks.Pleroma.Database do -  alias Mix.Tasks.Pleroma.Common    alias Pleroma.Conversation    alias Pleroma.Object    alias Pleroma.Repo    alias Pleroma.User    require Logger +  import Mix.Pleroma    use Mix.Task    @shortdoc "A collection of database related tasks" @@ -45,7 +45,7 @@ defmodule Mix.Tasks.Pleroma.Database do          ]        ) -    Common.start_pleroma() +    start_pleroma()      Logger.info("Removing embedded objects")      Repo.query!( @@ -66,12 +66,12 @@ defmodule Mix.Tasks.Pleroma.Database do    end    def run(["bump_all_conversations"]) do -    Common.start_pleroma() +    start_pleroma()      Conversation.bump_for_all_activities()    end    def run(["update_users_following_followers_counts"]) do -    Common.start_pleroma() +    start_pleroma()      users = Repo.all(User)      Enum.each(users, &User.remove_duplicated_following/1) @@ -89,7 +89,7 @@ defmodule Mix.Tasks.Pleroma.Database do          ]        ) -    Common.start_pleroma() +    start_pleroma()      deadline = Pleroma.Config.get([:instance, :remote_post_retention_days]) diff --git a/lib/mix/tasks/pleroma/instance.ex b/lib/mix/tasks/pleroma/instance.ex index 44e49cb69..c6738dbcc 100644 --- a/lib/mix/tasks/pleroma/instance.ex +++ b/lib/mix/tasks/pleroma/instance.ex @@ -4,7 +4,7 @@  defmodule Mix.Tasks.Pleroma.Instance do    use Mix.Task -  alias Mix.Tasks.Pleroma.Common +  import Mix.Pleroma    @shortdoc "Manages Pleroma instance"    @moduledoc """ @@ -31,6 +31,8 @@ defmodule Mix.Tasks.Pleroma.Instance do    - `--dbpass DBPASS` - the password to use for the database connection    - `--indexable Y/N` - Allow/disallow indexing site by search engines    - `--db-configurable Y/N` - Allow/disallow configuring instance from admin part +  - `--uploads-dir` - the directory uploads go in when using a local uploader +  - `--static-dir` - the directory custom public files should be read from (custom emojis, frontend bundle overrides, robots.txt, etc.)    """    def run(["gen" | rest]) do @@ -50,7 +52,9 @@ defmodule Mix.Tasks.Pleroma.Instance do            dbuser: :string,            dbpass: :string,            indexable: :string, -          db_configurable: :string +          db_configurable: :string, +          uploads_dir: :string, +          static_dir: :string          ],          aliases: [            o: :output, @@ -70,7 +74,7 @@ defmodule Mix.Tasks.Pleroma.Instance do      if proceed? do        [domain, port | _] =          String.split( -          Common.get_option( +          get_option(              options,              :domain,              "What domain will your instance use? (e.g pleroma.soykaf.com)" @@ -79,16 +83,16 @@ defmodule Mix.Tasks.Pleroma.Instance do          ) ++ [443]        name = -        Common.get_option( +        get_option(            options,            :instance_name,            "What is the name of your instance? (e.g. Pleroma/Soykaf)"          ) -      email = Common.get_option(options, :admin_email, "What is your admin email address?") +      email = get_option(options, :admin_email, "What is your admin email address?")        notify_email = -        Common.get_option( +        get_option(            options,            :notify_email,            "What email address do you want to use for sending email notifications?", @@ -96,7 +100,7 @@ defmodule Mix.Tasks.Pleroma.Instance do          )        indexable = -        Common.get_option( +        get_option(            options,            :indexable,            "Do you want search engines to index your site? (y/n)", @@ -104,21 +108,19 @@ defmodule Mix.Tasks.Pleroma.Instance do          ) === "y"        db_configurable? = -        Common.get_option( +        get_option(            options,            :db_configurable, -          "Do you want to be able to configure instance from admin part? (y/n)", +          "Do you want to store the configuration in the database (allows controlling it from admin-fe)? (y/n)",            "y"          ) === "y" -      dbhost = -        Common.get_option(options, :dbhost, "What is the hostname of your database?", "localhost") +      dbhost = get_option(options, :dbhost, "What is the hostname of your database?", "localhost") -      dbname = -        Common.get_option(options, :dbname, "What is the name of your database?", "pleroma_dev") +      dbname = get_option(options, :dbname, "What is the name of your database?", "pleroma_dev")        dbuser = -        Common.get_option( +        get_option(            options,            :dbuser,            "What is the user used to connect to your database?", @@ -126,7 +128,7 @@ defmodule Mix.Tasks.Pleroma.Instance do          )        dbpass = -        Common.get_option( +        get_option(            options,            :dbpass,            "What is the password used to connect to your database?", @@ -134,13 +136,30 @@ defmodule Mix.Tasks.Pleroma.Instance do            "autogenerated"          ) +      uploads_dir = +        get_option( +          options, +          :upload_dir, +          "What directory should media uploads go in (when using the local uploader)?", +          Pleroma.Config.get([Pleroma.Uploaders.Local, :uploads]) +        ) + +      static_dir = +        get_option( +          options, +          :static_dir, +          "What directory should custom public files be read from (custom emojis, frontend bundle overrides, robots.txt, etc.)?", +          Pleroma.Config.get([:instance, :static_dir]) +        ) +        secret = :crypto.strong_rand_bytes(64) |> Base.encode64() |> binary_part(0, 64)        signing_salt = :crypto.strong_rand_bytes(8) |> Base.encode64() |> binary_part(0, 8)        {web_push_public_key, web_push_private_key} = :crypto.generate_key(:ecdh, :prime256v1) +      template_dir = Application.app_dir(:pleroma, "priv") <> "/templates"        result_config =          EEx.eval_file( -          "sample_config.eex" |> Path.expand(__DIR__), +          template_dir <> "/sample_config.eex",            domain: domain,            port: port,            email: email, @@ -150,47 +169,48 @@ defmodule Mix.Tasks.Pleroma.Instance do            dbname: dbname,            dbuser: dbuser,            dbpass: dbpass, -          version: Pleroma.Mixfile.project() |> Keyword.get(:version),            secret: secret,            signing_salt: signing_salt,            web_push_public_key: Base.url_encode64(web_push_public_key, padding: false),            web_push_private_key: Base.url_encode64(web_push_private_key, padding: false), -          db_configurable?: db_configurable? +          db_configurable?: db_configurable?, +          static_dir: static_dir, +          uploads_dir: uploads_dir          )        result_psql =          EEx.eval_file( -          "sample_psql.eex" |> Path.expand(__DIR__), +          template_dir <> "/sample_psql.eex",            dbname: dbname,            dbuser: dbuser,            dbpass: dbpass          ) -      Common.shell_info( +      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) -      Common.shell_info("Writing #{psql_path}.") +      shell_info("Writing #{psql_path}.")        File.write(psql_path, result_psql) -      write_robots_txt(indexable) +      write_robots_txt(indexable, template_dir) -      Common.shell_info( +      shell_info(          "\n" <>            """            To get started:            1. Verify the contents of the generated files. -          2. Run `sudo -u postgres psql -f #{Common.escape_sh_path(psql_path)}`. +          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 #{Common.escape_sh_path(config_path)} 'config/prod.secret.exs'`." +            "3. Run `mv #{escape_sh_path(config_path)} 'config/prod.secret.exs'`."            end        )      else -      Common.shell_error( +      shell_error(          "The task would have overwritten the following files:\n" <>            (Enum.map(paths, &"- #{&1}\n") |> Enum.join("")) <>            "Rerun with `--force` to overwrite them." @@ -198,10 +218,10 @@ defmodule Mix.Tasks.Pleroma.Instance do      end    end -  defp write_robots_txt(indexable) do +  defp write_robots_txt(indexable, template_dir) do      robots_txt =        EEx.eval_file( -        Path.expand("robots_txt.eex", __DIR__), +        template_dir <> "/robots_txt.eex",          indexable: indexable        ) @@ -215,10 +235,10 @@ defmodule Mix.Tasks.Pleroma.Instance do      if File.exists?(robots_txt_path) do        File.cp!(robots_txt_path, "#{robots_txt_path}.bak") -      Common.shell_info("Backing up existing robots.txt to #{robots_txt_path}.bak") +      shell_info("Backing up existing robots.txt to #{robots_txt_path}.bak")      end      File.write(robots_txt_path, robots_txt) -    Common.shell_info("Writing #{robots_txt_path}.") +    shell_info("Writing #{robots_txt_path}.")    end  end diff --git a/lib/mix/tasks/pleroma/relay.ex b/lib/mix/tasks/pleroma/relay.ex index 213ae24d2..83ed0ed02 100644 --- a/lib/mix/tasks/pleroma/relay.ex +++ b/lib/mix/tasks/pleroma/relay.ex @@ -4,7 +4,7 @@  defmodule Mix.Tasks.Pleroma.Relay do    use Mix.Task -  alias Mix.Tasks.Pleroma.Common +  import Mix.Pleroma    alias Pleroma.Web.ActivityPub.Relay    @shortdoc "Manages remote relays" @@ -24,24 +24,24 @@ defmodule Mix.Tasks.Pleroma.Relay do    Example: ``mix pleroma.relay unfollow https://example.org/relay``    """    def run(["follow", target]) do -    Common.start_pleroma() +    start_pleroma()      with {:ok, _activity} <- Relay.follow(target) do        # put this task to sleep to allow the genserver to push out the messages        :timer.sleep(500)      else -      {:error, e} -> Common.shell_error("Error while following #{target}: #{inspect(e)}") +      {:error, e} -> shell_error("Error while following #{target}: #{inspect(e)}")      end    end    def run(["unfollow", target]) do -    Common.start_pleroma() +    start_pleroma()      with {:ok, _activity} <- Relay.unfollow(target) do        # put this task to sleep to allow the genserver to push out the messages        :timer.sleep(500)      else -      {:error, e} -> Common.shell_error("Error while following #{target}: #{inspect(e)}") +      {:error, e} -> shell_error("Error while following #{target}: #{inspect(e)}")      end    end  end diff --git a/lib/mix/tasks/pleroma/uploads.ex b/lib/mix/tasks/pleroma/uploads.ex index 8855b5538..be45383ee 100644 --- a/lib/mix/tasks/pleroma/uploads.ex +++ b/lib/mix/tasks/pleroma/uploads.ex @@ -4,7 +4,7 @@  defmodule Mix.Tasks.Pleroma.Uploads do    use Mix.Task -  alias Mix.Tasks.Pleroma.Common +  import Mix.Pleroma    alias Pleroma.Upload    alias Pleroma.Uploaders.Local    require Logger @@ -24,7 +24,7 @@ defmodule Mix.Tasks.Pleroma.Uploads do    """    def run(["migrate_local", target_uploader | args]) do      delete? = Enum.member?(args, "--delete") -    Common.start_pleroma() +    start_pleroma()      local_path = Pleroma.Config.get!([Local, :uploads])      uploader = Module.concat(Pleroma.Uploaders, target_uploader) @@ -38,10 +38,10 @@ defmodule Mix.Tasks.Pleroma.Uploads do        Pleroma.Config.put([Upload, :uploader], uploader)      end -    Common.shell_info("Migrating files from local #{local_path} to #{to_string(uploader)}") +    shell_info("Migrating files from local #{local_path} to #{to_string(uploader)}")      if delete? do -      Common.shell_info( +      shell_info(          "Attention: uploaded files will be deleted, hope you have backups! (--delete ; cancel with ^C)"        ) @@ -78,7 +78,7 @@ defmodule Mix.Tasks.Pleroma.Uploads do        |> Enum.filter(& &1)      total_count = length(uploads) -    Common.shell_info("Found #{total_count} uploads") +    shell_info("Found #{total_count} uploads")      uploads      |> Task.async_stream( @@ -90,7 +90,7 @@ defmodule Mix.Tasks.Pleroma.Uploads do              :ok            error -> -            Common.shell_error("failed to upload #{inspect(upload.path)}: #{inspect(error)}") +            shell_error("failed to upload #{inspect(upload.path)}: #{inspect(error)}")          end        end,        timeout: 150_000 @@ -99,10 +99,10 @@ defmodule Mix.Tasks.Pleroma.Uploads do      # credo:disable-for-next-line Credo.Check.Warning.UnusedEnumOperation      |> Enum.reduce(0, fn done, count ->        count = count + length(done) -      Common.shell_info("Uploaded #{count}/#{total_count} files") +      shell_info("Uploaded #{count}/#{total_count} files")        count      end) -    Common.shell_info("Done!") +    shell_info("Done!")    end  end diff --git a/lib/mix/tasks/pleroma/user.ex b/lib/mix/tasks/pleroma/user.ex index 7eaa49836..ab158f57e 100644 --- a/lib/mix/tasks/pleroma/user.ex +++ b/lib/mix/tasks/pleroma/user.ex @@ -5,9 +5,10 @@  defmodule Mix.Tasks.Pleroma.User do    use Mix.Task    import Ecto.Changeset -  alias Mix.Tasks.Pleroma.Common +  import Mix.Pleroma    alias Pleroma.User    alias Pleroma.UserInviteToken +  alias Pleroma.Web.OAuth    @shortdoc "Manages Pleroma users"    @moduledoc """ @@ -49,6 +50,10 @@ defmodule Mix.Tasks.Pleroma.User do        mix pleroma.user delete_activities NICKNAME +  ## Sign user out from all applications (delete user's OAuth tokens and authorizations). + +      mix pleroma.user sign_out NICKNAME +    ## Deactivate or activate the user's account.        mix pleroma.user toggle_activated NICKNAME @@ -115,7 +120,7 @@ defmodule Mix.Tasks.Pleroma.User do      admin? = Keyword.get(options, :admin, false)      assume_yes? = Keyword.get(options, :assume_yes, false) -    Common.shell_info(""" +    shell_info("""      A user will be created with the following information:        - nickname: #{nickname}        - email: #{email} @@ -128,10 +133,10 @@ defmodule Mix.Tasks.Pleroma.User do        - admin: #{if(admin?, do: "true", else: "false")}      """) -    proceed? = assume_yes? or Common.shell_yes?("Continue?") +    proceed? = assume_yes? or shell_yes?("Continue?")      if proceed? do -      Common.start_pleroma() +      start_pleroma()        params = %{          nickname: nickname, @@ -145,7 +150,7 @@ defmodule Mix.Tasks.Pleroma.User do        changeset = User.register_changeset(%User{}, params, need_confirmation: false)        {:ok, _user} = User.register(changeset) -      Common.shell_info("User #{nickname} created") +      shell_info("User #{nickname} created")        if moderator? do          run(["set", nickname, "--moderator"]) @@ -159,43 +164,43 @@ defmodule Mix.Tasks.Pleroma.User do          run(["reset_password", nickname])        end      else -      Common.shell_info("User will not be created.") +      shell_info("User will not be created.")      end    end    def run(["rm", nickname]) do -    Common.start_pleroma() +    start_pleroma()      with %User{local: true} = user <- User.get_cached_by_nickname(nickname) do        User.perform(:delete, user) -      Common.shell_info("User #{nickname} deleted.") +      shell_info("User #{nickname} deleted.")      else        _ -> -        Common.shell_error("No local user #{nickname}") +        shell_error("No local user #{nickname}")      end    end    def run(["toggle_activated", nickname]) do -    Common.start_pleroma() +    start_pleroma()      with %User{} = user <- User.get_cached_by_nickname(nickname) do        {:ok, user} = User.deactivate(user, !user.info.deactivated) -      Common.shell_info( +      shell_info(          "Activation status of #{nickname}: #{if(user.info.deactivated, do: "de", else: "")}activated"        )      else        _ -> -        Common.shell_error("No user #{nickname}") +        shell_error("No user #{nickname}")      end    end    def run(["reset_password", nickname]) do -    Common.start_pleroma() +    start_pleroma()      with %User{local: true} = user <- User.get_cached_by_nickname(nickname),           {:ok, token} <- Pleroma.PasswordResetToken.create_token(user) do -      Common.shell_info("Generated password reset token for #{user.nickname}") +      shell_info("Generated password reset token for #{user.nickname}")        IO.puts(          "URL: #{ @@ -208,15 +213,15 @@ defmodule Mix.Tasks.Pleroma.User do        )      else        _ -> -        Common.shell_error("No local user #{nickname}") +        shell_error("No local user #{nickname}")      end    end    def run(["unsubscribe", nickname]) do -    Common.start_pleroma() +    start_pleroma()      with %User{} = user <- User.get_cached_by_nickname(nickname) do -      Common.shell_info("Deactivating #{user.nickname}") +      shell_info("Deactivating #{user.nickname}")        User.deactivate(user)        {:ok, friends} = User.get_friends(user) @@ -224,7 +229,7 @@ defmodule Mix.Tasks.Pleroma.User do        Enum.each(friends, fn friend ->          user = User.get_cached_by_id(user.id) -        Common.shell_info("Unsubscribing #{friend.nickname} from #{user.nickname}") +        shell_info("Unsubscribing #{friend.nickname} from #{user.nickname}")          User.unfollow(user, friend)        end) @@ -233,16 +238,16 @@ defmodule Mix.Tasks.Pleroma.User do        user = User.get_cached_by_id(user.id)        if Enum.empty?(user.following) do -        Common.shell_info("Successfully unsubscribed all followers from #{user.nickname}") +        shell_info("Successfully unsubscribed all followers from #{user.nickname}")        end      else        _ -> -        Common.shell_error("No user #{nickname}") +        shell_error("No user #{nickname}")      end    end    def run(["set", nickname | rest]) do -    Common.start_pleroma() +    start_pleroma()      {options, [], []} =        OptionParser.parse( @@ -274,33 +279,33 @@ defmodule Mix.Tasks.Pleroma.User do          end      else        _ -> -        Common.shell_error("No local user #{nickname}") +        shell_error("No local user #{nickname}")      end    end    def run(["tag", nickname | tags]) do -    Common.start_pleroma() +    start_pleroma()      with %User{} = user <- User.get_cached_by_nickname(nickname) do        user = user |> User.tag(tags) -      Common.shell_info("Tags of #{user.nickname}: #{inspect(tags)}") +      shell_info("Tags of #{user.nickname}: #{inspect(tags)}")      else        _ -> -        Common.shell_error("Could not change user tags for #{nickname}") +        shell_error("Could not change user tags for #{nickname}")      end    end    def run(["untag", nickname | tags]) do -    Common.start_pleroma() +    start_pleroma()      with %User{} = user <- User.get_cached_by_nickname(nickname) do        user = user |> User.untag(tags) -      Common.shell_info("Tags of #{user.nickname}: #{inspect(tags)}") +      shell_info("Tags of #{user.nickname}: #{inspect(tags)}")      else        _ -> -        Common.shell_error("Could not change user tags for #{nickname}") +        shell_error("Could not change user tags for #{nickname}")      end    end @@ -321,14 +326,12 @@ defmodule Mix.Tasks.Pleroma.User do        end)        |> Enum.into(%{}) -    Common.start_pleroma() +    start_pleroma()      with {:ok, val} <- options[:expires_at],           options = Map.put(options, :expires_at, val),           {:ok, invite} <- UserInviteToken.create_invite(options) do -      Common.shell_info( -        "Generated user invite token " <> String.replace(invite.invite_type, "_", " ") -      ) +      shell_info("Generated user invite token " <> String.replace(invite.invite_type, "_", " "))        url =          Pleroma.Web.Router.Helpers.redirect_url( @@ -340,14 +343,14 @@ defmodule Mix.Tasks.Pleroma.User do        IO.puts(url)      else        error -> -        Common.shell_error("Could not create invite token: #{inspect(error)}") +        shell_error("Could not create invite token: #{inspect(error)}")      end    end    def run(["invites"]) do -    Common.start_pleroma() +    start_pleroma() -    Common.shell_info("Invites list:") +    shell_info("Invites list:")      UserInviteToken.list_invites()      |> Enum.each(fn invite -> @@ -361,7 +364,7 @@ defmodule Mix.Tasks.Pleroma.User do            " | Max use: #{max_use}    Left use: #{max_use - invite.uses}"          end -      Common.shell_info( +      shell_info(          "ID: #{invite.id} | Token: #{invite.token} | Token type: #{invite.invite_type} | Used: #{            invite.used          }#{expire_info}#{using_info}" @@ -370,40 +373,54 @@ defmodule Mix.Tasks.Pleroma.User do    end    def run(["revoke_invite", token]) do -    Common.start_pleroma() +    start_pleroma()      with {:ok, invite} <- UserInviteToken.find_by_token(token),           {:ok, _} <- UserInviteToken.update_invite(invite, %{used: true}) do -      Common.shell_info("Invite for token #{token} was revoked.") +      shell_info("Invite for token #{token} was revoked.")      else -      _ -> Common.shell_error("No invite found with token #{token}") +      _ -> shell_error("No invite found with token #{token}")      end    end    def run(["delete_activities", nickname]) do -    Common.start_pleroma() +    start_pleroma()      with %User{local: true} = user <- User.get_cached_by_nickname(nickname) do        {:ok, _} = User.delete_user_activities(user) -      Common.shell_info("User #{nickname} statuses deleted.") +      shell_info("User #{nickname} statuses deleted.")      else        _ -> -        Common.shell_error("No local user #{nickname}") +        shell_error("No local user #{nickname}")      end    end    def run(["toggle_confirmed", nickname]) do -    Common.start_pleroma() +    start_pleroma()      with %User{} = user <- User.get_cached_by_nickname(nickname) do        {:ok, user} = User.toggle_confirmation(user)        message = if user.info.confirmation_pending, do: "needs", else: "doesn't need" -      Common.shell_info("#{nickname} #{message} confirmation.") +      shell_info("#{nickname} #{message} confirmation.") +    else +      _ -> +        shell_error("No local user #{nickname}") +    end +  end + +  def run(["sign_out", nickname]) do +    start_pleroma() + +    with %User{local: true} = user <- User.get_cached_by_nickname(nickname) do +      OAuth.Token.delete_user_tokens(user) +      OAuth.Authorization.delete_user_authorizations(user) + +      shell_info("#{nickname} signed out from all apps.")      else        _ -> -        Common.shell_error("No local user #{nickname}") +        shell_error("No local user #{nickname}")      end    end @@ -416,7 +433,7 @@ defmodule Mix.Tasks.Pleroma.User do      {:ok, user} = User.update_and_set_cache(user_cng) -    Common.shell_info("Moderator status of #{user.nickname}: #{user.info.is_moderator}") +    shell_info("Moderator status of #{user.nickname}: #{user.info.is_moderator}")      user    end @@ -429,7 +446,7 @@ defmodule Mix.Tasks.Pleroma.User do      {:ok, user} = User.update_and_set_cache(user_cng) -    Common.shell_info("Admin status of #{user.nickname}: #{user.info.is_admin}") +    shell_info("Admin status of #{user.nickname}: #{user.info.is_admin}")      user    end @@ -442,7 +459,7 @@ defmodule Mix.Tasks.Pleroma.User do      {:ok, user} = User.update_and_set_cache(user_cng) -    Common.shell_info("Locked status of #{user.nickname}: #{user.info.locked}") +    shell_info("Locked status of #{user.nickname}: #{user.info.locked}")      user    end  end diff --git a/lib/pleroma/release_tasks.ex b/lib/pleroma/release_tasks.ex index eb6eff61c..d6720cd05 100644 --- a/lib/pleroma/release_tasks.ex +++ b/lib/pleroma/release_tasks.ex @@ -17,6 +17,7 @@ defmodule Pleroma.ReleaseTasks do    end    defp mix_task(task, args) do +    Application.load(:pleroma)      {:ok, modules} = :application.get_key(:pleroma, :modules)      module = diff --git a/lib/pleroma/web/oauth/oauth_controller.ex b/lib/pleroma/web/oauth/oauth_controller.ex index 35a7c582e..3f8e3b074 100644 --- a/lib/pleroma/web/oauth/oauth_controller.ex +++ b/lib/pleroma/web/oauth/oauth_controller.ex @@ -64,26 +64,34 @@ defmodule Pleroma.Web.OAuth.OAuthController do    defp handle_existing_authorization(           %Plug.Conn{assigns: %{token: %Token{} = token}} = conn, -         params +         %{"redirect_uri" => @oob_token_redirect_uri}         ) do -    token = Repo.preload(token, :app) +    render(conn, "oob_token_exists.html", %{token: token}) +  end + +  defp handle_existing_authorization( +         %Plug.Conn{assigns: %{token: %Token{} = token}} = conn, +         %{} = params +       ) do +    app = Repo.preload(token, :app).app      redirect_uri =        if is_binary(params["redirect_uri"]) do          params["redirect_uri"]        else -        default_redirect_uri(token.app) +        default_redirect_uri(app)        end -    redirect_uri = redirect_uri(conn, redirect_uri) - -    if redirect_uri == @oob_token_redirect_uri do -      render(conn, "oob_token_exists.html", %{token: token}) -    else +    if redirect_uri in String.split(app.redirect_uris) do +      redirect_uri = redirect_uri(conn, redirect_uri)        url_params = %{access_token: token.token}        url_params = UriHelper.append_param_if_present(url_params, :state, params["state"])        url = UriHelper.append_uri_params(redirect_uri, url_params)        redirect(conn, external: url) +    else +      conn +      |> put_flash(:error, "Unlisted redirect_uri.") +      |> redirect(external: redirect_uri(conn, redirect_uri))      end    end @@ -101,17 +109,27 @@ defmodule Pleroma.Web.OAuth.OAuthController do    end    def after_create_authorization(%Plug.Conn{} = conn, %Authorization{} = auth, %{ +        "authorization" => %{"redirect_uri" => @oob_token_redirect_uri} +      }) do +    render(conn, "oob_authorization_created.html", %{auth: auth}) +  end + +  def after_create_authorization(%Plug.Conn{} = conn, %Authorization{} = auth, %{          "authorization" => %{"redirect_uri" => redirect_uri} = auth_attrs        }) do -    redirect_uri = redirect_uri(conn, redirect_uri) +    app = Repo.preload(auth, :app).app -    if redirect_uri == @oob_token_redirect_uri do -      render(conn, "oob_authorization_created.html", %{auth: auth}) -    else +    # An extra safety measure before we redirect (also done in `do_create_authorization/2`) +    if redirect_uri in String.split(app.redirect_uris) do +      redirect_uri = redirect_uri(conn, redirect_uri)        url_params = %{code: auth.token}        url_params = UriHelper.append_param_if_present(url_params, :state, auth_attrs["state"])        url = UriHelper.append_uri_params(redirect_uri, url_params)        redirect(conn, external: url) +    else +      conn +      |> put_flash(:error, "Unlisted redirect_uri.") +      |> redirect(external: redirect_uri(conn, redirect_uri))      end    end @@ -324,7 +342,7 @@ defmodule Pleroma.Web.OAuth.OAuthController do              })            conn -          |> put_session(:registration_id, registration.id) +          |> put_session_registration_id(registration.id)            |> registration_details(%{"authorization" => registration_params})        end      else @@ -445,7 +463,7 @@ defmodule Pleroma.Web.OAuth.OAuthController do      |> Scopes.validates(app.scopes)    end -  defp default_redirect_uri(%App{} = app) do +  def default_redirect_uri(%App{} = app) do      app.redirect_uris      |> String.split()      |> Enum.at(0) diff --git a/lib/pleroma/web/rich_media/parsers/meta_tags_parser.ex b/lib/pleroma/web/rich_media/parsers/meta_tags_parser.ex index 82f1cce29..4a7c5eae0 100644 --- a/lib/pleroma/web/rich_media/parsers/meta_tags_parser.ex +++ b/lib/pleroma/web/rich_media/parsers/meta_tags_parser.ex @@ -1,19 +1,15 @@  defmodule Pleroma.Web.RichMedia.Parsers.MetaTagsParser do    def parse(html, data, prefix, error_message, key_name, value_name \\ "content") do -    meta_data = -      html -      |> get_elements(key_name, prefix) -      |> Enum.reduce(data, fn el, acc -> -        attributes = normalize_attributes(el, prefix, key_name, value_name) +    with elements = [_ | _] <- get_elements(html, key_name, prefix), +         meta_data = +           Enum.reduce(elements, data, fn el, acc -> +             attributes = normalize_attributes(el, prefix, key_name, value_name) -        Map.merge(acc, attributes) -      end) -      |> maybe_put_title(html) - -    if Enum.empty?(meta_data) do -      {:error, error_message} -    else +             Map.merge(acc, attributes) +           end) do        {:ok, meta_data} +    else +      _e -> {:error, error_message}      end    end @@ -31,17 +27,4 @@ defmodule Pleroma.Web.RichMedia.Parsers.MetaTagsParser do      %{String.to_atom(data[key_name]) => data[value_name]}    end - -  defp maybe_put_title(%{title: _} = meta, _), do: meta - -  defp maybe_put_title(meta, html) do -    case get_page_title(html) do -      "" -> meta -      title -> Map.put_new(meta, :title, title) -    end -  end - -  defp get_page_title(html) do -    Floki.find(html, "title") |> Floki.text() -  end  end @@ -37,14 +37,14 @@ defmodule Pleroma.Mixfile do          pleroma: [            include_executables_for: [:unix],            applications: [ex_syslogger: :load, syslog: :load], -          steps: [:assemble, ©_pleroma_ctl/1] +          steps: [:assemble, ©_files/1]          ]        ]      ]    end -  def copy_pleroma_ctl(%{path: target_path} = release) do -    File.cp!("./rel/pleroma_ctl", Path.join([target_path, "bin", "pleroma_ctl"])) +  def copy_files(%{path: target_path} = release) do +    File.cp_r!("./rel/files", target_path)      release    end @@ -209,6 +209,7 @@ defmodule Pleroma.Mixfile do      branch_name =        with {branch_name, 0} <- System.cmd("git", ["rev-parse", "--abbrev-ref", "HEAD"]), +           branch_name <- System.get_env("PLEROMA_BUILD_BRANCH") || branch_name,             true <- branch_name != "master" do          branch_name =            String.trim(branch_name) diff --git a/lib/mix/tasks/pleroma/robots_txt.eex b/priv/templates/robots_txt.eex index 1af3c47ee..1af3c47ee 100644 --- a/lib/mix/tasks/pleroma/robots_txt.eex +++ b/priv/templates/robots_txt.eex diff --git a/lib/mix/tasks/pleroma/sample_config.eex b/priv/templates/sample_config.eex index 73d9217be..526593d0a 100644 --- a/lib/mix/tasks/pleroma/sample_config.eex +++ b/priv/templates/sample_config.eex @@ -3,7 +3,11 @@  # NOTE: This file should not be committed to a repo or otherwise made public  # without removing sensitive information. -use Mix.Config +<%= if Code.ensure_loaded?(Config) do +  "import Config" +else +  "use Mix.Config" +end %>  config :pleroma, Pleroma.Web.Endpoint,     url: [host: "<%= domain %>", scheme: "https", port: <%= port %>], @@ -16,7 +20,6 @@ config :pleroma, :instance,    notify_email: "<%= notify_email %>",    limit: 5000,    registrations_open: true, -  dedupe_media: false,    dynamic_configuration: <%= db_configurable? %>  config :pleroma, :media_proxy, @@ -38,6 +41,9 @@ config :web_push_encryption, :vapid_details,    public_key: "<%= web_push_public_key %>",    private_key: "<%= web_push_private_key %>" +config :pleroma, :instance, static_dir: "<%= static_dir %>" +config :pleroma, Pleroma.Uploaders.Local, uploads: "<%= uploads_dir %>" +  # Enable Strict-Transport-Security once SSL is working:  # config :pleroma, :http_security,  #   sts: true diff --git a/lib/mix/tasks/pleroma/sample_psql.eex b/priv/templates/sample_psql.eex index f0ac05e57..f0ac05e57 100644 --- a/lib/mix/tasks/pleroma/sample_psql.eex +++ b/priv/templates/sample_psql.eex diff --git a/rel/files/bin/pleroma_ctl b/rel/files/bin/pleroma_ctl new file mode 100755 index 000000000..b0e1874a9 --- /dev/null +++ b/rel/files/bin/pleroma_ctl @@ -0,0 +1,118 @@ +#!/bin/sh +# XXX: This should be removed when elixir's releases get custom command support + +detect_flavour() { +	arch="$(arch)" +	if [ "$arch" = "x86_64" ]; then +		arch="amd64" +	elif [ "$arch" = "armv7l" ]; then +		arch="arm" +	elif [ "$arch" = "aarch64" ]; then +		arch="arm64" +	else +		echo "Unsupported arch: $arch" >&2 +		exit 1 +	fi + +	if getconf GNU_LIBC_VERSION >/dev/null; then +		libc_postfix="" +	elif [ "$(ldd 2>&1 | head -c 9)" = "musl libc" ]; then +		libc_postfix="-musl" +	elif [ "$(find /lib/libc.musl* | wc -l)" ]; then +		libc_postfix="-musl" +	else +		echo "Unsupported libc" >&2 +		exit 1 +	fi + +	echo "$arch$libc_postfix" +} + +detect_branch() { +	version="$(cut -d' ' -f2 <"$RELEASE_ROOT"/releases/start_erl.data)" +	branch="$(echo "$version" | cut -d'-' -f 4)" +	if [ "$branch" = "develop" ]; then +		echo "develop" +	elif [ "$branch" = "" ]; then +		echo "master" +	else +		echo "Releases are built only for master and develop branches" >&2 +		exit 1 +	fi +} +update() { +	set -e +	RELEASE_ROOT=$(dirname "$SCRIPTPATH") +	uri="${PLEROMA_CTL_URI:-https://git.pleroma.social}" +	project_id="${PLEROMA_CTL_PROJECT_ID:-2}" +	project_branch="$(detect_branch)" +	flavour="${PLEROMA_CTL_FLAVOUR:-$(detect_flavour)}" +	echo "Detected flavour: $flavour" +	tmp="${PLEROMA_CTL_TMP_DIR:-/tmp}" +	artifact="$tmp/pleroma.zip" +	full_uri="${uri}/api/v4/projects/${project_id}/jobs/artifacts/${project_branch}/download?job=${flavour}" +	echo "Downloading the artifact from ${full_uri} to ${artifact}" +	curl "$full_uri" -o "${artifact}" +	echo "Unpacking ${artifact} to ${tmp}" +	unzip -q "$artifact" -d "$tmp" +	echo "Copying files over to $RELEASE_ROOT" +	if [ "$1" != "--no-rm" ]; then +		rm -r "${RELEASE_ROOT:-?}"/* +	fi +	cp -rf "$tmp/release"/* "$RELEASE_ROOT" +	echo "Removing temporary files" +	rm -r "$tmp/release" +	rm "$artifact" +	echo "Done! Please refer to the changelog/release notes for changes and update instructions" +	set +e +} + +if [ -z "$1" ] || [ "$1" = "help" ]; then +	# TODO: Just list the commands on `pleroma_ctl help` and output help for the individual command on `pleroma_ctl help $COMMAND` +	echo "Usage: $(basename "$0") COMMAND [ARGS] + +    The known commands are: + +        create +	  Create database schema (needs to be executed only once) + +        migrate +	  Execute database migrations (needs to be done after updates) + +        rollback [VERSION] +	  Rollback database migrations (needs to be done before downgrading) + +	update [OPTIONS] +	  Update the instance using the latest CI artifact for the current branch. + +	  The only supported option is --no-rm, when set the script won't delete the whole directory, but +	  just force copy over files from the new release. This wastes more space, but may be useful if +	  some files are stored inside of the release directories (although you really shouldn't store them +	  there), or if you want to be able to quickly revert a broken update. + +	  The script will try to detect your architecture and ABI and set a flavour automatically, +	  but if it is wrong, you can overwrite it by setting PLEROMA_CTL_FLAVOUR to the desired flavour. + +	  By default the artifact will be downloaded from https://git.pleroma.social for pleroma/pleroma (project id: 2) +	  to /tmp/, you can overwrite these settings by setting PLEROMA_CTL_URI, PLEROMA_CTL_PROJECT_ID and PLEROMA_CTL_TMP_DIR +	  respectively. + + +    and any mix tasks under Pleroma namespace, for example \`mix pleroma.user COMMAND\` is +    equivalent to \`$(basename "$0") user COMMAND\` + +    By default pleroma_ctl will try calling into a running instance to execute non migration-related commands, +    if for some reason this is undesired, set PLEROMA_CTL_RPC_DISABLED environment variable +" +else +	SCRIPT=$(readlink -f "$0") +	SCRIPTPATH=$(dirname "$SCRIPT") + +	if [ "$1" = "update" ]; then +		update "$2" +	elif [ "$1" = "migrate" ] || [ "$1" = "rollback" ] || [ "$1" = "create" ] || [ "$1 $2" = "instance gen" ] || [ -n "$PLEROMA_CTL_RPC_DISABLED" ]; then +		"$SCRIPTPATH"/pleroma eval 'Pleroma.ReleaseTasks.run("'"$*"'")' +	else +		"$SCRIPTPATH"/pleroma rpc 'Pleroma.ReleaseTasks.run("'"$*"'")' +	fi +fi diff --git a/rel/pleroma_ctl b/rel/pleroma_ctl deleted file mode 100755 index ac7339762..000000000 --- a/rel/pleroma_ctl +++ /dev/null @@ -1,26 +0,0 @@ -#!/bin/sh -# XXX: This should be removed when elixir's releases get custom command support -if [ -z "$1" ] || [ "$1" = "help" ]; then -	echo "Usage: $(basename "$0") COMMAND [ARGS] - -    The known commands are: - -        create                     Create database schema (needs to be executed only once) -        migrate                    Execute database migrations (needs to be done after updates) -        rollback [VERSION]         Rollback database migrations (needs to be done before downgrading) - -    and any mix tasks under Pleroma namespace, for example \`mix pleroma.user COMMAND\` is -    equivalent to \`$(basename "$0") user COMMAND\` - -    By default pleroma_ctl will try calling into a running instance to execute non migration-related commands, -    if for some reason this is undesired, set PLEROMA_CTL_RPC_DISABLED environment variable -" -else -	SCRIPT=$(readlink -f "$0") -	SCRIPTPATH=$(dirname "$SCRIPT") -	if [ "$1" = "migrate" ] || [ "$1" = "rollback" ] || [ "$1" = "create" ] || [ -n "$PLEROMA_CTL_RPC_DISABLED" ]; then -		"$SCRIPTPATH"/pleroma eval 'Pleroma.ReleaseTasks.run("'"$*"'")' -	else -		"$SCRIPTPATH"/pleroma rpc 'Pleroma.ReleaseTasks.run("'"$*"'")' -	fi -fi diff --git a/test/fixtures/rich_media/ogp-missing-title.html b/test/fixtures/rich_media/ogp-missing-title.html deleted file mode 100644 index fcdbedfc6..000000000 --- a/test/fixtures/rich_media/ogp-missing-title.html +++ /dev/null @@ -1,12 +0,0 @@ -<html prefix="og: http://ogp.me/ns#"> - -<head> -  <title>The Rock (1996)</title> -  <meta property="og:type" content="video.movie" /> -  <meta property="og:url" content="http://www.imdb.com/title/tt0117500/" /> -  <meta property="og:image" content="http://ia.media-imdb.com/images/rock.jpg" /> -  <meta property="og:description" -    content="Directed by Michael Bay. With Sean Connery, Nicolas Cage, Ed Harris, John Spencer."> -</head> - -</html> diff --git a/test/plugs/rate_limiter_test.exs b/test/plugs/rate_limiter_test.exs index b3798bf03..b8d6aff89 100644 --- a/test/plugs/rate_limiter_test.exs +++ b/test/plugs/rate_limiter_test.exs @@ -20,7 +20,7 @@ defmodule Pleroma.Plugs.RateLimiterTest do    end    test "it restricts by opts" do -    scale = 100 +    scale = 1000      limit = 5      Pleroma.Config.put([:rate_limit, @limiter_name], {scale, limit}) @@ -64,7 +64,7 @@ defmodule Pleroma.Plugs.RateLimiterTest do    test "optional limits for authenticated users" do      Ecto.Adapters.SQL.Sandbox.checkout(Pleroma.Repo) -    scale = 100 +    scale = 1000      limit = 5      Pleroma.Config.put([:rate_limit, @limiter_name], [{1, 10}, {scale, limit}]) diff --git a/test/tasks/config_test.exs b/test/tasks/config_test.exs index 7d3b1860c..d448b0444 100644 --- a/test/tasks/config_test.exs +++ b/test/tasks/config_test.exs @@ -5,7 +5,7 @@ defmodule Mix.Tasks.Pleroma.ConfigTest do    setup_all do      Mix.shell(Mix.Shell.Process) -    temp_file = "config/temp.migrated.secret.exs" +    temp_file = "config/temp.exported_from_db.secret.exs"      dynamic = Pleroma.Config.get([:instance, :dynamic_configuration]) diff --git a/test/web/admin_api/admin_api_controller_test.exs b/test/web/admin_api/admin_api_controller_test.exs index 18f64f2b7..2a5912645 100644 --- a/test/web/admin_api/admin_api_controller_test.exs +++ b/test/web/admin_api/admin_api_controller_test.exs @@ -1334,7 +1334,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do      setup %{conn: conn} do        admin = insert(:user, info: %{is_admin: true}) -      temp_file = "config/test.migrated.secret.exs" +      temp_file = "config/test.exported_from_db.secret.exs"        on_exit(fn ->          Application.delete_env(:pleroma, :key1) diff --git a/test/web/oauth/oauth_controller_test.exs b/test/web/oauth/oauth_controller_test.exs index 242b7fdb3..aae34804d 100644 --- a/test/web/oauth/oauth_controller_test.exs +++ b/test/web/oauth/oauth_controller_test.exs @@ -10,6 +10,7 @@ defmodule Pleroma.Web.OAuth.OAuthControllerTest do    alias Pleroma.Registration    alias Pleroma.Repo    alias Pleroma.Web.OAuth.Authorization +  alias Pleroma.Web.OAuth.OAuthController    alias Pleroma.Web.OAuth.Token    @oauth_config_path [:oauth2, :issue_new_refresh_token] @@ -49,7 +50,7 @@ defmodule Pleroma.Web.OAuth.OAuthControllerTest do            %{              "response_type" => "code",              "client_id" => app.client_id, -            "redirect_uri" => app.redirect_uris, +            "redirect_uri" => OAuthController.default_redirect_uri(app),              "scope" => "read"            }          ) @@ -72,7 +73,7 @@ defmodule Pleroma.Web.OAuth.OAuthControllerTest do              "authorization" => %{                "scope" => "read follow",                "client_id" => app.client_id, -              "redirect_uri" => app.redirect_uris, +              "redirect_uri" => OAuthController.default_redirect_uri(app),                "state" => "a_state"              }            } @@ -98,11 +99,12 @@ defmodule Pleroma.Web.OAuth.OAuthControllerTest do      test "with user-bound registration, GET /oauth/<provider>/callback redirects to `redirect_uri` with `code`",           %{app: app, conn: conn} do        registration = insert(:registration) +      redirect_uri = OAuthController.default_redirect_uri(app)        state_params = %{          "scope" => Enum.join(app.scopes, " "),          "client_id" => app.client_id, -        "redirect_uri" => app.redirect_uris, +        "redirect_uri" => redirect_uri,          "state" => ""        } @@ -121,7 +123,7 @@ defmodule Pleroma.Web.OAuth.OAuthControllerTest do            )          assert response = html_response(conn, 302) -        assert redirected_to(conn) =~ ~r/#{app.redirect_uris}\?code=.+/ +        assert redirected_to(conn) =~ ~r/#{redirect_uri}\?code=.+/        end      end @@ -132,7 +134,7 @@ defmodule Pleroma.Web.OAuth.OAuthControllerTest do        state_params = %{          "scope" => "read write",          "client_id" => app.client_id, -        "redirect_uri" => app.redirect_uris, +        "redirect_uri" => OAuthController.default_redirect_uri(app),          "state" => "a_state"        } @@ -165,7 +167,7 @@ defmodule Pleroma.Web.OAuth.OAuthControllerTest do        state_params = %{          "scope" => Enum.join(app.scopes, " "),          "client_id" => app.client_id, -        "redirect_uri" => app.redirect_uris, +        "redirect_uri" => OAuthController.default_redirect_uri(app),          "state" => ""        } @@ -199,7 +201,7 @@ defmodule Pleroma.Web.OAuth.OAuthControllerTest do              "authorization" => %{                "scopes" => app.scopes,                "client_id" => app.client_id, -              "redirect_uri" => app.redirect_uris, +              "redirect_uri" => OAuthController.default_redirect_uri(app),                "state" => "a_state",                "nickname" => nil,                "email" => "john@doe.com" @@ -218,6 +220,7 @@ defmodule Pleroma.Web.OAuth.OAuthControllerTest do             conn: conn           } do        registration = insert(:registration, user: nil, info: %{"nickname" => nil, "email" => nil}) +      redirect_uri = OAuthController.default_redirect_uri(app)        conn =          conn @@ -229,7 +232,7 @@ defmodule Pleroma.Web.OAuth.OAuthControllerTest do              "authorization" => %{                "scopes" => app.scopes,                "client_id" => app.client_id, -              "redirect_uri" => app.redirect_uris, +              "redirect_uri" => redirect_uri,                "state" => "a_state",                "nickname" => "availablenick",                "email" => "available@email.com" @@ -238,7 +241,36 @@ defmodule Pleroma.Web.OAuth.OAuthControllerTest do          )        assert response = html_response(conn, 302) -      assert redirected_to(conn) =~ ~r/#{app.redirect_uris}\?code=.+/ +      assert redirected_to(conn) =~ ~r/#{redirect_uri}\?code=.+/ +    end + +    test "with unlisted `redirect_uri`, POST /oauth/register?op=register results in HTTP 401", +         %{ +           app: app, +           conn: conn +         } do +      registration = insert(:registration, user: nil, info: %{"nickname" => nil, "email" => nil}) +      unlisted_redirect_uri = "http://cross-site-request.com" + +      conn = +        conn +        |> put_session(:registration_id, registration.id) +        |> post( +          "/oauth/register", +          %{ +            "op" => "register", +            "authorization" => %{ +              "scopes" => app.scopes, +              "client_id" => app.client_id, +              "redirect_uri" => unlisted_redirect_uri, +              "state" => "a_state", +              "nickname" => "availablenick", +              "email" => "available@email.com" +            } +          } +        ) + +      assert response = html_response(conn, 401)      end      test "with invalid params, POST /oauth/register?op=register renders registration_details page", @@ -254,7 +286,7 @@ defmodule Pleroma.Web.OAuth.OAuthControllerTest do          "authorization" => %{            "scopes" => app.scopes,            "client_id" => app.client_id, -          "redirect_uri" => app.redirect_uris, +          "redirect_uri" => OAuthController.default_redirect_uri(app),            "state" => "a_state",            "nickname" => "availablenickname",            "email" => "available@email.com" @@ -286,6 +318,7 @@ defmodule Pleroma.Web.OAuth.OAuthControllerTest do           } do        user = insert(:user, password_hash: Comeonin.Pbkdf2.hashpwsalt("testpassword"))        registration = insert(:registration, user: nil) +      redirect_uri = OAuthController.default_redirect_uri(app)        conn =          conn @@ -297,7 +330,7 @@ defmodule Pleroma.Web.OAuth.OAuthControllerTest do              "authorization" => %{                "scopes" => app.scopes,                "client_id" => app.client_id, -              "redirect_uri" => app.redirect_uris, +              "redirect_uri" => redirect_uri,                "state" => "a_state",                "name" => user.nickname,                "password" => "testpassword" @@ -306,7 +339,37 @@ defmodule Pleroma.Web.OAuth.OAuthControllerTest do          )        assert response = html_response(conn, 302) -      assert redirected_to(conn) =~ ~r/#{app.redirect_uris}\?code=.+/ +      assert redirected_to(conn) =~ ~r/#{redirect_uri}\?code=.+/ +    end + +    test "with unlisted `redirect_uri`, POST /oauth/register?op=connect results in HTTP 401`", +         %{ +           app: app, +           conn: conn +         } do +      user = insert(:user, password_hash: Comeonin.Pbkdf2.hashpwsalt("testpassword")) +      registration = insert(:registration, user: nil) +      unlisted_redirect_uri = "http://cross-site-request.com" + +      conn = +        conn +        |> put_session(:registration_id, registration.id) +        |> post( +          "/oauth/register", +          %{ +            "op" => "connect", +            "authorization" => %{ +              "scopes" => app.scopes, +              "client_id" => app.client_id, +              "redirect_uri" => unlisted_redirect_uri, +              "state" => "a_state", +              "name" => user.nickname, +              "password" => "testpassword" +            } +          } +        ) + +      assert response = html_response(conn, 401)      end      test "with invalid params, POST /oauth/register?op=connect renders registration_details page", @@ -322,7 +385,7 @@ defmodule Pleroma.Web.OAuth.OAuthControllerTest do          "authorization" => %{            "scopes" => app.scopes,            "client_id" => app.client_id, -          "redirect_uri" => app.redirect_uris, +          "redirect_uri" => OAuthController.default_redirect_uri(app),            "state" => "a_state",            "name" => user.nickname,            "password" => "wrong password" @@ -358,7 +421,7 @@ defmodule Pleroma.Web.OAuth.OAuthControllerTest do            %{              "response_type" => "code",              "client_id" => app.client_id, -            "redirect_uri" => app.redirect_uris, +            "redirect_uri" => OAuthController.default_redirect_uri(app),              "scope" => "read"            }          ) @@ -378,7 +441,7 @@ defmodule Pleroma.Web.OAuth.OAuthControllerTest do              "authorization" => %{                "response_type" => "code",                "client_id" => app.client_id, -              "redirect_uri" => app.redirect_uris, +              "redirect_uri" => OAuthController.default_redirect_uri(app),                "scope" => "read"              }            } @@ -399,7 +462,7 @@ defmodule Pleroma.Web.OAuth.OAuthControllerTest do            %{              "response_type" => "code",              "client_id" => app.client_id, -            "redirect_uri" => app.redirect_uris, +            "redirect_uri" => OAuthController.default_redirect_uri(app),              "scope" => "read",              "force_login" => "true"            } @@ -423,7 +486,7 @@ defmodule Pleroma.Web.OAuth.OAuthControllerTest do            %{              "response_type" => "code",              "client_id" => app.client_id, -            "redirect_uri" => app.redirect_uris, +            "redirect_uri" => OAuthController.default_redirect_uri(app),              "state" => "specific_client_state",              "scope" => "read"            } @@ -433,6 +496,31 @@ defmodule Pleroma.Web.OAuth.OAuthControllerTest do                 "https://redirect.url?access_token=#{token.token}&state=specific_client_state"      end +    test "with existing authentication and unlisted non-OOB `redirect_uri`, redirects without credentials", +         %{ +           app: app, +           conn: conn +         } do +      unlisted_redirect_uri = "http://cross-site-request.com" +      token = insert(:oauth_token, app_id: app.id) + +      conn = +        conn +        |> put_session(:oauth_token, token.token) +        |> get( +          "/oauth/authorize", +          %{ +            "response_type" => "code", +            "client_id" => app.client_id, +            "redirect_uri" => unlisted_redirect_uri, +            "state" => "specific_client_state", +            "scope" => "read" +          } +        ) + +      assert redirected_to(conn) == unlisted_redirect_uri +    end +      test "with existing authentication and OOB `redirect_uri`, redirects to app with `token` and `state` params",           %{             app: app, @@ -461,6 +549,7 @@ defmodule Pleroma.Web.OAuth.OAuthControllerTest do      test "redirects with oauth authorization" do        user = insert(:user)        app = insert(:oauth_app, scopes: ["read", "write", "follow"]) +      redirect_uri = OAuthController.default_redirect_uri(app)        conn =          build_conn() @@ -469,14 +558,14 @@ defmodule Pleroma.Web.OAuth.OAuthControllerTest do              "name" => user.nickname,              "password" => "test",              "client_id" => app.client_id, -            "redirect_uri" => app.redirect_uris, +            "redirect_uri" => redirect_uri,              "scope" => "read write",              "state" => "statepassed"            }          })        target = redirected_to(conn) -      assert target =~ app.redirect_uris +      assert target =~ redirect_uri        query = URI.parse(target).query |> URI.query_decoder() |> Map.new() @@ -489,6 +578,7 @@ defmodule Pleroma.Web.OAuth.OAuthControllerTest do      test "returns 401 for wrong credentials", %{conn: conn} do        user = insert(:user)        app = insert(:oauth_app) +      redirect_uri = OAuthController.default_redirect_uri(app)        result =          conn @@ -497,7 +587,7 @@ defmodule Pleroma.Web.OAuth.OAuthControllerTest do              "name" => user.nickname,              "password" => "wrong",              "client_id" => app.client_id, -            "redirect_uri" => app.redirect_uris, +            "redirect_uri" => redirect_uri,              "state" => "statepassed",              "scope" => Enum.join(app.scopes, " ")            } @@ -506,7 +596,7 @@ defmodule Pleroma.Web.OAuth.OAuthControllerTest do        # Keep the details        assert result =~ app.client_id -      assert result =~ app.redirect_uris +      assert result =~ redirect_uri        # Error message        assert result =~ "Invalid Username/Password" @@ -515,6 +605,7 @@ defmodule Pleroma.Web.OAuth.OAuthControllerTest do      test "returns 401 for missing scopes", %{conn: conn} do        user = insert(:user)        app = insert(:oauth_app) +      redirect_uri = OAuthController.default_redirect_uri(app)        result =          conn @@ -523,7 +614,7 @@ defmodule Pleroma.Web.OAuth.OAuthControllerTest do              "name" => user.nickname,              "password" => "test",              "client_id" => app.client_id, -            "redirect_uri" => app.redirect_uris, +            "redirect_uri" => redirect_uri,              "state" => "statepassed",              "scope" => ""            } @@ -532,7 +623,7 @@ defmodule Pleroma.Web.OAuth.OAuthControllerTest do        # Keep the details        assert result =~ app.client_id -      assert result =~ app.redirect_uris +      assert result =~ redirect_uri        # Error message        assert result =~ "This action is outside the authorized scopes" @@ -541,6 +632,7 @@ defmodule Pleroma.Web.OAuth.OAuthControllerTest do      test "returns 401 for scopes beyond app scopes", %{conn: conn} do        user = insert(:user)        app = insert(:oauth_app, scopes: ["read", "write"]) +      redirect_uri = OAuthController.default_redirect_uri(app)        result =          conn @@ -549,7 +641,7 @@ defmodule Pleroma.Web.OAuth.OAuthControllerTest do              "name" => user.nickname,              "password" => "test",              "client_id" => app.client_id, -            "redirect_uri" => app.redirect_uris, +            "redirect_uri" => redirect_uri,              "state" => "statepassed",              "scope" => "read write follow"            } @@ -558,7 +650,7 @@ defmodule Pleroma.Web.OAuth.OAuthControllerTest do        # Keep the details        assert result =~ app.client_id -      assert result =~ app.redirect_uris +      assert result =~ redirect_uri        # Error message        assert result =~ "This action is outside the authorized scopes" @@ -577,7 +669,7 @@ defmodule Pleroma.Web.OAuth.OAuthControllerTest do          |> post("/oauth/token", %{            "grant_type" => "authorization_code",            "code" => auth.token, -          "redirect_uri" => app.redirect_uris, +          "redirect_uri" => OAuthController.default_redirect_uri(app),            "client_id" => app.client_id,            "client_secret" => app.client_secret          }) @@ -631,7 +723,7 @@ defmodule Pleroma.Web.OAuth.OAuthControllerTest do          |> post("/oauth/token", %{            "grant_type" => "authorization_code",            "code" => auth.token, -          "redirect_uri" => app.redirect_uris +          "redirect_uri" => OAuthController.default_redirect_uri(app)          })        assert %{"access_token" => token, "scope" => scope} = json_response(conn, 200) @@ -676,7 +768,7 @@ defmodule Pleroma.Web.OAuth.OAuthControllerTest do          |> post("/oauth/token", %{            "grant_type" => "authorization_code",            "code" => auth.token, -          "redirect_uri" => app.redirect_uris +          "redirect_uri" => OAuthController.default_redirect_uri(app)          })        assert resp = json_response(conn, 400) @@ -755,7 +847,7 @@ defmodule Pleroma.Web.OAuth.OAuthControllerTest do          |> post("/oauth/token", %{            "grant_type" => "authorization_code",            "code" => "Imobviouslyinvalid", -          "redirect_uri" => app.redirect_uris, +          "redirect_uri" => OAuthController.default_redirect_uri(app),            "client_id" => app.client_id,            "client_secret" => app.client_secret          }) diff --git a/test/web/rich_media/parser_test.exs b/test/web/rich_media/parser_test.exs index a49ba9549..3a9cc1854 100644 --- a/test/web/rich_media/parser_test.exs +++ b/test/web/rich_media/parser_test.exs @@ -11,15 +11,6 @@ defmodule Pleroma.Web.RichMedia.ParserTest do        %{          method: :get, -        url: "http://example.com/ogp-missing-title" -      } -> -        %Tesla.Env{ -          status: 200, -          body: File.read!("test/fixtures/rich_media/ogp-missing-title.html") -        } - -      %{ -        method: :get,          url: "http://example.com/twitter-card"        } ->          %Tesla.Env{status: 200, body: File.read!("test/fixtures/rich_media/twitter_card.html")} @@ -60,19 +51,6 @@ defmodule Pleroma.Web.RichMedia.ParserTest do                }}    end -  test "falls back to <title> when ogp:title is missing" do -    assert Pleroma.Web.RichMedia.Parser.parse("http://example.com/ogp-missing-title") == -             {:ok, -              %{ -                image: "http://ia.media-imdb.com/images/rock.jpg", -                title: "The Rock (1996)", -                description: -                  "Directed by Michael Bay. With Sean Connery, Nicolas Cage, Ed Harris, John Spencer.", -                type: "video.movie", -                url: "http://www.imdb.com/title/tt0117500/" -              }} -  end -    test "parses twitter card" do      assert Pleroma.Web.RichMedia.Parser.parse("http://example.com/twitter-card") ==               {:ok,  | 
