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

Auto plug middleware for simpler installation #431

Merged
merged 3 commits into from
Oct 9, 2019
Merged
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
14 changes: 5 additions & 9 deletions Appraisals
Original file line number Diff line number Diff line change
Expand Up @@ -17,24 +17,20 @@ appraise "rack_1_6" do
gem "rack-test", ">= 0.6"
end

appraise 'rails_6_0' do
gem 'actionpack', '~> 6.0.0'
gem 'activesupport', '~> 6.0.0'
appraise 'rails_6-0' do
gem 'railties', '~> 6.0.0'
end

appraise 'rails_5-2' do
gem 'actionpack', '~> 5.2.0'
gem 'activesupport', '~> 5.2.0'
gem 'railties', '~> 5.2.0'
end

appraise 'rails_5-1' do
gem 'actionpack', '~> 5.1.0'
gem 'activesupport', '~> 5.1.0'
gem 'railties', '~> 5.1.0'
end

appraise 'rails_4-2' do
gem 'actionpack', '~> 4.2.0'
gem 'activesupport', '~> 4.2.0'
gem 'railties', '~> 4.2.0'

# Override rack-test version constraint by making it more loose
# so it's compatible with actionpack 4.2.x
Expand Down
9 changes: 7 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,14 +68,19 @@ Or install it yourself as:

Then tell your ruby web application to use rack-attack as a middleware.

a) For __rails__ applications:

a) For __rails__ applications with versions >= 5 it is used by default. For older rails versions you should enable it explicitly:
```ruby
# In config/application.rb

config.middleware.use Rack::Attack
```

You can disable it permanently (like for specific environment) or temporarily (can be useful for specific test cases) by writing:

```ruby
Rack::Attack.enabled = false
```

b) For __rack__ applications:

