Skip to content

Commit

Permalink
Merge pull request #52 from Matchbook/env_constraints
Browse files Browse the repository at this point in the history
Add environment constraints
  • Loading branch information
rymai committed May 3, 2013
2 parents 91c7234 + 45fa663 commit 025ac24
Show file tree
Hide file tree
Showing 4 changed files with 127 additions and 7 deletions.
23 changes: 20 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,16 @@ Add Rack::SslEnforcer to your `Gemfile`:
In order for Rack::SslEnforcer to properly work it has to be at the top
of the Rack Middleware.

Using `enable :session` will place Rack::Session::Cookie before Rack::Ssl::Enforcer
Using `enable :session` will place Rack::Session::Cookie before Rack::Ssl::Enforcer
and will prevent Rack::Ssl::Enforcer from marking cookies as secure.

To fix this issue do not use `enable :sessions` instead add the
Rack::Session::Cookie middleware after Rack::Ssl::Enforcer.
Rack::Session::Cookie middleware after Rack::Ssl::Enforcer.

Eg:

```ruby
use Rack::SslEnforcer
use Rack::SslEnforcer
set :session_secret, 'asdfa2342923422f1adc05c837fa234230e3594b93824b00e930ab0fb94b'

#Enable sinatra sessions
Expand Down Expand Up @@ -103,6 +103,23 @@ config.middleware.use Rack::SslEnforcer, :except_methods => ['GET', 'HEAD']

Note: The `:hosts` constraint takes precedence over the `:path` constraint. Please see the tests for examples.


### Environment constraints

You can enforce SSL connections only for certain environments with `:only_environments` or prevent certain environments from being forced to SSL with `:except_environments`.
Environment constraints may be a `String`, a `Regex` or an array of `String` or `Regex` (possibly mixed), as shown in the following examples:

```ruby
config.middleware.use Rack::SslEnforcer, :except_environments => 'development'

config.middleware.use Rack::SslEnforcer, :except_environments => /^[0-9a-f]+_local$/i

config.middleware.use Rack::SslEnforcer, :only_environments => ['production', /^QA/]
```

Note: The `:environments` constraint requires one the following environment variables to be set: `RACK_ENV`, `RAILS_ENV`, `ENV`.


### Force-redirection to non-SSL connection if constraint is not matched

Use the `:strict` option to force non-SSL connection for all requests not matching the constraints you set. Examples:
Expand Down
9 changes: 5 additions & 4 deletions lib/rack/ssl-enforcer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@ module Rack
class SslEnforcer

CONSTRAINTS_BY_TYPE = {
:hosts => [:only_hosts, :except_hosts],
:path => [:only, :except],
:methods => [:only_methods, :except_methods]
:hosts => [:only_hosts, :except_hosts],
:path => [:only, :except],
:methods => [:only_methods, :except_methods],
:environments => [:only_environments, :except_environments]
}

# Warning: If you set the option force_secure_cookies to false, make sure that your cookies
Expand Down Expand Up @@ -119,7 +120,7 @@ def enforce_ssl_for?(keys)
else
provided_keys.all? do |key|
rules = [@options[key]].flatten.compact
rules.send([:except_hosts, :except].include?(key) ? :all? : :any?) do |rule|
rules.send([:except_hosts, :except_environments, :except].include?(key) ? :all? : :any?) do |rule|
SslEnforcerConstraint.new(key, rule, @request).matches?
end
end
Expand Down
2 changes: 2 additions & 0 deletions lib/rack/ssl-enforcer/constraint.rb
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ def tested_string
@request.host
when /methods/
@request.request_method
when /environments/
ENV["RACK_ENV"] || ENV["RAILS_ENV"] || ENV["ENV"]
else
@request.path
end
Expand Down
100 changes: 100 additions & 0 deletions test/rack-ssl-enforcer_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -804,6 +804,106 @@ class TestRackSslEnforcer < Test::Unit::TestCase
end
end

context ':only_environments (String)' do
setup { mock_app :only_environments => 'production' }

should 'redirect to HTTPS for "production" environment' do
ENV["RACK_ENV"] = "production"
get 'http://www.example.org/'
assert_equal 301, last_response.status
assert_equal 'https://www.example.org/', last_response.location
end

should 'not redirect for "development" environment' do
ENV["RACK_ENV"] = "development"
get 'http://www.example.org/'
assert_equal 200, last_response.status
assert_equal 'Hello world!', last_response.body
end
end

context ':only_environments (Array + Regex)' do
setup { mock_app :only_environments => ['production', /QA\d+/] }

should 'redirect to HTTPS for "production" environment' do
ENV["RACK_ENV"] = "production"
get 'http://www.example.org/'
assert_equal 301, last_response.status
assert_equal 'https://www.example.org/', last_response.location
end

should 'redirect to HTTPS for "QA2" environment' do
ENV["RACK_ENV"] = "QA2"
get 'http://www.example.org/'
assert_equal 301, last_response.status
assert_equal 'https://www.example.org/', last_response.location
end

should 'redirect to HTTPS for "QA15" environment' do
ENV["RACK_ENV"] = "QA15"
get 'http://www.example.org/'
assert_equal 301, last_response.status
assert_equal 'https://www.example.org/', last_response.location
end

should 'not redirect for "development" environment' do
ENV["RACK_ENV"] = "development"
get 'http://www.example.org/'
assert_equal 200, last_response.status
assert_equal 'Hello world!', last_response.body
end
end

context ':except_environments (String)' do
setup { mock_app :except_environments => 'development' }

should 'redirect to HTTPS for "production" environment' do
ENV["RACK_ENV"] = "production"
get 'http://www.example.org/'
assert_equal 301, last_response.status
assert_equal 'https://www.example.org/', last_response.location
end

should 'not redirect for "development" environment' do
ENV["RACK_ENV"] = "development"
get 'http://www.example.org/'
assert_equal 200, last_response.status
assert_equal 'Hello world!', last_response.body
end
end

context ':except_environments (Array + Regex)' do
setup { mock_app :except_environments => ['development', /\w+_local/] }

should 'redirect to HTTPS for "production" environment' do
ENV["RACK_ENV"] = "production"
get 'http://www.example.org/'
assert_equal 301, last_response.status
assert_equal 'https://www.example.org/', last_response.location
end

should 'not redirect for "development" environment' do
ENV["RACK_ENV"] = "development"
get 'http://www.example.org/'
assert_equal 200, last_response.status
assert_equal 'Hello world!', last_response.body
end

should 'not redirect for "jebediah_local" environment' do
ENV["RACK_ENV"] = "jebediah_local"
get 'http://www.example.org/'
assert_equal 200, last_response.status
assert_equal 'Hello world!', last_response.body
end

should 'not redirect for "el_guapo_local" environment' do
ENV["RACK_ENV"] = "el_guapo_local"
get 'http://www.example.org/'
assert_equal 200, last_response.status
assert_equal 'Hello world!', last_response.body
end
end

context 'complex example' do
setup { mock_app :only => '/cart', :ignore => %r{/assets}, :strict => true }

Expand Down

0 comments on commit 025ac24

Please sign in to comment.