Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Configure middleware position in Rails #491

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,21 @@ You can disable it permanently (like for specific environment) or temporarily (c
Rack::Attack.enabled = false
```

You can also tweak its position in the Rack middleware stack.

```ruby
# in config/application.rb

# by index
config.rack_attack.middleware_position = 0

# relative to another middleware
config.rack_attack.middleware_position = {
before: AnotherMiddleware, # optional
after: YetAnotherMiddleware # optional
}
```

b) For __rack__ applications:

```ruby
Expand Down
4 changes: 2 additions & 2 deletions lib/rack/attack.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,6 @@
require 'rack/attack/store_proxy/redis_cache_store_proxy'
require 'rack/attack/store_proxy/active_support_redis_store_proxy'

require 'rack/attack/railtie' if defined?(::Rails)

module Rack
class Attack
class Error < StandardError; end
Expand Down Expand Up @@ -129,3 +127,5 @@ def call(env)
end
end
end

require 'rack/attack/railtie' if defined?(::Rails)
3 changes: 2 additions & 1 deletion lib/rack/attack/configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ class Configuration
end

attr_reader :safelists, :blocklists, :throttles, :anonymous_blocklists, :anonymous_safelists
attr_accessor :blocklisted_callback, :throttled_callback, :throttled_response_retry_after_header
attr_accessor :blocklisted_callback, :middleware_position, :throttled_callback,
:throttled_response_retry_after_header

attr_reader :blocklisted_response, :throttled_response # Keeping these for backwards compatibility

Expand Down
26 changes: 25 additions & 1 deletion lib/rack/attack/railtie.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,32 @@
module Rack
class Attack
class Railtie < ::Rails::Railtie
# Initialize rack-attack own configuration object in application config
config.rack_attack = Rack::Attack.configuration

# Set Rack middleware position. By default it is unset.
config.rack_attack.middleware_position = nil

initializer "rack-attack.middleware" do |app|
app.middleware.use(Rack::Attack)
insert_middleware_at(app, app.config.rack_attack.middleware_position)
end

private

def insert_middleware_at(app, position)
if position.nil?
app.middleware.use(Rack::Attack)
elsif position.is_a?(Integer)
app.middleware.insert_before(position, Rack::Attack)
elsif position.is_a?(Hash)
app.middleware.insert_before(position[:before], Rack::Attack) if position.key?(:before)
app.middleware.insert_after(position[:after], Rack::Attack) if position.key?(:after)
else
raise <<~ERROR
The middleware position you have set is invalid. Please be sure
`config.rack_attack.middleware_position` is set up correctly.
ERROR
end
end
end
end
Expand Down
40 changes: 38 additions & 2 deletions spec/acceptance/rails_middleware_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,45 @@
end
end

it "is used by default" do
it "is placed at the end by default" do
@app.initialize!
assert @app.middleware.include?(Rack::Attack)

assert @app.middleware.last == Rack::Attack
end

it "is placed at a specific index when the configured position is an integer" do
old_config = @app.config.rack_attack.clone
@app.config.rack_attack.middleware_position = 0

@app.initialize!

assert @app.middleware[0] == Rack::Attack

@app.config.rack_attack = old_config
end

it "is placed before a specific middleware when configured with :before" do
old_config = @app.config.rack_attack.clone
@app.config.rack_attack.middleware_position = { before: Rack::Runtime }

@app.initialize!

middlewares = @app.middleware.middlewares
assert middlewares.index(Rack::Attack) == middlewares.index(Rack::Runtime) - 1

@app.config.rack_attack = old_config
end

it "is placed after a specific middleware when configured with :after" do
old_config = @app.config.rack_attack.clone
@app.config.rack_attack.middleware_position = { after: Rack::Runtime }

@app.initialize!

middlewares = @app.middleware.middlewares
assert middlewares.index(Rack::Attack) == middlewares.index(Rack::Runtime) + 1

@app.config.rack_attack = old_config
end
end
end