Skip to content

Commit

Permalink
Merge pull request #431 from fatkodima/use-middleware
Browse files Browse the repository at this point in the history
Auto plug middleware for simpler installation
  • Loading branch information
grzuy authored Oct 9, 2019
2 parents e5ceab0 + f22b24c commit 95347e3
Show file tree
Hide file tree
Showing 12 changed files with 111 additions and 26 deletions.
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

0 comments on commit 95347e3

Please sign in to comment.