aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md2
-rw-r--r--config/config.exs5
-rw-r--r--config/test.exs3
-rw-r--r--docs/config.md2
-rw-r--r--lib/pleroma/upload/filter/dedupe.ex15
-rw-r--r--lib/pleroma/upload/filter/mogrifun.ex24
-rw-r--r--lib/pleroma/upload/filter/mogrify.ex11
-rw-r--r--lib/pleroma/web/mastodon_api/mastodon_api_controller.ex25
-rw-r--r--lib/pleroma/web/router.ex2
-rw-r--r--lib/pleroma/web/twitter_api/twitter_api_controller.ex7
-rw-r--r--test/upload/filter/dedupe_test.exs31
-rw-r--r--test/upload/filter/mogrifun_test.exs44
-rw-r--r--test/upload/filter/mogrify_test.exs51
-rw-r--r--test/upload/filter_test.exs39
-rw-r--r--test/web/mastodon_api/mastodon_api_controller_test.exs52
-rw-r--r--test/web/twitter_api/twitter_api_controller_test.exs10
16 files changed, 288 insertions, 35 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0bc72a3fc..0d91ad817 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -33,6 +33,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Mastodon API: Add support for muting/unmuting notifications
- Mastodon API: Add support for the `blocked_by` attribute in the relationship API (`GET /api/v1/accounts/relationships`). <https://github.com/tootsuite/mastodon/pull/10373>
- Mastodon API: Add `pleroma.deactivated` to the Account entity
+- Mastodon API: added `/auth/password` endpoint for password reset with rate limit.
- Admin API: Return users' tags when querying reports
- Admin API: Return avatar and display name when querying users
- Admin API: Allow querying user by ID
@@ -41,6 +42,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Configuration: `enabled` option for `Pleroma.Emails.Mailer`, defaulting to `false`.
- Configuration: Pleroma.Plugs.RateLimiter `bucket_name`, `params` options.
- Addressable lists
+- Twitter API: added rate limit for `/api/account/password_reset` endpoint.
### Changed
- Configuration: Filter.AnonymizeFilename added ability to retain file extension with custom text
diff --git a/config/config.exs b/config/config.exs
index 7d539f994..38780eef7 100644
--- a/config/config.exs
+++ b/config/config.exs
@@ -528,8 +528,11 @@ config :http_signatures,
config :pleroma, :rate_limit,
search: [{1000, 10}, {1000, 30}],
app_account_creation: {1_800_000, 25},
+ relations_actions: {10_000, 10},
+ relation_id_action: {60_000, 2},
statuses_actions: {10_000, 15},
- status_id_action: {60_000, 3}
+ status_id_action: {60_000, 3},
+ password_reset: {1_800_000, 5}
# Import environment specific config. This must remain at the bottom
# of this file so it overrides the configuration defined above.
diff --git a/config/test.exs b/config/test.exs
index 96ecf3592..0af62aa14 100644
--- a/config/test.exs
+++ b/config/test.exs
@@ -67,7 +67,8 @@ config :pleroma, Pleroma.ScheduledActivity,
config :pleroma, :rate_limit,
search: [{1000, 30}, {1000, 30}],
- app_account_creation: {10_000, 5}
+ app_account_creation: {10_000, 5},
+ password_reset: {1000, 30}
config :pleroma, :http_security, report_uri: "https://endpoint.com"
diff --git a/docs/config.md b/docs/config.md
index 5f69f8daf..42556a0be 100644
--- a/docs/config.md
+++ b/docs/config.md
@@ -651,5 +651,7 @@ Supported rate limiters:
* `:search` for the search requests (account & status search etc.)
* `:app_account_creation` for registering user accounts from the same IP address
+* `:relations_actions` for actions on relations with all users (follow, unfollow)
+* `:relation_id_action` for actions on relation with a specific user (follow, unfollow)
* `:statuses_actions` for create / delete / fav / unfav / reblog / unreblog actions on any statuses
* `:status_id_action` for fav / unfav or reblog / unreblog actions on the same status by the same user
diff --git a/lib/pleroma/upload/filter/dedupe.ex b/lib/pleroma/upload/filter/dedupe.ex
index e4c225833..14928c355 100644
--- a/lib/pleroma/upload/filter/dedupe.ex
+++ b/lib/pleroma/upload/filter/dedupe.ex
@@ -6,10 +6,19 @@ defmodule Pleroma.Upload.Filter.Dedupe do
@behaviour Pleroma.Upload.Filter
alias Pleroma.Upload
- def filter(%Upload{name: name} = upload) do
- extension = String.split(name, ".") |> List.last()
- shasum = :crypto.hash(:sha256, File.read!(upload.tempfile)) |> Base.encode16(case: :lower)
+ def filter(%Upload{name: name, tempfile: tempfile} = upload) do
+ extension =
+ name
+ |> String.split(".")
+ |> List.last()
+
+ shasum =
+ :crypto.hash(:sha256, File.read!(tempfile))
+ |> Base.encode16(case: :lower)
+
filename = shasum <> "." <> extension
{:ok, %Upload{upload | id: shasum, path: filename}}
end
+
+ def filter(_), do: :ok
end
diff --git a/lib/pleroma/upload/filter/mogrifun.ex b/lib/pleroma/upload/filter/mogrifun.ex
index 35a5a1381..fee49fb51 100644
--- a/lib/pleroma/upload/filter/mogrifun.ex
+++ b/lib/pleroma/upload/filter/mogrifun.ex
@@ -4,6 +4,7 @@
defmodule Pleroma.Upload.Filter.Mogrifun do
@behaviour Pleroma.Upload.Filter
+ alias Pleroma.Upload.Filter
@filters [
{"implode", "1"},
@@ -34,31 +35,10 @@ defmodule Pleroma.Upload.Filter.Mogrifun do
]
def filter(%Pleroma.Upload{tempfile: file, content_type: "image" <> _}) do
- filter = Enum.random(@filters)
-
- file
- |> Mogrify.open()
- |> mogrify_filter(filter)
- |> Mogrify.save(in_place: true)
+ Filter.Mogrify.do_filter(file, [Enum.random(@filters)])
:ok
end
def filter(_), do: :ok
-
- defp mogrify_filter(mogrify, [filter | rest]) do
- mogrify
- |> mogrify_filter(filter)
- |> mogrify_filter(rest)
- end
-
- defp mogrify_filter(mogrify, []), do: mogrify
-
- defp mogrify_filter(mogrify, {action, options}) do
- Mogrify.custom(mogrify, action, options)
- end
-
- defp mogrify_filter(mogrify, string) when is_binary(string) do
- Mogrify.custom(mogrify, string)
- end
end
diff --git a/lib/pleroma/upload/filter/mogrify.ex b/lib/pleroma/upload/filter/mogrify.ex
index f459eeecb..91bfdd4f5 100644
--- a/lib/pleroma/upload/filter/mogrify.ex
+++ b/lib/pleroma/upload/filter/mogrify.ex
@@ -11,16 +11,19 @@ defmodule Pleroma.Upload.Filter.Mogrify do
def filter(%Pleroma.Upload{tempfile: file, content_type: "image" <> _}) do
filters = Pleroma.Config.get!([__MODULE__, :args])
+ do_filter(file, filters)
+ :ok
+ end
+
+ def filter(_), do: :ok
+
+ def do_filter(file, filters) do
file
|> Mogrify.open()
|> mogrify_filter(filters)
|> Mogrify.save(in_place: true)
-
- :ok
end
- def filter(_), do: :ok
-
defp mogrify_filter(mogrify, nil), do: mogrify
defp mogrify_filter(mogrify, [filter | rest]) do
diff --git a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex
index f4aa576f7..aff76e2ea 100644
--- a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex
+++ b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex
@@ -47,6 +47,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
require Logger
+ @rate_limited_relations_actions ~w(follow unfollow)a
+
@rate_limited_status_actions ~w(reblog_status unreblog_status fav_status unfav_status
post_status delete_status)a
@@ -62,9 +64,16 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
when action in ~w(fav_status unfav_status)a
)
+ plug(
+ RateLimiter,
+ {:relations_id_action, params: ["id", "uri"]} when action in @rate_limited_relations_actions
+ )
+
+ plug(RateLimiter, :relations_actions when action in @rate_limited_relations_actions)
plug(RateLimiter, :statuses_actions when action in @rate_limited_status_actions)
plug(RateLimiter, :app_account_creation when action == :account_register)
plug(RateLimiter, :search when action in [:search, :search2, :account_search])
+ plug(RateLimiter, :password_reset when action == :password_reset)
@local_mastodon_name "Mastodon-Local"
@@ -1808,6 +1817,22 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
end
end
+ def password_reset(conn, params) do
+ nickname_or_email = params["email"] || params["nickname"]
+
+ with {:ok, _} <- TwitterAPI.password_reset(nickname_or_email) do
+ conn
+ |> put_status(:no_content)
+ |> json("")
+ else
+ {:error, "unknown user"} ->
+ put_status(conn, :not_found)
+
+ {:error, _} ->
+ put_status(conn, :bad_request)
+ end
+ end
+
def try_render(conn, target, params)
when is_binary(target) do
case render(conn, target, params) do
diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex
index 3e5142e8a..52b8dc0bf 100644
--- a/lib/pleroma/web/router.ex
+++ b/lib/pleroma/web/router.ex
@@ -691,6 +691,8 @@ defmodule Pleroma.Web.Router do
get("/web/login", MastodonAPIController, :login)
delete("/auth/sign_out", MastodonAPIController, :logout)
+ post("/auth/password", MastodonAPIController, :password_reset)
+
scope [] do
pipe_through(:oauth_read_or_public)
get("/web/*path", MastodonAPIController, :index)
diff --git a/lib/pleroma/web/twitter_api/twitter_api_controller.ex b/lib/pleroma/web/twitter_api/twitter_api_controller.ex
index 0313560a8..8cb703501 100644
--- a/lib/pleroma/web/twitter_api/twitter_api_controller.ex
+++ b/lib/pleroma/web/twitter_api/twitter_api_controller.ex
@@ -27,6 +27,7 @@ defmodule Pleroma.Web.TwitterAPI.Controller do
require Logger
+ plug(Pleroma.Plugs.RateLimiter, :password_reset when action == :password_reset)
plug(:only_if_public_instance when action in [:public_timeline, :public_and_external_timeline])
action_fallback(:errors)
@@ -437,6 +438,12 @@ defmodule Pleroma.Web.TwitterAPI.Controller do
with {:ok, _} <- TwitterAPI.password_reset(nickname_or_email) do
json_response(conn, :no_content, "")
+ else
+ {:error, "unknown user"} ->
+ put_status(conn, :not_found)
+
+ {:error, _} ->
+ put_status(conn, :bad_request)
end
end
diff --git a/test/upload/filter/dedupe_test.exs b/test/upload/filter/dedupe_test.exs
new file mode 100644
index 000000000..fddd594dc
--- /dev/null
+++ b/test/upload/filter/dedupe_test.exs
@@ -0,0 +1,31 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Upload.Filter.DedupeTest do
+ use Pleroma.DataCase
+
+ alias Pleroma.Upload
+ alias Pleroma.Upload.Filter.Dedupe
+
+ @shasum "e30397b58d226d6583ab5b8b3c5defb0c682bda5c31ef07a9f57c1c4986e3781"
+
+ test "adds shasum" do
+ File.cp!(
+ "test/fixtures/image.jpg",
+ "test/fixtures/image_tmp.jpg"
+ )
+
+ upload = %Upload{
+ name: "an… image.jpg",
+ content_type: "image/jpg",
+ path: Path.absname("test/fixtures/image_tmp.jpg"),
+ tempfile: Path.absname("test/fixtures/image_tmp.jpg")
+ }
+
+ assert {
+ :ok,
+ %Pleroma.Upload{id: @shasum, path: "#{@shasum}.jpg"}
+ } = Dedupe.filter(upload)
+ end
+end
diff --git a/test/upload/filter/mogrifun_test.exs b/test/upload/filter/mogrifun_test.exs
new file mode 100644
index 000000000..d5a8751cc
--- /dev/null
+++ b/test/upload/filter/mogrifun_test.exs
@@ -0,0 +1,44 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Upload.Filter.MogrifunTest do
+ use Pleroma.DataCase
+ import Mock
+
+ alias Pleroma.Upload
+ alias Pleroma.Upload.Filter
+
+ test "apply mogrify filter" do
+ File.cp!(
+ "test/fixtures/image.jpg",
+ "test/fixtures/image_tmp.jpg"
+ )
+
+ upload = %Upload{
+ name: "an… image.jpg",
+ content_type: "image/jpg",
+ path: Path.absname("test/fixtures/image_tmp.jpg"),
+ tempfile: Path.absname("test/fixtures/image_tmp.jpg")
+ }
+
+ task =
+ Task.async(fn ->
+ assert_receive {:apply_filter, {}}, 4_000
+ end)
+
+ with_mocks([
+ {Mogrify, [],
+ [
+ open: fn _f -> %Mogrify.Image{} end,
+ custom: fn _m, _a -> send(task.pid, {:apply_filter, {}}) end,
+ custom: fn _m, _a, _o -> send(task.pid, {:apply_filter, {}}) end,
+ save: fn _f, _o -> :ok end
+ ]}
+ ]) do
+ assert Filter.Mogrifun.filter(upload) == :ok
+ end
+
+ Task.await(task)
+ end
+end
diff --git a/test/upload/filter/mogrify_test.exs b/test/upload/filter/mogrify_test.exs
new file mode 100644
index 000000000..c301440fd
--- /dev/null
+++ b/test/upload/filter/mogrify_test.exs
@@ -0,0 +1,51 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Upload.Filter.MogrifyTest do
+ use Pleroma.DataCase
+ import Mock
+
+ alias Pleroma.Config
+ alias Pleroma.Upload
+ alias Pleroma.Upload.Filter
+
+ setup do
+ filter = Config.get([Filter.Mogrify, :args])
+
+ on_exit(fn ->
+ Config.put([Filter.Mogrify, :args], filter)
+ end)
+ end
+
+ test "apply mogrify filter" do
+ Config.put([Filter.Mogrify, :args], [{"tint", "40"}])
+
+ File.cp!(
+ "test/fixtures/image.jpg",
+ "test/fixtures/image_tmp.jpg"
+ )
+
+ upload = %Upload{
+ name: "an… image.jpg",
+ content_type: "image/jpg",
+ path: Path.absname("test/fixtures/image_tmp.jpg"),
+ tempfile: Path.absname("test/fixtures/image_tmp.jpg")
+ }
+
+ task =
+ Task.async(fn ->
+ assert_receive {:apply_filter, {_, "tint", "40"}}, 4_000
+ end)
+
+ with_mock Mogrify,
+ open: fn _f -> %Mogrify.Image{} end,
+ custom: fn _m, _a -> :ok end,
+ custom: fn m, a, o -> send(task.pid, {:apply_filter, {m, a, o}}) end,
+ save: fn _f, _o -> :ok end do
+ assert Filter.Mogrify.filter(upload) == :ok
+ end
+
+ Task.await(task)
+ end
+end
diff --git a/test/upload/filter_test.exs b/test/upload/filter_test.exs
new file mode 100644
index 000000000..640cd7107
--- /dev/null
+++ b/test/upload/filter_test.exs
@@ -0,0 +1,39 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Upload.FilterTest do
+ use Pleroma.DataCase
+
+ alias Pleroma.Config
+ alias Pleroma.Upload.Filter
+
+ setup do
+ custom_filename = Config.get([Pleroma.Upload.Filter.AnonymizeFilename, :text])
+
+ on_exit(fn ->
+ Config.put([Pleroma.Upload.Filter.AnonymizeFilename, :text], custom_filename)
+ end)
+ end
+
+ test "applies filters" do
+ Config.put([Pleroma.Upload.Filter.AnonymizeFilename, :text], "custom-file.png")
+
+ File.cp!(
+ "test/fixtures/image.jpg",
+ "test/fixtures/image_tmp.jpg"
+ )
+
+ upload = %Pleroma.Upload{
+ name: "an… image.jpg",
+ content_type: "image/jpg",
+ path: Path.absname("test/fixtures/image_tmp.jpg"),
+ tempfile: Path.absname("test/fixtures/image_tmp.jpg")
+ }
+
+ assert Filter.filter([], upload) == {:ok, upload}
+
+ assert {:ok, upload} = Filter.filter([Pleroma.Upload.Filter.AnonymizeFilename], upload)
+ assert upload.name == "custom-file.png"
+ end
+end
diff --git a/test/web/mastodon_api/mastodon_api_controller_test.exs b/test/web/mastodon_api/mastodon_api_controller_test.exs
index 85b4ad024..d9d8dafdb 100644
--- a/test/web/mastodon_api/mastodon_api_controller_test.exs
+++ b/test/web/mastodon_api/mastodon_api_controller_test.exs
@@ -23,6 +23,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
import Pleroma.Factory
import ExUnit.CaptureLog
import Tesla.Mock
+ import Swoosh.TestAssertions
@image "data:image/gif;base64,R0lGODlhEAAQAMQAAORHHOVSKudfOulrSOp3WOyDZu6QdvCchPGolfO0o/XBs/fNwfjZ0frl3/zy7////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAkAABAALAAAAAAQABAAAAVVICSOZGlCQAosJ6mu7fiyZeKqNKToQGDsM8hBADgUXoGAiqhSvp5QAnQKGIgUhwFUYLCVDFCrKUE1lBavAViFIDlTImbKC5Gm2hB0SlBCBMQiB0UjIQA7"
@@ -3807,4 +3808,55 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
assert Enum.empty?(response)
end
end
+
+ describe "POST /auth/password, with valid parameters" do
+ setup %{conn: conn} do
+ user = insert(:user)
+ conn = post(conn, "/auth/password?email=#{user.email}")
+ %{conn: conn, user: user}
+ end
+
+ test "it returns 204", %{conn: conn} do
+ assert json_response(conn, :no_content)
+ end
+
+ test "it creates a PasswordResetToken record for user", %{user: user} do
+ token_record = Repo.get_by(Pleroma.PasswordResetToken, user_id: user.id)
+ assert token_record
+ end
+
+ test "it sends an email to user", %{user: user} do
+ token_record = Repo.get_by(Pleroma.PasswordResetToken, user_id: user.id)
+
+ email = Pleroma.Emails.UserEmail.password_reset_email(user, token_record.token)
+ notify_email = Pleroma.Config.get([:instance, :notify_email])
+ instance_name = Pleroma.Config.get([:instance, :name])
+
+ assert_email_sent(
+ from: {instance_name, notify_email},
+ to: {user.name, user.email},
+ html_body: email.html_body
+ )
+ end
+ end
+
+ describe "POST /auth/password, with invalid parameters" do
+ setup do
+ user = insert(:user)
+ {:ok, user: user}
+ end
+
+ test "it returns 404 when user is not found", %{conn: conn, user: user} do
+ conn = post(conn, "/auth/password?email=nonexisting_#{user.email}")
+ assert conn.status == 404
+ refute conn.resp_body
+ end
+
+ test "it returns 400 when user is not local", %{conn: conn, user: user} do
+ {:ok, user} = Repo.update(Changeset.change(user, local: false))
+ conn = post(conn, "/auth/password?email=#{user.email}")
+ assert conn.status == 400
+ refute conn.resp_body
+ end
+ end
end
diff --git a/test/web/twitter_api/twitter_api_controller_test.exs b/test/web/twitter_api/twitter_api_controller_test.exs
index de6177575..622bf510e 100644
--- a/test/web/twitter_api/twitter_api_controller_test.exs
+++ b/test/web/twitter_api/twitter_api_controller_test.exs
@@ -1116,15 +1116,17 @@ defmodule Pleroma.Web.TwitterAPI.ControllerTest do
describe "POST /api/account/password_reset, with invalid parameters" do
setup [:valid_user]
- test "it returns 500 when user is not found", %{conn: conn, user: user} do
+ test "it returns 404 when user is not found", %{conn: conn, user: user} do
conn = post(conn, "/api/account/password_reset?email=nonexisting_#{user.email}")
- assert json_response(conn, :internal_server_error)
+ assert conn.status == 404
+ refute conn.resp_body
end
- test "it returns 500 when user is not local", %{conn: conn, user: user} do
+ test "it returns 400 when user is not local", %{conn: conn, user: user} do
{:ok, user} = Repo.update(Changeset.change(user, local: false))
conn = post(conn, "/api/account/password_reset?email=#{user.email}")
- assert json_response(conn, :internal_server_error)
+ assert conn.status == 400
+ refute conn.resp_body
end
end