```ruby
Expand Down
3 changes: 1 addition & 2 deletions gemfiles/rails_4_2.gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@

source "https://rubygems.org"

gem "actionpack", "~> 4.2.0"
gem "activesupport", "~> 4.2.0"
gem "railties", "~> 4.2.0"
gem "rack-test", ">= 0.6"

gemspec path: "../"
3 changes: 1 addition & 2 deletions gemfiles/rails_5_1.gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

source "https://rubygems.org"

gem "actionpack", "~> 5.1.0"
gem "activesupport", "~> 5.1.0"
gem "railties", "~> 5.1.0"

gemspec path: "../"
3 changes: 1 addition & 2 deletions gemfiles/rails_5_2.gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

source "https://rubygems.org"

gem "actionpack", "~> 5.2.0"
gem "activesupport", "~> 5.2.0"
gem "railties", "~> 5.2.0"

gemspec path: "../"
3 changes: 1 addition & 2 deletions gemfiles/rails_6_0.gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

source "https://rubygems.org"

gem "actionpack", "~> 6.0.0"
gem "activesupport", "~> 6.0.0"
gem "railties", "~> 6.0.0"

gemspec path: "../"
8 changes: 7 additions & 1 deletion lib/rack/attack.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
require 'rack/attack/request'
require "ipaddr"

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

module Rack
class Attack
class MisconfiguredStoreError < StandardError; end
Expand All @@ -28,7 +30,8 @@ class MissingStoreError < StandardError; end
autoload :Allow2Ban, 'rack/attack/allow2ban'

class << self
attr_accessor :notifier, :blocklisted_response, :throttled_response, :anonymous_blocklists, :anonymous_safelists
attr_accessor :enabled, :notifier, :blocklisted_response, :throttled_response,
:anonymous_blocklists, :anonymous_safelists

def safelist(name = nil, &block)
safelist = Safelist.new(name, &block)
Expand Down Expand Up @@ -134,6 +137,7 @@ def clear!
end

# Set defaults
@enabled = true
@anonymous_blocklists = []
@anonymous_safelists = []
@notifier = ActiveSupport::Notifications if defined?(ActiveSupport::Notifications)
Expand All @@ -148,6 +152,8 @@ def initialize(app)
end

def call(env)
return @app.call(env) unless self.class.enabled

env['PATH_INFO'] = PathNormalizer.normalize_path(env['PATH_INFO'])
request = Rack::Attack::Request.new(env)

Expand Down
21 changes: 21 additions & 0 deletions lib/rack/attack/railtie.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# frozen_string_literal: true

module Rack
class Attack
class Railtie < ::Rails::Railtie
initializer 'rack.attack.middleware', after: :load_config_initializers, before: :build_middleware_stack do |app|
if Gem::Version.new(::Rails::VERSION::STRING) >= Gem::Version.new("5")
middlewares = app.config.middleware
operations = middlewares.send(:operations) + middlewares.send(:delete_operations)

use_middleware = operations.none? do |operation|
middleware = operation[1]
middleware.include?(Rack::Attack)
end

middlewares.use(Rack::Attack) if use_middleware
end
end
end
end
end
5 changes: 1 addition & 4 deletions rack-attack.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,5 @@ Gem::Specification.new do |s|
s.add_development_dependency 'byebug', '~> 11.0'
end

# The following are potential runtime dependencies users may have,
# which rack-attack uses only for testing compatibility in test suite.
s.add_development_dependency 'actionpack', '~> 5.2'
s.add_development_dependency 'activesupport', '~> 5.2'
s.add_development_dependency 'railties', '>= 4.2'
end
41 changes: 41 additions & 0 deletions spec/acceptance/rails_middleware_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# frozen_string_literal: true

require_relative "../spec_helper"

if defined?(Rails)
describe "Middleware for Rails" do
before do
@app = Class.new(Rails::Application) do
config.eager_load = false
config.logger = Logger.new(nil) # avoid creating the log/ directory automatically
config.cache_store = :null_store # avoid creating tmp/ directory for cache
end
end

if Gem::Version.new(Rails::VERSION::STRING) >= Gem::Version.new("5")
it "is used by default" do
@app.initialize!
assert_equal 1, @app.middleware.count(Rack::Attack)
end

it "is not added when it was added explicitly" do
@app.config.middleware.use(Rack::Attack)
@app.initialize!
assert_equal 1, @app.middleware.count(Rack::Attack)
end

it "is not added when it was explicitly deleted" do
@app.config.middleware.delete(Rack::Attack)
@app.initialize!
refute @app.middleware.include?(Rack::Attack)
end
end

if Gem::Version.new(Rails::VERSION::STRING) < Gem::Version.new("5")
it "is not used by default" do
@app.initialize!
assert_equal 0, @app.middleware.count(Rack::Attack)
end
end
end
end
23 changes: 23 additions & 0 deletions spec/rack_attack_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -76,4 +76,27 @@
end
end
end

describe 'enabled' do
it 'should be enabled by default' do
_(Rack::Attack.enabled).must_equal true
end

it 'should directly pass request when disabled' do
bad_ip = '1.2.3.4'
Rack::Attack.blocklist("ip #{bad_ip}") { |req| req.ip == bad_ip }

get '/', {}, 'REMOTE_ADDR' => bad_ip
_(last_response.status).must_equal 403

prev_enabled = Rack::Attack.enabled
begin
Rack::Attack.enabled = false
get '/', {}, 'REMOTE_ADDR' => bad_ip
_(last_response.status).must_equal 200
ensure
Rack::Attack.enabled = prev_enabled
end
end
end
end
4 changes: 2 additions & 2 deletions spec/spec_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@
require "minitest/autorun"
require "minitest/pride"
require "rack/test"
require 'active_support'
require 'action_dispatch'
require "rails"

require "rack/attack"

Expand All @@ -30,6 +29,7 @@ class MiniTest::Spec
include Rack::Test::Methods

before do
Rails.cache = nil
@_original_throttled_response = Rack::Attack.throttled_response
@_original_blocklisted_response = Rack::Attack.blocklisted_response
end
Expand Down