FEATURE: Provide generic modifier interface for rate limitin
This commit is contained in:
parent
a4565044d0
commit
aceb4e8069
|
@ -59,13 +59,3 @@ discourse_solved:
|
||||||
enable_solved_tags:
|
enable_solved_tags:
|
||||||
type: tag_list
|
type: tag_list
|
||||||
default: ""
|
default: ""
|
||||||
solved_bypass_rate_limit:
|
|
||||||
default: false
|
|
||||||
client: false
|
|
||||||
description: "Allow high trust level users to bypass rate limiting for accepting solutions"
|
|
||||||
solved_min_trust_level_for_bypass:
|
|
||||||
default: 3
|
|
||||||
client: false
|
|
||||||
min: 0
|
|
||||||
max: 4
|
|
||||||
description: "Minimum trust level required to bypass rate limiting"
|
|
||||||
|
|
22
plugin.rb
22
plugin.rb
|
@ -352,26 +352,4 @@ after_initialize do
|
||||||
DiscourseDev::DiscourseSolved.populate(self)
|
DiscourseDev::DiscourseSolved.populate(self)
|
||||||
DiscourseAutomation::EntryPoint.inject(self) if defined?(DiscourseAutomation)
|
DiscourseAutomation::EntryPoint.inject(self) if defined?(DiscourseAutomation)
|
||||||
DiscourseAssign::EntryPoint.inject(self) if defined?(DiscourseAssign)
|
DiscourseAssign::EntryPoint.inject(self) if defined?(DiscourseAssign)
|
||||||
|
|
||||||
# Register a modifier to control rate limiting for solution acceptance
|
|
||||||
# Parameters:
|
|
||||||
# - default_value: The default rate limiting state (true to apply rate limiting, false to skip it)
|
|
||||||
# - user: The current user object, used to determine whether to apply rate limiting
|
|
||||||
register_modifier(:solved_answers_controller_run_rate_limiter) do |default_value, user|
|
|
||||||
get_setting = ->(name, default) do
|
|
||||||
SiteSetting.respond_to?(name) ? SiteSetting.public_send(name) : default
|
|
||||||
end
|
|
||||||
|
|
||||||
user_level = user.respond_to?(:trust_level) ? user.trust_level : 0
|
|
||||||
|
|
||||||
min_bypass_level = get_setting.call(:solved_min_trust_level_for_bypass, 3)
|
|
||||||
|
|
||||||
bypass_enabled = get_setting.call(:solved_bypass_rate_limit, false)
|
|
||||||
|
|
||||||
if bypass_enabled && user_level >= min_bypass_level
|
|
||||||
false
|
|
||||||
else
|
|
||||||
default_value
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -4,7 +4,6 @@ require "rails_helper"
|
||||||
|
|
||||||
describe DiscourseSolved::AnswerController do
|
describe DiscourseSolved::AnswerController do
|
||||||
fab!(:user)
|
fab!(:user)
|
||||||
fab!(:high_trust_user) { Fabricate(:user, trust_level: 3) }
|
|
||||||
fab!(:staff_user) { Fabricate(:admin) }
|
fab!(:staff_user) { Fabricate(:admin) }
|
||||||
fab!(:category)
|
fab!(:category)
|
||||||
fab!(:topic) { Fabricate(:topic, category: category) }
|
fab!(:topic) { Fabricate(:topic, category: category) }
|
||||||
|
@ -19,7 +18,6 @@ describe DiscourseSolved::AnswerController do
|
||||||
|
|
||||||
# Give permission to accept solutions
|
# Give permission to accept solutions
|
||||||
user.update!(trust_level: 1)
|
user.update!(trust_level: 1)
|
||||||
high_trust_user.update!(trust_level: 3)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "#accept" do
|
describe "#accept" do
|
||||||
|
@ -54,84 +52,27 @@ describe DiscourseSolved::AnswerController do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context "with bypass settings" do
|
context "with plugin modifier" do
|
||||||
before do
|
it "allows plugins to bypass rate limiting via modifier" do
|
||||||
SiteSetting.solved_bypass_rate_limit = true
|
|
||||||
SiteSetting.solved_min_trust_level_for_bypass = 3
|
|
||||||
end
|
|
||||||
|
|
||||||
it "applies rate limits to low trust users" do
|
|
||||||
sign_in(user)
|
sign_in(user)
|
||||||
|
|
||||||
# First request should succeed
|
# Example of how plugins can customize rate limiting behavior
|
||||||
post "/solution/accept.json", params: { id: solution_post.id }
|
DiscoursePluginRegistry.register_modifier(
|
||||||
expect(response.status).to eq(200)
|
:solved_answers_controller_run_rate_limiter,
|
||||||
|
) do |_, _|
|
||||||
# Try to make too many requests in a short time
|
false # Skip rate limiting completely
|
||||||
RateLimiter.any_instance.expects(:performed!).raises(RateLimiter::LimitExceeded.new(60))
|
|
||||||
|
|
||||||
post "/solution/accept.json", params: { id: solution_post.id }
|
|
||||||
expect(response.status).to eq(429) # Rate limited status
|
|
||||||
end
|
end
|
||||||
|
|
||||||
it "does not apply rate limits to high trust users" do
|
|
||||||
# 让high_trust_user成为话题创建者,这样他就有权限接受答案
|
|
||||||
topic.update!(user_id: high_trust_user.id)
|
|
||||||
|
|
||||||
sign_in(high_trust_user)
|
|
||||||
|
|
||||||
# First request should succeed without rate limiting
|
|
||||||
post "/solution/accept.json", params: { id: solution_post.id }
|
|
||||||
expect(response.status).to eq(200)
|
|
||||||
|
|
||||||
# Should be able to make another request without rate limiting
|
|
||||||
RateLimiter.any_instance.expects(:performed!).never
|
|
||||||
|
|
||||||
post "/solution/accept.json", params: { id: solution_post.id }
|
|
||||||
expect(response.status).to eq(200)
|
|
||||||
end
|
|
||||||
|
|
||||||
it "respects min trust level setting changes" do
|
|
||||||
# 让high_trust_user成为话题创建者,这样他就有权限接受答案
|
|
||||||
topic.update!(user_id: high_trust_user.id)
|
|
||||||
|
|
||||||
SiteSetting.solved_min_trust_level_for_bypass = 4
|
|
||||||
|
|
||||||
sign_in(high_trust_user) # TL3 user
|
|
||||||
|
|
||||||
# First request should succeed
|
# First request should succeed
|
||||||
post "/solution/accept.json", params: { id: solution_post.id }
|
post "/solution/accept.json", params: { id: solution_post.id }
|
||||||
expect(response.status).to eq(200)
|
expect(response.status).to eq(200)
|
||||||
|
|
||||||
# Now rate limiting should apply since TL3 < TL4 requirement
|
# Second request should also succeed because rate limiting is bypassed
|
||||||
RateLimiter.any_instance.expects(:performed!).raises(RateLimiter::LimitExceeded.new(60))
|
|
||||||
|
|
||||||
post "/solution/accept.json", params: { id: solution_post.id }
|
|
||||||
expect(response.status).to eq(429) # Rate limited status
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context "with bypass disabled" do
|
|
||||||
before do
|
|
||||||
SiteSetting.solved_bypass_rate_limit = false
|
|
||||||
SiteSetting.solved_min_trust_level_for_bypass = 3
|
|
||||||
|
|
||||||
# 让high_trust_user成为话题创建者,这样他就有权限接受答案
|
|
||||||
topic.update!(user_id: high_trust_user.id)
|
|
||||||
end
|
|
||||||
|
|
||||||
it "applies rate limits to all non-staff users" do
|
|
||||||
sign_in(high_trust_user) # TL3 user
|
|
||||||
|
|
||||||
# First request should succeed
|
|
||||||
post "/solution/accept.json", params: { id: solution_post.id }
|
post "/solution/accept.json", params: { id: solution_post.id }
|
||||||
expect(response.status).to eq(200)
|
expect(response.status).to eq(200)
|
||||||
|
|
||||||
# Rate limiting should apply despite high trust level because bypass is disabled
|
# Clean up
|
||||||
RateLimiter.any_instance.expects(:performed!).raises(RateLimiter::LimitExceeded.new(60))
|
DiscoursePluginRegistry.unregister_modifier(:solved_answers_controller_run_rate_limiter)
|
||||||
|
|
||||||
post "/solution/accept.json", params: { id: solution_post.id }
|
|
||||||
expect(response.status).to eq(429) # Rate limited status
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -148,13 +89,7 @@ describe DiscourseSolved::AnswerController do
|
||||||
sign_out
|
sign_out
|
||||||
end
|
end
|
||||||
|
|
||||||
context "with bypass settings" do
|
it "applies rate limits to regular users" do
|
||||||
before do
|
|
||||||
SiteSetting.solved_bypass_rate_limit = true
|
|
||||||
SiteSetting.solved_min_trust_level_for_bypass = 3
|
|
||||||
end
|
|
||||||
|
|
||||||
it "applies rate limits to low trust users" do
|
|
||||||
sign_in(user)
|
sign_in(user)
|
||||||
|
|
||||||
# Try to make too many requests in a short time
|
# Try to make too many requests in a short time
|
||||||
|
@ -163,19 +98,5 @@ describe DiscourseSolved::AnswerController do
|
||||||
post "/solution/unaccept.json", params: { id: solution_post.id }
|
post "/solution/unaccept.json", params: { id: solution_post.id }
|
||||||
expect(response.status).to eq(429) # Rate limited status
|
expect(response.status).to eq(429) # Rate limited status
|
||||||
end
|
end
|
||||||
|
|
||||||
it "does not apply rate limits to high trust users" do
|
|
||||||
# Give topic ownership to high trust user so they can unaccept
|
|
||||||
topic.update!(user_id: high_trust_user.id)
|
|
||||||
|
|
||||||
sign_in(high_trust_user)
|
|
||||||
|
|
||||||
# Should be able to unaccept without rate limiting
|
|
||||||
RateLimiter.any_instance.expects(:performed!).never
|
|
||||||
|
|
||||||
post "/solution/unaccept.json", params: { id: solution_post.id }
|
|
||||||
expect(response.status).to eq(200)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in New Issue