Skip to content

Commit 45fa663

Browse files
committed
add environment constraints
1 parent c4674cd commit 45fa663

File tree

4 files changed

+127
-7
lines changed

4 files changed

+127
-7
lines changed

README.md

+20-3
Original file line numberDiff line numberDiff line change
@@ -20,16 +20,16 @@ Add Rack::SslEnforcer to your `Gemfile`:
2020
In order for Rack::SslEnforcer to properly work it has to be at the top
2121
of the Rack Middleware.
2222

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

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

2929
Eg:
3030

3131
```ruby
32-
use Rack::SslEnforcer
32+
use Rack::SslEnforcer
3333
set :session_secret, 'asdfa2342923422f1adc05c837fa234230e3594b93824b00e930ab0fb94b'
3434

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

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

106+
107+
### Environment constraints
108+
109+
You can enforce SSL connections only for certain environments with `:only_environments` or prevent certain environments from being forced to SSL with `:except_environments`.
110+
Environment constraints may be a `String`, a `Regex` or an array of `String` or `Regex` (possibly mixed), as shown in the following examples:
111+
112+
```ruby
113+
config.middleware.use Rack::SslEnforcer, :except_environments => 'development'
114+
115+
config.middleware.use Rack::SslEnforcer, :except_environments => /^[0-9a-f]+_local$/i
116+
117+
config.middleware.use Rack::SslEnforcer, :only_environments => ['production', /^QA/]
118+
```
119+
120+
Note: The `:environments` constraint requires one the following environment variables to be set: `RACK_ENV`, `RAILS_ENV`, `ENV`.
121+
122+
106123
### Force-redirection to non-SSL connection if constraint is not matched
107124

108125
Use the `:strict` option to force non-SSL connection for all requests not matching the constraints you set. Examples:

lib/rack/ssl-enforcer.rb

+5-4
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,10 @@ module Rack
55
class SslEnforcer
66

77
CONSTRAINTS_BY_TYPE = {
8-
:hosts => [:only_hosts, :except_hosts],
9-
:path => [:only, :except],
10-
:methods => [:only_methods, :except_methods]
8+
:hosts => [:only_hosts, :except_hosts],
9+
:path => [:only, :except],
10+
:methods => [:only_methods, :except_methods],
11+
:environments => [:only_environments, :except_environments]
1112
}
1213

1314
# Warning: If you set the option force_secure_cookies to false, make sure that your cookies
@@ -117,7 +118,7 @@ def enforce_ssl_for?(keys)
117118
else
118119
provided_keys.all? do |key|
119120
rules = [@options[key]].flatten.compact
120-
rules.send([:except_hosts, :except].include?(key) ? :all? : :any?) do |rule|
121+
rules.send([:except_hosts, :except_environments, :except].include?(key) ? :all? : :any?) do |rule|
121122
SslEnforcerConstraint.new(key, rule, @request).matches?
122123
end
123124
end

lib/rack/ssl-enforcer/constraint.rb

+2
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ def tested_string
3131
@request.host
3232
when /methods/
3333
@request.request_method
34+
when /environments/
35+
ENV["RACK_ENV"] || ENV["RAILS_ENV"] || ENV["ENV"]
3436
else
3537
@request.path
3638
end

test/rack-ssl-enforcer_test.rb

+100
Original file line numberDiff line numberDiff line change
@@ -804,6 +804,106 @@ class TestRackSslEnforcer < Test::Unit::TestCase
804804
end
805805
end
806806

807+
context ':only_environments (String)' do
808+
setup { mock_app :only_environments => 'production' }
809+
810+
should 'redirect to HTTPS for "production" environment' do
811+
ENV["RACK_ENV"] = "production"
812+
get 'http://www.example.org/'
813+
assert_equal 301, last_response.status
814+
assert_equal 'https://www.example.org/', last_response.location
815+
end
816+
817+
should 'not redirect for "development" environment' do
818+
ENV["RACK_ENV"] = "development"
819+
get 'http://www.example.org/'
820+
assert_equal 200, last_response.status
821+
assert_equal 'Hello world!', last_response.body
822+
end
823+
end
824+
825+
context ':only_environments (Array + Regex)' do
826+
setup { mock_app :only_environments => ['production', /QA\d+/] }
827+
828+
should 'redirect to HTTPS for "production" environment' do
829+
ENV["RACK_ENV"] = "production"
830+
get 'http://www.example.org/'
831+
assert_equal 301, last_response.status
832+
assert_equal 'https://www.example.org/', last_response.location
833+
end
834+
835+
should 'redirect to HTTPS for "QA2" environment' do
836+
ENV["RACK_ENV"] = "QA2"
837+
get 'http://www.example.org/'
838+
assert_equal 301, last_response.status
839+
assert_equal 'https://www.example.org/', last_response.location
840+
end
841+
842+
should 'redirect to HTTPS for "QA15" environment' do
843+
ENV["RACK_ENV"] = "QA15"
844+
get 'http://www.example.org/'
845+
assert_equal 301, last_response.status
846+
assert_equal 'https://www.example.org/', last_response.location
847+
end
848+
849+
should 'not redirect for "development" environment' do
850+
ENV["RACK_ENV"] = "development"
851+
get 'http://www.example.org/'
852+
assert_equal 200, last_response.status
853+
assert_equal 'Hello world!', last_response.body
854+
end
855+
end
856+
857+
context ':except_environments (String)' do
858+
setup { mock_app :except_environments => 'development' }
859+
860+
should 'redirect to HTTPS for "production" environment' do
861+
ENV["RACK_ENV"] = "production"
862+
get 'http://www.example.org/'
863+
assert_equal 301, last_response.status
864+
assert_equal 'https://www.example.org/', last_response.location
865+
end
866+
867+
should 'not redirect for "development" environment' do
868+
ENV["RACK_ENV"] = "development"
869+
get 'http://www.example.org/'
870+
assert_equal 200, last_response.status
871+
assert_equal 'Hello world!', last_response.body
872+
end
873+
end
874+
875+
context ':except_environments (Array + Regex)' do
876+
setup { mock_app :except_environments => ['development', /\w+_local/] }
877+
878+
should 'redirect to HTTPS for "production" environment' do
879+
ENV["RACK_ENV"] = "production"
880+
get 'http://www.example.org/'
881+
assert_equal 301, last_response.status
882+
assert_equal 'https://www.example.org/', last_response.location
883+
end
884+
885+
should 'not redirect for "development" environment' do
886+
ENV["RACK_ENV"] = "development"
887+
get 'http://www.example.org/'
888+
assert_equal 200, last_response.status
889+
assert_equal 'Hello world!', last_response.body
890+
end
891+
892+
should 'not redirect for "jebediah_local" environment' do
893+
ENV["RACK_ENV"] = "jebediah_local"
894+
get 'http://www.example.org/'
895+
assert_equal 200, last_response.status
896+
assert_equal 'Hello world!', last_response.body
897+
end
898+
899+
should 'not redirect for "el_guapo_local" environment' do
900+
ENV["RACK_ENV"] = "el_guapo_local"
901+
get 'http://www.example.org/'
902+
assert_equal 200, last_response.status
903+
assert_equal 'Hello world!', last_response.body
904+
end
905+
end
906+
807907
context 'complex example' do
808908
setup { mock_app :only => '/cart', :ignore => %r{/assets}, :strict => true }
809909

0 commit comments

Comments
 (0)