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, |