mastoapi password reset
added rate limit to password reset configure rate limit in runtime
This commit is contained in:
parent
33fbb638cd
commit
10f82c88b8
|
@ -32,6 +32,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 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 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: 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 users' tags when querying reports
|
||||||
- Admin API: Return avatar and display name when querying users
|
- Admin API: Return avatar and display name when querying users
|
||||||
- Admin API: Allow querying user by ID
|
- Admin API: Allow querying user by ID
|
||||||
|
@ -40,6 +41,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: `enabled` option for `Pleroma.Emails.Mailer`, defaulting to `false`.
|
||||||
- Configuration: Pleroma.Plugs.RateLimiter `bucket_name`, `params` options.
|
- Configuration: Pleroma.Plugs.RateLimiter `bucket_name`, `params` options.
|
||||||
- Addressable lists
|
- Addressable lists
|
||||||
|
- Twitter API: added rate limit for `/api/account/password_reset` endpoint.
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
- Configuration: Filter.AnonymizeFilename added ability to retain file extension with custom text
|
- Configuration: Filter.AnonymizeFilename added ability to retain file extension with custom text
|
||||||
|
|
|
@ -531,7 +531,8 @@
|
||||||
relations_actions: {10_000, 10},
|
relations_actions: {10_000, 10},
|
||||||
relation_id_action: {60_000, 2},
|
relation_id_action: {60_000, 2},
|
||||||
statuses_actions: {10_000, 15},
|
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
|
# Import environment specific config. This must remain at the bottom
|
||||||
# of this file so it overrides the configuration defined above.
|
# of this file so it overrides the configuration defined above.
|
||||||
|
|
|
@ -67,7 +67,8 @@
|
||||||
|
|
||||||
config :pleroma, :rate_limit,
|
config :pleroma, :rate_limit,
|
||||||
search: [{1000, 30}, {1000, 30}],
|
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"
|
config :pleroma, :http_security, report_uri: "https://endpoint.com"
|
||||||
|
|
||||||
|
|
|
@ -73,6 +73,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|
||||||
plug(RateLimiter, :statuses_actions when action in @rate_limited_status_actions)
|
plug(RateLimiter, :statuses_actions when action in @rate_limited_status_actions)
|
||||||
plug(RateLimiter, :app_account_creation when action == :account_register)
|
plug(RateLimiter, :app_account_creation when action == :account_register)
|
||||||
plug(RateLimiter, :search when action in [:search, :search2, :account_search])
|
plug(RateLimiter, :search when action in [:search, :search2, :account_search])
|
||||||
|
plug(RateLimiter, :password_reset when action == :password_reset)
|
||||||
|
|
||||||
@local_mastodon_name "Mastodon-Local"
|
@local_mastodon_name "Mastodon-Local"
|
||||||
|
|
||||||
|
@ -1816,6 +1817,22 @@ def conversation_read(%{assigns: %{user: user}} = conn, %{"id" => participation_
|
||||||
end
|
end
|
||||||
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)
|
def try_render(conn, target, params)
|
||||||
when is_binary(target) do
|
when is_binary(target) do
|
||||||
case render(conn, target, params) do
|
case render(conn, target, params) do
|
||||||
|
|
|
@ -691,6 +691,8 @@ defmodule Pleroma.Web.Router do
|
||||||
get("/web/login", MastodonAPIController, :login)
|
get("/web/login", MastodonAPIController, :login)
|
||||||
delete("/auth/sign_out", MastodonAPIController, :logout)
|
delete("/auth/sign_out", MastodonAPIController, :logout)
|
||||||
|
|
||||||
|
post("/auth/password", MastodonAPIController, :password_reset)
|
||||||
|
|
||||||
scope [] do
|
scope [] do
|
||||||
pipe_through(:oauth_read_or_public)
|
pipe_through(:oauth_read_or_public)
|
||||||
get("/web/*path", MastodonAPIController, :index)
|
get("/web/*path", MastodonAPIController, :index)
|
||||||
|
|
|
@ -27,6 +27,7 @@ defmodule Pleroma.Web.TwitterAPI.Controller do
|
||||||
|
|
||||||
require Logger
|
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])
|
plug(:only_if_public_instance when action in [:public_timeline, :public_and_external_timeline])
|
||||||
action_fallback(:errors)
|
action_fallback(:errors)
|
||||||
|
|
||||||
|
@ -437,6 +438,12 @@ def password_reset(conn, params) do
|
||||||
|
|
||||||
with {:ok, _} <- TwitterAPI.password_reset(nickname_or_email) do
|
with {:ok, _} <- TwitterAPI.password_reset(nickname_or_email) do
|
||||||
json_response(conn, :no_content, "")
|
json_response(conn, :no_content, "")
|
||||||
|
else
|
||||||
|
{:error, "unknown user"} ->
|
||||||
|
put_status(conn, :not_found)
|
||||||
|
|
||||||
|
{:error, _} ->
|
||||||
|
put_status(conn, :bad_request)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -23,6 +23,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
|
||||||
import Pleroma.Factory
|
import Pleroma.Factory
|
||||||
import ExUnit.CaptureLog
|
import ExUnit.CaptureLog
|
||||||
import Tesla.Mock
|
import Tesla.Mock
|
||||||
|
import Swoosh.TestAssertions
|
||||||
|
|
||||||
@image "data:image/gif;base64,R0lGODlhEAAQAMQAAORHHOVSKudfOulrSOp3WOyDZu6QdvCchPGolfO0o/XBs/fNwfjZ0frl3/zy7////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAkAABAALAAAAAAQABAAAAVVICSOZGlCQAosJ6mu7fiyZeKqNKToQGDsM8hBADgUXoGAiqhSvp5QAnQKGIgUhwFUYLCVDFCrKUE1lBavAViFIDlTImbKC5Gm2hB0SlBCBMQiB0UjIQA7"
|
@image "data:image/gif;base64,R0lGODlhEAAQAMQAAORHHOVSKudfOulrSOp3WOyDZu6QdvCchPGolfO0o/XBs/fNwfjZ0frl3/zy7////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAkAABAALAAAAAAQABAAAAVVICSOZGlCQAosJ6mu7fiyZeKqNKToQGDsM8hBADgUXoGAiqhSvp5QAnQKGIgUhwFUYLCVDFCrKUE1lBavAViFIDlTImbKC5Gm2hB0SlBCBMQiB0UjIQA7"
|
||||||
|
|
||||||
|
@ -3807,4 +3808,55 @@ test "returns empty array when status has not been reblogged yet", %{
|
||||||
assert Enum.empty?(response)
|
assert Enum.empty?(response)
|
||||||
end
|
end
|
||||||
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
|
end
|
||||||
|
|
|
@ -1116,15 +1116,17 @@ test "it sends an email to user", %{user: user} do
|
||||||
describe "POST /api/account/password_reset, with invalid parameters" do
|
describe "POST /api/account/password_reset, with invalid parameters" do
|
||||||
setup [:valid_user]
|
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}")
|
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
|
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))
|
{:ok, user} = Repo.update(Changeset.change(user, local: false))
|
||||||
conn = post(conn, "/api/account/password_reset?email=#{user.email}")
|
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
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue