mirror of
https://git.pleroma.social/pleroma/pleroma.git
synced 2024-12-21 23:56:30 +00:00
Merge branch 'oauth-app-spam2' into 'develop'
OAuth App Spam, revisited See merge request pleroma/pleroma!4250
This commit is contained in:
commit
25db1a5d67
9 changed files with 101 additions and 1 deletions
1
changelog.d/oauth-app-spam.fix
Normal file
1
changelog.d/oauth-app-spam.fix
Normal file
|
@ -0,0 +1 @@
|
|||
Add a rate limiter to the OAuth App creation endpoint and ensure registered apps are assigned to users.
|
|
@ -597,7 +597,8 @@ config :pleroma, Oban,
|
|||
plugins: [{Oban.Plugins.Pruner, max_age: 900}],
|
||||
crontab: [
|
||||
{"0 0 * * 0", Pleroma.Workers.Cron.DigestEmailsWorker},
|
||||
{"0 0 * * *", Pleroma.Workers.Cron.NewUsersDigestWorker}
|
||||
{"0 0 * * *", Pleroma.Workers.Cron.NewUsersDigestWorker},
|
||||
{"*/10 * * * *", Pleroma.Workers.Cron.AppCleanupWorker}
|
||||
]
|
||||
|
||||
config :pleroma, Pleroma.Formatter,
|
||||
|
@ -711,6 +712,7 @@ config :pleroma, :rate_limit,
|
|||
timeline: {500, 3},
|
||||
search: [{1000, 10}, {1000, 30}],
|
||||
app_account_creation: {1_800_000, 25},
|
||||
oauth_app_creation: {900_000, 5},
|
||||
relations_actions: {10_000, 10},
|
||||
relation_id_action: {60_000, 2},
|
||||
statuses_actions: {10_000, 15},
|
||||
|
|
|
@ -19,6 +19,8 @@ defmodule Pleroma.Web.MastodonAPI.AppController do
|
|||
|
||||
action_fallback(Pleroma.Web.MastodonAPI.FallbackController)
|
||||
|
||||
plug(Pleroma.Web.Plugs.RateLimiter, [name: :oauth_app_creation] when action == :create)
|
||||
|
||||
plug(:skip_auth when action in [:create, :verify_credentials])
|
||||
|
||||
plug(Pleroma.Web.ApiSpec.CastAndValidate)
|
||||
|
|
|
@ -8,6 +8,7 @@ defmodule Pleroma.Web.OAuth.App do
|
|||
import Ecto.Query
|
||||
alias Pleroma.Repo
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.OAuth.Token
|
||||
|
||||
@type t :: %__MODULE__{}
|
||||
|
||||
|
@ -155,4 +156,29 @@ defmodule Pleroma.Web.OAuth.App do
|
|||
Map.put(acc, key, error)
|
||||
end)
|
||||
end
|
||||
|
||||
@spec maybe_update_owner(Token.t()) :: :ok
|
||||
def maybe_update_owner(%Token{app_id: app_id, user_id: user_id}) when not is_nil(user_id) do
|
||||
__MODULE__.update(app_id, %{user_id: user_id})
|
||||
|
||||
:ok
|
||||
end
|
||||
|
||||
def maybe_update_owner(_), do: :ok
|
||||
|
||||
@spec remove_orphans(pos_integer()) :: :ok
|
||||
def remove_orphans(limit \\ 100) do
|
||||
fifteen_mins_ago = DateTime.add(DateTime.utc_now(), -900, :second)
|
||||
|
||||
Repo.transaction(fn ->
|
||||
from(a in __MODULE__,
|
||||
where: is_nil(a.user_id) and a.inserted_at < ^fifteen_mins_ago,
|
||||
limit: ^limit
|
||||
)
|
||||
|> Repo.all()
|
||||
|> Enum.each(&Repo.delete(&1))
|
||||
end)
|
||||
|
||||
:ok
|
||||
end
|
||||
end
|
||||
|
|
|
@ -318,6 +318,8 @@ defmodule Pleroma.Web.OAuth.OAuthController do
|
|||
def token_exchange(%Plug.Conn{} = conn, params), do: bad_request(conn, params)
|
||||
|
||||
def after_token_exchange(%Plug.Conn{} = conn, %{token: token} = view_params) do
|
||||
App.maybe_update_owner(token)
|
||||
|
||||
conn
|
||||
|> AuthHelper.put_session_token(token.token)
|
||||
|> json(OAuthView.render("token.json", view_params))
|
||||
|
|
21
lib/pleroma/workers/cron/app_cleanup_worker.ex
Normal file
21
lib/pleroma/workers/cron/app_cleanup_worker.ex
Normal file
|
@ -0,0 +1,21 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Workers.Cron.AppCleanupWorker do
|
||||
@moduledoc """
|
||||
Cleans up registered apps that were never associated with a user.
|
||||
"""
|
||||
|
||||
use Oban.Worker, queue: "background"
|
||||
|
||||
alias Pleroma.Web.OAuth.App
|
||||
|
||||
@impl true
|
||||
def perform(_job) do
|
||||
App.remove_orphans()
|
||||
end
|
||||
|
||||
@impl true
|
||||
def timeout(_job), do: :timer.seconds(30)
|
||||
end
|
21
priv/repo/migrations/20240904142434_assign_app_user.exs
Normal file
21
priv/repo/migrations/20240904142434_assign_app_user.exs
Normal file
|
@ -0,0 +1,21 @@
|
|||
defmodule Pleroma.Repo.Migrations.AssignAppUser do
|
||||
use Ecto.Migration
|
||||
|
||||
alias Pleroma.Repo
|
||||
alias Pleroma.Web.OAuth.App
|
||||
alias Pleroma.Web.OAuth.Token
|
||||
|
||||
def up do
|
||||
Repo.all(Token)
|
||||
|> Enum.group_by(fn x -> Map.get(x, :app_id) end)
|
||||
|> Enum.each(fn {_app_id, tokens} ->
|
||||
token =
|
||||
Enum.filter(tokens, fn x -> not is_nil(x.user_id) end)
|
||||
|> List.first()
|
||||
|
||||
App.maybe_update_owner(token)
|
||||
end)
|
||||
end
|
||||
|
||||
def down, do: :ok
|
||||
end
|
|
@ -53,4 +53,21 @@ defmodule Pleroma.Web.OAuth.AppTest do
|
|||
|
||||
assert Enum.sort(App.get_user_apps(user)) == Enum.sort(apps)
|
||||
end
|
||||
|
||||
test "removes orphaned apps" do
|
||||
attrs = %{client_name: "Mastodon-Local", redirect_uris: "."}
|
||||
{:ok, %App{} = old_app} = App.get_or_make(attrs, ["write"])
|
||||
|
||||
attrs = %{client_name: "PleromaFE", redirect_uris: "."}
|
||||
{:ok, %App{} = app} = App.get_or_make(attrs, ["write"])
|
||||
|
||||
# backdate the old app so it's within the threshold for being cleaned up
|
||||
{:ok, _} =
|
||||
"UPDATE apps SET inserted_at = now() - interval '1 hour' WHERE id = #{old_app.id}"
|
||||
|> Pleroma.Repo.query()
|
||||
|
||||
App.remove_orphans()
|
||||
|
||||
assert [app] == Pleroma.Repo.all(App)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -12,6 +12,7 @@ defmodule Pleroma.Web.OAuth.OAuthControllerTest do
|
|||
alias Pleroma.MFA.TOTP
|
||||
alias Pleroma.Repo
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.OAuth.App
|
||||
alias Pleroma.Web.OAuth.Authorization
|
||||
alias Pleroma.Web.OAuth.OAuthController
|
||||
alias Pleroma.Web.OAuth.Token
|
||||
|
@ -770,6 +771,9 @@ defmodule Pleroma.Web.OAuth.OAuthControllerTest do
|
|||
|
||||
{:ok, auth} = Authorization.create_authorization(app, user, ["write"])
|
||||
|
||||
# Verify app has no associated user yet
|
||||
assert %Pleroma.Web.OAuth.App{user_id: nil} = Repo.get_by(App, %{id: app.id})
|
||||
|
||||
conn =
|
||||
build_conn()
|
||||
|> post("/oauth/token", %{
|
||||
|
@ -786,6 +790,10 @@ defmodule Pleroma.Web.OAuth.OAuthControllerTest do
|
|||
assert token
|
||||
assert token.scopes == auth.scopes
|
||||
assert user.ap_id == ap_id
|
||||
|
||||
# Verify app has an associated user now
|
||||
user_id = user.id
|
||||
assert %Pleroma.Web.OAuth.App{user_id: ^user_id} = Repo.get_by(App, %{id: app.id})
|
||||
end
|
||||
|
||||
test "issues a token for `password` grant_type with valid credentials, with full permissions by default" do
|
||||
|
|
Loading…
Reference in a new issue