FEATURE: add global rate limiter for admin api 60 per minute
Also move configuration of admin and user api rate limiting into global settings. This is not intended to be configurable per site
This commit is contained in:
parent
394abbe26b
commit
68d3c2c74f
|
@ -168,3 +168,10 @@ s3_access_key_id =
|
|||
s3_secret_access_key =
|
||||
s3_use_iam_profile = false
|
||||
s3_cdn_url =
|
||||
|
||||
|
||||
### rate limits apply to all sites
|
||||
max_user_api_reqs_per_minute = 20
|
||||
max_user_api_reqs_per_day = 2880
|
||||
|
||||
max_admin_api_reqs_per_key_per_minute = 60
|
||||
|
|
|
@ -1562,8 +1562,6 @@ en:
|
|||
|
||||
retain_web_hook_events_period_days: "Number of days to retain web hook event records."
|
||||
|
||||
max_user_api_reqs_per_day: "Maximum number of user API requests per key per day"
|
||||
max_user_api_reqs_per_minute: "Maximum number of user API requests per key per minute"
|
||||
allow_user_api_keys: "Allow generation of user API keys"
|
||||
allow_user_api_key_scopes: "List of scopes allowed for user API keys"
|
||||
max_api_keys_per_user: "Maximum number of user API keys per user"
|
||||
|
|
|
@ -1448,10 +1448,6 @@ api:
|
|||
default: 30
|
||||
|
||||
user_api:
|
||||
max_user_api_reqs_per_day:
|
||||
default: 2880
|
||||
max_user_api_reqs_per_minute:
|
||||
default: 20
|
||||
allow_user_api_keys:
|
||||
default: true
|
||||
allow_user_api_key_scopes:
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require_dependency "auth/current_user_provider"
|
||||
require_dependency "rate_limiter"
|
||||
|
||||
|
@ -79,13 +81,16 @@ class Auth::DefaultCurrentUserProvider
|
|||
raise Discourse::InvalidAccess.new(I18n.t('invalid_api_credentials'), nil, custom_message: "invalid_api_credentials") unless current_user
|
||||
raise Discourse::InvalidAccess if current_user.suspended? || !current_user.active
|
||||
@env[API_KEY_ENV] = true
|
||||
|
||||
limiter_min = RateLimiter.new(nil, "admin_api_min_#{api_key}", GlobalSetting.max_admin_api_reqs_per_key_per_minute, 60)
|
||||
limiter_min.performed!
|
||||
end
|
||||
|
||||
# user api key handling
|
||||
if user_api_key
|
||||
|
||||
limiter_min = RateLimiter.new(nil, "user_api_min_#{user_api_key}", SiteSetting.max_user_api_reqs_per_minute, 60)
|
||||
limiter_day = RateLimiter.new(nil, "user_api_day_#{user_api_key}", SiteSetting.max_user_api_reqs_per_day, 86400)
|
||||
limiter_min = RateLimiter.new(nil, "user_api_min_#{user_api_key}", GlobalSetting.max_user_api_reqs_per_minute, 60)
|
||||
limiter_day = RateLimiter.new(nil, "user_api_day_#{user_api_key}", GlobalSetting.max_user_api_reqs_per_day, 86400)
|
||||
|
||||
unless limiter_day.can_perform?
|
||||
limiter_day.performed!
|
||||
|
|
|
@ -16,6 +16,8 @@ describe Auth::DefaultCurrentUserProvider do
|
|||
TestProvider.new(env)
|
||||
end
|
||||
|
||||
context "server api" do
|
||||
|
||||
it "raises errors for incorrect api_key" do
|
||||
expect {
|
||||
provider("/?api_key=INCORRECT").current_user
|
||||
|
@ -81,6 +83,41 @@ describe Auth::DefaultCurrentUserProvider do
|
|||
expect(provider("/?api_key=hello&api_username=#{user.username.downcase}").current_user.id).to eq(user.id)
|
||||
end
|
||||
|
||||
context "rate limiting" do
|
||||
before do
|
||||
RateLimiter.enable
|
||||
end
|
||||
|
||||
after do
|
||||
RateLimiter.disable
|
||||
end
|
||||
|
||||
it "rate limits api requests per api key" do
|
||||
global_setting :max_admin_api_reqs_per_key_per_minute, 3
|
||||
|
||||
user = Fabricate(:user)
|
||||
key = SecureRandom.hex
|
||||
api_key = ApiKey.create!(key: key, created_by_id: -1)
|
||||
|
||||
provider("/?api_key=#{key}&api_username=#{user.username.downcase}").current_user
|
||||
provider("/?api_key=#{key}&api_username=system").current_user
|
||||
provider("/?api_key=#{key}&api_username=#{user.username.downcase}").current_user
|
||||
|
||||
expect do
|
||||
provider("/?api_key=#{key}&api_username=system").current_user
|
||||
end.to raise_error(RateLimiter::LimitExceeded)
|
||||
|
||||
# should not rake limit a random key
|
||||
api_key.destroy
|
||||
key = SecureRandom.hex
|
||||
ApiKey.create!(key: key, created_by_id: -1)
|
||||
provider("/?api_key=#{key}&api_username=#{user.username.downcase}").current_user
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
it "should not update last seen for ajax calls without Discourse-Visible header" do
|
||||
expect(provider("/topic/anything/goes",
|
||||
:method => "POST",
|
||||
|
@ -320,8 +357,8 @@ describe Auth::DefaultCurrentUserProvider do
|
|||
limiter1.clear!
|
||||
limiter2.clear!
|
||||
|
||||
SiteSetting.max_user_api_reqs_per_day = 3
|
||||
SiteSetting.max_user_api_reqs_per_minute = 4
|
||||
global_setting :max_user_api_reqs_per_day, 3
|
||||
global_setting :max_user_api_reqs_per_minute, 4
|
||||
|
||||
params = {
|
||||
"REQUEST_METHOD" => "GET",
|
||||
|
@ -336,8 +373,8 @@ describe Auth::DefaultCurrentUserProvider do
|
|||
provider("/", params).current_user
|
||||
}.to raise_error(RateLimiter::LimitExceeded)
|
||||
|
||||
SiteSetting.max_user_api_reqs_per_day = 4
|
||||
SiteSetting.max_user_api_reqs_per_minute = 3
|
||||
global_setting :max_user_api_reqs_per_day, 4
|
||||
global_setting :max_user_api_reqs_per_minute, 3
|
||||
|
||||
limiter1.clear!
|
||||
limiter2.clear!
|
||||
|
|
Loading…
Reference in New Issue